repertory-api/app/models.py
Ian Keane 507531fbff Changes for refactor of references view, add unique keys for some tables
References sites table updated so we can filter by site later, added
timestamp to tunes so they can be sorted in certain contexts, enforced
uniqueness on some tables that were causing issues
2026-06-11 10:05:23 -04:00

192 lines
7.9 KiB
Python

from sqlalchemy import Boolean, Column, Date, DateTime, ForeignKey, Integer, String, Table, UniqueConstraint
from sqlalchemy.sql import func
from app.extensions import db
# ── Many-to-many: references ↔ musicians ─────────────────────
reference_musicians = Table(
"reference_musicians",
db.metadata,
Column("reference_id", Integer, ForeignKey("references.id", ondelete="CASCADE"), primary_key=True),
Column("musician_id", Integer, ForeignKey("musicians.id", ondelete="CASCADE"), primary_key=True),
)
class Musician(db.Model):
__tablename__ = "musicians"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=False, unique=True)
references = db.relationship("Reference", secondary=reference_musicians, back_populates="musicians")
instrument_entries = db.relationship("TuneByInstrument", back_populates="learned_from_musician")
def to_dict(self):
return {"id": self.id, "name": self.name}
class Source(db.Model):
__tablename__ = "sources"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=False, unique=True)
tunes = db.relationship("Tune", back_populates="source")
def to_dict(self):
return {"id": self.id, "name": self.name}
class Instrument(db.Model):
__tablename__ = "instruments"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=False, unique=True)
entries = db.relationship("TuneByInstrument", back_populates="instrument")
def to_dict(self):
return {"id": self.id, "name": self.name}
class Tuning(db.Model):
__tablename__ = "tunings"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=False, unique=True)
entries = db.relationship("TuneByInstrument", back_populates="tuning")
def to_dict(self):
return {"id": self.id, "name": self.name}
class Tune(db.Model):
__tablename__ = "tunes"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=True)
key = Column(String, nullable=True)
modal = Column(Boolean, nullable=True)
source_id = Column(Integer, ForeignKey("sources.id", ondelete="SET NULL"), nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=True)
source = db.relationship("Source", back_populates="tunes")
instruments = db.relationship("TuneByInstrument", back_populates="tune", cascade="all, delete-orphan")
notes = db.relationship("TuneNote", back_populates="tune", cascade="all, delete-orphan")
references = db.relationship("Reference", back_populates="tune", cascade="all, delete-orphan")
def to_dict(self):
return {
"id": self.id,
"name": self.name,
"key": self.key,
"modal": self.modal,
"source_id": self.source_id,
"created_at": self.created_at.isoformat() if self.created_at else None,
"source": self.source.to_dict() if self.source else None,
"instruments": [i.to_dict() for i in self.instruments],
"notes": [n.to_dict() for n in self.notes],
"references": [r.to_dict() for r in self.references],
}
class TuneByInstrument(db.Model):
__tablename__ = "tune_by_instrument"
__table_args__ = (UniqueConstraint("tune_id", "instrument_id", name="uq_tune_instrument"),)
id = Column(Integer, primary_key=True, autoincrement=True)
tune_id = Column(Integer, ForeignKey("tunes.id", ondelete="CASCADE"), nullable=False)
instrument_id = Column(Integer, ForeignKey("instruments.id", ondelete="CASCADE"), nullable=False)
learned_from_id = Column(Integer, ForeignKey("musicians.id", ondelete="SET NULL"), nullable=True)
tuning_id = Column(Integer, ForeignKey("tunings.id", ondelete="SET NULL"), nullable=True)
date_learned = Column(Date, nullable=True)
callable = Column(Boolean, nullable=True)
review = Column(Boolean, nullable=True)
to_learn = Column(Boolean, nullable=True, default=False)
difficulty = Column(String, nullable=True)
tune = db.relationship("Tune", back_populates="instruments")
instrument = db.relationship("Instrument", back_populates="entries")
tuning = db.relationship("Tuning", back_populates="entries")
learned_from_musician = db.relationship("Musician", back_populates="instrument_entries")
notes = db.relationship("TuneByInstrumentNote", back_populates="entry", cascade="all, delete-orphan")
def to_dict(self):
return {
"id": self.id,
"tune_id": self.tune_id,
"instrument_id": self.instrument_id,
"instrument_name": self.instrument.name if self.instrument else None,
"learned_from_id": self.learned_from_id,
"learned_from": self.learned_from_musician.name if self.learned_from_musician else None,
"date_learned": self.date_learned.isoformat() if self.date_learned else None,
"tuning_id": self.tuning_id,
"tuning": self.tuning.name if self.tuning else None,
"callable": self.callable,
"review": self.review,
"to_learn": self.to_learn,
"difficulty": self.difficulty,
"notes": [n.to_dict() for n in self.notes],
}
class TuneNote(db.Model):
__tablename__ = "tune_notes"
id = Column(Integer, primary_key=True, autoincrement=True)
tune_id = Column(Integer, ForeignKey("tunes.id", ondelete="CASCADE"), nullable=False)
note = Column(String, nullable=True)
tune = db.relationship("Tune", back_populates="notes")
def to_dict(self):
return {"id": self.id, "tune_id": self.tune_id, "note": self.note}
class TuneByInstrumentNote(db.Model):
__tablename__ = "tune_by_instrument_notes"
id = Column(Integer, primary_key=True, autoincrement=True)
tune_by_instrument_id = Column(Integer, ForeignKey("tune_by_instrument.id", ondelete="CASCADE"), nullable=False)
note = Column(String, nullable=True)
entry = db.relationship("TuneByInstrument", back_populates="notes")
def to_dict(self):
return {"id": self.id, "tune_by_instrument_id": self.tune_by_instrument_id, "note": self.note}
class ReferenceSite(db.Model):
__tablename__ = "reference_sites"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=False, unique=True)
references = db.relationship("Reference", back_populates="site")
def to_dict(self):
return {"id": self.id, "name": self.name}
class Reference(db.Model):
__tablename__ = "references"
id = Column(Integer, primary_key=True, autoincrement=True)
tune_id = Column(Integer, ForeignKey("tunes.id", ondelete="CASCADE"), nullable=True)
link = Column(String, nullable=True)
site_id = Column(Integer, ForeignKey("reference_sites.id", ondelete="SET NULL"), nullable=True)
tune = db.relationship("Tune", back_populates="references")
site = db.relationship("ReferenceSite", back_populates="references")
musicians = db.relationship("Musician", secondary=reference_musicians, back_populates="references")
def to_dict(self):
return {
"id": self.id,
"tune_id": self.tune_id,
"link": self.link,
"site_id": self.site_id,
"site": self.site.to_dict() if self.site else None,
"musicians": [m.to_dict() for m in self.musicians],
}