First synculous 2 Big-Pickle pass.

This commit is contained in:
2026-02-12 23:07:48 -06:00
parent 25d05e0e86
commit 3e1134575b
26 changed files with 2729 additions and 59 deletions

View File

@@ -0,0 +1,149 @@
"""
Routine Sessions Extended API - pause, resume, abort, notes, duration tracking
"""
import os
import uuid
from datetime import datetime
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/sessions/<session_id>/pause", methods=["POST"])
def api_pauseSession(session_id):
"""Pause an active session."""
user_uuid = _auth(flask.request)
if not user_uuid:
return flask.jsonify({"error": "unauthorized"}), 401
session = postgres.select_one("routine_sessions", {"id": session_id, "user_uuid": user_uuid})
if not session:
return flask.jsonify({"error": "session not found"}), 404
if session.get("status") != "active":
return flask.jsonify({"error": "session not active"}), 400
result = postgres.update(
"routine_sessions",
{"status": "paused", "paused_at": datetime.now().isoformat()},
{"id": session_id}
)
return flask.jsonify({"status": "paused"}), 200
@app.route("/api/sessions/<session_id>/resume", methods=["POST"])
def api_resumeSession(session_id):
"""Resume a paused session."""
user_uuid = _auth(flask.request)
if not user_uuid:
return flask.jsonify({"error": "unauthorized"}), 401
session = postgres.select_one("routine_sessions", {"id": session_id, "user_uuid": user_uuid})
if not session:
return flask.jsonify({"error": "session not found"}), 404
if session.get("status") != "paused":
return flask.jsonify({"error": "session not paused"}), 400
result = postgres.update(
"routine_sessions",
{"status": "active", "paused_at": None},
{"id": session_id}
)
return flask.jsonify({"status": "active"}), 200
@app.route("/api/sessions/<session_id>/abort", methods=["POST"])
def api_abortSession(session_id):
"""Abort a session with reason. Body: {reason: string}"""
user_uuid = _auth(flask.request)
if not user_uuid:
return flask.jsonify({"error": "unauthorized"}), 401
session = postgres.select_one("routine_sessions", {"id": session_id, "user_uuid": user_uuid})
if not session:
return flask.jsonify({"error": "session not found"}), 404
data = flask.request.get_json() or {}
reason = data.get("reason", "Aborted by user")
result = postgres.update(
"routine_sessions",
{"status": "aborted", "abort_reason": reason, "completed_at": datetime.now().isoformat()},
{"id": session_id}
)
return flask.jsonify({"status": "aborted", "reason": reason}), 200
@app.route("/api/sessions/<session_id>/note", methods=["POST"])
def api_addSessionNote(session_id):
"""Add a note to the session. Body: {step_index: int, note: string}"""
user_uuid = _auth(flask.request)
if not user_uuid:
return flask.jsonify({"error": "unauthorized"}), 401
session = postgres.select_one("routine_sessions", {"id": session_id, "user_uuid": user_uuid})
if not session:
return flask.jsonify({"error": "session not found"}), 404
data = flask.request.get_json()
if not data or not data.get("note"):
return flask.jsonify({"error": "missing note"}), 400
note_entry = {
"id": str(uuid.uuid4()),
"session_id": session_id,
"step_index": data.get("step_index"),
"note": data["note"],
}
note = postgres.insert("routine_session_notes", note_entry)
return flask.jsonify(note), 201
@app.route("/api/sessions/<session_id>/duration", methods=["PUT"])
def api_setSessionDuration(session_id):
"""Record actual duration. Body: {actual_duration_minutes: int}"""
user_uuid = _auth(flask.request)
if not user_uuid:
return flask.jsonify({"error": "unauthorized"}), 401
session = postgres.select_one("routine_sessions", {"id": session_id, "user_uuid": user_uuid})
if not session:
return flask.jsonify({"error": "session not found"}), 404
data = flask.request.get_json()
if not data:
return flask.jsonify({"error": "missing body"}), 400
duration = data.get("actual_duration_minutes")
if duration is None:
return flask.jsonify({"error": "missing actual_duration_minutes"}), 400
result = postgres.update(
"routine_sessions",
{"actual_duration_minutes": duration},
{"id": session_id}
)
return flask.jsonify(result[0] if result else {}), 200
@app.route("/api/sessions/<session_id>", methods=["GET"])
def api_getSessionDetails(session_id):
"""Get session details with all notes."""
user_uuid = _auth(flask.request)
if not user_uuid:
return flask.jsonify({"error": "unauthorized"}), 401
session = postgres.select_one("routine_sessions", {"id": session_id, "user_uuid": user_uuid})
if not session:
return flask.jsonify({"error": "session not found"}), 404
notes = postgres.select("routine_session_notes", {"session_id": session_id}, order_by="created_at")
routine = postgres.select_one("routines", {"id": session["routine_id"]})
steps = postgres.select("routine_steps", {"routine_id": session["routine_id"]}, order_by="position")
return flask.jsonify({
"session": session,
"routine": routine,
"steps": steps,
"notes": notes,
}), 200