feat(tags): 'Reset content tagging' admin action
Wipe every general + character tag so the operator can re-tag from scratch via
the Camie auto-suggest, while PRESERVING fandoms, series (+ series_page order),
and each image's stored tagger_predictions (so suggestions repopulate
immediately). One set-based DELETE FROM tag WHERE kind IN ('general','character')
— the five tag-referencing tables all cascade, so applications + aliases +
allowlist + rejections + centroids clear automatically; series tags aren't
deleted so series survive; Tag.fandom_id is SET NULL so fandoms are untouched.
Reuses the established dry-run-preview -> confirm pattern: cleanup_service.
reset_content_tagging() + POST /api/admin/tags/reset-content +
TagMaintenanceCard section with a backup-first warning and a red confirm
showing exact counts (tags by kind + image applications). Irreversible except
via DB backup restore; the wipe only fires when the operator confirms.
Tests: service dry-run counts + live delete preserves fandom/series/series_page
while content tags + their image_tag cascade away; API dry-run wiring.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -435,3 +435,26 @@ async def test_trigger_vacuum_queues_the_task(client, monkeypatch):
|
||||
resp = await client.post("/api/admin/maintenance/vacuum")
|
||||
assert resp.status_code == 202
|
||||
assert calls == [1]
|
||||
|
||||
|
||||
# --- Tier-A: POST /tags/reset-content -------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reset_content_tagging_dry_run_returns_counts(client, db):
|
||||
db.add_all([
|
||||
Tag(name="solo", kind=TagKind.general),
|
||||
Tag(name="naruto", kind=TagKind.character),
|
||||
Tag(name="Naruto", kind=TagKind.fandom),
|
||||
Tag(name="my-series", kind=TagKind.series),
|
||||
])
|
||||
await db.commit()
|
||||
resp = await client.post(
|
||||
"/api/admin/tags/reset-content", json={"dry_run": True}
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
body = await resp.get_json()
|
||||
assert body["count"] == 2
|
||||
assert body["by_kind"] == {"general": 1, "character": 1}
|
||||
# dry-run leaves the rows in place — fandom + series untouched too.
|
||||
assert "deleted" not in body
|
||||
|
||||
Reference in New Issue
Block a user