From d1e99c51de18b7b237f5909c0da12662c6bcb0cc Mon Sep 17 00:00:00 2001 From: Ian Keane Date: Thu, 11 Jun 2026 12:55:16 -0400 Subject: [PATCH] Reduce to_learn, callable, review into one status field --- app/models.py | 8 +- app/routes_instruments.py | 6 +- app/routes_tunes.py | 25 ++----- .../versions/f1a2b3c4d5e6_status_field.py | 74 +++++++++++++++++++ 4 files changed, 84 insertions(+), 29 deletions(-) create mode 100644 migrations/versions/f1a2b3c4d5e6_status_field.py diff --git a/app/models.py b/app/models.py index 68eaed7..2d535fe 100644 --- a/app/models.py +++ b/app/models.py @@ -101,9 +101,7 @@ class TuneByInstrument(db.Model): 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) + status = Column(String, nullable=True) difficulty = Column(String, nullable=True) tune = db.relationship("Tune", back_populates="instruments") @@ -123,9 +121,7 @@ class TuneByInstrument(db.Model): "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, + "status": self.status, "difficulty": self.difficulty, "notes": [n.to_dict() for n in self.notes], } diff --git a/app/routes_instruments.py b/app/routes_instruments.py index 9df26a1..23e3311 100644 --- a/app/routes_instruments.py +++ b/app/routes_instruments.py @@ -45,9 +45,7 @@ def create_tune_instrument(tune_id): learned_from_id=data.get("learned_from_id"), tuning_id=data.get("tuning_id"), date_learned=data.get("date_learned"), - callable=data.get("callable"), - review=data.get("review"), - to_learn=data.get("to_learn", False), + status=data.get("status"), difficulty=data.get("difficulty"), ) db.session.add(entry) @@ -64,7 +62,7 @@ def update_tune_instrument(tune_id, entry_id): entry = db.get_or_404(TuneByInstrument, entry_id) data = request.get_json(force=True) fields = ("instrument_id", "learned_from_id", "tuning_id", "date_learned", - "callable", "review", "to_learn", "difficulty") + "status", "difficulty") for field in fields: if field in data: setattr(entry, field, data[field]) diff --git a/app/routes_tunes.py b/app/routes_tunes.py index 6cf6cf6..72fd5bb 100644 --- a/app/routes_tunes.py +++ b/app/routes_tunes.py @@ -17,9 +17,7 @@ def list_tunes(): instrument_id=1 key=D tuning_id=1 - callable=true|false - review=true|false - to_learn=true|false + status=callable|review|to_learn difficulty=easy|hard modal=true|false source_id=1 @@ -45,26 +43,17 @@ def list_tunes(): instrument_id = request.args.get("instrument_id") tuning = request.args.get("tuning") - callable_ = request.args.get("callable") - review = request.args.get("review") - to_learn = request.args.get("to_learn") + status = request.args.get("status") difficulty = request.args.get("difficulty") - def _bool(val): - return val.lower() in ("true", "1", "yes") - - if any([instrument_id, tuning, callable_, review, to_learn, difficulty]): + if any([instrument_id, tuning, status, difficulty]): q = q.join(Tune.instruments) if instrument_id: q = q.filter(TuneByInstrument.instrument_id == int(instrument_id)) if tuning: q = q.filter(TuneByInstrument.tuning_id == int(tuning)) - if callable_ is not None: - q = q.filter(TuneByInstrument.callable == _bool(callable_)) - if review is not None: - q = q.filter(TuneByInstrument.review == _bool(review)) - if to_learn is not None: - q = q.filter(TuneByInstrument.to_learn == _bool(to_learn)) + if status: + q = q.filter(TuneByInstrument.status == status) if difficulty: q = q.filter(TuneByInstrument.difficulty.ilike(difficulty)) @@ -105,9 +94,7 @@ def create_tune(): learned_from_id=inst_data.get("learned_from_id"), tuning_id=inst_data.get("tuning_id"), date_learned=inst_data.get("date_learned"), - callable=inst_data.get("callable"), - review=inst_data.get("review"), - to_learn=inst_data.get("to_learn", False), + status=inst_data.get("status"), difficulty=inst_data.get("difficulty"), ) db.session.add(entry) diff --git a/migrations/versions/f1a2b3c4d5e6_status_field.py b/migrations/versions/f1a2b3c4d5e6_status_field.py new file mode 100644 index 0000000..b8b47af --- /dev/null +++ b/migrations/versions/f1a2b3c4d5e6_status_field.py @@ -0,0 +1,74 @@ +"""replace callable/review/to_learn with status on tune_by_instrument + +Revision ID: f1a2b3c4d5e6 +Revises: e5f6a2b3c4d1 +Create Date: 2026-06-09 +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.sql import table, column, select + +revision = "f1a2b3c4d5e6" +down_revision = "e5f6a2b3c4d1" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # Add new status column + op.add_column( + "tune_by_instrument", + sa.Column("status", sa.String(), nullable=True), + ) + + # Migrate existing data (priority: callable > review > to_learn) + conn = op.get_bind() + tbi = table( + "tune_by_instrument", + column("id", sa.Integer), + column("callable", sa.Boolean), + column("review", sa.Boolean), + column("to_learn", sa.Boolean), + column("status", sa.String), + ) + rows = conn.execute(select(tbi.c.id, tbi.c.callable, tbi.c.review, tbi.c.to_learn)).fetchall() + for row in rows: + id_, callable_, review_, to_learn_ = row + if callable_: + status = "callable" + elif review_: + status = "review" + elif to_learn_: + status = "to_learn" + else: + status = None + if status: + conn.execute(tbi.update().where(tbi.c.id == id_).values(status=status)) + + # Drop old columns + op.drop_column("tune_by_instrument", "callable") + op.drop_column("tune_by_instrument", "review") + op.drop_column("tune_by_instrument", "to_learn") + + +def downgrade() -> None: + op.add_column("tune_by_instrument", sa.Column("callable", sa.Boolean(), nullable=True)) + op.add_column("tune_by_instrument", sa.Column("review", sa.Boolean(), nullable=True)) + op.add_column("tune_by_instrument", sa.Column("to_learn", sa.Boolean(), nullable=True)) + conn = op.get_bind() + tbi = table( + "tune_by_instrument", + column("id", sa.Integer), + column("callable", sa.Boolean), + column("review", sa.Boolean), + column("to_learn", sa.Boolean), + column("status", sa.String), + ) + rows = conn.execute(select(tbi.c.id, tbi.c.status)).fetchall() + for id_, status in rows: + conn.execute(tbi.update().where(tbi.c.id == id_).values( + callable =(status == "callable"), + review =(status == "review"), + to_learn =(status == "to_learn"), + )) + op.drop_column("tune_by_instrument", "status")