presentercoach / apps /api /src /app /schemas.py
nathycodes's picture
Deploy Tech Presenter Coach
ec17af3
Raw History Blame Contribute Delete
8.74 kB
from datetime import date, datetime
from typing import Literal
from pydantic import BaseModel, ConfigDict, EmailStr, Field, model_validator
class UserCreate(BaseModel):
email: EmailStr
password: str = Field(min_length=6)
class UserLogin(BaseModel):
email: str
password: str
class UserOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
email: EmailStr
is_admin: bool = False
class Token(BaseModel):
access_token: str
refresh_token: str | None = None
token_type: str = "bearer"
expires_in: int | None = None
user: UserOut | None = None
class SetupStatus(BaseModel):
setup_required: bool
GoalMetric = Literal[
"wpm",
"filler_rate",
"overall_score",
"practice_days_per_week",
"session_duration_minutes",
"eye_contact_score",
"delivery_score",
]
GoalComparison = Literal["gte", "lte", "between"]
class UserGoalCreate(BaseModel):
metric_name: GoalMetric
comparison: GoalComparison
target_value: float = Field(ge=0)
target_upper: float | None = Field(default=None, ge=0)
is_active: bool = True
@model_validator(mode="after")
def validate_range(self):
if self.comparison == "between":
if self.target_upper is None or self.target_upper <= self.target_value:
raise ValueError("between goals require target_upper greater than target_value")
elif self.target_upper is not None:
raise ValueError("target_upper is only valid for between goals")
return self
class UserGoalUpdate(BaseModel):
comparison: GoalComparison | None = None
target_value: float | None = Field(default=None, ge=0)
target_upper: float | None = Field(default=None, ge=0)
is_active: bool | None = None
class UserGoalOut(BaseModel):
id: int
metric_name: GoalMetric
comparison: GoalComparison
target_value: float
target_upper: float | None
is_active: bool
current_value: float | None
achieved: bool
created_at: datetime
updated_at: datetime
class PresenterProfileUpdate(BaseModel):
display_name: str | None = Field(default=None, max_length=120)
primary_role: str | None = Field(default=None, max_length=120)
focus_track: Literal[
"tech_storytelling",
"keynote_clarity",
"camera_confidence",
"executive_pitch",
"tech_presenter",
"tech_news_creator",
"engineering_leader",
] | None = None
experience_level: Literal["beginner", "intermediate", "advanced"] | None = None
target_wpm_min: int | None = Field(default=None, ge=80, le=200)
target_wpm_max: int | None = Field(default=None, ge=90, le=220)
max_filler_rate: float | None = Field(default=None, ge=0, le=20)
min_eye_contact: float | None = Field(default=None, ge=0, le=1)
daily_minutes_goal: int | None = Field(default=None, ge=5, le=120)
weaknesses: list[str] | None = None
speaking_goal_statement: str | None = Field(default=None, max_length=500)
reset_start_date: bool = False
class PresenterProfileOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
display_name: str
primary_role: str
focus_track: str
experience_level: str
target_wpm_min: int
target_wpm_max: int
max_filler_rate: float
min_eye_contact: float
daily_minutes_goal: int
weaknesses: list[str] = Field(default_factory=list)
speaking_goal_statement: str | None = None
curriculum_start_date: date
class CurriculumCompleteRequest(BaseModel):
session_id: int | None = None
score: float | None = None
reflection_notes: str | None = Field(default=None, max_length=2000)
class StoryboardRequest(BaseModel):
headline: str = Field(min_length=3, max_length=300)
summary: str = Field(default="", max_length=4000)
source: str | None = Field(default=None, max_length=120)
url: str | None = Field(default=None, max_length=1000)
category: str | None = Field(default="AI & Systems", max_length=80)
framework: Literal["briefing_60s", "explainer_2m", "contrarian_take", "war_story"] = "briefing_60s"
personal_angle: str | None = Field(default=None, max_length=1000)
class WeeklyNoteUpdate(BaseModel):
week_start_date: date
wins_note: str | None = Field(default=None, max_length=2000)
focus_next_week: str | None = Field(default=None, max_length=2000)
class TranscriptUpdateRequest(BaseModel):
transcript_segments: list["TranscriptSegmentCreate"] = Field(default_factory=list)
class TranscriptionRequest(BaseModel):
backend: str | None = None
model: str | None = None
class TranscriptionModelDownloadRequest(BaseModel):
backend: str
model: str
class InferenceEngineCheckRequest(BaseModel):
engine_id: str
base_url: str | None = None
class InferenceEngineStatusOut(BaseModel):
id: str
label: str
base_url: str
live: bool
detail: str
available_models: list[str] = Field(default_factory=list)
class TranscriptionModelOut(BaseModel):
id: str
name: str
repo_id: str
description: str
languages: str
available: bool = False
local_path: str | None = None
class TranscriptionBackendOut(BaseModel):
id: str
label: str
supported: bool
live: bool
detail: str
models: list[TranscriptionModelOut] = Field(default_factory=list)
default_model: str | None = None
class TranscriptionSettingsOut(BaseModel):
default_backend: str
backends: list[TranscriptionBackendOut] = Field(default_factory=list)
text_inference_engines: list[InferenceEngineStatusOut] = Field(default_factory=list)
class TranscriptSegmentCreate(BaseModel):
start_ms: int
end_ms: int
text: str
class TranscriptSegmentOut(TranscriptSegmentCreate):
model_config = ConfigDict(from_attributes=True)
id: int
class DocumentBlockOut(BaseModel):
index: int
text: str
class SessionDocumentOut(BaseModel):
name: str
mime_type: str
blocks: list[DocumentBlockOut] = Field(default_factory=list)
class MetricsPointCreate(BaseModel):
t: int
wpm: float = 0
rms: float = 0
eye_contact: float = 0
smile: float = 0
yaw: float = 0
pitch: float = 0
roll: float = 0
filler_count: int = 0
pause_ms: int = 0
class ScoreCreate(BaseModel):
data: dict
class ScoreOut(ScoreCreate):
model_config = ConfigDict(from_attributes=True)
id: int
class SessionCreate(BaseModel):
title: str | None = None
mode: str | None = None
prompt: str | None = None
goal: str | None = None
curriculum_day: int | None = Field(default=None, ge=1, le=30)
exercise_id: str | None = None
story_framework: str | None = None
target_keywords: list[str] = Field(default_factory=list)
teleprompter_script: str | None = None
started_at: datetime | None = None
ended_at: datetime | None = None
duration_ms: int = 0
wpm_avg: float = 0
filler_count: int = 0
eye_contact_pct: float = 0
tags: list[str] = Field(default_factory=list)
transcript_segments: list[TranscriptSegmentCreate] = Field(default_factory=list)
metrics: list[MetricsPointCreate] = Field(default_factory=list)
score: dict | None = None
class SessionOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
title: str | None
mode: str | None
prompt: str | None
goal: str | None
curriculum_day: int | None = None
exercise_id: str | None = None
story_framework: str | None = None
target_keywords: list[str] = Field(default_factory=list)
teleprompter_script: str | None = None
started_at: datetime | None
ended_at: datetime | None
duration_ms: int
wpm_avg: float
filler_count: int
eye_contact_pct: float
video_path: str | None
transcription_status: str
created_at: datetime
tags: list[str] = Field(default_factory=list)
transcript_segments: list[TranscriptSegmentOut] = Field(default_factory=list)
metrics: list[MetricsPointCreate] = Field(default_factory=list)
score: dict | None = None
document: SessionDocumentOut | None = None
class SessionListItem(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
title: str | None
mode: str | None
curriculum_day: int | None = None
duration_ms: int
wpm_avg: float
filler_count: int
eye_contact_pct: float
created_at: datetime
transcription_status: str
video_available: bool = False
score: dict | None = None
class UploadResponse(BaseModel):
session_id: int
status: str
video_path: str | None = None
class ExportResponse(BaseModel):
zip_path: str