This reverts 2529b51. Not a retreat — a reordering, on the operator's
call, and the better sequence.
The squash's acceptance test (run 4971) found ~130 places where the ORM
models do not describe the deployed schema (#3275), including a
unique=True the database never had and two UNIQUE indexes that exist
only in migrations. Collapsing now would have baked all of that into the
one file a public installer starts from.
So: fix the drift first as ordinary migrations on the intact chain, let
the operator deploy so their database moves to the corrected head, and
only then collapse. The baseline is then generated from reconciled
models and reproduces a schema worth reproducing.
Nothing is lost by reverting. The baseline was never deployed, and
regenerating it after the fixes is strictly better than patching this
copy — it will come out of autogenerate correct rather than needing the
same hand-finishing twice.
81 lines
2.5 KiB
Python
81 lines
2.5 KiB
Python
"""backfill post.post_title from description first-line — 2026-05-27
|
|
|
|
Revision ID: 0024
|
|
Revises: 0023
|
|
Create Date: 2026-05-27
|
|
|
|
SubscribeStar gallery-dl always writes `title: ""` and embeds the leading
|
|
sentence inside `content` HTML. FC's sidecar parser was leaving
|
|
post_title NULL for every SubscribeStar post since FC-3 shipped. The
|
|
parser fix (sidecar._first_line_text fallback) now synthesizes a title
|
|
at parse time; this migration applies the same logic retroactively to
|
|
existing rows.
|
|
|
|
Operator-flagged 2026-05-27 after inspecting
|
|
/mnt/Data/Patreon/Cheunart/subscribestar/ sidecars.
|
|
|
|
Idempotent: only touches rows where post_title IS NULL or empty AND
|
|
description IS NOT NULL. Re-running the migration is a no-op.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
from sqlalchemy import text
|
|
|
|
revision: str = "0024"
|
|
down_revision: Union[str, None] = "0023"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
_TAG_RE = re.compile(r"<[^>]+>")
|
|
_WS_RE = re.compile(r"\s+")
|
|
|
|
|
|
def _first_line_text(body: str, limit: int = 120) -> str | None:
|
|
"""Mirror of sidecar._first_line_text. Kept inline so the migration
|
|
doesn't carry a runtime import dependency from app code that may
|
|
have moved by the time the migration is replayed years from now."""
|
|
if not body:
|
|
return None
|
|
text_ = _TAG_RE.sub(" ", body)
|
|
text_ = text_.replace("\xa0", " ")
|
|
for line in text_.splitlines():
|
|
line = _WS_RE.sub(" ", line).strip()
|
|
if line:
|
|
if len(line) > limit:
|
|
return line[: limit - 1].rstrip() + "…"
|
|
return line
|
|
return None
|
|
|
|
|
|
def upgrade() -> None:
|
|
bind = op.get_bind()
|
|
rows = bind.execute(
|
|
text(
|
|
"SELECT id, description FROM post "
|
|
"WHERE (post_title IS NULL OR post_title = '') "
|
|
"AND description IS NOT NULL AND description <> ''"
|
|
)
|
|
).fetchall()
|
|
updated = 0
|
|
for row in rows:
|
|
derived = _first_line_text(row.description)
|
|
if not derived:
|
|
continue
|
|
bind.execute(
|
|
text("UPDATE post SET post_title = :t WHERE id = :id"),
|
|
{"t": derived, "id": row.id},
|
|
)
|
|
updated += 1
|
|
print(f"0024: backfilled post_title on {updated} row(s)")
|
|
|
|
|
|
def downgrade() -> None:
|
|
# No safe restore — we can't tell which post_titles were derived vs
|
|
# genuinely present. Leave the column alone on rollback.
|
|
pass
|