""" Victories API - compute noteworthy achievements from session history """ import os import uuid from datetime import datetime, timedelta import flask import jwt import core.auth as auth import core.postgres as postgres import core.tz as tz 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/victories", methods=["GET"]) def api_getVictories(): """Compute noteworthy achievements. Query: ?days=30""" user_uuid = _auth(flask.request) if not user_uuid: return flask.jsonify({"error": "unauthorized"}), 401 days = flask.request.args.get("days", 30, type=int) cutoff = tz.user_now() - timedelta(days=days) sessions = postgres.select("routine_sessions", {"user_uuid": user_uuid}) recent = [ s for s in sessions if s.get("created_at") and s["created_at"] >= cutoff ] completed = [s for s in recent if s.get("status") == "completed"] victories = [] # Comeback: completed after 2+ day gap if completed: sorted_completed = sorted(completed, key=lambda s: s["created_at"]) for i in range(1, len(sorted_completed)): prev = sorted_completed[i - 1]["created_at"] curr = sorted_completed[i]["created_at"] gap = (curr - prev).days if gap >= 2: victories.append({ "type": "comeback", "message": f"Came back after {gap} days — that takes real strength", "date": curr.isoformat() if hasattr(curr, 'isoformat') else str(curr), }) # Weekend completion for s in completed: created = s["created_at"] if hasattr(created, 'weekday') and created.weekday() >= 5: # Saturday=5, Sunday=6 victories.append({ "type": "weekend", "message": "Completed a routine on the weekend", "date": created.isoformat() if hasattr(created, 'isoformat') else str(created), }) break # Only show once # Variety: 3+ different routines in a week routine_ids_by_week = {} for s in completed: created = s["created_at"] if hasattr(created, 'isocalendar'): week_key = created.isocalendar()[:2] if week_key not in routine_ids_by_week: routine_ids_by_week[week_key] = set() routine_ids_by_week[week_key].add(s.get("routine_id")) for week_key, routine_ids in routine_ids_by_week.items(): if len(routine_ids) >= 3: victories.append({ "type": "variety", "message": f"Completed {len(routine_ids)} different routines in one week", "date": None, }) break # Full week consistency: completed every day for 7 consecutive days if completed: dates_set = set() for s in completed: created = s["created_at"] if hasattr(created, 'date'): dates_set.add(created.date()) sorted_dates = sorted(dates_set) max_streak = 1 current_streak = 1 for i in range(1, len(sorted_dates)): if (sorted_dates[i] - sorted_dates[i-1]).days == 1: current_streak += 1 max_streak = max(max_streak, current_streak) else: current_streak = 1 if max_streak >= 7: victories.append({ "type": "consistency", "message": f"Completed routines every day for {max_streak} days straight", "date": None, }) # Limit and deduplicate seen_types = set() unique_victories = [] for v in victories: if v["type"] not in seen_types: unique_victories.append(v) seen_types.add(v["type"]) return flask.jsonify(unique_victories[:10]), 200