"""retire deviantart (#3069) — quiesce the rows the dropped platform leaves behind `deviantart` is no longer a registered platform, so nothing can create or edit a source with that key any more. Existing rows are a different question, and the two tables want opposite treatment: * `source` rows are DISABLED, not deleted. The row is the only place the artist's DeviantArt URL is recorded, and losing it is unrecoverable — whereas a disabled row is visible in the UI and reversible by hand. Disabling is also required for correctness, not just tidiness: with the platform unregistered the download path falls through to gallery-dl, which carries its OWN built-in deviantart extractor, so an enabled row would have gone on downloading from a platform the product dropped. * the `credential` row IS deleted. It is an encrypted DeviantArt session cookie for a site FC will never call again — keeping a live credential we have no use for is strictly worse than dropping it, and re-exporting from the browser is the recovery path if that judgment is ever wrong. A no-op on an instance that never had a DeviantArt source, which is the expected case. Revision ID: 0088 Revises: 0087 Create Date: 2026-08-27 """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op revision: str = "0088" down_revision: Union[str, None] = "0087" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None _RETIRED = "deviantart" # Written into last_error so the disabled row explains itself in the UI rather # than looking like an unexplained toggle someone flipped. _REASON = ( "Platform retired: FabledCurator no longer supports DeviantArt " "(dropped 2026-08-27, #3069). This source was disabled automatically; " "the URL is kept for reference and the row can be deleted by hand." ) def upgrade() -> None: conn = op.get_bind() disabled = conn.execute( sa.text( "UPDATE source SET enabled = false, last_error = :reason " "WHERE platform = :p AND enabled = true" ), {"reason": _REASON, "p": _RETIRED}, ).rowcount creds = conn.execute( sa.text("DELETE FROM credential WHERE platform = :p"), {"p": _RETIRED} ).rowcount print(f"0088: disabled {disabled} deviantart source(s), removed {creds} credential(s)") def downgrade() -> None: # Re-enabling is deliberately NOT done: the platform is gone from the # registry, so a re-enabled source would still have no backend to run on. # Clearing the stamped reason is the only half that means anything. op.get_bind().execute( sa.text("UPDATE source SET last_error = NULL WHERE platform = :p AND last_error = :reason"), {"p": _RETIRED, "reason": _REASON}, )