FInFront / phase /Teacher_view /contentmanage.py
lanna_lalala;-
added folders
0aa6283
# phase/Teacher_view/contentmanage.py
import json
import os
from datetime import datetime
import streamlit as st
from utils import db as dbapi
import utils.api as api # backend Space client
# Switch automatically: if DISABLE_DB=1 (default), use backend API; else use local DB
USE_LOCAL_DB = os.getenv("DISABLE_DB", "1") != "1"
# ---------- small UI helpers ----------
def _pill(text):
return f"<span style='background:#eef6ff;border:1px solid #cfe3ff;border-radius:999px;padding:2px 8px;font-size:12px;margin-right:6px'>{text}</span>"
def _progress(val: float):
pct = max(0, min(100, int(round(val * 100))))
return f"""
<div style="height:8px;background:#eef2ff;border-radius:999px;overflow:hidden">
<div style="width:{pct}%;height:100%;background:#3b82f6"></div>
</div>
"""
def _fmt_date(v):
if isinstance(v, datetime):
return v.strftime("%Y-%m-%d")
try:
s = str(v)
return s[:10]
except Exception:
return ""
# ---------- Quiz generator via backend LLM (llama 3.1 8B) ----------
def _generate_quiz_from_text(content: str, n_questions: int = 5, subject: str = "finance", level: str = "beginner"):
"""
Calls your backend, which uses GEN_MODEL (llama-3.1-8b-instruct).
Returns a normalized list like:
[{"question":"...","options":["A","B","C","D"],"answer_key":"B","points":1}, ...]
"""
def _normalize(items):
out = []
for it in (items or [])[:n_questions]:
q = str(it.get("question", "")).strip()
opts = it.get("options", [])
if not q or not isinstance(opts, list):
continue
while len(opts) < 4:
opts.append("Option")
opts = opts[:4]
key = str(it.get("answer_key", "A")).strip().upper()[:1]
if key not in ("A","B","C","D"):
key = "A"
out.append({"question": q, "options": opts, "answer_key": key, "points": 1})
return out
try:
resp = api.generate_quiz_from_text(content, n_questions=n_questions, subject=subject, level=level)
items = resp.get("items", resp) # allow backend to return either shape
return _normalize(items)
except Exception as e:
with st.expander("Quiz generation error details"):
st.code(str(e))
st.warning("Quiz generation failed via backend. Check the /quiz/generate endpoint and GEN_MODEL.")
return []
# ---------- Thin wrappers that choose DB or Backend ----------
def _list_classes_by_teacher(teacher_id: int):
if USE_LOCAL_DB and hasattr(dbapi, "list_classes_by_teacher"):
return dbapi.list_classes_by_teacher(teacher_id)
try:
return api.list_classes_by_teacher(teacher_id)
except Exception:
return []
def _list_all_students_for_teacher(teacher_id: int):
if USE_LOCAL_DB and hasattr(dbapi, "list_all_students_for_teacher"):
return dbapi.list_all_students_for_teacher(teacher_id)
try:
return api.list_all_students_for_teacher(teacher_id)
except Exception:
return []
def _list_lessons_by_teacher(teacher_id: int):
if USE_LOCAL_DB and hasattr(dbapi, "list_lessons_by_teacher"):
return dbapi.list_lessons_by_teacher(teacher_id)
try:
return api.list_lessons_by_teacher(teacher_id)
except Exception:
return []
def _list_quizzes_by_teacher(teacher_id: int):
if USE_LOCAL_DB and hasattr(dbapi, "list_quizzes_by_teacher"):
return dbapi.list_quizzes_by_teacher(teacher_id)
try:
return api.list_quizzes_by_teacher(teacher_id)
except Exception:
return []
def _create_lesson(teacher_id: int, title: str, description: str, subject: str, level: str, sections: list[dict]):
if USE_LOCAL_DB and hasattr(dbapi, "create_lesson"):
return dbapi.create_lesson(teacher_id, title, description, subject, level, sections)
return api.create_lesson(teacher_id, title, description, subject, level, sections)
def _update_lesson(lesson_id: int, teacher_id: int, title: str, description: str, subject: str, level: str, sections: list[dict]):
if USE_LOCAL_DB and hasattr(dbapi, "update_lesson"):
return dbapi.update_lesson(lesson_id, teacher_id, title, description, subject, level, sections)
return api.update_lesson(lesson_id, teacher_id, title, description, subject, level, sections)
def _delete_lesson(lesson_id: int, teacher_id: int):
if USE_LOCAL_DB and hasattr(dbapi, "delete_lesson"):
return dbapi.delete_lesson(lesson_id, teacher_id)
return api.delete_lesson(lesson_id, teacher_id)
def _get_lesson(lesson_id: int):
if USE_LOCAL_DB and hasattr(dbapi, "get_lesson"):
return dbapi.get_lesson(lesson_id)
return api.get_lesson(lesson_id)
def _create_quiz(lesson_id: int, title: str, items: list[dict], settings: dict):
if USE_LOCAL_DB and hasattr(dbapi, "create_quiz"):
return dbapi.create_quiz(lesson_id, title, items, settings)
return api.create_quiz(lesson_id, title, items, settings)
def _update_quiz(quiz_id: int, teacher_id: int, title: str, items: list[dict], settings: dict):
if USE_LOCAL_DB and hasattr(dbapi, "update_quiz"):
return dbapi.update_quiz(quiz_id, teacher_id, title, items, settings)
return api.update_quiz(quiz_id, teacher_id, title, items, settings)
def _delete_quiz(quiz_id: int, teacher_id: int):
if USE_LOCAL_DB and hasattr(dbapi, "delete_quiz"):
return dbapi.delete_quiz(quiz_id, teacher_id)
return api.delete_quiz(quiz_id, teacher_id)
def _list_assigned_students_for_lesson(lesson_id: int):
if USE_LOCAL_DB and hasattr(dbapi, "list_assigned_students_for_lesson"):
return dbapi.list_assigned_students_for_lesson(lesson_id)
return api.list_assigned_students_for_lesson(lesson_id)
def _list_assigned_students_for_quiz(quiz_id: int):
if USE_LOCAL_DB and hasattr(dbapi, "list_assigned_students_for_quiz"):
return dbapi.list_assigned_students_for_quiz(quiz_id)
return api.list_assigned_students_for_quiz(quiz_id)
def _assign_to_class(lesson_id: int | None, quiz_id: int | None, class_id: int, teacher_id: int):
if USE_LOCAL_DB and hasattr(dbapi, "assign_to_class"):
return dbapi.assign_to_class(lesson_id, quiz_id, class_id, teacher_id)
return api.assign_to_class(lesson_id, quiz_id, class_id, teacher_id)
# ---------- Create panels ----------
def _create_lesson_panel(teacher_id: int):
st.markdown("### ✍️ Create New Lesson")
classes = _list_classes_by_teacher(teacher_id)
class_opts = {f"{c['name']} (code {c['code']})": c["class_id"] for c in classes} if classes else {}
if "cl_topic_count" not in st.session_state:
st.session_state.cl_topic_count = 2 # start with two topics
cols_btn = st.columns([1,1,6])
with cols_btn[0]:
if st.button("➕ Add topic", type="secondary"):
st.session_state.cl_topic_count = min(20, st.session_state.cl_topic_count + 1)
st.rerun()
with cols_btn[1]:
if st.button("➖ Remove last", type="secondary", disabled=st.session_state.cl_topic_count <= 1):
st.session_state.cl_topic_count = max(1, st.session_state.cl_topic_count - 1)
st.rerun()
with st.form("create_lesson_form", clear_on_submit=False):
c1, c2 = st.columns([2,1])
title = c1.text_input("Title", placeholder="e.g., Jamaican Money Recognition")
level = c2.selectbox("Level", ["beginner","intermediate","advanced"], index=0)
description = st.text_area("Short description")
subject = st.selectbox("Subject", ["numeracy","finance"], index=0)
st.markdown("#### Topics")
topic_rows = []
for i in range(1, st.session_state.cl_topic_count + 1):
with st.expander(f"Topic {i}", expanded=True if i <= 2 else False):
t = st.text_input(f"Topic {i} title", key=f"t_title_{i}")
b = st.text_area(f"Topic {i} content", key=f"t_body_{i}", height=150)
topic_rows.append((t, b))
add_summary = st.checkbox("Append a Summary section at the end", value=True)
summary_text = ""
if add_summary:
summary_text = st.text_area(
"Summary notes",
key="summary_notes",
height=120,
placeholder="Key ideas, local examples, common mistakes, quick recap..."
)
st.markdown("#### Assign to class (optional)")
assign_classes = st.multiselect("Choose one or more classes", list(class_opts.keys()))
st.markdown("#### Auto-generate a quiz from this lesson (optional)")
gen_quiz = st.checkbox("Generate a quiz from content", value=False)
q_count = st.slider("", 3, 10, 5)
submitted = st.form_submit_button("Create lesson", type="primary")
if not submitted:
return
sections = []
for t, b in topic_rows:
if (t or b):
sections.append({"title": t or "Topic", "content": b or ""})
if add_summary:
sections.append({
"title": "Summary",
"content": (summary_text or "Write a short recap of the most important ideas.").strip()
})
if not title or not sections:
st.error("Please add a title and at least one topic.")
return
# create lesson (DB or backend)
try:
lesson_id = _create_lesson(teacher_id, title, description, subject, level, sections)
st.success(f"✅ Lesson created (ID {lesson_id}).")
except Exception as e:
st.error(f"Failed to create lesson: {e}")
return
# assign to chosen classes (lesson only for now)
for label in assign_classes:
try:
_assign_to_class(lesson_id, None, class_opts[label], teacher_id)
except Exception as e:
st.warning(f"Could not assign to {label}: {e}")
# auto-generate quiz via backend LLM
if gen_quiz:
text = "\n\n".join([s["title"] + "\n" + (s["content"] or "") for s in sections])
with st.spinner("Generating quiz from lesson content..."):
items = _generate_quiz_from_text(text, n_questions=q_count, subject=subject, level=level)
if items:
try:
qid = _create_quiz(lesson_id, f"{title} - Quiz", items, {})
st.success(f"🧠 Quiz generated and saved (ID {qid}).")
for label in assign_classes:
_assign_to_class(lesson_id, qid, class_opts[label], teacher_id)
except Exception as e:
st.warning(f"Lesson saved, but failed to save quiz: {e}")
st.session_state.show_create_lesson = False
st.rerun()
def _create_quiz_panel(teacher_id: int):
st.markdown("### 🏆 Create New Quiz")
lessons = _list_lessons_by_teacher(teacher_id)
lesson_map = {f"{L['title']} (#{L['lesson_id']})": L["lesson_id"] for L in lessons}
if not lesson_map:
st.info("Create a lesson first, then link a quiz to it.")
return
if "cq_q_count" not in st.session_state:
st.session_state.cq_q_count = 5
with st.form("create_quiz_form", clear_on_submit=False):
c1, c2 = st.columns([2,1])
title = c1.text_input("Title", placeholder="e.g., Currency Basics Quiz")
lesson_label = c2.selectbox("Linked Lesson", list(lesson_map.keys()))
st.markdown("#### Questions (up to 10)")
items = []
for i in range(1, st.session_state.cq_q_count + 1):
with st.expander(f"Question {i}", expanded=(i <= 2)):
q = st.text_area(f"Prompt {i}", key=f"q_{i}")
cA, cB = st.columns(2)
a = cA.text_input(f"Option A (correct?)", key=f"optA_{i}")
b = cB.text_input(f"Option B", key=f"optB_{i}")
cC, cD = st.columns(2)
c = cC.text_input(f"Option C", key=f"optC_{i}")
d = cD.text_input(f"Option D", key=f"optD_{i}")
correct = st.radio("Correct answer", ["A","B","C","D"], index=0, key=f"ans_{i}", horizontal=True)
items.append({"question": q, "options": [a,b,c,d], "answer_key": correct, "points": 1})
row = st.columns([1,1,4,2])
with row[0]:
if st.form_submit_button("➕ Add question", type="secondary", disabled=st.session_state.cq_q_count >= 10):
st.session_state.cq_q_count = min(10, st.session_state.cq_q_count + 1)
st.rerun()
with row[1]:
if st.form_submit_button("➖ Remove last", type="secondary", disabled=st.session_state.cq_q_count <= 1):
st.session_state.cq_q_count = max(1, st.session_state.cq_q_count - 1)
st.rerun()
submitted = row[3].form_submit_button("Create quiz", type="primary")
if not submitted:
return
if not title:
st.error("Please add a quiz title.")
return
cleaned = []
for it in items:
q = (it["question"] or "").strip()
opts = [o for o in it["options"] if (o or "").strip()]
if len(opts) < 2 or not q:
continue
while len(opts) < 4:
opts.append("Option")
cleaned.append({"question": q, "options": opts[:4], "answer_key": it["answer_key"], "points": 1})
if not cleaned:
st.error("Add at least one valid question.")
return
try:
qid = _create_quiz(lesson_map[lesson_label], title, cleaned, {})
st.success(f"✅ Quiz created (ID {qid}).")
st.session_state.show_create_quiz = False
st.rerun()
except Exception as e:
st.error(f"Failed to create quiz: {e}")
def _edit_lesson_panel(teacher_id: int, lesson_id: int):
try:
data = _get_lesson(lesson_id)
except Exception as e:
st.error(f"Could not load lesson #{lesson_id}: {e}")
return
L = data.get("lesson", {})
secs = data.get("sections", []) or []
key_cnt = f"el_cnt_{lesson_id}"
if key_cnt not in st.session_state:
st.session_state[key_cnt] = max(1, len(secs))
st.markdown("### ✏️ Edit Lesson")
tools = st.columns([1,1,8])
with tools[0]:
if st.button("➕ Add section", key=f"el_add_{lesson_id}", use_container_width=True):
st.session_state[key_cnt] = min(50, st.session_state[key_cnt] + 1)
st.rerun()
with tools[1]:
if st.button("➖ Remove last", key=f"el_rem_{lesson_id}",
disabled=st.session_state[key_cnt] <= 1, use_container_width=True):
st.session_state[key_cnt] = max(1, st.session_state[key_cnt] - 1)
st.rerun()
with st.form(f"edit_lesson_form_{lesson_id}", clear_on_submit=False):
c1, c2 = st.columns([2,1])
title = c1.text_input("Title", value=L.get("title") or "")
level = c2.selectbox(
"Level",
["beginner","intermediate","advanced"],
index=["beginner","intermediate","advanced"].index(L.get("level") or "beginner")
)
description = st.text_area("Short description", value=L.get("description") or "")
subject = st.selectbox("Subject", ["numeracy","finance"], index=(0 if (L.get("subject")=="numeracy") else 1))
st.markdown("#### Sections")
edited_sections = []
total = st.session_state[key_cnt]
for i in range(1, total + 1):
s = secs[i-1] if i-1 < len(secs) else {"title":"", "content":""}
with st.expander(f"Section {i}", expanded=(i <= 2)):
t = st.text_input(f"Title {i}", value=s.get("title") or "", key=f"el_t_{lesson_id}_{i}")
b = st.text_area(f"Content {i}", value=s.get("content") or "", height=150, key=f"el_b_{lesson_id}_{i}")
edited_sections.append({"title": t or "Section", "content": b or ""})
save = st.form_submit_button("💾 Save changes", type="primary", use_container_width=True)
actions = st.columns([8,2])
with actions[1]:
cancel_clicked = st.button("✖ Cancel", key=f"el_cancel_{lesson_id}", type="secondary", use_container_width=True)
if cancel_clicked:
st.session_state.show_edit_lesson = False
st.session_state.edit_lesson_id = None
st.rerun()
if not save:
return
if not title or not any((s["title"] or s["content"]).strip() for s in edited_sections):
st.error("Title and at least one non-empty section are required.")
return
ok = False
try:
ok = _update_lesson(lesson_id, teacher_id, title, description, subject, level, edited_sections)
except Exception as e:
st.error(f"Update failed: {e}")
if ok:
st.success("✅ Lesson updated.")
st.session_state.show_edit_lesson = False
st.session_state.edit_lesson_id = None
st.rerun()
else:
st.error("Could not update this lesson. Check ownership or backend errors.")
def _edit_quiz_panel(teacher_id: int, quiz_id: int):
# Load quiz
try:
data = (dbapi.get_quiz(quiz_id) if (USE_LOCAL_DB and hasattr(dbapi, "get_quiz")) else api._req("GET", f"/quizzes/{quiz_id}").json())
except Exception as e:
st.error(f"Quiz not found: {e}")
return
Q = data.get("quiz")
raw_items = data.get("items", [])
if not Q:
st.error("Quiz not found.")
return
def _dec(x):
if isinstance(x, str):
try:
return json.loads(x)
except Exception:
return x
return x
items = []
for it in raw_items:
opts = _dec(it.get("options")) or []
while len(opts) < 4:
opts.append("Option")
opts = opts[:4]
ans = _dec(it.get("answer_key"))
if isinstance(ans, list) and ans:
ans = ans[0]
ans = (str(ans) or "A").upper()[:1]
if ans not in ("A","B","C","D"):
ans = "A"
items.append({
"question": (it.get("question") or "").strip(),
"options": opts,
"answer_key": ans,
"points": int(it.get("points") or 1),
})
key_cnt = f"eq_cnt_{quiz_id}"
if key_cnt not in st.session_state:
st.session_state[key_cnt] = max(1, len(items) or 5)
st.markdown("### ✏️ Edit Quiz")
with st.form(f"edit_quiz_form_{quiz_id}", clear_on_submit=False):
title = st.text_input("Title", value=Q.get("title") or f"Quiz #{quiz_id}")
edited = []
total = st.session_state[key_cnt]
for i in range(1, total + 1):
it = items[i-1] if i-1 < len(items) else {"question":"", "options":["","","",""], "answer_key":"A", "points":1}
with st.expander(f"Question {i}", expanded=(i <= 2)):
q = st.text_area(f"Prompt {i}", value=it["question"], key=f"eq_q_{quiz_id}_{i}")
cA, cB = st.columns(2)
a = cA.text_input(f"Option A", value=it["options"][0], key=f"eq_A_{quiz_id}_{i}")
b = cB.text_input(f"Option B", value=it["options"][1], key=f"eq_B_{quiz_id}_{i}")
cC, cD = st.columns(2)
c = cC.text_input(f"Option C", value=it["options"][2], key=f"eq_C_{quiz_id}_{i}")
d = cD.text_input(f"Option D", value=it["options"][3], key=f"eq_D_{quiz_id}_{i}")
correct = st.radio("Correct answer", ["A","B","C","D"],
index=["A","B","C","D"].index(it["answer_key"]),
key=f"eq_ans_{quiz_id}_{i}", horizontal=True)
edited.append({"question": q, "options": [a,b,c,d], "answer_key": correct, "points": 1})
row = st.columns([1,1,6,2,2])
with row[0]:
if st.form_submit_button("➕ Add question", type="secondary"):
st.session_state[key_cnt] = min(20, st.session_state[key_cnt] + 1)
st.rerun()
with row[1]:
if st.form_submit_button("➖ Remove last", type="secondary", disabled=st.session_state[key_cnt] <= 1):
st.session_state[key_cnt] = max(1, st.session_state[key_cnt] - 1)
st.rerun()
save = row[3].form_submit_button("💾 Save", type="primary")
cancel = row[4].form_submit_button("✖ Cancel", type="secondary")
if cancel:
st.session_state.show_edit_quiz = False
st.session_state.edit_quiz_id = None
st.rerun()
if not save:
return
cleaned = []
for it in edited:
q = (it["question"] or "").strip()
opts = [o for o in it["options"] if (o or "").strip()]
if not q or len(opts) < 2:
continue
while len(opts) < 4:
opts.append("Option")
cleaned.append({
"question": q,
"options": opts[:4],
"answer_key": it["answer_key"],
"points": 1
})
if not title or not cleaned:
st.error("Title and at least one valid question are required.")
return
ok = False
try:
ok = _update_quiz(quiz_id, teacher_id, title, cleaned, settings={})
except Exception as e:
st.error(f"Save failed: {e}")
if ok:
st.success("✅ Quiz updated.")
st.session_state.show_edit_quiz = False
st.session_state.edit_quiz_id = None
st.rerun()
else:
st.error("Could not update this quiz. Check ownership or backend errors.")
# ---------- Main page ----------
def show_page():
user = st.session_state.user
teacher_id = user["user_id"]
st.title("📚 Content Management")
st.caption("Create and manage custom lessons and quizzes")
# preload lists
lessons = _list_lessons_by_teacher(teacher_id)
quizzes = _list_quizzes_by_teacher(teacher_id)
# top action bar
a1, a2, _sp = st.columns([3,3,4])
if a1.button("➕ Create Lesson", use_container_width=True):
st.session_state.show_create_lesson = True
if a2.button("🏆 Create Quiz", use_container_width=True):
st.session_state.show_create_quiz = True
# create panels
if st.session_state.get("show_create_lesson"):
with st.container(border=True):
_create_lesson_panel(teacher_id)
st.markdown("---")
if st.session_state.get("show_create_quiz"):
with st.container(border=True):
_create_quiz_panel(teacher_id)
st.markdown("---")
# inline editors
if st.session_state.get("show_edit_lesson") and st.session_state.get("edit_lesson_id"):
with st.container(border=True):
_edit_lesson_panel(teacher_id, st.session_state.edit_lesson_id)
st.markdown("---")
if st.session_state.get("show_edit_quiz") and st.session_state.get("edit_quiz_id"):
with st.container(border=True):
_edit_quiz_panel(teacher_id, st.session_state.edit_quiz_id)
st.markdown("---")
# Tabs
tab1, tab2 = st.tabs([f"Custom Lessons ({len(lessons)})", f"Custom Quizzes ({len(quizzes)})"])
# ========== LESSONS ==========
with tab1:
if not lessons:
st.info("No lessons yet. Use **Create Lesson** above.")
else:
all_students = _list_all_students_for_teacher(teacher_id)
student_options = {f"{s['name']} · {s['email']}": s["user_id"] for s in all_students}
for L in lessons:
assignees = _list_assigned_students_for_lesson(L["lesson_id"])
assignee_names = [a.get("name") for a in assignees]
created = _fmt_date(L.get("created_at"))
count = len(assignees)
with st.container(border=True):
c1, c2 = st.columns([8,3])
with c1:
st.markdown(f"### {L['title']}")
st.caption(L.get("description") or "")
st.markdown(
_pill((L.get("level") or "beginner").capitalize()) +
_pill(L.get("subject","finance")) +
_pill(f"{count} student{'s' if count != 1 else ''} assigned") +
_pill(f"Created {created}"),
unsafe_allow_html=True
)
with c2:
b1, b2 = st.columns([1,1])
with b1:
if st.button("Edit", key=f"edit_{L['lesson_id']}"):
st.session_state.edit_lesson_id = L["lesson_id"]
st.session_state.show_edit_lesson = True
st.rerun()
with b2:
if st.button("Delete", key=f"del_{L['lesson_id']}"):
ok, msg = _delete_lesson(L["lesson_id"], teacher_id)
if ok: st.success("Lesson deleted"); st.rerun()
else: st.error(msg or "Delete failed")
st.markdown("**Assigned Students:**")
if assignee_names:
st.markdown(" ".join(_pill(n) for n in assignee_names if n), unsafe_allow_html=True)
else:
st.caption("No students assigned yet.")
# ========== QUIZZES ==========
with tab2:
if not quizzes:
st.info("No quizzes yet. Use **Create Quiz** above.")
else:
for Q in quizzes:
assignees = _list_assigned_students_for_quiz(Q["quiz_id"])
created = _fmt_date(Q.get("created_at"))
num_qs = int(Q.get("num_items", 0))
with st.container(border=True):
c1, c2 = st.columns([8,3])
with c1:
st.markdown(f"### {Q['title']}")
st.caption(f"Lesson: {Q.get('lesson_title','')}")
st.markdown(
_pill(f"{num_qs} question{'s' if num_qs != 1 else ''}") +
_pill(f"{len(assignees)} students assigned") +
_pill(f"Created {created}"),
unsafe_allow_html=True
)
with c2:
b1, b2 = st.columns(2)
with b1:
if st.button("Edit", key=f"editq_{Q['quiz_id']}"):
st.session_state.edit_quiz_id = Q["quiz_id"]
st.session_state.show_edit_quiz = True
st.rerun()
with b2:
if st.button("Delete", key=f"delq_{Q['quiz_id']}"):
ok, msg = _delete_quiz(Q["quiz_id"], teacher_id)
if ok: st.success("Quiz deleted"); st.rerun()
else: st.error(msg or "Delete failed")
st.markdown("**Assigned Students:**")
if assignees:
st.markdown(" ".join(_pill(a.get('name')) for a in assignees if a.get('name')), unsafe_allow_html=True)
else:
st.caption("No students assigned yet.")
with st.expander("View questions", expanded=False):
# Load items on demand to avoid heavy initial load
try:
data = (dbapi.get_quiz(Q["quiz_id"]) if (USE_LOCAL_DB and hasattr(dbapi, "get_quiz"))
else api._req("GET", f"/quizzes/{Q['quiz_id']}").json())
except Exception as e:
st.info(f"Could not fetch items: {e}")
data = None
items = data.get("items", []) if data else []
if not items:
st.info("No items found for this quiz.")
else:
labels = ["A","B","C","D"]
for i, it in enumerate(items, start=1):
opts = it.get("options")
if isinstance(opts, str):
try:
opts = json.loads(opts)
except Exception:
opts = [opts]
answer = it.get("answer_key")
if isinstance(answer, str):
try:
answer = json.loads(answer)
except Exception:
pass
st.markdown(f"**Q{i}.** {it.get('question','').strip()}")
for j, opt in enumerate((opts or [])[:4]):
st.write(f"{labels[j]}) {opt}")
ans_text = answer if isinstance(answer, str) else ",".join(answer or [])
st.caption(f"Answer: {ans_text}")
st.markdown("---")