first commit
This commit is contained in:
BIN
api/routes/__pycache__/example.cpython-312.pyc
Normal file
BIN
api/routes/__pycache__/example.cpython-312.pyc
Normal file
Binary file not shown.
56
api/routes/example.py
Normal file
56
api/routes/example.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
Example route module - Copy this pattern for your domain.
|
||||
|
||||
This module demonstrates:
|
||||
1. Registering routes with Flask app
|
||||
2. Using auth validation
|
||||
3. Making database calls via postgres module
|
||||
"""
|
||||
|
||||
import os
|
||||
import flask
|
||||
import jwt
|
||||
import core.auth as auth
|
||||
import core.postgres as postgres
|
||||
|
||||
|
||||
def _get_user_uuid(token):
|
||||
"""Decode JWT to extract user UUID. Returns None on failure."""
|
||||
try:
|
||||
payload = jwt.decode(token, os.getenv("JWT_SECRET"), algorithms=["HS256"])
|
||||
return payload.get("sub")
|
||||
except (jwt.ExpiredSignatureError, jwt.InvalidTokenError):
|
||||
return None
|
||||
|
||||
|
||||
def register(app):
|
||||
"""Register routes with the Flask app."""
|
||||
|
||||
@app.route("/api/example", methods=["GET"])
|
||||
def api_listExamples():
|
||||
header = flask.request.headers.get("Authorization", "")
|
||||
if not header.startswith("Bearer "):
|
||||
return flask.jsonify({"error": "missing token"}), 401
|
||||
token = header[7:]
|
||||
|
||||
user_uuid = _get_user_uuid(token)
|
||||
if not user_uuid or not auth.verifyLoginToken(token, userUUID=user_uuid):
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
|
||||
items = postgres.select("examples")
|
||||
return flask.jsonify(items), 200
|
||||
|
||||
@app.route("/api/example", methods=["POST"])
|
||||
def api_addExample():
|
||||
header = flask.request.headers.get("Authorization", "")
|
||||
if not header.startswith("Bearer "):
|
||||
return flask.jsonify({"error": "missing token"}), 401
|
||||
token = header[7:]
|
||||
|
||||
user_uuid = _get_user_uuid(token)
|
||||
if not user_uuid or not auth.verifyLoginToken(token, userUUID=user_uuid):
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
|
||||
data = flask.request.get_json()
|
||||
item = postgres.insert("examples", data)
|
||||
return flask.jsonify(item), 201
|
||||
160
api/routes/medications.py
Normal file
160
api/routes/medications.py
Normal file
@@ -0,0 +1,160 @@
|
||||
"""
|
||||
Medications API - medication scheduling, logging, and adherence tracking
|
||||
"""
|
||||
|
||||
import os
|
||||
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):
|
||||
"""Extract and verify token. Returns user_uuid or None."""
|
||||
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):
|
||||
|
||||
# ── Medications CRUD ──────────────────────────────────────────
|
||||
|
||||
@app.route("/api/medications", methods=["GET"])
|
||||
def api_listMedications():
|
||||
"""List all medications for the logged-in user."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/medications", methods=["POST"])
|
||||
def api_addMedication():
|
||||
"""Add a medication. Body: {name, dosage, unit, frequency, times: ["08:00","20:00"], notes?}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
@app.route("/api/medications/<med_id>", methods=["GET"])
|
||||
def api_getMedication(med_id):
|
||||
"""Get a single medication with its schedule."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/medications/<med_id>", methods=["PUT"])
|
||||
def api_updateMedication(med_id):
|
||||
"""Update medication details. Body: {name?, dosage?, unit?, frequency?, times?, notes?, active?}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
@app.route("/api/medications/<med_id>", methods=["DELETE"])
|
||||
def api_deleteMedication(med_id):
|
||||
"""Delete a medication and its logs."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
# ── Medication Logging (take / skip / snooze) ─────────────────
|
||||
|
||||
@app.route("/api/medications/<med_id>/take", methods=["POST"])
|
||||
def api_takeMedication(med_id):
|
||||
"""Log that a dose was taken. Body: {scheduled_time?, notes?}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json() or {}
|
||||
pass
|
||||
|
||||
@app.route("/api/medications/<med_id>/skip", methods=["POST"])
|
||||
def api_skipMedication(med_id):
|
||||
"""Log a skipped dose. Body: {scheduled_time?, reason?}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json() or {}
|
||||
pass
|
||||
|
||||
@app.route("/api/medications/<med_id>/snooze", methods=["POST"])
|
||||
def api_snoozeMedication(med_id):
|
||||
"""Snooze a reminder. Body: {minutes: 15}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
# ── Medication Log / History ──────────────────────────────────
|
||||
|
||||
@app.route("/api/medications/<med_id>/log", methods=["GET"])
|
||||
def api_getMedLog(med_id):
|
||||
"""Get dose log for a medication. Query: ?days=30"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/medications/today", methods=["GET"])
|
||||
def api_todaysMeds():
|
||||
"""Get today's medication schedule with taken/pending status."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
# ── Adherence Stats ───────────────────────────────────────────
|
||||
|
||||
@app.route("/api/medications/adherence", methods=["GET"])
|
||||
def api_adherenceStats():
|
||||
"""Get adherence stats across all meds. Query: ?days=30"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/medications/<med_id>/adherence", methods=["GET"])
|
||||
def api_medAdherence(med_id):
|
||||
"""Get adherence stats for a single medication. Query: ?days=30"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
# ── Refills ───────────────────────────────────────────────────
|
||||
|
||||
@app.route("/api/medications/<med_id>/refill", methods=["PUT"])
|
||||
def api_setRefill(med_id):
|
||||
"""Set refill info. Body: {quantity_remaining, refill_date?, pharmacy_notes?}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
@app.route("/api/medications/refills-due", methods=["GET"])
|
||||
def api_refillsDue():
|
||||
"""Get medications that need refills soon. Query: ?days_ahead=7"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
204
api/routes/routines.py
Normal file
204
api/routes/routines.py
Normal file
@@ -0,0 +1,204 @@
|
||||
"""
|
||||
Routines API - Brilli-style routine management
|
||||
|
||||
Routines have ordered steps. Users start sessions to walk through them.
|
||||
"""
|
||||
|
||||
import os
|
||||
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):
|
||||
"""Extract and verify token. Returns user_uuid or None."""
|
||||
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):
|
||||
|
||||
# ── Routines CRUD ─────────────────────────────────────────────
|
||||
|
||||
@app.route("/api/routines", methods=["GET"])
|
||||
def api_listRoutines():
|
||||
"""List all routines for the logged-in user."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/routines", methods=["POST"])
|
||||
def api_createRoutine():
|
||||
"""Create a new routine. Body: {name, description?, icon?}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
@app.route("/api/routines/<routine_id>", methods=["GET"])
|
||||
def api_getRoutine(routine_id):
|
||||
"""Get a routine with its steps."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/routines/<routine_id>", methods=["PUT"])
|
||||
def api_updateRoutine(routine_id):
|
||||
"""Update routine details. Body: {name?, description?, icon?}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
@app.route("/api/routines/<routine_id>", methods=["DELETE"])
|
||||
def api_deleteRoutine(routine_id):
|
||||
"""Delete a routine and all its steps/sessions."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
# ── Steps CRUD ────────────────────────────────────────────────
|
||||
|
||||
@app.route("/api/routines/<routine_id>/steps", methods=["GET"])
|
||||
def api_listSteps(routine_id):
|
||||
"""List steps for a routine, ordered by position."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/routines/<routine_id>/steps", methods=["POST"])
|
||||
def api_addStep(routine_id):
|
||||
"""Add a step to a routine. Body: {name, duration_minutes?, position?}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
@app.route("/api/routines/<routine_id>/steps/<step_id>", methods=["PUT"])
|
||||
def api_updateStep(routine_id, step_id):
|
||||
"""Update a step. Body: {name?, duration_minutes?, position?}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
@app.route("/api/routines/<routine_id>/steps/<step_id>", methods=["DELETE"])
|
||||
def api_deleteStep(routine_id, step_id):
|
||||
"""Delete a step from a routine."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/routines/<routine_id>/steps/reorder", methods=["PUT"])
|
||||
def api_reorderSteps(routine_id):
|
||||
"""Reorder steps. Body: {step_ids: [ordered list of step UUIDs]}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
# ── Routine Sessions (active run-through) ─────────────────────
|
||||
|
||||
@app.route("/api/routines/<routine_id>/start", methods=["POST"])
|
||||
def api_startRoutine(routine_id):
|
||||
"""Start a routine session. Returns the session with first step."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/sessions/active", methods=["GET"])
|
||||
def api_getActiveSession():
|
||||
"""Get the user's currently active routine session, if any."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/sessions/<session_id>/complete-step", methods=["POST"])
|
||||
def api_completeStep(session_id):
|
||||
"""Mark current step done, advance to next. Body: {step_id}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
@app.route("/api/sessions/<session_id>/skip-step", methods=["POST"])
|
||||
def api_skipStep(session_id):
|
||||
"""Skip current step, advance to next. Body: {step_id}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
@app.route("/api/sessions/<session_id>/cancel", methods=["POST"])
|
||||
def api_cancelSession(session_id):
|
||||
"""Cancel an active routine session."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
# ── Routine History / Stats ───────────────────────────────────
|
||||
|
||||
@app.route("/api/routines/<routine_id>/history", methods=["GET"])
|
||||
def api_routineHistory(routine_id):
|
||||
"""Get past sessions for a routine. Query: ?days=7"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
# ── Routine Scheduling ────────────────────────────────────────
|
||||
|
||||
@app.route("/api/routines/<routine_id>/schedule", methods=["PUT"])
|
||||
def api_setRoutineSchedule(routine_id):
|
||||
"""Set when this routine should run. Body: {days: ["mon","tue",...], time: "08:00", remind: true}"""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
data = flask.request.get_json()
|
||||
pass
|
||||
|
||||
@app.route("/api/routines/<routine_id>/schedule", methods=["GET"])
|
||||
def api_getRoutineSchedule(routine_id):
|
||||
"""Get the schedule for a routine."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
|
||||
@app.route("/api/routines/<routine_id>/schedule", methods=["DELETE"])
|
||||
def api_deleteRoutineSchedule(routine_id):
|
||||
"""Remove the schedule from a routine."""
|
||||
user_uuid = _auth(flask.request)
|
||||
if not user_uuid:
|
||||
return flask.jsonify({"error": "unauthorized"}), 401
|
||||
pass
|
||||
Reference in New Issue
Block a user