""" Seed script: populates the database with station data. Idempotent — safe to run multiple times. Uses get-or-create (upsert) for each entity. Usage: KMTN_DATABASE_URL=... python seed.py (run from the backend/ directory) """ import asyncio from datetime import date from sqlalchemy import select from app.database import async_session from app.models import Program, Event, DonationTier # ── Seed data ────────────────────────────────────────────── PROGRAMS: list[dict] = [ {"day_of_week": 1, "day_label": "Monday", "time": "6:00 AM", "title": "Morning Mountain Mist", "host": "Diana Walsh", "genre": "Ambient / Nature"}, {"day_of_week": 1, "day_label": "Monday", "time": "8:00 AM", "title": "Community Roundup", "host": "Tom Breen", "genre": "News / Talk"}, {"day_of_week": 1, "day_label": "Monday", "time": "10:00 AM", "title": "Bluegrass Trails", "host": "Sarah Lynn", "genre": "Bluegrass"}, {"day_of_week": 1, "day_label": "Monday", "time": "12:00 PM", "title": "Lunchtime Jazz", "host": "Mike Darrow", "genre": "Jazz"}, {"day_of_week": 1, "day_label": "Monday", "time": "2:00 PM", "title": "Folk Roots Hour", "host": "Nadia Cole", "genre": "Folk"}, {"day_of_week": 1, "day_label": "Monday", "time": "4:00 PM", "title": "Afternoon Acoustics", "host": "Jen Reeves", "genre": "Acoustic"}, {"day_of_week": 1, "day_label": "Monday", "time": "6:00 PM", "title": "Evening Echoes", "host": "Carlos Mendez", "genre": "Indie / Alternative"}, {"day_of_week": 1, "day_label": "Monday", "time": "8:00 PM", "title": "Classical Mountains", "host": "Elena Cross", "genre": "Classical"}, {"day_of_week": 1, "day_label": "Monday", "time": "10:00 PM", "title": "Night Owl Session", "host": "DJ Kofi", "genre": "Electronic"}, {"day_of_week": 1, "day_label": "Monday", "time": "12:00 AM", "title": "Late Night Jazz", "host": "Mike Darrow", "genre": "Jazz"}, ] EVENTS: list[dict] = [ { "date": date(2026, 7, 12), "title": "Summer Sounds Festival", "description": "A full day of live local music, community booths, and station meet-and-greet at the foothills park. Free admission — donations welcome.", "location": "Foothills Community Park", "icon": "🎵", }, { "date": date(2026, 8, 5), "title": "Annual Fundraiser Gala", "description": "Our biggest fundraiser of the year! Enjoy dinner, silent auction, and performances from past guest artists.", "location": "Mountain View Ballroom", "icon": "🎙️", }, { "date": date(2026, 9, 18), "title": "Volunteer Open House", "description": "Tour the studio, meet the team, and learn how you can help keep our free stream alive. New volunteers always welcome!", "location": "KMTN Studio — 42 Pine St", "icon": "🤝", }, { "date": date(2026, 10, 30), "title": "Harvest Moon Broadcast", "description": "Special live broadcast from the harvest moon gathering. Featuring folk, bluegrass, and indigenous music.", "location": "Ridgefield Amphitheater", "icon": "🌙", }, ] TIERS: list[dict] = [ { "name": "Seed", "amount": 5.00, "icon": "🌱", "benefits": ["Digital thank-you card", "Name listed on our website", "Quarterly newsletter"], "display_order": 1, }, { "name": "Stream", "amount": 15.00, "icon": "🎵", "benefits": ["All Seed benefits", "KMountain Flower pin", "Priority show-request line", "Annual station update call"], "display_order": 2, }, { "name": "Ridge", "amount": 30.00, "icon": "🏔️", "benefits": ["All Stream benefits", "Official member patch", "Exclusive show invitations", "Annual station tour"], "display_order": 3, }, { "name": "Summit", "amount": 60.00, "icon": "⭐", "benefits": ["All Ridge benefits", "Featured on the Summit Wall", "Name on-air during anniversary special", "Lifetime patron recognition"], "display_order": 4, }, ] # ── Helpers ──────────────────────────────────────────────── async def _upsert_program(session, data: dict) -> None: """Get-or-create a program matched on (day_of_week, time).""" existing = await session.execute( select(Program).where( Program.day_of_week == data["day_of_week"], Program.time == data["time"], ) ) program = existing.scalar_one_or_none() if program: for key, value in data.items(): setattr(program, key, value) else: session.add(Program(**data)) async def _upsert_event(session, data: dict) -> None: """Get-or-create an event matched on (date, title).""" existing = await session.execute( select(Event).where( Event.date == data["date"], Event.title == data["title"], ) ) event = existing.scalar_one_or_none() if event: for key, value in data.items(): setattr(event, key, value) else: session.add(Event(**data, active=True)) async def _upsert_tier(session, data: dict) -> None: """Get-or-create a donation tier matched on name.""" existing = await session.execute( select(DonationTier).where(DonationTier.name == data["name"]) ) tier = existing.scalar_one_or_none() if tier: for key, value in data.items(): setattr(tier, key, value) else: session.add(DonationTier(**data)) # ── Main ────────────────────────────────────────────────── async def seed() -> None: """Populate the database with station data (idempotent).""" async with async_session() as session: for data in PROGRAMS: await _upsert_program(session, data) await session.commit() print(f" ✓ Seeded {len(PROGRAMS)} programs") for data in EVENTS: await _upsert_event(session, data) await session.commit() print(f" ✓ Seeded {len(EVENTS)} events") for data in TIERS: await _upsert_tier(session, data) await session.commit() print(f" ✓ Seeded {len(TIERS)} donation tiers") async def truncate_all() -> None: """Remove all seed data from the database.""" async with async_session() as session: await session.execute(Program.__table__.delete()) await session.execute(Event.__table__.delete()) await session.execute(DonationTier.__table__.delete()) await session.commit() print(" ✓ Truncated all tables") if __name__ == "__main__": asyncio.run(seed())