short_answer_de/en/se Felder + Distractor-Wörter in Fragen-Sidebar

- Directus: questions.short_answer_de/en/se Text-Felder angelegt
- Backend: short_answer_de beim Erstellen speichern
- Backend: get_object_questions_list gibt short_answer_de + distractor_words zurück
- Frontend: Sidebar zeigt Kurzantwort (blau) + Ablenker-Chips pro Frage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 20:15:14 +02:00
parent f4a4b40914
commit 47af0d705c
3 changed files with 68 additions and 11 deletions

40
app.py
View File

@@ -937,7 +937,7 @@ def _find_or_create_word(title_de: str, level: int, token: str):
def _find_or_create_question(question_de: str, answer_de: str, level: int,
short_answer_id, obj_id: str, token: str):
short_answer_id, short_answer_de: str, obj_id: str, token: str):
"""Return (question_id, is_new). Creates question with status=draft if missing."""
enc = urllib.parse.quote(question_de, safe="")
data, status = _directus(
@@ -957,6 +957,8 @@ def _find_or_create_question(question_de: str, answer_de: str, level: int,
}
if short_answer_id:
body["short_answer"] = short_answer_id
if short_answer_de:
body["short_answer_de"] = short_answer_de
data, status = _directus("POST", "/items/questions", token, body)
if status in (200, 201):
@@ -1098,7 +1100,7 @@ def generate_questions(obj_id: str):
# Frage anlegen / verknüpfen
try:
q_id, q_is_new = _find_or_create_question(q_de, a_de, level, short_answer_id, obj_id, token)
q_id, q_is_new = _find_or_create_question(q_de, a_de, level, short_answer_id, short_text, obj_id, token)
except Exception as e:
print(f"[generate_questions] question error level {level}: {e}")
continue
@@ -1180,17 +1182,47 @@ def publish_questions(obj_id: str):
@app.route("/api/object/<obj_id>/questions", methods=["GET"])
def get_object_questions_list(obj_id: str):
"""Gibt alle verknüpften Fragen eines Objekts zurück (2-Schritt-Query)."""
"""Gibt alle verknüpften Fragen eines Objekts zurück (mit short_answer_de + distractor_words)."""
token = request.headers.get("Authorization", "")
# Schritt 1: Frage-IDs aus Junction
junc, _ = _directus("GET",
f"/items/questions_objects?filter[objects_id][_eq]={obj_id}&fields=questions_id&limit=200", token)
q_ids = [e["questions_id"] for e in (junc.get("data") or []) if e.get("questions_id")]
if not q_ids:
return jsonify({"data": []})
# Schritt 2: Fragen laden (inkl. short_answer_de)
ids_param = urllib.parse.quote(",".join(q_ids), safe="")
q_data, _ = _directus("GET",
f"/items/questions?filter[id][_in]={ids_param}&fields=id,question_de,answer_de,level,status&limit=200", token)
f"/items/questions?filter[id][_in]={ids_param}&fields=id,question_de,answer_de,short_answer_de,level,status&limit=200", token)
items = sorted(q_data.get("data") or [], key=lambda x: x.get("level") or 0)
# Schritt 3: Distractor-Wörter pro Frage (Bulk)
dw_junc, _ = _directus("GET",
f"/items/questions_distractor_words?filter[questions_id][_in]={ids_param}&fields=questions_id,words_id&limit=5000", token)
dw_entries = dw_junc.get("data") or []
# Wort-IDs sammeln und Titel laden
all_word_ids = list({e["words_id"] for e in dw_entries if e.get("words_id")})
word_title_map: dict[str, str] = {}
if all_word_ids:
wids_param = urllib.parse.quote(",".join(all_word_ids), safe="")
w_data, _ = _directus("GET",
f"/items/words?filter[id][_in]={wids_param}&fields=id,title_de&limit=5000", token)
word_title_map = {w["id"]: w["title_de"] for w in (w_data.get("data") or [])}
# Distractor-Wörter je Frage gruppieren
dw_by_question: dict[str, list[str]] = {}
for e in dw_entries:
qid = e.get("questions_id")
wid = e.get("words_id")
if qid and wid and wid in word_title_map:
dw_by_question.setdefault(qid, []).append(word_title_map[wid])
for item in items:
item["distractor_words"] = dw_by_question.get(item["id"], [])
return jsonify({"data": items})