mitai-jinkendo/backend/routers/training_phases.py
Lars 12d516c881
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
refactor: split goals.py into 5 modular routers
Code Splitting Results:
- goals.py: 1339 → 655 lines (-684 lines, -51%)
- Created 4 new routers:
  * goal_types.py (426 lines) - Goal Type Definitions CRUD
  * goal_progress.py (155 lines) - Progress tracking
  * training_phases.py (107 lines) - Training phases
  * fitness_tests.py (94 lines) - Fitness tests

Benefits:
 Improved maintainability (smaller, focused files)
 Better context window efficiency for AI tools
 Clearer separation of concerns
 Easier testing and debugging

All routers registered in main.py.
Backward compatible - no API changes.
2026-03-28 06:31:31 +01:00

108 lines
3.5 KiB
Python

"""
Training Phases Router - Training Phase Detection & Management
Endpoints for managing training phases:
- List training phases
- Create manual training phases
- Update phase status (accept/reject auto-detected phases)
Part of v9h Goal System.
"""
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from typing import Optional
from datetime import date
from db import get_db, get_cursor, r2d
from auth import require_auth
router = APIRouter(prefix="/api/goals", tags=["training-phases"])
# ============================================================================
# Pydantic Models
# ============================================================================
class TrainingPhaseCreate(BaseModel):
"""Create training phase (manual or auto-detected)"""
phase_type: str # calorie_deficit, calorie_surplus, deload, maintenance, periodization
start_date: date
end_date: Optional[date] = None
notes: Optional[str] = None
# ============================================================================
# Endpoints
# ============================================================================
@router.get("/phases")
def list_training_phases(session: dict = Depends(require_auth)):
"""List training phases"""
pid = session['profile_id']
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("""
SELECT id, phase_type, detected_automatically, confidence_score,
status, start_date, end_date, duration_days,
detection_params, notes, created_at
FROM training_phases
WHERE profile_id = %s
ORDER BY start_date DESC
""", (pid,))
return [r2d(row) for row in cur.fetchall()]
@router.post("/phases")
def create_training_phase(data: TrainingPhaseCreate, session: dict = Depends(require_auth)):
"""Create training phase (manual)"""
pid = session['profile_id']
with get_db() as conn:
cur = get_cursor(conn)
duration = None
if data.end_date:
duration = (data.end_date - data.start_date).days
cur.execute("""
INSERT INTO training_phases (
profile_id, phase_type, detected_automatically,
status, start_date, end_date, duration_days, notes
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
RETURNING id
""", (
pid, data.phase_type, False,
'active', data.start_date, data.end_date, duration, data.notes
))
phase_id = cur.fetchone()['id']
return {"id": phase_id, "message": "Trainingsphase erstellt"}
@router.put("/phases/{phase_id}/status")
def update_phase_status(
phase_id: str,
status: str,
session: dict = Depends(require_auth)
):
"""Update training phase status (accept/reject auto-detected phases)"""
pid = session['profile_id']
valid_statuses = ['suggested', 'accepted', 'active', 'completed', 'rejected']
if status not in valid_statuses:
raise HTTPException(
status_code=400,
detail=f"Ungültiger Status. Erlaubt: {', '.join(valid_statuses)}"
)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"UPDATE training_phases SET status = %s WHERE id = %s AND profile_id = %s",
(status, phase_id, pid)
)
if cur.rowcount == 0:
raise HTTPException(status_code=404, detail="Trainingsphase nicht gefunden")
return {"message": "Status aktualisiert"}