refactor: replace _ensure_junction/_ensure_link with batch Directus ops in generate_questions
- Remove per-call _ensure_junction (junction tables already exist) - Load existing word/question links upfront (2 GET requests instead of N) - Batch POST all new words_objects links in a single request - Batch POST related_words and distractor_words per new question - Eliminates O(N) serial GET+POST pattern in favour of O(1) upfront dedup + batch writes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
81
app.py
81
app.py
@@ -1145,14 +1145,6 @@ def generate_questions(obj_id: str):
|
|||||||
if not levels:
|
if not levels:
|
||||||
return jsonify({"error": "No levels in AI response"}), 500
|
return jsonify({"error": "No levels in AI response"}), 500
|
||||||
|
|
||||||
# Junction-Collections sicherstellen (einmalig)
|
|
||||||
for col, f1, f2 in [
|
|
||||||
("words_objects", "words_id", "objects_id"),
|
|
||||||
("questions_objects", "questions_id", "objects_id"),
|
|
||||||
("questions_distractor_words", "questions_id", "words_id"),
|
|
||||||
]:
|
|
||||||
_ensure_junction(col, f1, f2, token)
|
|
||||||
|
|
||||||
stats = {
|
stats = {
|
||||||
"words_created": 0,
|
"words_created": 0,
|
||||||
"words_linked": 0,
|
"words_linked": 0,
|
||||||
@@ -1173,6 +1165,21 @@ def generate_questions(obj_id: str):
|
|||||||
# Mehrwortige Einträge → überspringen (KI-Fehler)
|
# Mehrwortige Einträge → überspringen (KI-Fehler)
|
||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
|
# Bestehende Verlinkungen vorab laden (Dedup ohne N×GET)
|
||||||
|
ew_data, _ = _directus(
|
||||||
|
"GET",
|
||||||
|
f"/items/words_objects?filter[objects_id][_eq]={obj_id}&fields[]=words_id&limit=2000",
|
||||||
|
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 [])}
|
||||||
|
|
||||||
# Alle eindeutigen Wörter aus allen Leveln vorab sammeln und einmalig laden
|
# Alle eindeutigen Wörter aus allen Leveln vorab sammeln und einmalig laden
|
||||||
all_words_by_level: dict[str, int] = {} # title_de → first level seen
|
all_words_by_level: dict[str, int] = {} # title_de → first level seen
|
||||||
for lvl in levels:
|
for lvl in levels:
|
||||||
@@ -1185,20 +1192,22 @@ def generate_questions(obj_id: str):
|
|||||||
|
|
||||||
# Wörter einmalig anlegen / finden (globaler Cache über alle Level)
|
# Wörter einmalig anlegen / finden (globaler Cache über alle Level)
|
||||||
global_word_map: dict[str, str] = {} # title_de → id
|
global_word_map: dict[str, str] = {} # title_de → id
|
||||||
|
new_word_links: list[dict] = []
|
||||||
for w, lvl_num in all_words_by_level.items():
|
for w, lvl_num in all_words_by_level.items():
|
||||||
try:
|
try:
|
||||||
wid, is_new = _find_or_create_word(w, lvl_num, token)
|
wid, is_new = _find_or_create_word(w, lvl_num, token)
|
||||||
global_word_map[w] = wid
|
global_word_map[w] = wid
|
||||||
_ensure_link(
|
if wid not in existing_word_ids:
|
||||||
"words_objects",
|
new_word_links.append({"words_id": wid, "objects_id": obj_id})
|
||||||
{"words_id": wid, "objects_id": obj_id},
|
existing_word_ids.add(wid)
|
||||||
{"words_id": wid, "objects_id": obj_id},
|
|
||||||
token,
|
|
||||||
)
|
|
||||||
stats["words_created" if is_new else "words_linked"] += 1
|
stats["words_created" if is_new else "words_linked"] += 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[generate_questions] word error '{w}': {e}")
|
print(f"[generate_questions] word error '{w}': {e}")
|
||||||
|
|
||||||
|
# Alle neuen Wort-Verlinkungen in einem Batch speichern
|
||||||
|
if new_word_links:
|
||||||
|
_directus("POST", "/items/words_objects", token, new_word_links)
|
||||||
|
|
||||||
for lvl in levels:
|
for lvl in levels:
|
||||||
level = int(lvl.get("level") or 1)
|
level = int(lvl.get("level") or 1)
|
||||||
q_de = (lvl.get("question") or "").strip()
|
q_de = (lvl.get("question") or "").strip()
|
||||||
@@ -1221,33 +1230,27 @@ def generate_questions(obj_id: str):
|
|||||||
|
|
||||||
stats["questions_created" if q_is_new else "questions_linked"] += 1
|
stats["questions_created" if q_is_new else "questions_linked"] += 1
|
||||||
|
|
||||||
# Frage ↔ Objekt
|
# Frage ↔ Objekt (nur wenn noch nicht verknüpft)
|
||||||
_ensure_link(
|
if q_id not in existing_question_ids:
|
||||||
"questions_objects",
|
_directus("POST", "/items/questions_objects", token,
|
||||||
{"questions_id": q_id, "objects_id": obj_id},
|
{"questions_id": q_id, "objects_id": obj_id})
|
||||||
{"questions_id": q_id, "objects_id": obj_id},
|
existing_question_ids.add(q_id)
|
||||||
token,
|
|
||||||
)
|
|
||||||
|
|
||||||
# related_words
|
# related_words + distractor_words nur für neue Fragen (batch)
|
||||||
for w in words_list:
|
if q_is_new:
|
||||||
if w in global_word_map:
|
rw_links = [
|
||||||
_ensure_link(
|
{"questions_id": q_id, "words_id": global_word_map[w]}
|
||||||
"questions_words",
|
for w in words_list if w in global_word_map
|
||||||
{"questions_id": q_id, "words_id": global_word_map[w]},
|
]
|
||||||
{"questions_id": q_id, "words_id": global_word_map[w]},
|
if rw_links:
|
||||||
token,
|
_directus("POST", "/items/questions_words", token, rw_links)
|
||||||
)
|
|
||||||
|
|
||||||
# distractor_words
|
dw_links = [
|
||||||
for w in distractor_list:
|
{"questions_id": q_id, "words_id": global_word_map[w]}
|
||||||
if w in global_word_map:
|
for w in distractor_list if w in global_word_map
|
||||||
_ensure_link(
|
]
|
||||||
"questions_distractor_words",
|
if dw_links:
|
||||||
{"questions_id": q_id, "words_id": global_word_map[w]},
|
_directus("POST", "/items/questions_distractor_words", token, dw_links)
|
||||||
{"questions_id": q_id, "words_id": global_word_map[w]},
|
|
||||||
token,
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"[generate_questions] obj={obj_id} stats={stats}")
|
print(f"[generate_questions] obj={obj_id} stats={stats}")
|
||||||
return jsonify({"ok": True, "object_id": obj_id, "stats": stats})
|
return jsonify({"ok": True, "object_id": obj_id, "stats": stats})
|
||||||
|
|||||||
Reference in New Issue
Block a user