from sqlalchemy.orm import Session from sqlalchemy import func, and_, or_, text from typing import Optional, List, Dict, Any from datetime import datetime, timedelta from app.models.submission import Submission from app.repositories.base import BaseRepository class SubmissionRepository(BaseRepository): def __init__(self, db: Session): super().__init__(db, Submission) def filter_by_field_value(self, form_id: int, field_name: str, value: Any): """Фильтрация по значению поля в JSONB""" return self.db.query(Submission).filter( Submission.form_id == form_id, Submission.data[field_name].astext == str(value) ) def filter_by_multiple_fields(self, form_id: int, filters: Dict[str, Any]): """Фильтрация по нескольким полям""" query = self.db.query(Submission).filter(Submission.form_id == form_id) for field_name, value in filters.items(): # Используем JSONB оператор @> для точного совпадения query = query.filter( Submission.data[field_name].astext == str(value) ) return query def search_in_jsonb(self, form_id: int, search_text: str, fields: Optional[List[str]] = None): """Полнотекстовый поиск по JSONB полям""" if fields: # Поиск только в указанных полях conditions = [] for field in fields: conditions.append( Submission.data[field].astext.ilike(f'%{search_text}%') ) return self.db.query(Submission).filter( Submission.form_id == form_id, or_(*conditions) ) else: # Поиск по всем полям через преобразование JSONB в текст return self.db.query(Submission).filter( Submission.form_id == form_id, func.cast(Submission.data, String).ilike(f'%{search_text}%') ) def get_field_statistics(self, form_id: int, field_name: str): """Статистика по полю""" from sqlalchemy import case results = self.db.query( Submission.data[field_name].astext.label('value'), func.count(Submission.id).label('count') ).filter( Submission.form_id == form_id, Submission.data[field_name].isnot(None) ).group_by( Submission.data[field_name].astext ).all() return {row.value: row.count for row in results} def get_daily_submissions(self, form_id: int, days: int = 30): """Статистика по дням""" start_date = datetime.utcnow() - timedelta(days=days) results = self.db.query( func.date(Submission.submitted_at).label('date'), func.count(Submission.id).label('count') ).filter( Submission.form_id == form_id, Submission.submitted_at >= start_date ).group_by( func.date(Submission.submitted_at) ).order_by( 'date' ).all() return [{"date": r.date, "count": r.count} for r in results] def get_advanced_analytics(self, form_id: int): """Расширенная аналитика с использованием JSONB функций PostgreSQL""" # Количество уникальных значений для каждого поля query = text(""" SELECT COUNT(*) as total_submissions, COUNT(DISTINCT data->>'email') as unique_emails, AVG((data->>'age')::int) as average_age, jsonb_object_agg( data->>'country', COUNT(*) ) as country_distribution FROM submissions WHERE form_id = :form_id GROUP BY form_id """) result = self.db.execute(query, {"form_id": form_id}).first() return dict(result._mapping) if result else {}