Files
kmtnflower/backend/app/schemas.py
T

420 lines
11 KiB
Python

from datetime import date as _date
from typing import Optional
from pydantic import BaseModel, computed_field
# ── Shared ───────────────────────────────────────────────
DAY_NAMES = {
1: "Monday",
2: "Tuesday",
3: "Wednesday",
4: "Thursday",
5: "Friday",
6: "Saturday",
7: "Sunday",
}
# ── Event ────────────────────────────────────────────────
class EventCreate(BaseModel):
date: _date
title: str
description: str
location: str
icon: str
rsvp_url: Optional[str] = None
class EventUpdate(BaseModel):
date: Optional[_date] = None
title: Optional[str] = None
description: Optional[str] = None
location: Optional[str] = None
icon: Optional[str] = None
rsvp_url: Optional[str] = None
active: Optional[bool] = None
class EventResponse(BaseModel):
id: int
date: _date
title: str
description: str
location: str
icon: str
rsvp_url: Optional[str]
active: bool
model_config = {"from_attributes": True}
# ── StationConfig ─────────────────────────────────────────
class StationConfigResponse(BaseModel):
id: int
key: str
callsign: str
name_primary: str
name_secondary: str
tagline: str
frequency: str
founded_year: int
nonprofit_type: str
ein: str
logo_url: str
hero_background_url: str
hero_icon_url: str
hero_divider_url: str
address: str
phone: str
email: str
website: str
donation_url: str
stream_url: str
stream_metadata_url: str
play_store_icon_url: str
play_store_url: str
app_store_embed_html: str
# Page content
hero_subtitle: str
about_intro: str
about_donation_text: str
schedule_intro: str
events_intro: str
# Color theme
color_primary: str
color_primary_light: str
color_primary_dark: str
color_primary_muted: str
color_accent: str
color_accent_light: str
color_accent_dark: str
color_accent_muted: str
color_background: str
color_text: str
color_success: str
color_danger: str
color_info: str
color_white: str
color_light: str
color_medium: str
color_black: str
model_config = {"from_attributes": True}
class StationConfigUpdate(BaseModel):
callsign: Optional[str] = None
name_primary: Optional[str] = None
name_secondary: Optional[str] = None
tagline: Optional[str] = None
frequency: Optional[str] = None
founded_year: Optional[int] = None
nonprofit_type: Optional[str] = None
ein: Optional[str] = None
logo_url: Optional[str] = None
hero_background_url: Optional[str] = None
hero_icon_url: Optional[str] = None
hero_divider_url: Optional[str] = None
address: Optional[str] = None
phone: Optional[str] = None
email: Optional[str] = None
website: Optional[str] = None
donation_url: Optional[str] = None
stream_url: Optional[str] = None
stream_metadata_url: Optional[str] = None
play_store_icon_url: Optional[str] = None
play_store_url: Optional[str] = None
app_store_embed_html: Optional[str] = None
# Page content
hero_subtitle: Optional[str] = None
about_intro: Optional[str] = None
about_donation_text: Optional[str] = None
schedule_intro: Optional[str] = None
events_intro: Optional[str] = None
# Color theme
color_primary: Optional[str] = None
color_primary_light: Optional[str] = None
color_primary_dark: Optional[str] = None
color_primary_muted: Optional[str] = None
color_accent: Optional[str] = None
color_accent_light: Optional[str] = None
color_accent_dark: Optional[str] = None
color_accent_muted: Optional[str] = None
color_background: Optional[str] = None
color_text: Optional[str] = None
color_success: Optional[str] = None
color_danger: Optional[str] = None
color_info: Optional[str] = None
color_white: Optional[str] = None
color_light: Optional[str] = None
color_medium: Optional[str] = None
color_black: Optional[str] = None
# ── HistoryEntry ──────────────────────────────────────────
class HistoryEntryCreate(BaseModel):
year: int
title: str
body: str
display_order: int
class HistoryEntryUpdate(BaseModel):
year: Optional[int] = None
title: Optional[str] = None
body: Optional[str] = None
display_order: Optional[int] = None
active: Optional[bool] = None
class HistoryEntryResponse(BaseModel):
id: int
year: int
title: str
body: str
display_order: int
active: bool
model_config = {"from_attributes": True}
# ── TeamMember ─────────────────────────────────────────────
class TeamMemberCreate(BaseModel):
name: str
title: str
bio: Optional[str] = None
photo_url: Optional[str] = None
display_order: int
class TeamMemberUpdate(BaseModel):
name: Optional[str] = None
title: Optional[str] = None
bio: Optional[str] = None
photo_url: Optional[str] = None
display_order: Optional[int] = None
active: Optional[bool] = None
class TeamMemberResponse(BaseModel):
id: int
name: str
title: str
bio: Optional[str]
photo_url: Optional[str]
display_order: int
active: bool
model_config = {"from_attributes": True}
# ── CommunityHighlight ─────────────────────────────────────
class CommunityHighlightCreate(BaseModel):
icon: str
title: str
description: str
display_order: int
class CommunityHighlightUpdate(BaseModel):
icon: Optional[str] = None
title: Optional[str] = None
description: Optional[str] = None
display_order: Optional[int] = None
active: Optional[bool] = None
class CommunityHighlightResponse(BaseModel):
id: int
icon: str
title: str
description: str
display_order: int
active: bool
model_config = {"from_attributes": True}
# ── Underwriter ────────────────────────────────────────────
class UnderwriterCreate(BaseModel):
name: str
logo_url: Optional[str] = None
description: str
website_url: Optional[str] = None
display_order: int = 0
class UnderwriterUpdate(BaseModel):
name: Optional[str] = None
logo_url: Optional[str] = None
description: Optional[str] = None
website_url: Optional[str] = None
display_order: Optional[int] = None
active: Optional[bool] = None
class UnderwriterResponse(BaseModel):
id: int
name: str
logo_url: Optional[str]
description: str
website_url: Optional[str]
display_order: int
active: bool
model_config = {"from_attributes": True}
# ── ShowSchedule ──────────────────────────────────────────
class ShowScheduleResponse(BaseModel):
id: int
show_id: int
day_of_week: int
time: str
@computed_field
@property
def day_label(self) -> str:
return DAY_NAMES.get(self.day_of_week, "Unknown")
model_config = {"from_attributes": True}
class ShowScheduleCreate(BaseModel):
day_of_week: int
time: str
# ── Show ────────────────────────────────────────────────────
class ShowResponse(BaseModel):
id: int
title: str
description: str
host: str
genre: str
show_art_url: Optional[str]
hero_image_url: Optional[str]
display_order: int
active: bool
schedules: list[ShowScheduleResponse]
model_config = {"from_attributes": True}
class ShowCreate(BaseModel):
title: str
description: str
host: str
genre: str
show_art_url: Optional[str] = None
hero_image_url: Optional[str] = None
display_order: int = 0
schedules: list[ShowScheduleCreate]
class ShowUpdate(BaseModel):
title: Optional[str] = None
description: Optional[str] = None
host: Optional[str] = None
genre: Optional[str] = None
show_art_url: Optional[str] = None
hero_image_url: Optional[str] = None
display_order: Optional[int] = None
active: Optional[bool] = None
schedules: Optional[list[ShowScheduleCreate]] = None
# ── Stats ────────────────────────────────────────────────────────
class StatsSummaryResponse(BaseModel):
total_unique_visitors: int
class TimeSeriesPoint(BaseModel):
date: str
unique_visitors: int
class GeoStatResponse(BaseModel):
country: str
country_code: str
latitude: Optional[float] = None
longitude: Optional[float] = None
visitors: int
class ParseResultResponse(BaseModel):
files_processed: int
rows_inserted: int
# ── Mobile Build Orchestration ───────────────────────────
class MobileBuildDefaultsResponse(BaseModel):
tenant_key: str
app_name: str
tagline: str
support_email: str
website: str
stream_url: str
stream_metadata_url: str
class MobileBuildCreate(BaseModel):
platform_android: bool = False
platform_ios: bool = False
android_application_id: Optional[str] = None
ios_bundle_id: Optional[str] = None
version_name: str
build_number: str
release_profile: str = "release"
class MobileBuildArtifactResponse(BaseModel):
id: int
request_id: int
platform: str
artifact_type: str
file_name: str
sha256: str
size: int
created_at: str
download_url: str
class MobileBuildRequestResponse(BaseModel):
id: int
tenant_key: str
platform_android: bool
platform_ios: bool
android_application_id: Optional[str]
ios_bundle_id: Optional[str]
version_name: str
build_number: str
release_profile: str
status: str
preflight_passed: bool
preflight_report: str
build_log: str
error_message: Optional[str]
created_at: str
updated_at: str
started_at: Optional[str]
completed_at: Optional[str]
artifacts: list[MobileBuildArtifactResponse]
class MobileBuildPreflightResponse(BaseModel):
passed: bool
issues: list[str]