New programming screen

This commit is contained in:
2026-06-21 21:55:55 +00:00
parent 96d0fef792
commit e627fe3637
24 changed files with 1297 additions and 122 deletions
+177 -1
View File
@@ -14,7 +14,7 @@ from datetime import date
from sqlalchemy import select
from app.database import async_session
from app.models import Program, Event, DonationTier, StationConfig
from app.models import Program, Show, ShowSchedule, Event, DonationTier, StationConfig
from app.models import HistoryEntry, TeamMember, CommunityHighlight
# ── Seed data ──────────────────────────────────────────────
@@ -32,6 +32,149 @@ PROGRAMS: list[dict] = [
{"day_of_week": 1, "day_label": "Monday", "time": "12:00 AM", "title": "Late Night Jazz", "host": "Mike Darrow", "genre": "Jazz"},
]
SHOWS: list[dict] = [
{
"title": "Morning Mountain Mist",
"description": "Begin your week with ambient soundscapes blended with real mountain field recordings. Birdsong, wind, and gentle melodies to ease you into the day.",
"host": "Diana Walsh",
"genre": "Ambient / Nature",
"show_art_url": None,
"hero_image_url": None,
"display_order": 1,
"schedules": [
{"day_of_week": 1, "time": "6:00 AM"},
{"day_of_week": 3, "time": "6:00 AM"},
{"day_of_week": 5, "time": "6:00 AM"},
],
},
{
"title": "Community Roundup",
"description": "Local news, town hall highlights, and conversations with the people who make this mountain community tick. Your weekly civic check-in.",
"host": "Tom Breen",
"genre": "News / Talk",
"show_art_url": None,
"hero_image_url": None,
"display_order": 2,
"schedules": [
{"day_of_week": 1, "time": "8:00 AM"},
{"day_of_week": 4, "time": "8:00 AM"},
],
},
{
"title": "Bluegrass Trails",
"description": "Flatpickin' banjo, booming bass, and foot-stomping energy. From traditional Appalachian tunes to modern bluegrass fusion.",
"host": "Sarah Lynn",
"genre": "Bluegrass",
"show_art_url": None,
"hero_image_url": None,
"display_order": 3,
"schedules": [
{"day_of_week": 1, "time": "10:00 AM"},
{"day_of_week": 2, "time": "10:00 AM"},
{"day_of_week": 5, "time": "7:00 PM"},
],
},
{
"title": "Lunchtime Jazz",
"description": "Smooth standards, bebop bursts, and everything in between — the perfect soundtrack for your midday break.",
"host": "Mike Darrow",
"genre": "Jazz",
"show_art_url": None,
"hero_image_url": None,
"display_order": 4,
"schedules": [
{"day_of_week": 1, "time": "12:00 PM"},
{"day_of_week": 1, "time": "12:00 AM"},
{"day_of_week": 3, "time": "12:00 PM"},
{"day_of_week": 5, "time": "12:00 PM"},
],
},
{
"title": "Folk Roots Hour",
"description": "Acoustic storytelling from the heart of the mountains. Singer-songwriters, traditional ballads, and the voices that keep our heritage alive.",
"host": "Nadia Cole",
"genre": "Folk",
"show_art_url": None,
"hero_image_url": None,
"display_order": 5,
"schedules": [
{"day_of_week": 2, "time": "2:00 PM"},
{"day_of_week": 4, "time": "2:00 PM"},
],
},
{
"title": "Afternoon Acoustics",
"description": "Stripped-down arrangements and raw vocals. Indie folk, solo artists, and the beauty of unamplified sound.",
"host": "Jen Reeves",
"genre": "Acoustic",
"show_art_url": None,
"hero_image_url": None,
"display_order": 6,
"schedules": [
{"day_of_week": 1, "time": "2:00 PM"},
{"day_of_week": 3, "time": "2:00 PM"},
{"day_of_week": 6, "time": "1:00 PM"},
],
},
{
"title": "Evening Echoes",
"description": "Indie, alternative, and underground tracks from local bands and global artists. Where the next big sound finds its first home.",
"host": "Carlos Mendez",
"genre": "Indie / Alternative",
"show_art_url": None,
"hero_image_url": None,
"display_order": 7,
"schedules": [
{"day_of_week": 1, "time": "6:00 PM"},
{"day_of_week": 2, "time": "6:00 PM"},
{"day_of_week": 4, "time": "6:00 PM"},
{"day_of_week": 6, "time": "7:00 PM"},
],
},
{
"title": "Classical Mountains",
"description": "Symphonies, chamber music, and solo piano performed by the world's greatest musicians. Elevate your evening.",
"host": "Elena Cross",
"genre": "Classical",
"show_art_url": None,
"hero_image_url": None,
"display_order": 8,
"schedules": [
{"day_of_week": 1, "time": "8:00 PM"},
{"day_of_week": 3, "time": "8:00 PM"},
{"day_of_week": 5, "time": "8:00 PM"},
],
},
{
"title": "Night Owl Session",
"description": "Electronic beats, synth waves, and deep house to carry you through the late hours. The mountains never sleep.",
"host": "DJ Kofi",
"genre": "Electronic",
"show_art_url": None,
"hero_image_url": None,
"display_order": 9,
"schedules": [
{"day_of_week": 1, "time": "10:00 PM"},
{"day_of_week": 5, "time": "10:00 PM"},
{"day_of_week": 6, "time": "10:00 PM"},
{"day_of_week": 7, "time": "9:00 PM"},
],
},
{
"title": "Weekend Sunrise",
"description": "A gentle mix of acoustic covers, nature sounds, and listener requests to start your weekend on the right note.",
"host": "Diana Walsh",
"genre": "Acoustic / Ambient",
"show_art_url": None,
"hero_image_url": None,
"display_order": 10,
"schedules": [
{"day_of_week": 6, "time": "7:00 AM"},
{"day_of_week": 7, "time": "7:00 AM"},
],
},
]
EVENTS: list[dict] = [
{
"date": date(2026, 7, 12),
@@ -289,6 +432,29 @@ async def _upsert_team_member(session, data: dict) -> None:
session.add(TeamMember(**data, active=True))
async def _upsert_show(session, data: dict) -> None:
"""Get-or-create a show matched on title, with nested schedule slots."""
schedules = data.pop("schedules", [])
existing = await session.execute(
select(Show).where(Show.title == data["title"])
)
show = existing.scalar_one_or_none()
if show:
for key, value in data.items():
setattr(show, key, value)
# Replace schedule slots
for sched in show.schedules:
session.delete(sched)
for slot in schedules:
session.add(ShowSchedule(show_id=show.id, **slot))
else:
show_data = {**data, "active": True}
show = Show(**show_data)
for slot in schedules:
show.schedules.append(ShowSchedule(**slot))
session.add(show)
async def _upsert_community_highlight(session, data: dict) -> None:
"""Get-or-create a community highlight matched on (icon, title)."""
existing = await session.execute(
@@ -344,10 +510,20 @@ async def seed() -> None:
await session.commit()
print(f" ✓ Seeded {len(COMMUNITY_HIGHLIGHTS)} community highlights")
# Make a copy since _upsert_show mutates the dict (pops 'schedules')
import copy
for data in copy.deepcopy(SHOWS):
await _upsert_show(session, data)
await session.commit()
print(f" ✓ Seeded {len(SHOWS)} shows")
async def truncate_all() -> None:
"""Remove all seed data from the database."""
async with async_session() as session:
# Delete child tables first to respect foreign keys
await session.execute(ShowSchedule.__table__.delete())
await session.execute(Show.__table__.delete())
await session.execute(Program.__table__.delete())
await session.execute(Event.__table__.delete())
await session.execute(DonationTier.__table__.delete())