Globale Orphan-Bereinigung: /api/purge-all-orphans + UI-Button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
37
app.py
37
app.py
@@ -1304,6 +1304,43 @@ def purge_orphan_junctions(obj_id: str):
|
|||||||
return jsonify({"ok": True, "orphans_removed": removed})
|
return jsonify({"ok": True, "orphans_removed": removed})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/purge-all-orphans", methods=["POST"])
|
||||||
|
def purge_all_orphans():
|
||||||
|
"""
|
||||||
|
Bereinigt verwaiste Junction-Einträge für ALLE Objekte auf einmal.
|
||||||
|
Lädt alle Junction-Zeilen und prüft, ob das referenzierte Item noch existiert.
|
||||||
|
"""
|
||||||
|
token = request.headers.get("Authorization", "")
|
||||||
|
removed = 0
|
||||||
|
|
||||||
|
for junc_col, fk_field, item_col in [
|
||||||
|
("questions_objects", "questions_id", "questions"),
|
||||||
|
("words_objects", "words_id", "words"),
|
||||||
|
]:
|
||||||
|
junc_data, _ = _directus("GET",
|
||||||
|
f"/items/{junc_col}?fields=id,{fk_field}&limit=10000", token)
|
||||||
|
rows = junc_data.get("data") or []
|
||||||
|
|
||||||
|
# collect all unique FK values
|
||||||
|
fk_ids = list({row[fk_field] for row in rows if row.get(fk_field)})
|
||||||
|
if not fk_ids:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# fetch which IDs still exist in one bulk call
|
||||||
|
ids_param = ",".join(fk_ids)
|
||||||
|
existing_data, _ = _directus("GET",
|
||||||
|
f"/items/{item_col}?filter[id][_in]={ids_param}&fields=id&limit=10000", token)
|
||||||
|
existing_ids = {e["id"] for e in (existing_data.get("data") or [])}
|
||||||
|
|
||||||
|
orphan_junc_ids = [row["id"] for row in rows
|
||||||
|
if row.get(fk_field) and row[fk_field] not in existing_ids]
|
||||||
|
if orphan_junc_ids:
|
||||||
|
_directus("DELETE", f"/items/{junc_col}", token, orphan_junc_ids)
|
||||||
|
removed += len(orphan_junc_ids)
|
||||||
|
|
||||||
|
return jsonify({"ok": True, "orphans_removed": removed})
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/fix-distractor-field", methods=["POST"])
|
@app.route("/api/fix-distractor-field", methods=["POST"])
|
||||||
def fix_distractor_field():
|
def fix_distractor_field():
|
||||||
"""Setzt special=m2m auf questions.distractor_words (einmalig)."""
|
"""Setzt special=m2m auf questions.distractor_words (einmalig)."""
|
||||||
|
|||||||
@@ -291,3 +291,13 @@ export async function purgeOrphans(objId: string, token: string): Promise<{ orph
|
|||||||
if (!res.ok) throw new Error('Fehler beim Bereinigen')
|
if (!res.ok) throw new Error('Fehler beim Bereinigen')
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function purgeAllOrphans(token: string): Promise<{ orphans_removed: number }> {
|
||||||
|
const res = await fetch('/api/purge-all-orphans', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
})
|
||||||
|
const data = await res.json()
|
||||||
|
if (!res.ok) throw new Error('Fehler beim globalen Bereinigen')
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
deleteQuestion,
|
deleteQuestion,
|
||||||
deleteWord,
|
deleteWord,
|
||||||
purgeOrphans,
|
purgeOrphans,
|
||||||
|
purgeAllOrphans,
|
||||||
type GenerateStats,
|
type GenerateStats,
|
||||||
type ObjectQuestion,
|
type ObjectQuestion,
|
||||||
type ObjectWord,
|
type ObjectWord,
|
||||||
@@ -337,6 +338,23 @@ export default function GenerateIt() {
|
|||||||
>
|
>
|
||||||
⚙ Schema
|
⚙ Schema
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="btn-ghost btn-sm"
|
||||||
|
title="Alle verwaisten Junction-Einträge global bereinigen"
|
||||||
|
onClick={async () => {
|
||||||
|
if (!token) return
|
||||||
|
if (!confirm('Alle verwaisten Junction-Einträge (gelöschte Fragen/Wörter) global bereinigen?')) return
|
||||||
|
try {
|
||||||
|
const r = await purgeAllOrphans(token)
|
||||||
|
alert(`Bereinigt: ${r.orphans_removed} verwaiste Einträge entfernt`)
|
||||||
|
} catch (e: unknown) {
|
||||||
|
alert(`Fehler: ${e instanceof Error ? e.message : e}`)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
🧹 Bereinigen
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user