""" core/stats.py - Statistics calculations for routines This module contains functions for calculating routine statistics, completion rates, streaks, and weekly summaries. """ from datetime import datetime, timedelta, date import core.postgres as postgres def get_routine_stats(routine_id, user_uuid, days=30): """ Get completion statistics for a routine over a period. Returns dict with completion_rate, avg_duration, total_time, etc. """ sessions = postgres.select( "routine_sessions", {"routine_id": routine_id, "user_uuid": user_uuid}, limit=days * 3, ) completed = sum(1 for s in sessions if s.get("status") == "completed") aborted = sum(1 for s in sessions if s.get("status") == "aborted") total_duration = sum( s.get("actual_duration_minutes", 0) or 0 for s in sessions if s.get("actual_duration_minutes") ) avg_duration = total_duration / completed if completed > 0 else 0 completion_rate = (completed / len(sessions) * 100) if sessions else 0 return { "total_sessions": len(sessions), "completed": completed, "aborted": aborted, "completion_rate_percent": round(completion_rate, 1), "avg_duration_minutes": round(avg_duration, 1), "total_time_minutes": total_duration, } def get_user_streaks(user_uuid): """ Get all streaks for a user across all routines. Returns list of streak objects with routine names. """ streaks = postgres.select("routine_streaks", {"user_uuid": user_uuid}) routines = postgres.select("routines", {"user_uuid": user_uuid}) routine_map = {r["id"]: r["name"] for r in routines} result = [] for streak in streaks: result.append({ "routine_id": streak["routine_id"], "routine_name": routine_map.get(streak["routine_id"], "Unknown"), "current_streak": streak["current_streak"], "longest_streak": streak["longest_streak"], "last_completed_date": streak.get("last_completed_date"), }) return result def get_weekly_summary(user_uuid): """ Get weekly progress summary for a user. Returns total completed, total time, routines started, per-routine breakdown. """ routines = postgres.select("routines", {"user_uuid": user_uuid}) if not routines: return { "total_completed": 0, "total_time_minutes": 0, "routines_started": 0, "routines": [], } week_ago = datetime.now() - timedelta(days=7) sessions = postgres.select("routine_sessions", {"user_uuid": user_uuid}) week_sessions = [ s for s in sessions if s.get("created_at") and s["created_at"] >= week_ago ] completed = [s for s in week_sessions if s.get("status") == "completed"] total_time = sum( s.get("actual_duration_minutes", 0) or 0 for s in completed if s.get("actual_duration_minutes") ) routine_summaries = [] for routine in routines: r_sessions = [s for s in week_sessions if s.get("routine_id") == routine["id"]] r_completed = sum(1 for s in r_sessions if s.get("status") == "completed") routine_summaries.append({ "routine_id": routine["id"], "name": routine["name"], "completed_this_week": r_completed, }) return { "total_completed": len(completed), "total_time_minutes": total_time, "routines_started": len(set(s.get("routine_id") for s in week_sessions)), "routines": routine_summaries, } def calculate_completion_rate(sessions, completed_only=True): """Calculate completion rate from a list of sessions.""" if not sessions: return 0.0 if completed_only: completed = sum(1 for s in sessions if s.get("status") == "completed") return (completed / len(sessions)) * 100 return 0.0 def get_monthly_summary(user_uuid, year=None, month=None): """ Get monthly progress summary. Defaults to current month if year/month not specified. """ if year is None or month is None: now = datetime.now() year = now.year month = now.month start_date = datetime(year, month, 1) if month == 12: end_date = datetime(year + 1, 1, 1) else: end_date = datetime(year, month + 1, 1) sessions = postgres.select("routine_sessions", {"user_uuid": user_uuid}) month_sessions = [ s for s in sessions if s.get("created_at") and start_date <= s["created_at"] < end_date ] completed = [s for s in month_sessions if s.get("status") == "completed"] total_time = sum( s.get("actual_duration_minutes", 0) or 0 for s in completed if s.get("actual_duration_minutes") ) return { "year": year, "month": month, "total_sessions": len(month_sessions), "completed": len(completed), "total_time_minutes": total_time, }