""" Routine Stats API - completion statistics and streaks """ import os from datetime import datetime, timedelta import flask import jwt import core.auth as auth import core.postgres as postgres def _get_user_uuid(token): try: payload = jwt.decode(token, os.getenv("JWT_SECRET"), algorithms=["HS256"]) return payload.get("sub") except (jwt.ExpiredSignatureError, jwt.InvalidTokenError): return None def _auth(request): header = request.headers.get("Authorization", "") if not header.startswith("Bearer "): return None token = header[7:] user_uuid = _get_user_uuid(token) if not user_uuid or not auth.verifyLoginToken(token, userUUID=user_uuid): return None return user_uuid def register(app): @app.route("/api/routines//stats", methods=["GET"]) def api_routineStats(routine_id): """Get completion stats for a routine. Query: ?days=30""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 routine = postgres.select_one("routines", {"id": routine_id, "user_uuid": user_uuid}) if not routine: return flask.jsonify({"error": "routine not found"}), 404 days = flask.request.args.get("days", 30, type=int) sessions = postgres.select( "routine_sessions", where={"routine_id": routine_id}, limit=days, ) 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 flask.jsonify({ "routine_id": routine_id, "routine_name": routine["name"], "period_days": days, "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, }), 200 @app.route("/api/routines/streaks", methods=["GET"]) def api_userStreaks(): """Get all streaks for the user.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 streaks = postgres.select("routine_streaks", where={"user_uuid": user_uuid}) routines = postgres.select("routines", where={"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 flask.jsonify(result), 200 @app.route("/api/routines//streak", methods=["GET"]) def api_routineStreak(routine_id): """Get streak for a specific routine.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 routine = postgres.select_one("routines", {"id": routine_id, "user_uuid": user_uuid}) if not routine: return flask.jsonify({"error": "routine not found"}), 404 streak = postgres.select_one("routine_streaks", {"routine_id": routine_id, "user_uuid": user_uuid}) if not streak: return flask.jsonify({ "routine_id": routine_id, "routine_name": routine["name"], "current_streak": 0, "longest_streak": 0, "last_completed_date": None, }), 200 return flask.jsonify({ "routine_id": routine_id, "routine_name": routine["name"], "current_streak": streak["current_streak"], "longest_streak": streak["longest_streak"], "last_completed_date": streak.get("last_completed_date"), }), 200 @app.route("/api/routines/weekly-summary", methods=["GET"]) def api_weeklySummary(): """Get weekly progress summary.""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 routines = postgres.select("routines", where={"user_uuid": user_uuid}) routine_ids = [r["id"] for r in routines] if not routine_ids: return flask.jsonify({ "total_completed": 0, "total_time_minutes": 0, "routines_started": 0, "routines": [], }), 200 week_ago = (datetime.now() - timedelta(days=7)).isoformat() sessions = postgres.select("routine_sessions", where={"user_uuid": user_uuid}) week_sessions = [s for s in sessions if s.get("created_at") and str(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 flask.jsonify({ "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, }), 200