Files
api-copp/route/stats_crud.py
2026-04-09 17:24:07 +05:00

135 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from fastapi import APIRouter, Query
from typing import Optional
from sqlalchemy import func
from bd import make_engine
from bd.tables.response import Response
from bd.tables.poll import Poll
from bd.tables.group import Group
from bd.tables.organization import Organization
router = APIRouter(tags=["stats"], prefix="/stats")
_MONTH_NAMES = ["Янв", "Фев", "Мар", "Апр", "Май", "Июн",
"Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"]
def get_session():
from sqlalchemy.orm import sessionmaker
return sessionmaker(bind=make_engine(), future=True)
@router.get("/summary")
def get_summary():
"""Общая сводка: кол-во ответов, тестов, активных тестов, организаций с активностью."""
Session = get_session()
with Session() as session:
total_responses = session.query(func.count(Response.id)).scalar() or 0
total_polls = session.query(func.count(Poll.id)).scalar() or 0
active_polls = session.query(func.count(Poll.id)).filter(Poll.is_active.is_(True)).scalar() or 0
orgs_active = session.query(func.count(func.distinct(Response.organization_id))).scalar() or 0
return {
"total_responses": total_responses,
"total_polls": total_polls,
"active_polls": active_polls,
"orgs_active": orgs_active,
}
@router.get("/years")
def get_years():
"""Список лет, в которых есть хотя бы один ответ."""
Session = get_session()
with Session() as session:
rows = session.query(
func.extract("year", Response.submitted_at).label("yr")
).distinct().order_by("yr").all()
return [int(r.yr) for r in rows if r.yr is not None]
@router.get("/responses")
def get_responses_grouped(
group_by: str = Query("poll", pattern="^(poll|organization|group|month)$"),
poll_id: Optional[str] = None,
org_id: Optional[str] = None,
group_id: Optional[str] = None,
year: Optional[int] = None,
):
"""
Возвращает агрегированные данные для графиков.
- group_by=poll → количество ответов по каждому тесту
- group_by=organization → количество ответов по каждой организации
- group_by=group → количество ответов по каждой группе
- group_by=month → количество ответов по месяцам (хронологически)
Все фильтры опциональны и комбинируются.
"""
Session = get_session()
with Session() as session:
if group_by == "poll":
q = session.query(Poll.title, func.count(Response.id).label("cnt"))
q = q.join(Response, Poll.id == Response.poll_id)
if org_id:
q = q.filter(Response.organization_id == org_id)
if group_id:
q = q.filter(Response.group_id == group_id)
if year:
q = q.filter(func.extract("year", Response.submitted_at) == year)
if poll_id:
q = q.filter(Response.poll_id == poll_id)
rows = q.group_by(Poll.title).order_by(func.count(Response.id).desc()).limit(20).all()
return [{"label": r[0], "count": r[1]} for r in rows]
if group_by == "organization":
q = session.query(Organization.name_organization, func.count(Response.id).label("cnt"))
q = q.outerjoin(Response, Organization.id == Response.organization_id)
if poll_id:
q = q.filter(Response.poll_id == poll_id)
if group_id:
q = q.filter(Response.group_id == group_id)
if year:
q = q.filter(func.extract("year", Response.submitted_at) == year)
rows = q.group_by(Organization.name_organization).order_by(
func.count(Response.id).desc()
).limit(20).all()
return [{"label": r[0], "count": r[1] or 0} for r in rows]
if group_by == "group":
q = session.query(Group.name_group, func.count(Response.id).label("cnt"))
q = q.outerjoin(Response, Group.id == Response.group_id)
if poll_id:
q = q.filter(Response.poll_id == poll_id)
if org_id:
q = q.filter(Response.organization_id == org_id)
if year:
q = q.filter(func.extract("year", Response.submitted_at) == year)
rows = q.group_by(Group.name_group).order_by(
func.count(Response.id).desc()
).limit(20).all()
return [{"label": r[0], "count": r[1] or 0} for r in rows]
if group_by == "month":
yr_col = func.extract("year", Response.submitted_at).label("yr")
mo_col = func.extract("month", Response.submitted_at).label("mo")
q = session.query(yr_col, mo_col, func.count(Response.id).label("cnt"))
if poll_id:
q = q.filter(Response.poll_id == poll_id)
if org_id:
q = q.filter(Response.organization_id == org_id)
if group_id:
q = q.filter(Response.group_id == group_id)
if year:
q = q.filter(func.extract("year", Response.submitted_at) == year)
rows = q.group_by("yr", "mo").order_by("yr", "mo").all()
return [
{
"label": f"{_MONTH_NAMES[int(r.mo) - 1]} {int(r.yr)}",
"count": r.cnt,
}
for r in rows
]
return []