ab63d94249
MLSettings gains presentation_auto_apply_enabled / _threshold (default 0.90) + presentation_conflict_threshold (default 0.50): banner/editor auto-hide with a FLAT threshold (decoupled from content-head graduation), plus the "also looks like content" conflict cut. New presentation_review table (image, presentation tag, conflict tag + score, created/resolved_at) records auto-hides flagged for review. Migration 0082 (columns + table), ml_admin API (editable + get_settings + _validate bounds), settings roundtrip/bounds test. The sweep that reads these knobs + the Settings UI land in step 4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
"""presentation-chrome auto-hide (#141) — settings knobs + review table
|
|
|
|
MLSettings gains presentation_auto_apply_enabled / _threshold and
|
|
presentation_conflict_threshold: banner + editor-screenshot auto-hide on the
|
|
sweep with a FLAT threshold (decoupled from content-head graduation), and a
|
|
conflict threshold that flags an auto-hide that "also looks like content".
|
|
|
|
New table presentation_review records an auto-hidden chrome image that also
|
|
scored high on a content head, surfaced in the Hidden view for a keep-hidden /
|
|
un-hide decision. Resolved rows are pruned by retention.
|
|
|
|
Revision ID: 0082
|
|
Revises: 0081
|
|
Create Date: 2026-07-07
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "0082"
|
|
down_revision: Union[str, None] = "0081"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column(
|
|
"ml_settings",
|
|
sa.Column(
|
|
"presentation_auto_apply_enabled", sa.Boolean(), nullable=False,
|
|
server_default=sa.text("true"),
|
|
),
|
|
)
|
|
op.add_column(
|
|
"ml_settings",
|
|
sa.Column(
|
|
"presentation_auto_apply_threshold", sa.Float(), nullable=False,
|
|
server_default=sa.text("0.90"),
|
|
),
|
|
)
|
|
op.add_column(
|
|
"ml_settings",
|
|
sa.Column(
|
|
"presentation_conflict_threshold", sa.Float(), nullable=False,
|
|
server_default=sa.text("0.50"),
|
|
),
|
|
)
|
|
op.create_table(
|
|
"presentation_review",
|
|
sa.Column(
|
|
"image_record_id", sa.Integer(),
|
|
sa.ForeignKey("image_record.id", ondelete="CASCADE"),
|
|
primary_key=True,
|
|
),
|
|
sa.Column(
|
|
"tag_id", sa.Integer(),
|
|
sa.ForeignKey("tag.id", ondelete="CASCADE"), primary_key=True,
|
|
),
|
|
sa.Column(
|
|
"conflict_tag_id", sa.Integer(),
|
|
sa.ForeignKey("tag.id", ondelete="SET NULL"), nullable=True,
|
|
),
|
|
sa.Column("conflict_score", sa.Float(), nullable=False),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), nullable=False,
|
|
server_default=sa.func.now(),
|
|
),
|
|
sa.Column("resolved_at", sa.DateTime(timezone=True), nullable=True),
|
|
)
|
|
# The review list queries the unresolved flags (resolved_at IS NULL).
|
|
op.create_index(
|
|
"ix_presentation_review_resolved_at", "presentation_review",
|
|
["resolved_at"],
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index(
|
|
"ix_presentation_review_resolved_at", table_name="presentation_review"
|
|
)
|
|
op.drop_table("presentation_review")
|
|
op.drop_column("ml_settings", "presentation_conflict_threshold")
|
|
op.drop_column("ml_settings", "presentation_auto_apply_threshold")
|
|
op.drop_column("ml_settings", "presentation_auto_apply_enabled")
|