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
83 lines
3 KiB
Python
83 lines
3 KiB
Python
"""unique name constraints on musicians and sources
|
|
|
|
Revision ID: e5f6a2b3c4d1
|
|
Revises: d7e3b19f0a2c
|
|
Create Date: 2026-06-09
|
|
"""
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.sql import text
|
|
|
|
revision = "e5f6a2b3c4d1"
|
|
down_revision = "d7e3b19f0a2c"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def _dedup_musicians(conn):
|
|
"""Keep the lowest-id musician for each name; re-point FKs and delete dupes."""
|
|
rows = conn.execute(text(
|
|
"SELECT MIN(id) AS keep_id, LOWER(name) AS lname "
|
|
"FROM musicians GROUP BY LOWER(name) HAVING COUNT(*) > 1"
|
|
)).fetchall()
|
|
|
|
for row in rows:
|
|
keep_id = row[0]
|
|
lname = row[1]
|
|
dupe_ids = [r[0] for r in conn.execute(
|
|
text("SELECT id FROM musicians WHERE LOWER(name) = :n AND id != :k"),
|
|
{"n": lname, "k": keep_id}
|
|
).fetchall()]
|
|
|
|
for dupe_id in dupe_ids:
|
|
# Re-point learned_from_id in tune_by_instrument
|
|
conn.execute(text(
|
|
"UPDATE tune_by_instrument SET learned_from_id = :k "
|
|
"WHERE learned_from_id = :d"
|
|
), {"k": keep_id, "d": dupe_id})
|
|
# Re-point reference_musicians (skip if keep_id row already exists for that ref)
|
|
conn.execute(text(
|
|
"DELETE FROM reference_musicians WHERE musician_id = :d "
|
|
"AND reference_id IN ("
|
|
" SELECT reference_id FROM reference_musicians WHERE musician_id = :k"
|
|
")"
|
|
), {"d": dupe_id, "k": keep_id})
|
|
conn.execute(text(
|
|
"UPDATE reference_musicians SET musician_id = :k WHERE musician_id = :d"
|
|
), {"k": keep_id, "d": dupe_id})
|
|
conn.execute(text("DELETE FROM musicians WHERE id = :d"), {"d": dupe_id})
|
|
|
|
|
|
def _dedup_sources(conn):
|
|
"""Keep the lowest-id source for each name; re-point tunes.source_id and delete dupes."""
|
|
rows = conn.execute(text(
|
|
"SELECT MIN(id) AS keep_id, LOWER(name) AS lname "
|
|
"FROM sources GROUP BY LOWER(name) HAVING COUNT(*) > 1"
|
|
)).fetchall()
|
|
|
|
for row in rows:
|
|
keep_id = row[0]
|
|
lname = row[1]
|
|
dupe_ids = [r[0] for r in conn.execute(
|
|
text("SELECT id FROM sources WHERE LOWER(name) = :n AND id != :k"),
|
|
{"n": lname, "k": keep_id}
|
|
).fetchall()]
|
|
|
|
for dupe_id in dupe_ids:
|
|
conn.execute(text(
|
|
"UPDATE tunes SET source_id = :k WHERE source_id = :d"
|
|
), {"k": keep_id, "d": dupe_id})
|
|
conn.execute(text("DELETE FROM sources WHERE id = :d"), {"d": dupe_id})
|
|
|
|
|
|
def upgrade() -> None:
|
|
conn = op.get_bind()
|
|
_dedup_musicians(conn)
|
|
_dedup_sources(conn)
|
|
op.create_unique_constraint("uq_musicians_name", "musicians", ["name"])
|
|
op.create_unique_constraint("uq_sources_name", "sources", ["name"])
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_constraint("uq_musicians_name", "musicians", type_="unique")
|
|
op.drop_constraint("uq_sources_name", "sources", type_="unique")
|