elkay: api.py submit_quiz generate_quiz
Browse files- utils/api.py +34 -30
utils/api.py
CHANGED
|
@@ -643,46 +643,50 @@ def record_profit_puzzler_play(user_id: int, puzzles_solved: int, mistakes: int,
|
|
| 643 |
return _try_candidates("POST", [("/games/profit_puzzler/record", {"json": payload})])
|
| 644 |
|
| 645 |
|
| 646 |
-
def generate_quiz(lesson_id: int, level_slug: str, lesson_title: str):
|
| 647 |
"""
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
This uses _try_candidates so the code will automatically try likely prefixes
|
| 651 |
-
(e.g. "", "/api", "/v1", "/api/v1") and also fallback to the compatible
|
| 652 |
-
/quiz/generate endpoint (which returns {"items": [...]})
|
| 653 |
"""
|
| 654 |
payload = {
|
| 655 |
-
"lesson_id": int(lesson_id),
|
| 656 |
"level_slug": level_slug,
|
| 657 |
-
"lesson_title": lesson_title
|
|
|
|
| 658 |
}
|
| 659 |
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
except Exception as e:
|
| 668 |
-
# Re-raise with helpful context (Streamlit UI will show this)
|
| 669 |
-
raise RuntimeError(f"generate_quiz failed when contacting backend {BACKEND}: {e}") from e
|
| 670 |
-
|
| 671 |
-
# backend may return {"quiz": [...]} or {"items": [...]} or directly the list
|
| 672 |
if isinstance(resp, dict):
|
| 673 |
-
return resp.get("
|
| 674 |
-
return resp
|
| 675 |
|
| 676 |
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 680 |
"level_slug": level_slug,
|
| 681 |
-
"user_answers": user_answers,
|
| 682 |
-
"original_quiz": original_quiz
|
| 683 |
-
}
|
| 684 |
-
|
| 685 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 686 |
|
| 687 |
def tutor_explain(lesson_id: int, level_slug: str, wrong: list[dict]):
|
| 688 |
r = requests.post(f"{BACKEND}/tutor/explain", json={
|
|
|
|
| 643 |
return _try_candidates("POST", [("/games/profit_puzzler/record", {"json": payload})])
|
| 644 |
|
| 645 |
|
| 646 |
+
def generate_quiz(*, lesson_id: int | None, level_slug: str | None, lesson_title: str | None):
|
| 647 |
"""
|
| 648 |
+
Ask the backend to build a quiz from lesson chunks.
|
| 649 |
+
Returns a list of items: [{question, options:[...], answer_key:"A"}]
|
|
|
|
|
|
|
|
|
|
| 650 |
"""
|
| 651 |
payload = {
|
| 652 |
+
"lesson_id": int(lesson_id) if lesson_id is not None else None,
|
| 653 |
"level_slug": level_slug,
|
| 654 |
+
"lesson_title": lesson_title,
|
| 655 |
+
"k": 12, # how many chunks to pull server-side (optional)
|
| 656 |
}
|
| 657 |
|
| 658 |
+
resp = _try_candidates("POST", [
|
| 659 |
+
("/quiz/auto", {"json": payload}),
|
| 660 |
+
# optional alias if you add one later:
|
| 661 |
+
("/api/quiz/auto", {"json": payload}),
|
| 662 |
+
])
|
| 663 |
+
|
| 664 |
+
# Normalize shapes: {"items":[...]} or just [...]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 665 |
if isinstance(resp, dict):
|
| 666 |
+
return resp.get("items") or resp.get("quiz") or []
|
| 667 |
+
return resp if isinstance(resp, list) else []
|
| 668 |
|
| 669 |
|
| 670 |
+
|
| 671 |
+
def submit_quiz(*, lesson_id: int | None, level_slug: str | None,
|
| 672 |
+
user_answers: list[dict], original_quiz: list[dict]):
|
| 673 |
+
"""
|
| 674 |
+
Grade the quiz on the backend. Returns:
|
| 675 |
+
{"score":{"correct":int,"total":int}, "wrong":[...], "feedback":str}
|
| 676 |
+
"""
|
| 677 |
+
payload = {
|
| 678 |
+
"lesson_id": int(lesson_id) if lesson_id is not None else None,
|
| 679 |
"level_slug": level_slug,
|
| 680 |
+
"user_answers": user_answers, # [{"question":"...", "answer":"A"}...]
|
| 681 |
+
"original_quiz": original_quiz, # [{"question","options","answer_key"}...]
|
| 682 |
+
}
|
| 683 |
+
|
| 684 |
+
return _try_candidates("POST", [
|
| 685 |
+
("/quiz/grade", {"json": payload}),
|
| 686 |
+
# optional alias if you add one later:
|
| 687 |
+
("/api/quiz/grade", {"json": payload}),
|
| 688 |
+
])
|
| 689 |
+
|
| 690 |
|
| 691 |
def tutor_explain(lesson_id: int, level_slug: str, wrong: list[dict]):
|
| 692 |
r = requests.post(f"{BACKEND}/tutor/explain", json={
|