This commit is contained in:
2026-04-09 19:28:41 +03:00
commit 9fa723bb4c
43 changed files with 2804 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
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 {}