diff --git a/backend/models.py b/backend/models.py index 5cba7c0..79afb10 100644 --- a/backend/models.py +++ b/backend/models.py @@ -54,6 +54,23 @@ class EntityTag(SQLModel, table=True): # --- Core Entities --- +class VerticalScene(SQLModel, table=True): + """Join table linking verticals to scenes (many-to-many)""" + vertical_id: int = Field(foreign_key="vertical.id", primary_key=True) + scene_id: int = Field(foreign_key="scene.id", primary_key=True) + + +class Scene(SQLModel, table=True): + """Genre/scene categorization for bands (e.g., 'Jam', 'Bluegrass', 'Dead Family')""" + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(unique=True, index=True) + slug: str = Field(unique=True, index=True) + description: Optional[str] = Field(default=None) + + # Relationships + verticals: List["Vertical"] = Relationship(back_populates="scenes", link_model=VerticalScene) + + class Vertical(SQLModel, table=True): """Represents a Fandom Vertical (e.g., 'Phish', 'Goose', 'Star Wars')""" id: Optional[int] = Field(default=None, primary_key=True) @@ -64,8 +81,17 @@ class Vertical(SQLModel, table=True): # Link to primary artist/band for this vertical primary_artist_id: Optional[int] = Field(default=None, foreign_key="artist.id") + # Setlist.fm integration for universal import + setlistfm_mbid: Optional[str] = Field(default=None, description="MusicBrainz ID for Setlist.fm") + + # Admin/status fields + is_active: bool = Field(default=True, description="Show in band selector") + is_featured: bool = Field(default=False, description="Highlight in discovery") + + # Relationships shows: List["Show"] = Relationship(back_populates="vertical") songs: List["Song"] = Relationship(back_populates="vertical") + scenes: List["Scene"] = Relationship(back_populates="verticals", link_model=VerticalScene) class Venue(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) diff --git a/backend/seed_scenes.py b/backend/seed_scenes.py new file mode 100644 index 0000000..438efab --- /dev/null +++ b/backend/seed_scenes.py @@ -0,0 +1,98 @@ +"""Seed script to create initial scenes and assign bands to them""" + +from sqlmodel import Session, select +from database import engine +from models import Scene, Vertical, VerticalScene + + +# Scene definitions +SCENES = [ + {"name": "Jam", "slug": "jam", "description": "Improvisational rock bands with extended jams"}, + {"name": "Bluegrass", "slug": "bluegrass", "description": "Progressive and traditional bluegrass"}, + {"name": "Dead Family", "slug": "dead-family", "description": "Grateful Dead and related projects"}, + {"name": "Funk", "slug": "funk", "description": "Funk, soul, and groove-oriented bands"}, +] + +# Band -> Scene assignments +BAND_SCENES = { + "goose": ["jam"], + "phish": ["jam"], + "grateful-dead": ["jam", "dead-family"], + "dead-and-company": ["jam", "dead-family"], + "billy-strings": ["bluegrass", "jam"], + # Expansion Wave 1 + "pigeons-playing-ping-pong": ["jam", "funk"], + "eggy": ["jam"], + "dogs-in-a-pile": ["jam"], + "greensky-bluegrass": ["bluegrass", "jam"], + "daniel-donato": ["jam"], + # Expansion Wave 2 + "umphreys-mcgee": ["jam"], + "moe": ["jam"], + "widespread-panic": ["jam"], + "sturgill-simpson": ["bluegrass"], + "slightly-stoopid": ["jam", "funk"], +} + + +def seed_scenes(): + """Create scenes if they don't exist""" + print("Seeding scenes...") + + with Session(engine) as session: + for scene_data in SCENES: + existing = session.exec( + select(Scene).where(Scene.slug == scene_data["slug"]) + ).first() + + if not existing: + scene = Scene(**scene_data) + session.add(scene) + print(f" Created scene: {scene_data['name']}") + else: + print(f" Scene exists: {scene_data['name']}") + + session.commit() + print("Scenes seeded.") + + +def assign_bands_to_scenes(): + """Assign existing bands to their scenes""" + print("\nAssigning bands to scenes...") + + with Session(engine) as session: + for band_slug, scene_slugs in BAND_SCENES.items(): + vertical = session.exec( + select(Vertical).where(Vertical.slug == band_slug) + ).first() + + if not vertical: + continue + + for scene_slug in scene_slugs: + scene = session.exec( + select(Scene).where(Scene.slug == scene_slug) + ).first() + + if not scene: + continue + + # Check if assignment exists + existing = session.exec( + select(VerticalScene) + .where(VerticalScene.vertical_id == vertical.id) + .where(VerticalScene.scene_id == scene.id) + ).first() + + if not existing: + vs = VerticalScene(vertical_id=vertical.id, scene_id=scene.id) + session.add(vs) + print(f" Assigned {band_slug} -> {scene_slug}") + + session.commit() + print("Band scene assignments complete.") + + +if __name__ == "__main__": + seed_scenes() + assign_bands_to_scenes()