54 lines
2.4 KiB
Python
54 lines
2.4 KiB
Python
"""lesson_rule_links — a lesson points at the rule it is an instance of
|
|
|
|
Revision ID: 0111
|
|
Revises: 0110
|
|
Create Date: 2026-10-01
|
|
|
|
One row per (lesson, rule) pair with a state: `suggested` while evidence
|
|
accumulates, `confirmed` or `rejected` once a judgment is made. Only a
|
|
confirmed link changes what surfaces; a rejected one is kept so the pair is not
|
|
proposed again. CASCADE on both ends — a link to a record that no longer exists
|
|
says nothing. No backfill: no lesson has ever been linked, and inventing a link
|
|
would assert a judgment nobody made.
|
|
"""
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.dialects import postgresql
|
|
from alembic import op
|
|
|
|
revision = "0111"
|
|
down_revision = "0110"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
# One place, so the CHECK and the model's LINK_STATES cannot drift (a new
|
|
# value later means DROP + ADD CONSTRAINT in the same migration).
|
|
_STATES = ("suggested", "confirmed", "rejected")
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"lesson_rule_links",
|
|
sa.Column("id", sa.BigInteger(), primary_key=True),
|
|
sa.Column("lesson_id", sa.Integer(), sa.ForeignKey("notes.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("rule_id", sa.BigInteger(), sa.ForeignKey("rules.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("state", sa.Text(), nullable=False, server_default="suggested"),
|
|
sa.Column("note", sa.Text(), nullable=True),
|
|
sa.Column("evidence", postgresql.JSONB(), nullable=True),
|
|
sa.Column("judged_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
|
|
sa.UniqueConstraint("lesson_id", "rule_id", name="uq_lesson_rule_links_pair"),
|
|
)
|
|
op.create_check_constraint(
|
|
"ck_lesson_rule_links_state", "lesson_rule_links",
|
|
"state IN (" + ", ".join(f"'{s}'" for s in _STATES) + ")",
|
|
)
|
|
op.create_index("ix_lesson_rule_links_lesson_id", "lesson_rule_links", ["lesson_id"])
|
|
op.create_index("ix_lesson_rule_links_rule_id", "lesson_rule_links", ["rule_id"])
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index("ix_lesson_rule_links_rule_id", table_name="lesson_rule_links")
|
|
op.drop_index("ix_lesson_rule_links_lesson_id", table_name="lesson_rule_links")
|
|
op.drop_constraint("ck_lesson_rule_links_state", "lesson_rule_links", type_="check")
|
|
op.drop_table("lesson_rule_links")
|