first
This commit is contained in:
135
app/services/analytics_service.py
Normal file
135
app/services/analytics_service.py
Normal file
@@ -0,0 +1,135 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Dict, Any, List
|
||||
from app.repositories.submission_repository import SubmissionRepository
|
||||
from app.repositories.form_repository import FormRepository
|
||||
|
||||
|
||||
class AnalyticsService:
|
||||
def __init__(self, db: Session):
|
||||
self.db = db
|
||||
self.submission_repo = SubmissionRepository(db)
|
||||
self.form_repo = FormRepository(db)
|
||||
|
||||
def get_form_report(self, form_id: int) -> Dict[str, Any]:
|
||||
"""Полный отчет по форме"""
|
||||
form = self.form_repo.get_by_id(form_id)
|
||||
if not form:
|
||||
raise ValueError("Form not found")
|
||||
|
||||
# Базовая статистика
|
||||
total_submissions = self.submission_repo.count(form_id=form_id)
|
||||
|
||||
# Аналитика по каждому полю
|
||||
fields_analytics = {}
|
||||
for form_field in form.fields:
|
||||
field = form_field.field
|
||||
stats = self.submission_repo.get_field_statistics(form_id, field.name)
|
||||
|
||||
fields_analytics[field.name] = {
|
||||
"label": field.label,
|
||||
"type": field.field_type,
|
||||
"completion_rate": self._calculate_completion_rate(form_id, field.name),
|
||||
"unique_values": len(stats),
|
||||
"distribution": stats,
|
||||
"null_count": self._get_null_count(form_id, field.name)
|
||||
}
|
||||
|
||||
# Временная аналитика
|
||||
daily_stats = self.submission_repo.get_daily_submissions(form_id)
|
||||
|
||||
return {
|
||||
"form_name": form.name,
|
||||
"total_submissions": total_submissions,
|
||||
"fields_analytics": fields_analytics,
|
||||
"daily_statistics": daily_stats,
|
||||
"peak_hours": self._get_peak_hours(form_id),
|
||||
"submission_trend": self._calculate_trend(daily_stats)
|
||||
}
|
||||
|
||||
def _calculate_completion_rate(self, form_id: int, field_name: str) -> float:
|
||||
"""Процент заполнения поля"""
|
||||
total = self.submission_repo.count(form_id=form_id)
|
||||
if total == 0:
|
||||
return 0.0
|
||||
|
||||
filled = self.submission_repo.count(
|
||||
form_id=form_id,
|
||||
filters={f"data->>'{field_name}'": "is not null"}
|
||||
)
|
||||
return (filled / total) * 100
|
||||
|
||||
def _get_null_count(self, form_id: int, field_name: str) -> int:
|
||||
"""Количество пустых значений в поле"""
|
||||
return self.submission_repo.count(
|
||||
form_id=form_id,
|
||||
filters={f"data->>'{field_name}'": "is null"}
|
||||
)# app/services/analytics_service.py (базовая версия)
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func
|
||||
from datetime import datetime, timedelta
|
||||
from app.models.submission import Submission
|
||||
from app.models.form import Form
|
||||
|
||||
class AnalyticsService:
|
||||
def __init__(self, db: Session):
|
||||
self.db = db
|
||||
|
||||
def get_form_report(self, form_id: int, days: int = 30):
|
||||
form = self.db.query(Form).filter(Form.id == form_id).first()
|
||||
if not form:
|
||||
raise ValueError("Form not found")
|
||||
|
||||
start_date = datetime.utcnow() - timedelta(days=days)
|
||||
submissions = self.db.query(Submission).filter(
|
||||
Submission.form_id == form_id,
|
||||
Submission.submitted_at >= start_date
|
||||
).all()
|
||||
|
||||
return {
|
||||
"form_id": form_id,
|
||||
"form_name": form.name,
|
||||
"total_submissions": len(submissions),
|
||||
"unique_submitters": len(set(s.submitted_by for s in submissions if s.submitted_by)),
|
||||
"avg_completion_time": None,
|
||||
"completion_rate": 100.0,
|
||||
"fields_stats": [],
|
||||
"daily_stats": [],
|
||||
"peak_hours": {},
|
||||
"trend": "stable",
|
||||
"last_submission_at": submissions[-1].submitted_at if submissions else None,
|
||||
"first_submission_at": submissions[0].submitted_at if submissions else None
|
||||
}
|
||||
|
||||
def get_field_statistics(self, form_id: int, field_name: str):
|
||||
return None
|
||||
|
||||
def get_global_overview(self, days: int):
|
||||
return {"total_forms": 0, "total_submissions": 0, "active_forms": 0}
|
||||
|
||||
def _get_peak_hours(self, form_id: int) -> Dict[str, int]:
|
||||
"""Часы пик заполнения"""
|
||||
from sqlalchemy import func
|
||||
|
||||
results = self.db.query(
|
||||
func.extract('hour', Submission.submitted_at).label('hour'),
|
||||
func.count(Submission.id).label('count')
|
||||
).filter(
|
||||
Submission.form_id == form_id
|
||||
).group_by('hour').all()
|
||||
|
||||
return {int(r.hour): r.count for r in results}
|
||||
|
||||
def _calculate_trend(self, daily_stats: List[Dict]) -> str:
|
||||
"""Расчет тренда (рост/падение/стабильно)"""
|
||||
if len(daily_stats) < 2:
|
||||
return "insufficient_data"
|
||||
|
||||
recent_avg = sum(d['count'] for d in daily_stats[-7:]) / 7
|
||||
previous_avg = sum(d['count'] for d in daily_stats[-14:-7]) / 7
|
||||
|
||||
if recent_avg > previous_avg * 1.1:
|
||||
return "increasing"
|
||||
elif recent_avg < previous_avg * 0.9:
|
||||
return "decreasing"
|
||||
else:
|
||||
return "stable"
|
||||
Reference in New Issue
Block a user