From 5357805530f8b02e1cb2a03c223c8e5e50549163 Mon Sep 17 00:00:00 2001 From: Tim Leikauf Date: Wed, 6 May 2026 22:19:42 +0200 Subject: [PATCH] refactor: generate_questions auf natives Directus M2M umgestellt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dedup via deep field query auf Object (1 GET statt 2 Junction-GETs) - Wörter batch-linked via PATCH objects/{id}.linked_words create - Fragen batch-linked via PATCH objects/{id}.linked_questions create - related_words + distractor_words via PATCH questions/{id} create - Keine direkten Junction-Table-POSTs mehr Co-Authored-By: Claude Sonnet 4.6 --- app.py | 60 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/app.py b/app.py index 0c3cb52..ef6f9de 100644 --- a/app.py +++ b/app.py @@ -1165,20 +1165,17 @@ def generate_questions(obj_id: str): # Mehrwortige Einträge → überspringen (KI-Fehler) return tokens - # Bestehende Verlinkungen vorab laden (Dedup ohne N×GET) - ew_data, _ = _directus( + # Bestehende Verlinkungen vorab laden – ein einziger GET auf das Objekt + obj_links_data, _ = _directus( "GET", - f"/items/words_objects?filter[objects_id][_eq]={obj_id}&fields[]=words_id&limit=2000", + f"/items/objects/{obj_id}" + f"?fields[]=linked_words.words_id" + f"&fields[]=linked_questions.questions_id", token, ) - existing_word_ids: set[str] = {e["words_id"] for e in (ew_data.get("data") or [])} - - eq_data, _ = _directus( - "GET", - f"/items/questions_objects?filter[objects_id][_eq]={obj_id}&fields[]=questions_id&limit=200", - token, - ) - existing_question_ids: set[str] = {e["questions_id"] for e in (eq_data.get("data") or [])} + obj_data = obj_links_data.get("data") or {} + existing_word_ids: set[str] = {lw["words_id"] for lw in (obj_data.get("linked_words") or [])} + existing_question_ids: set[str] = {lq["questions_id"] for lq in (obj_data.get("linked_questions") or [])} # Alle eindeutigen Wörter aus allen Leveln vorab sammeln und einmalig laden all_words_by_level: dict[str, int] = {} # title_de → first level seen @@ -1198,15 +1195,18 @@ def generate_questions(obj_id: str): wid, is_new = _find_or_create_word(w, lvl_num, token) global_word_map[w] = wid if wid not in existing_word_ids: - new_word_links.append({"words_id": wid, "objects_id": obj_id}) + new_word_links.append({"words_id": wid}) existing_word_ids.add(wid) stats["words_created" if is_new else "words_linked"] += 1 except Exception as e: print(f"[generate_questions] word error '{w}': {e}") - # Alle neuen Wort-Verlinkungen in einem Batch speichern + # Alle neuen Wort-Verlinkungen in einem Batch via natives M2M if new_word_links: - _directus("POST", "/items/words_objects", token, new_word_links) + _directus("PATCH", f"/items/objects/{obj_id}", token, + {"linked_words": {"create": new_word_links}}) + + new_question_links: list[dict] = [] for lvl in levels: level = int(lvl.get("level") or 1) @@ -1230,27 +1230,27 @@ def generate_questions(obj_id: str): stats["questions_created" if q_is_new else "questions_linked"] += 1 - # Frage ↔ Objekt (nur wenn noch nicht verknüpft) + # Frage ↔ Objekt (für Batch am Ende sammeln) if q_id not in existing_question_ids: - _directus("POST", "/items/questions_objects", token, - {"questions_id": q_id, "objects_id": obj_id}) + new_question_links.append({"questions_id": q_id}) existing_question_ids.add(q_id) - # related_words + distractor_words nur für neue Fragen (batch) + # related_words + distractor_words nur für neue Fragen (batch, natives M2M) if q_is_new: - rw_links = [ - {"questions_id": q_id, "words_id": global_word_map[w]} - for w in words_list if w in global_word_map - ] - if rw_links: - _directus("POST", "/items/questions_words", token, rw_links) + q_patch: dict = {} + rw = [{"words_id": global_word_map[w]} for w in words_list if w in global_word_map] + if rw: + q_patch["related_words"] = {"create": rw} + dw = [{"words_id": global_word_map[w]} for w in distractor_list if w in global_word_map] + if dw: + q_patch["distractor_words"] = {"create": dw} + if q_patch: + _directus("PATCH", f"/items/questions/{q_id}", token, q_patch) - dw_links = [ - {"questions_id": q_id, "words_id": global_word_map[w]} - for w in distractor_list if w in global_word_map - ] - if dw_links: - _directus("POST", "/items/questions_distractor_words", token, dw_links) + # Alle neuen Fragen-Verlinkungen in einem Batch via natives M2M + if new_question_links: + _directus("PATCH", f"/items/objects/{obj_id}", token, + {"linked_questions": {"create": new_question_links}}) print(f"[generate_questions] obj={obj_id} stats={stats}") return jsonify({"ok": True, "object_id": obj_id, "stats": stats})