diff --git a/app.py b/app.py index bcc4a4b..5213315 100644 --- a/app.py +++ b/app.py @@ -1304,6 +1304,43 @@ def purge_orphan_junctions(obj_id: str): 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"]) def fix_distractor_field(): """Setzt special=m2m auf questions.distractor_words (einmalig).""" diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 6c43754..654d7b3 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -291,3 +291,13 @@ export async function purgeOrphans(objId: string, token: string): Promise<{ orph if (!res.ok) throw new Error('Fehler beim Bereinigen') 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 +} diff --git a/frontend/src/pages/GenerateIt.tsx b/frontend/src/pages/GenerateIt.tsx index f5d91fd..7bd3b7f 100644 --- a/frontend/src/pages/GenerateIt.tsx +++ b/frontend/src/pages/GenerateIt.tsx @@ -15,6 +15,7 @@ import { deleteQuestion, deleteWord, purgeOrphans, + purgeAllOrphans, type GenerateStats, type ObjectQuestion, type ObjectWord, @@ -337,6 +338,23 @@ export default function GenerateIt() { > ⚙ Schema + + )