215 lines
8.3 KiB
Python
215 lines
8.3 KiB
Python
"""
|
|
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, StationConfig
|
|
|
|
# ── 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,
|
|
},
|
|
]
|
|
|
|
STATION_CONFIG: dict = {
|
|
"key": "default",
|
|
"callsign": "KMTN",
|
|
"name_primary": "KMountain",
|
|
"name_secondary": "Flower Radio",
|
|
"tagline": "Community Radio — Est. 1987",
|
|
"frequency": "98.7 FM",
|
|
"founded_year": 1987,
|
|
"nonprofit_type": "501(c)(3)",
|
|
"ein": "84-XXXXXXX",
|
|
"logo_url": "svg/small-flower.svg",
|
|
"hero_background_url": "svg/mountain-landscape.svg",
|
|
"hero_icon_url": "svg/orange-flower.svg",
|
|
"hero_divider_url": "svg/mountain-divider.svg",
|
|
"address": "42 Pine Street\nMountain View, CO 80424",
|
|
"phone": "(970) 555-0198",
|
|
"email": "hello@kmountainflower.org",
|
|
"website": "kmountainflower.org",
|
|
}
|
|
|
|
|
|
# ── 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))
|
|
|
|
|
|
async def _upsert_station_config(session, data: dict) -> None:
|
|
"""Get-or-create the singleton station config (key='default')."""
|
|
existing = await session.execute(
|
|
select(StationConfig).where(StationConfig.key == "default")
|
|
)
|
|
config = existing.scalar_one_or_none()
|
|
if config:
|
|
for key, value in data.items():
|
|
setattr(config, key, value)
|
|
else:
|
|
session.add(StationConfig(**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")
|
|
|
|
await _upsert_station_config(session, STATION_CONFIG)
|
|
await session.commit()
|
|
print(" ✓ Seeded station config")
|
|
|
|
|
|
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.execute(StationConfig.__table__.delete())
|
|
await session.commit()
|
|
print(" ✓ Truncated all tables")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(seed())
|