feat: Enhance musician API with cross-band sit-in tracking
Some checks failed
Deploy Fediversion / deploy (push) Failing after 1s
Some checks failed
Deploy Fediversion / deploy (push) Failing after 1s
- Add sit_in_summary to GET /musicians/{slug}
- Add stats: total_bands, current_bands, total_sit_ins
- Include song_title, show_date, vertical in guest appearances
- Add seed_musicians.py for initial musician data
This commit is contained in:
parent
5b236608f8
commit
159cbc853c
2 changed files with 152 additions and 1 deletions
|
|
@ -60,6 +60,8 @@ def list_musicians(
|
|||
@router.get("/{slug}")
|
||||
def get_musician(slug: str, session: Session = Depends(get_session)):
|
||||
"""Get musician details with band memberships and guest appearances"""
|
||||
from models import Show, Song, Vertical
|
||||
|
||||
musician = session.exec(select(Musician).where(Musician.slug == slug)).first()
|
||||
if not musician:
|
||||
raise HTTPException(status_code=404, detail="Musician not found")
|
||||
|
|
@ -70,8 +72,10 @@ def get_musician(slug: str, session: Session = Depends(get_session)):
|
|||
).all()
|
||||
|
||||
bands = []
|
||||
primary_band_ids = set()
|
||||
for m in memberships:
|
||||
artist = session.get(Artist, m.artist_id)
|
||||
primary_band_ids.add(m.artist_id)
|
||||
bands.append({
|
||||
"id": m.id,
|
||||
"artist_id": m.artist_id,
|
||||
|
|
@ -80,20 +84,44 @@ def get_musician(slug: str, session: Session = Depends(get_session)):
|
|||
"role": m.role,
|
||||
"start_date": str(m.start_date) if m.start_date else None,
|
||||
"end_date": str(m.end_date) if m.end_date else None,
|
||||
"is_current": m.end_date is None,
|
||||
})
|
||||
|
||||
# Get guest appearances
|
||||
# Get guest appearances with full context
|
||||
appearances = session.exec(
|
||||
select(PerformanceGuest).where(PerformanceGuest.musician_id == musician.id)
|
||||
).all()
|
||||
|
||||
guests = []
|
||||
sit_in_verticals = {} # Track which bands they've sat in with
|
||||
|
||||
for g in appearances:
|
||||
perf = session.get(Performance, g.performance_id)
|
||||
if not perf:
|
||||
continue
|
||||
|
||||
show = session.get(Show, perf.show_id) if perf else None
|
||||
song = session.get(Song, perf.song_id) if perf else None
|
||||
vertical = session.get(Vertical, show.vertical_id) if show else None
|
||||
|
||||
if vertical:
|
||||
if vertical.id not in sit_in_verticals:
|
||||
sit_in_verticals[vertical.id] = {
|
||||
"vertical_id": vertical.id,
|
||||
"vertical_name": vertical.name,
|
||||
"vertical_slug": vertical.slug,
|
||||
"count": 0
|
||||
}
|
||||
sit_in_verticals[vertical.id]["count"] += 1
|
||||
|
||||
guests.append({
|
||||
"id": g.id,
|
||||
"performance_id": g.performance_id,
|
||||
"performance_slug": perf.slug if perf else None,
|
||||
"song_title": song.title if song else None,
|
||||
"show_date": str(show.date.date()) if show and show.date else None,
|
||||
"vertical_name": vertical.name if vertical else None,
|
||||
"vertical_slug": vertical.slug if vertical else None,
|
||||
"instrument": g.instrument,
|
||||
})
|
||||
|
||||
|
|
@ -108,6 +136,13 @@ def get_musician(slug: str, session: Session = Depends(get_session)):
|
|||
},
|
||||
"bands": bands,
|
||||
"guest_appearances": guests,
|
||||
"sit_in_summary": list(sit_in_verticals.values()),
|
||||
"stats": {
|
||||
"total_bands": len(bands),
|
||||
"current_bands": len([b for b in bands if b.get("is_current")]),
|
||||
"total_sit_ins": len(guests),
|
||||
"bands_sat_in_with": len(sit_in_verticals),
|
||||
}
|
||||
}
|
||||
|
||||
# --- Admin Endpoints (for now, no auth check - can be added later) ---
|
||||
|
|
|
|||
116
backend/seed_musicians.py
Normal file
116
backend/seed_musicians.py
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
"""
|
||||
Seed script to create common cross-band musicians.
|
||||
|
||||
These are musicians known for sitting in with multiple bands.
|
||||
"""
|
||||
|
||||
from sqlmodel import Session, select
|
||||
from database import engine
|
||||
from models import Musician, Artist, BandMembership
|
||||
import re
|
||||
|
||||
|
||||
def generate_slug(name: str) -> str:
|
||||
"""Generate URL-safe slug"""
|
||||
slug = name.lower()
|
||||
slug = re.sub(r'[^\w\s-]', '', slug)
|
||||
slug = re.sub(r'[\s_]+', '-', slug)
|
||||
return slug.strip('-')
|
||||
|
||||
|
||||
# Notable jam scene musicians who appear across bands
|
||||
CROSS_BAND_MUSICIANS = [
|
||||
# Grateful Dead / Dead Family
|
||||
{"name": "Bob Weir", "primary_instrument": "Guitar", "bands": ["grateful-dead", "dead-and-company"]},
|
||||
{"name": "Mickey Hart", "primary_instrument": "Drums", "bands": ["grateful-dead", "dead-and-company"]},
|
||||
{"name": "Bill Kreutzmann", "primary_instrument": "Drums", "bands": ["grateful-dead", "dead-and-company"]},
|
||||
{"name": "John Mayer", "primary_instrument": "Guitar", "bands": ["dead-and-company"]},
|
||||
{"name": "Oteil Burbridge", "primary_instrument": "Bass", "bands": ["dead-and-company"]},
|
||||
{"name": "Jeff Chimenti", "primary_instrument": "Keyboards", "bands": ["dead-and-company"]},
|
||||
|
||||
# Goose
|
||||
{"name": "Rick Mitarotonda", "primary_instrument": "Guitar, Vocals", "bands": ["goose"]},
|
||||
{"name": "Peter Anspach", "primary_instrument": "Keyboards, Guitar", "bands": ["goose"]},
|
||||
{"name": "Trevor Weekz", "primary_instrument": "Bass", "bands": ["goose"]},
|
||||
{"name": "Ben Atkind", "primary_instrument": "Drums", "bands": ["goose"]},
|
||||
{"name": "Jeff Arevalo", "primary_instrument": "Percussion", "bands": ["goose"]},
|
||||
|
||||
# Phish
|
||||
{"name": "Trey Anastasio", "primary_instrument": "Guitar", "bands": ["phish"]},
|
||||
{"name": "Page McConnell", "primary_instrument": "Keyboards", "bands": ["phish"]},
|
||||
{"name": "Mike Gordon", "primary_instrument": "Bass", "bands": ["phish"]},
|
||||
{"name": "Jon Fishman", "primary_instrument": "Drums", "bands": ["phish"]},
|
||||
|
||||
# Billy Strings
|
||||
{"name": "Billy Strings", "primary_instrument": "Guitar, Vocals", "bands": ["billy-strings"]},
|
||||
{"name": "Billy Failing", "primary_instrument": "Banjo", "bands": ["billy-strings"]},
|
||||
{"name": "Royal Masat", "primary_instrument": "Bass", "bands": ["billy-strings"]},
|
||||
{"name": "Jarrod Walker", "primary_instrument": "Mandolin", "bands": ["billy-strings"]},
|
||||
|
||||
# Cross-band sit-in regulars
|
||||
{"name": "Marcus King", "primary_instrument": "Guitar, Vocals", "bands": []},
|
||||
{"name": "Pigeons Playing Ping Pong", "primary_instrument": "Funk", "bands": []},
|
||||
{"name": "Karina Rykman", "primary_instrument": "Bass, Vocals", "bands": []},
|
||||
]
|
||||
|
||||
|
||||
def seed_musicians():
|
||||
"""Create musicians if they don't exist"""
|
||||
print("Seeding musicians...\n")
|
||||
|
||||
with Session(engine) as session:
|
||||
created = 0
|
||||
|
||||
for m in CROSS_BAND_MUSICIANS:
|
||||
slug = generate_slug(m["name"])
|
||||
|
||||
existing = session.exec(
|
||||
select(Musician).where(Musician.slug == slug)
|
||||
).first()
|
||||
|
||||
if existing:
|
||||
print(f" Exists: {m['name']}")
|
||||
musician = existing
|
||||
else:
|
||||
musician = Musician(
|
||||
name=m["name"],
|
||||
slug=slug,
|
||||
primary_instrument=m["primary_instrument"]
|
||||
)
|
||||
session.add(musician)
|
||||
session.commit()
|
||||
session.refresh(musician)
|
||||
created += 1
|
||||
print(f" Created: {m['name']}")
|
||||
|
||||
# Create band memberships
|
||||
for band_slug in m.get("bands", []):
|
||||
# Find vertical by slug to get artist
|
||||
from models import Vertical
|
||||
vertical = session.exec(
|
||||
select(Vertical).where(Vertical.slug == band_slug)
|
||||
).first()
|
||||
|
||||
if vertical and vertical.primary_artist_id:
|
||||
# Check if membership exists
|
||||
existing_membership = session.exec(
|
||||
select(BandMembership)
|
||||
.where(BandMembership.musician_id == musician.id)
|
||||
.where(BandMembership.artist_id == vertical.primary_artist_id)
|
||||
).first()
|
||||
|
||||
if not existing_membership:
|
||||
membership = BandMembership(
|
||||
musician_id=musician.id,
|
||||
artist_id=vertical.primary_artist_id,
|
||||
role=m["primary_instrument"]
|
||||
)
|
||||
session.add(membership)
|
||||
print(f" -> Added to {band_slug}")
|
||||
|
||||
session.commit()
|
||||
print(f"\nCreated {created} new musicians")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
seed_musicians()
|
||||
Loading…
Add table
Reference in a new issue