Files
photoanalyzer/migrations/versions/0002_duplicate_engine.py

100 lines
3.5 KiB
Python

"""Duplicate engine: canonical link, phash version, clusters, members, negative links.
Revision ID: 0002_duplicate_engine
Revises: 0001_initial_identity
Create Date: 2026-07-15
"""
import sqlalchemy as sa
from alembic import op
revision = "0002_duplicate_engine"
down_revision = "0001_initial_identity"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Plain ADD COLUMN: SQLite cannot ALTER-ADD a foreign key, and the ORM model
# keeps the ForeignKey for relationship metadata. canonical_asset_id is a soft
# self-link validated in the service (no cycles), not DB-enforced.
op.add_column("assets", sa.Column("phash_version", sa.Integer(), nullable=True))
op.add_column("assets", sa.Column("canonical_asset_id", sa.String(), nullable=True))
op.create_index("ix_assets_canonical_asset_id", "assets", ["canonical_asset_id"])
op.create_table(
"duplicate_clusters",
sa.Column("id", sa.String(), primary_key=True),
sa.Column("method", sa.String(), nullable=False),
sa.Column("confidence", sa.String(), nullable=False),
sa.Column("state", sa.String(), nullable=False),
sa.Column("decision", sa.String(), nullable=True),
sa.Column(
"canonical_asset_id", sa.String(), sa.ForeignKey("assets.id"), nullable=True
),
sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
nullable=False,
server_default=sa.text("CURRENT_TIMESTAMP"),
),
sa.Column(
"updated_at",
sa.DateTime(timezone=True),
nullable=False,
server_default=sa.text("CURRENT_TIMESTAMP"),
),
sa.Column("decided_at", sa.DateTime(timezone=True), nullable=True),
)
op.create_index("ix_duplicate_clusters_state", "duplicate_clusters", ["state"])
op.create_table(
"duplicate_members",
sa.Column(
"cluster_id",
sa.String(),
sa.ForeignKey("duplicate_clusters.id"),
primary_key=True,
),
sa.Column(
"asset_id", sa.String(), sa.ForeignKey("assets.id"), primary_key=True
),
sa.Column("role", sa.String(), nullable=False, server_default="member"),
sa.Column("distance", sa.Integer(), nullable=True),
sa.Column("evidence", sa.String(), nullable=True),
sa.Column(
"added_at",
sa.DateTime(timezone=True),
nullable=False,
server_default=sa.text("CURRENT_TIMESTAMP"),
),
)
op.create_index("ix_duplicate_members_asset_id", "duplicate_members", ["asset_id"])
op.create_table(
"duplicate_negative_links",
sa.Column(
"asset_a", sa.String(), sa.ForeignKey("assets.id"), primary_key=True
),
sa.Column(
"asset_b", sa.String(), sa.ForeignKey("assets.id"), primary_key=True
),
sa.Column("reason", sa.String(), nullable=True),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
nullable=False,
server_default=sa.text("CURRENT_TIMESTAMP"),
),
)
def downgrade() -> None:
op.drop_table("duplicate_negative_links")
op.drop_table("duplicate_members")
op.drop_table("duplicate_clusters")
op.drop_index("ix_assets_canonical_asset_id", "assets")
op.drop_column("assets", "canonical_asset_id")
op.drop_column("assets", "phash_version")