423 lines
11 KiB
Python
423 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
|
|
|
|
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
|
|
|
|
|
|
# ── 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 MobileBuildUploadedFileResponse(BaseModel):
|
|
id: int
|
|
request_id: int
|
|
platform: str
|
|
file_kind: str
|
|
original_name: str
|
|
size: int
|
|
uploaded_at: str
|
|
|
|
|
|
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]
|
|
files: list[MobileBuildUploadedFileResponse]
|
|
artifacts: list[MobileBuildArtifactResponse]
|
|
|
|
|
|
class MobileBuildPreflightResponse(BaseModel):
|
|
passed: bool
|
|
issues: list[str]
|