first
This commit is contained in:
105
app/repositories/submission_repository.py
Normal file
105
app/repositories/submission_repository.py
Normal 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 {}
|
||||
Reference in New Issue
Block a user