- Add slug fields to Song, Venue, Show, Tour, Performance models - Update routers to support lookup by slug or ID - Create slugify.py utility for generating URL-safe slugs - Add migration script to generate slugs for existing data - Performance slugs use songslug-YYYY-MM-DD format
224 lines
7.9 KiB
Python
224 lines
7.9 KiB
Python
"""
|
|
Migration script to add slug columns and generate slugs for existing data
|
|
"""
|
|
import os
|
|
import sys
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from sqlmodel import create_engine, Session, select, text
|
|
from slugify import generate_slug, generate_show_slug, generate_performance_slug
|
|
|
|
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://elmeg:elmeg@localhost/elmeg")
|
|
engine = create_engine(DATABASE_URL)
|
|
|
|
def add_slug_columns():
|
|
"""Add slug columns to tables if they don't exist"""
|
|
with engine.connect() as conn:
|
|
# Add slug to song
|
|
conn.execute(text("ALTER TABLE song ADD COLUMN IF NOT EXISTS slug VARCHAR UNIQUE"))
|
|
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_song_slug ON song(slug)"))
|
|
|
|
# Add slug to venue
|
|
conn.execute(text("ALTER TABLE venue ADD COLUMN IF NOT EXISTS slug VARCHAR UNIQUE"))
|
|
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_venue_slug ON venue(slug)"))
|
|
|
|
# Add slug to show
|
|
conn.execute(text("ALTER TABLE show ADD COLUMN IF NOT EXISTS slug VARCHAR UNIQUE"))
|
|
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_show_slug ON show(slug)"))
|
|
|
|
# Add slug to tour
|
|
conn.execute(text("ALTER TABLE tour ADD COLUMN IF NOT EXISTS slug VARCHAR UNIQUE"))
|
|
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_tour_slug ON tour(slug)"))
|
|
|
|
# Add slug to performance
|
|
conn.execute(text("ALTER TABLE performance ADD COLUMN IF NOT EXISTS slug VARCHAR UNIQUE"))
|
|
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_performance_slug ON performance(slug)"))
|
|
|
|
conn.commit()
|
|
print("✓ Slug columns added")
|
|
|
|
def generate_song_slugs():
|
|
"""Generate slugs for all songs"""
|
|
with Session(engine) as session:
|
|
# Get all songs without slugs
|
|
result = session.exec(text("SELECT id, title FROM song WHERE slug IS NULL"))
|
|
songs = result.fetchall()
|
|
|
|
existing_slugs = set()
|
|
# Get existing slugs
|
|
existing = session.exec(text("SELECT slug FROM song WHERE slug IS NOT NULL"))
|
|
for row in existing.fetchall():
|
|
existing_slugs.add(row[0])
|
|
|
|
count = 0
|
|
for song_id, title in songs:
|
|
base_slug = generate_slug(title, 50)
|
|
slug = base_slug
|
|
counter = 2
|
|
|
|
while slug in existing_slugs:
|
|
slug = f"{base_slug}-{counter}"
|
|
counter += 1
|
|
|
|
existing_slugs.add(slug)
|
|
session.execute(
|
|
text("UPDATE song SET slug = :slug WHERE id = :id"),
|
|
{"slug": slug, "id": song_id}
|
|
)
|
|
count += 1
|
|
|
|
session.commit()
|
|
print(f"✓ Generated slugs for {count} songs")
|
|
|
|
def generate_venue_slugs():
|
|
"""Generate slugs for all venues"""
|
|
with Session(engine) as session:
|
|
result = session.exec(text("SELECT id, name, city FROM venue WHERE slug IS NULL"))
|
|
venues = result.fetchall()
|
|
|
|
existing_slugs = set()
|
|
existing = session.exec(text("SELECT slug FROM venue WHERE slug IS NOT NULL"))
|
|
for row in existing.fetchall():
|
|
existing_slugs.add(row[0])
|
|
|
|
count = 0
|
|
for venue_id, name, city in venues:
|
|
# Include city to help disambiguate
|
|
base_slug = generate_slug(f"{name} {city}", 60)
|
|
slug = base_slug
|
|
counter = 2
|
|
|
|
while slug in existing_slugs:
|
|
slug = f"{base_slug}-{counter}"
|
|
counter += 1
|
|
|
|
existing_slugs.add(slug)
|
|
session.execute(
|
|
text("UPDATE venue SET slug = :slug WHERE id = :id"),
|
|
{"slug": slug, "id": venue_id}
|
|
)
|
|
count += 1
|
|
|
|
session.commit()
|
|
print(f"✓ Generated slugs for {count} venues")
|
|
|
|
def generate_show_slugs():
|
|
"""Generate slugs for all shows"""
|
|
with Session(engine) as session:
|
|
result = session.exec(text("""
|
|
SELECT s.id, s.date, v.name
|
|
FROM show s
|
|
LEFT JOIN venue v ON s.venue_id = v.id
|
|
WHERE s.slug IS NULL
|
|
"""))
|
|
shows = result.fetchall()
|
|
|
|
existing_slugs = set()
|
|
existing = session.exec(text("SELECT slug FROM show WHERE slug IS NOT NULL"))
|
|
for row in existing.fetchall():
|
|
existing_slugs.add(row[0])
|
|
|
|
count = 0
|
|
for show_id, date, venue_name in shows:
|
|
date_str = date.strftime("%Y-%m-%d") if date else "unknown"
|
|
venue_slug = generate_slug(venue_name or "unknown", 25)
|
|
base_slug = f"{date_str}-{venue_slug}"
|
|
slug = base_slug
|
|
counter = 2
|
|
|
|
while slug in existing_slugs:
|
|
slug = f"{base_slug}-{counter}"
|
|
counter += 1
|
|
|
|
existing_slugs.add(slug)
|
|
session.execute(
|
|
text("UPDATE show SET slug = :slug WHERE id = :id"),
|
|
{"slug": slug, "id": show_id}
|
|
)
|
|
count += 1
|
|
|
|
session.commit()
|
|
print(f"✓ Generated slugs for {count} shows")
|
|
|
|
def generate_tour_slugs():
|
|
"""Generate slugs for all tours"""
|
|
with Session(engine) as session:
|
|
result = session.exec(text("SELECT id, name FROM tour WHERE slug IS NULL"))
|
|
tours = result.fetchall()
|
|
|
|
existing_slugs = set()
|
|
existing = session.exec(text("SELECT slug FROM tour WHERE slug IS NOT NULL"))
|
|
for row in existing.fetchall():
|
|
existing_slugs.add(row[0])
|
|
|
|
count = 0
|
|
for tour_id, name in tours:
|
|
base_slug = generate_slug(name, 50)
|
|
slug = base_slug
|
|
counter = 2
|
|
|
|
while slug in existing_slugs:
|
|
slug = f"{base_slug}-{counter}"
|
|
counter += 1
|
|
|
|
existing_slugs.add(slug)
|
|
session.execute(
|
|
text("UPDATE tour SET slug = :slug WHERE id = :id"),
|
|
{"slug": slug, "id": tour_id}
|
|
)
|
|
count += 1
|
|
|
|
session.commit()
|
|
print(f"✓ Generated slugs for {count} tours")
|
|
|
|
def generate_performance_slugs():
|
|
"""Generate slugs for all performances (songslug-date format)"""
|
|
with Session(engine) as session:
|
|
result = session.exec(text("""
|
|
SELECT p.id, s.slug as song_slug, sh.date
|
|
FROM performance p
|
|
JOIN song s ON p.song_id = s.id
|
|
JOIN show sh ON p.show_id = sh.id
|
|
WHERE p.slug IS NULL
|
|
"""))
|
|
performances = result.fetchall()
|
|
|
|
existing_slugs = set()
|
|
existing = session.exec(text("SELECT slug FROM performance WHERE slug IS NOT NULL"))
|
|
for row in existing.fetchall():
|
|
existing_slugs.add(row[0])
|
|
|
|
count = 0
|
|
for perf_id, song_slug, date in performances:
|
|
date_str = date.strftime("%Y-%m-%d") if date else "unknown"
|
|
base_slug = f"{song_slug}-{date_str}"
|
|
slug = base_slug
|
|
counter = 2
|
|
|
|
# Handle multiple performances of same song in same show
|
|
while slug in existing_slugs:
|
|
slug = f"{base_slug}-{counter}"
|
|
counter += 1
|
|
|
|
existing_slugs.add(slug)
|
|
session.execute(
|
|
text("UPDATE performance SET slug = :slug WHERE id = :id"),
|
|
{"slug": slug, "id": perf_id}
|
|
)
|
|
count += 1
|
|
|
|
session.commit()
|
|
print(f"✓ Generated slugs for {count} performances")
|
|
|
|
def run_migration():
|
|
print("=== Running Slug Migration ===")
|
|
add_slug_columns()
|
|
generate_song_slugs()
|
|
generate_venue_slugs()
|
|
generate_tour_slugs()
|
|
generate_show_slugs()
|
|
generate_performance_slugs() # Must run after song slugs
|
|
print("=== Migration Complete ===")
|
|
|
|
if __name__ == "__main__":
|
|
run_migration()
|