feat: Add cross-band entity relationship models
Some checks failed
Deploy Fediversion / deploy (push) Failing after 1s

Schema additions for fediversion multi-band architecture:

- SongCanon: Canonical master songs for cross-band linking
  (e.g., 'Dark Star' links GD, D&C, Billy Strings versions)

- UserVerticalPreference: User band display preferences
  - display_mode: primary/secondary/attribution_only/hidden
  - priority: Sort order for UI
  - notify_on_show: Per-band notification settings

- Vertical additions:
  - primary_artist_id: Link to main Artist entity
  - color: Hex branding color
  - emoji: Display emoji

- Song additions:
  - canon_id: Link to SongCanon for cross-band tracking
This commit is contained in:
fullsizemalt 2025-12-28 14:51:22 -08:00
parent 42ede48a70
commit 56f52de7fc

View file

@ -61,6 +61,13 @@ class Vertical(SQLModel, table=True):
slug: str = Field(unique=True, index=True)
description: Optional[str] = Field(default=None)
# Link to primary artist/band for this vertical
primary_artist_id: Optional[int] = Field(default=None, foreign_key="artist.id")
# Theming
color: Optional[str] = Field(default=None, description="Hex color for branding")
emoji: Optional[str] = Field(default=None, description="Display emoji")
shows: List["Show"] = Relationship(back_populates="vertical")
songs: List["Song"] = Relationship(back_populates="vertical")
@ -155,6 +162,18 @@ class Show(SQLModel, table=True):
attendances: List["Attendance"] = Relationship(back_populates="show")
performances: List["Performance"] = Relationship(back_populates="show")
class SongCanon(SQLModel, table=True):
"""Canonical 'master' song independent of band - enables cross-band song linking"""
id: Optional[int] = Field(default=None, primary_key=True)
title: str = Field(index=True)
slug: str = Field(unique=True, index=True)
original_artist: Optional[str] = Field(default=None, description="Original songwriter/band")
original_artist_id: Optional[int] = Field(default=None, foreign_key="artist.id")
notes: Optional[str] = Field(default=None)
# All vertical-specific versions of this song
versions: List["Song"] = Relationship(back_populates="canon")
class Song(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
title: str = Field(index=True)
@ -164,11 +183,15 @@ class Song(SQLModel, table=True):
notes: Optional[str] = Field(default=None)
youtube_link: Optional[str] = Field(default=None)
# New Relation
artist_id: Optional[int] = Field(default=None, foreign_key="artist.id")
artist: Optional[Artist] = Relationship(back_populates="songs")
# Link to canonical song for cross-band tracking
canon_id: Optional[int] = Field(default=None, foreign_key="songcanon.id")
canon: Optional[SongCanon] = Relationship(back_populates="versions")
vertical: Vertical = Relationship(back_populates="songs")
# Artist who wrote/performs this version
artist_id: Optional[int] = Field(default=None, foreign_key="artist.id")
artist: Optional["Artist"] = Relationship(back_populates="songs")
vertical: "Vertical" = Relationship(back_populates="songs")
class Sequence(SQLModel, table=True):
"""Named groupings of consecutive songs, e.g. 'Autumn Crossing' = Travelers > Elmeg the Wise"""
@ -346,7 +369,20 @@ class UserPreferences(SQLModel, table=True):
email_on_chase: bool = Field(default=True, description="Email when your chase song is played")
email_digest: bool = Field(default=False, description="Weekly digest email")
user: User = Relationship(back_populates="preferences")
user: "User" = Relationship(back_populates="preferences")
class UserVerticalPreference(SQLModel, table=True):
"""User preferences for which bands to display prominently vs. attribution-only"""
id: Optional[int] = Field(default=None, primary_key=True)
user_id: int = Field(foreign_key="user.id", index=True)
vertical_id: int = Field(foreign_key="vertical.id", index=True)
display_mode: str = Field(default="primary", description="primary, secondary, attribution_only, hidden")
priority: int = Field(default=0, description="Sort order - lower = higher priority")
notify_on_show: bool = Field(default=True, description="Notify when this band plays a show")
created_at: datetime = Field(default_factory=datetime.utcnow)
user: "User" = Relationship()
vertical: "Vertical" = Relationship()
class Profile(SQLModel, table=True):
"""A user's identity within a specific context or global"""