lanna_lalala;- commited on
Commit
fdf0e7a
·
1 Parent(s): 618422f

miniquiz try

Browse files
phase/Student_view/lesson.py CHANGED
@@ -1,7 +1,6 @@
1
  import streamlit as st
2
  from typing import List, Dict, Any, Optional, Tuple
3
  import re
4
- import datetime
5
  import os
6
  from utils import db as dbapi
7
  from utils import api as backend_api # unified backend client
@@ -28,10 +27,7 @@ _SS_DEFAULTS = {
28
  "topic_idx": 0, # 0-based within module
29
  "mode": "catalog", # catalog | lesson | quiz | results
30
  "topics_cache": {}, # {(level, module_id): [(title, text), ...]}
31
- # "quiz_data": None, # original quiz payload (list[dict])
32
- # "quiz_answers": {}, # q_index -> "A"|"B"|"C"|"D"
33
- # "quiz_result": None, # backend result dict
34
- # "chatbot_feedback": None, # str
35
  }
36
 
37
  def _ensure_state():
@@ -219,90 +215,6 @@ def _extract_takeaways(text: str, max_items: int = 5) -> List[str]:
219
  sents = re.split(r"(?<=[.!?])\s+", text.strip())
220
  return [s for s in sents if len(s) > 20][:min(max_items, 3)]
221
 
222
- # def _start_quiz(level: str, module_id: int) -> Optional[List[Dict[str, Any]]]:
223
- # """Ask backend to generate a 5-question mini quiz for this module."""
224
- # module_conf = next(m for m in MODULES_META[level] if m["id"] == module_id)
225
- # try:
226
- # quiz = backend_api.generate_quiz(
227
- # lesson_id=module_id,
228
- # level_slug=level,
229
- # lesson_title=module_conf["title"],
230
- # )
231
- # if isinstance(quiz, list) and quiz:
232
- # return quiz
233
- # return None
234
- # except Exception as e:
235
- # st.error(f"Could not generate quiz: {e}")
236
- # return None
237
-
238
- # def _submit_quiz(level: str, module_id: int, original_quiz: List[Dict[str, Any]], answers_map: Dict[int, str]) -> Optional[Dict[str, Any]]:
239
- # """Submit answers and get score + tutor feedback."""
240
- # user_answers = []
241
- # for i, q in enumerate(original_quiz):
242
- # user_answers.append({
243
- # "question": q.get("question", f"Q{i+1}"),
244
- # "answer": answers_map.get(i, ""), # "A".."D"
245
- # })
246
-
247
- # # student_id is required by utils/api.submit_quiz
248
- # student_id = int(((st.session_state.get("user") or {}).get("user_id") or 0))
249
-
250
- # try:
251
- # result = backend_api.submit_quiz(
252
- # student_id=student_id,
253
- # lesson_id=module_id,
254
- # level_slug=level,
255
- # user_answers=user_answers,
256
- # original_quiz=original_quiz,
257
- # )
258
- # return result
259
- # except Exception as e:
260
- # st.error(f"Could not submit quiz: {e}")
261
- # return None
262
-
263
- # def _send_quiz_summary_to_chatbot(result: Dict[str, Any]):
264
- # """
265
- # Send a concise, actionable summary of the quiz outcome to the chatbot,
266
- # then navigate to the Chatbot page with the conversation pre-seeded.
267
- # """
268
- # level = st.session_state.level
269
- # module_id = st.session_state.module_id
270
- # mod = next(m for m in MODULES_META[level] if m["id"] == module_id)
271
-
272
- # score = result.get("score", {})
273
- # correct = int(score.get("correct", 0))
274
- # total = int(score.get("total", 0))
275
- # feedback = (result.get("feedback") or st.session_state.get("chatbot_feedback") or "").strip()
276
-
277
- # user_prompt = (
278
- # f"I just finished the quiz for '{mod['title']}' (module {module_id}) "
279
- # f"and scored {correct}/{total}. Please give me 2–3 targeted tips and 1 tiny action "
280
- # f"to improve before the next lesson. If there were wrong answers, explain them simply.\n\n"
281
- # f"Context from grader:\n{feedback}"
282
- # )
283
-
284
- # try:
285
- # # Call FastAPI /chat via utils.api.chat_ai
286
- # bot_reply = (backend_api.chat_ai(
287
- # query=user_prompt,
288
- # lesson_id=module_id,
289
- # level_slug=level,
290
- # history=[]
291
- # ) or "").strip()
292
- # except Exception:
293
- # bot_reply = f"(Chatbot unavailable) Based on your result: {feedback or 'Nice work!'}"
294
-
295
- # # Seed Chatbot page
296
- # msgs = st.session_state.get("messages") or [{
297
- # "id": "1",
298
- # "text": "Hi! I'm your AI Financial Tutor. What would you like to learn today?",
299
- # "sender": "assistant",
300
- # "timestamp": datetime.datetime.now(),
301
- # }]
302
- # msgs.append({"text": user_prompt, "sender": "user", "timestamp": datetime.datetime.now()})
303
- # msgs.append({"text": bot_reply, "sender": "assistant", "timestamp": datetime.datetime.now()})
304
- # st.session_state.messages = msgs
305
- # st.session_state.current_page = "Chatbot"
306
 
307
  def _fallback_text(title: str, module_id: int, topic_ordinal: int) -> str:
308
  """
@@ -387,8 +299,6 @@ def _get_topics(level: str, module_id: int) -> List[Tuple[str, str]]:
387
  st.session_state.topics_cache[cache_key] = out
388
  return out
389
 
390
- # def _letter_for(i: int) -> str:
391
- # return chr(ord("A") + i)
392
 
393
  def _render_lesson():
394
  ensure_quiz_state() # make sure quiz keys exist
@@ -483,51 +393,6 @@ def _render_lesson():
483
  st.button(label, key=f"jump_{i}", on_click=lambda j=i: st.session_state.update({"topic_idx": j}) or st.rerun())
484
 
485
 
486
- # def _render_quiz():
487
- # quiz: List[Dict[str, Any]] = st.session_state.quiz_data or []
488
- # if not quiz:
489
- # st.session_state.mode = "lesson"
490
- # st.rerun()
491
-
492
- # st.markdown("### Lesson Quiz")
493
-
494
- # # Render each question (single page)
495
- # for q_idx, q in enumerate(quiz):
496
- # st.markdown(f"**Q{q_idx+1}. {q.get('question','').strip()}**")
497
- # opts = q.get("options") or []
498
-
499
- # def _on_select():
500
- # sel = st.session_state[f"ans_{q_idx}"] # "A. option text"
501
- # letter = sel.split(".", 1)[0] if isinstance(sel, str) else ""
502
- # st.session_state.quiz_answers[q_idx] = letter
503
-
504
- # labels = [f"{_letter_for(i)}. {opt}" for i, opt in enumerate(opts)]
505
- # saved_letter = st.session_state.quiz_answers.get(q_idx)
506
- # pre_idx = next((i for i, l in enumerate(labels) if saved_letter and l.startswith(f"{saved_letter}.")), None)
507
-
508
- # st.radio(
509
- # "",
510
- # labels,
511
- # index=pre_idx,
512
- # key=f"ans_{q_idx}",
513
- # on_change=_on_select,
514
- # )
515
- # st.divider()
516
-
517
- # all_answered = len(st.session_state.quiz_answers) == len(quiz)
518
- # if st.button("Submit Quiz", disabled=not all_answered):
519
- # with st.spinner("Grading…"):
520
- # result = _submit_quiz(
521
- # st.session_state.level,
522
- # st.session_state.module_id,
523
- # quiz,
524
- # st.session_state.quiz_answers,
525
- # )
526
- # if result:
527
- # st.session_state.quiz_result = result
528
- # st.session_state.chatbot_feedback = result.get("feedback")
529
- # _send_quiz_summary_to_chatbot(result)
530
- # st.rerun()
531
 
532
  def _render_results():
533
  result = st.session_state.quiz_result or {}
@@ -557,10 +422,7 @@ def _render_results():
557
 
558
  # Let mini_quiz handle the full results UI (review, feedback, nav)
559
  quiz_results(planned_topics=planned)
560
- # try:
561
- # quiz_index = [t.strip().lower() for t in planned].index("quiz")
562
- # except ValueError:
563
- # quiz_index = None
564
 
565
  c1, c2, c3 = st.columns([1, 1, 1])
566
  with c1:
 
1
  import streamlit as st
2
  from typing import List, Dict, Any, Optional, Tuple
3
  import re
 
4
  import os
5
  from utils import db as dbapi
6
  from utils import api as backend_api # unified backend client
 
27
  "topic_idx": 0, # 0-based within module
28
  "mode": "catalog", # catalog | lesson | quiz | results
29
  "topics_cache": {}, # {(level, module_id): [(title, text), ...]}
30
+
 
 
 
31
  }
32
 
33
  def _ensure_state():
 
215
  sents = re.split(r"(?<=[.!?])\s+", text.strip())
216
  return [s for s in sents if len(s) > 20][:min(max_items, 3)]
217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
 
219
  def _fallback_text(title: str, module_id: int, topic_ordinal: int) -> str:
220
  """
 
299
  st.session_state.topics_cache[cache_key] = out
300
  return out
301
 
 
 
302
 
303
  def _render_lesson():
304
  ensure_quiz_state() # make sure quiz keys exist
 
393
  st.button(label, key=f"jump_{i}", on_click=lambda j=i: st.session_state.update({"topic_idx": j}) or st.rerun())
394
 
395
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
 
397
  def _render_results():
398
  result = st.session_state.quiz_result or {}
 
422
 
423
  # Let mini_quiz handle the full results UI (review, feedback, nav)
424
  quiz_results(planned_topics=planned)
425
+
 
 
 
426
 
427
  c1, c2, c3 = st.columns([1, 1, 1])
428
  with c1:
phase/Student_view/mini_quiz.py CHANGED
@@ -29,11 +29,11 @@ def ensure_quiz_state():
29
  # Backend calls (isolated here)
30
  # ---------------------------------------------
31
 
 
32
  def start_quiz(level: str, module_id: int, lesson_title: str) -> bool:
33
- """Generate and stage a quiz for the given module. Returns True on success."""
34
  ensure_quiz_state()
35
  try:
36
- quiz = backend_api.generate_quiz(
37
  lesson_id=module_id,
38
  level_slug=level,
39
  lesson_title=lesson_title,
@@ -42,24 +42,38 @@ def start_quiz(level: str, module_id: int, lesson_title: str) -> bool:
42
  st.error(f"Could not generate quiz: {e}")
43
  return False
44
 
45
- if isinstance(quiz, list) and quiz:
46
- st.session_state.quiz_data = quiz
47
- st.session_state.quiz_answers = {}
48
- st.session_state.mode = "quiz"
49
- return True
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
- st.error("Quiz could not be generated. Please try again.")
52
- return False
 
 
53
 
54
 
55
  def submit_quiz(level: str, module_id: int, original_quiz: List[Dict[str, Any]], answers_map: Dict[int, str]) -> Optional[Dict[str, Any]]:
56
  """Submit answers and return the grading result dict."""
 
57
  user_answers = []
58
- for i, q in enumerate(original_quiz):
59
- user_answers.append({
60
- "question": q.get("question", f"Q{i+1}"),
61
- "answer": answers_map.get(i, ""), # "A".."D"
62
- })
63
 
64
  # student_id is required by utils.api.submit_quiz
65
  student_id = int(((st.session_state.get("user") or {}).get("user_id") or 0))
 
29
  # Backend calls (isolated here)
30
  # ---------------------------------------------
31
 
32
+ # --- in start_quiz() ---
33
  def start_quiz(level: str, module_id: int, lesson_title: str) -> bool:
 
34
  ensure_quiz_state()
35
  try:
36
+ resp = backend_api.generate_quiz(
37
  lesson_id=module_id,
38
  level_slug=level,
39
  lesson_title=lesson_title,
 
42
  st.error(f"Could not generate quiz: {e}")
43
  return False
44
 
45
+ # Accept both the old (list) and new (dict with 'items') shapes
46
+ items = resp.get("items") if isinstance(resp, dict) else resp
47
+ if not isinstance(items, list) or not items:
48
+ st.error("Quiz could not be generated. Please try again.")
49
+ return False
50
+
51
+ # Normalize: ensure id + answer_key are present
52
+ normalized = []
53
+ for i, q in enumerate(items, start=1):
54
+ qid = q.get("id") or q.get("qid") or q.get("position") or f"q{i}"
55
+ answer_key = q.get("answer_key") or q.get("answer") # backend may send either
56
+ normalized.append({
57
+ "id": qid,
58
+ "question": (q.get("question") or "").strip(),
59
+ "options": q.get("options") or [],
60
+ "answer_key": answer_key, # keep for grading payload
61
+ })
62
 
63
+ st.session_state.quiz_data = normalized
64
+ st.session_state.quiz_answers = {}
65
+ st.session_state.mode = "quiz"
66
+ return True
67
 
68
 
69
  def submit_quiz(level: str, module_id: int, original_quiz: List[Dict[str, Any]], answers_map: Dict[int, str]) -> Optional[Dict[str, Any]]:
70
  """Submit answers and return the grading result dict."""
71
+ # --- in submit_quiz() ---
72
  user_answers = []
73
+ for q in original_quiz:
74
+ qid = q.get("id") or q.get("question") # fallback
75
+ user_answers.append({"id": qid, "answer": answers_map.get(original_quiz.index(q), "")})
76
+
 
77
 
78
  # student_id is required by utils.api.submit_quiz
79
  student_id = int(((st.session_state.get("user") or {}).get("user_id") or 0))
utils/api.py CHANGED
@@ -27,7 +27,7 @@ _session.mount("https://", HTTPAdapter(max_retries=retry))
27
  _session.mount("http://", HTTPAdapter(max_retries=retry))
28
  _session.headers.update({
29
  "Accept": "application/json, */*;q=0.1",
30
- "User-Agent": "FinEdu-Frontend/1.0 (+spaces)",
31
  })
32
  if TOKEN:
33
  _session.headers["Authorization"] = f"Bearer {TOKEN}"
 
27
  _session.mount("http://", HTTPAdapter(max_retries=retry))
28
  _session.headers.update({
29
  "Accept": "application/json, */*;q=0.1",
30
+ "User-Agent": "FInFront/1.0 (+spaces)",
31
  })
32
  if TOKEN:
33
  _session.headers["Authorization"] = f"Bearer {TOKEN}"