Initial commit: Happy Up monorepo through Sprint 5.
Document-driven MVP with FastAPI backend, Vue H5, WeChat mini shell, product demo, and Docker dev stack. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.db.models import AnalysisTask, Child, Report, TrainingPlan, TrainingRecord, Video
|
||||
|
||||
|
||||
def _tenant_child_ids_subquery(tenant_id: int):
|
||||
return select(Child.id).where(Child.tenant_id == tenant_id).scalar_subquery()
|
||||
|
||||
|
||||
def get_dashboard_metrics(db: Session, tenant_id: int | None = None) -> dict:
|
||||
since = datetime.now(timezone.utc) - timedelta(days=30)
|
||||
|
||||
child_q = select(func.count()).select_from(Child).where(Child.status == "active")
|
||||
video_q = select(func.count()).select_from(Video)
|
||||
report_q = select(func.count()).select_from(Report).where(Report.status == "published")
|
||||
plan_q = select(func.count()).select_from(TrainingPlan).where(TrainingPlan.status == "active")
|
||||
new_child_q = select(func.count()).select_from(Child).where(Child.created_at >= since)
|
||||
succeeded_q = select(func.count()).select_from(AnalysisTask).where(
|
||||
AnalysisTask.status == "SUCCEEDED"
|
||||
)
|
||||
reassessment_q = select(func.count()).select_from(AnalysisTask).where(
|
||||
AnalysisTask.task_type == "reassessment", AnalysisTask.status == "SUCCEEDED"
|
||||
)
|
||||
pending_review_q = select(func.count()).select_from(Report).where(Report.reviewed_by.is_(None))
|
||||
pending_training_q = select(func.count()).select_from(TrainingRecord).where(
|
||||
TrainingRecord.score.is_not(None)
|
||||
)
|
||||
|
||||
if tenant_id:
|
||||
tenant_children = _tenant_child_ids_subquery(tenant_id)
|
||||
child_q = child_q.where(Child.tenant_id == tenant_id)
|
||||
new_child_q = new_child_q.where(Child.tenant_id == tenant_id)
|
||||
video_q = video_q.where(Video.child_id.in_(tenant_children))
|
||||
report_q = report_q.where(Report.child_id.in_(tenant_children))
|
||||
plan_q = plan_q.where(TrainingPlan.child_id.in_(tenant_children))
|
||||
succeeded_q = succeeded_q.where(AnalysisTask.child_id.in_(tenant_children))
|
||||
reassessment_q = reassessment_q.where(AnalysisTask.child_id.in_(tenant_children))
|
||||
pending_review_q = pending_review_q.where(Report.child_id.in_(tenant_children))
|
||||
pending_training_q = pending_training_q.where(TrainingRecord.child_id.in_(tenant_children))
|
||||
|
||||
new_children = db.scalar(new_child_q) or 0
|
||||
uploaded_videos = db.scalar(video_q) or 0
|
||||
completed_reports = db.scalar(report_q) or 0
|
||||
active_plans = db.scalar(plan_q) or 0
|
||||
succeeded_tasks = db.scalar(succeeded_q) or 0
|
||||
reassessment_done = db.scalar(reassessment_q) or 0
|
||||
|
||||
conversion_rate = round(completed_reports / max(succeeded_tasks, 1), 2)
|
||||
reassessment_rate = round(reassessment_done / max(active_plans, 1), 2)
|
||||
|
||||
pending_review = db.scalar(pending_review_q) or 0
|
||||
pending_training = db.scalar(pending_training_q) or 0
|
||||
|
||||
return {
|
||||
"newChildren": new_children,
|
||||
"uploadedVideos": uploaded_videos,
|
||||
"completedReports": completed_reports,
|
||||
"activePlans": active_plans,
|
||||
"conversionRate": min(conversion_rate, 1.0),
|
||||
"reassessmentCompletionRate": min(reassessment_rate, 1.0),
|
||||
"pendingReviewReports": pending_review,
|
||||
"pendingTrainingReview": pending_training,
|
||||
}
|
||||
Reference in New Issue
Block a user