# ============================================== # Breakpilot Drive - Unit API Models # ============================================== # Pydantic models for the Unit API. # Extracted from unit_api.py for file-size compliance. from pydantic import BaseModel, Field from typing import List, Optional, Dict, Any from datetime import datetime class UnitDefinitionResponse(BaseModel): """Unit definition response""" unit_id: str template: str version: str locale: List[str] grade_band: List[str] duration_minutes: int difficulty: str definition: Dict[str, Any] class CreateSessionRequest(BaseModel): """Request to create a unit session""" unit_id: str student_id: str locale: str = "de-DE" difficulty: str = "base" class SessionResponse(BaseModel): """Response after creating a session""" session_id: str unit_definition_url: str session_token: str telemetry_endpoint: str expires_at: datetime class TelemetryEvent(BaseModel): """Single telemetry event""" ts: Optional[str] = None type: str = Field(..., alias="type") stop_id: Optional[str] = None metrics: Optional[Dict[str, Any]] = None class Config: populate_by_name = True class TelemetryPayload(BaseModel): """Batch telemetry payload""" session_id: str events: List[TelemetryEvent] class TelemetryResponse(BaseModel): """Response after receiving telemetry""" accepted: int class PostcheckAnswer(BaseModel): """Single postcheck answer""" question_id: str answer: str class CompleteSessionRequest(BaseModel): """Request to complete a session""" postcheck_answers: Optional[List[PostcheckAnswer]] = None class SessionSummaryResponse(BaseModel): """Response with session summary""" summary: Dict[str, Any] next_recommendations: Dict[str, Any] class UnitListItem(BaseModel): """Unit list item""" unit_id: str template: str difficulty: str duration_minutes: int locale: List[str] grade_band: List[str] class RecommendedUnit(BaseModel): """Recommended unit with reason""" unit_id: str template: str difficulty: str reason: str class CreateUnitRequest(BaseModel): """Request to create a new unit definition""" unit_id: str = Field(..., description="Unique unit identifier") template: str = Field(..., description="Template type: flight_path or station_loop") version: str = Field(default="1.0.0", description="Version string") locale: List[str] = Field(default=["de-DE"], description="Supported locales") grade_band: List[str] = Field(default=["5", "6", "7"], description="Target grade levels") duration_minutes: int = Field(default=8, ge=3, le=20, description="Expected duration") difficulty: str = Field(default="base", description="Difficulty level: base or advanced") subject: Optional[str] = Field(default=None, description="Subject area") topic: Optional[str] = Field(default=None, description="Topic within subject") learning_objectives: List[str] = Field(default=[], description="Learning objectives") stops: List[Dict[str, Any]] = Field(default=[], description="Unit stops/stations") precheck: Optional[Dict[str, Any]] = Field(default=None, description="Pre-check configuration") postcheck: Optional[Dict[str, Any]] = Field(default=None, description="Post-check configuration") teacher_controls: Optional[Dict[str, Any]] = Field(default=None, description="Teacher control settings") assets: Optional[Dict[str, Any]] = Field(default=None, description="Asset configuration") metadata: Optional[Dict[str, Any]] = Field(default=None, description="Additional metadata") status: str = Field(default="draft", description="Publication status: draft or published") class UpdateUnitRequest(BaseModel): """Request to update an existing unit definition""" version: Optional[str] = None locale: Optional[List[str]] = None grade_band: Optional[List[str]] = None duration_minutes: Optional[int] = Field(default=None, ge=3, le=20) difficulty: Optional[str] = None subject: Optional[str] = None topic: Optional[str] = None learning_objectives: Optional[List[str]] = None stops: Optional[List[Dict[str, Any]]] = None precheck: Optional[Dict[str, Any]] = None postcheck: Optional[Dict[str, Any]] = None teacher_controls: Optional[Dict[str, Any]] = None assets: Optional[Dict[str, Any]] = None metadata: Optional[Dict[str, Any]] = None status: Optional[str] = None class ValidationError(BaseModel): """Single validation error""" field: str message: str severity: str = "error" # error or warning class ValidationResult(BaseModel): """Result of unit validation""" valid: bool errors: List[ValidationError] = [] warnings: List[ValidationError] = []