150 lines
6.1 KiB
Python
150 lines
6.1 KiB
Python
"""
|
|
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
|