elkay: lesson.py and teacher link
Browse files
phase/Student_view/lesson.py
CHANGED
|
@@ -1,13 +1,16 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
from typing import List, Dict, Any, Optional, Tuple
|
| 3 |
import re
|
| 4 |
-
import datetime
|
|
|
|
|
|
|
| 5 |
|
| 6 |
# Internal API client (already used across the app)
|
| 7 |
# Uses BACKEND_URL/BACKEND_TOKEN env vars and has retry logic
|
| 8 |
# See utils/api.py for details
|
| 9 |
from utils import api as backend_api
|
| 10 |
|
|
|
|
| 11 |
|
| 12 |
FALLBACK_TAG = "<!--fallback-->"
|
| 13 |
|
|
@@ -33,6 +36,19 @@ def _ensure_state():
|
|
| 33 |
if k not in st.session_state:
|
| 34 |
st.session_state[k] = v
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
# ---------------------------------------------
|
| 38 |
# Content metadata (UI only)
|
|
@@ -616,9 +632,86 @@ def _render_results():
|
|
| 616 |
# ---------------------------------------------
|
| 617 |
# Public entry point(s)
|
| 618 |
# ---------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 619 |
|
| 620 |
def render():
|
| 621 |
_ensure_state()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 622 |
|
| 623 |
# Breadcrumb
|
| 624 |
st.caption("Learning Path · " + st.session_state.level.capitalize())
|
|
|
|
| 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 |
|
| 8 |
# Internal API client (already used across the app)
|
| 9 |
# Uses BACKEND_URL/BACKEND_TOKEN env vars and has retry logic
|
| 10 |
# See utils/api.py for details
|
| 11 |
from utils import api as backend_api
|
| 12 |
|
| 13 |
+
USE_LOCAL_DB = os.getenv("DISABLE_DB", "1") != "1"
|
| 14 |
|
| 15 |
FALLBACK_TAG = "<!--fallback-->"
|
| 16 |
|
|
|
|
| 36 |
if k not in st.session_state:
|
| 37 |
st.session_state[k] = v
|
| 38 |
|
| 39 |
+
def _fetch_assigned_lesson(lesson_id: int) -> dict:
|
| 40 |
+
"""
|
| 41 |
+
Returns {"lesson": {...}, "sections": [...]}
|
| 42 |
+
Falls back to backend_api if you're not using the local DB.
|
| 43 |
+
"""
|
| 44 |
+
if USE_LOCAL_DB and hasattr(dbapi, "get_lesson"):
|
| 45 |
+
return dbapi.get_lesson(lesson_id) or {}
|
| 46 |
+
|
| 47 |
+
# If your backend exposes GET /lessons/{id}
|
| 48 |
+
try:
|
| 49 |
+
return backend_api.get_lesson(lesson_id) or {}
|
| 50 |
+
except Exception:
|
| 51 |
+
return {}
|
| 52 |
|
| 53 |
# ---------------------------------------------
|
| 54 |
# Content metadata (UI only)
|
|
|
|
| 632 |
# ---------------------------------------------
|
| 633 |
# Public entry point(s)
|
| 634 |
# ---------------------------------------------
|
| 635 |
+
def _render_assigned_lesson(lesson_id: int, assignment_id: Optional[int] = None):
|
| 636 |
+
data = _fetch_assigned_lesson(lesson_id)
|
| 637 |
+
if not data or not data.get("lesson"):
|
| 638 |
+
st.error("Lesson not found or not available.")
|
| 639 |
+
if st.button("⬅ Back to Classes"):
|
| 640 |
+
# Clear the route and bounce back to the Teacher Link page
|
| 641 |
+
st.session_state.lesson_route = None
|
| 642 |
+
st.session_state.current_page = "Teacher Link"
|
| 643 |
+
st.rerun()
|
| 644 |
+
return
|
| 645 |
+
|
| 646 |
+
L = data["lesson"]
|
| 647 |
+
sections = sorted(data.get("sections", []), key=lambda s: int(s.get("position", 0)))
|
| 648 |
+
|
| 649 |
+
st.markdown(f"### {L.get('title', 'Untitled')}")
|
| 650 |
+
if L.get("description"):
|
| 651 |
+
st.caption(L["description"])
|
| 652 |
+
|
| 653 |
+
# Track which section we’re on
|
| 654 |
+
key = f"_dbsec_idx_{lesson_id}"
|
| 655 |
+
if key not in st.session_state:
|
| 656 |
+
st.session_state[key] = 0
|
| 657 |
+
|
| 658 |
+
idx = st.session_state[key]
|
| 659 |
+
total = max(1, len(sections))
|
| 660 |
+
st.progress((idx + 1) / total, text=f"Unit {idx + 1} of {total}")
|
| 661 |
+
|
| 662 |
+
if not sections:
|
| 663 |
+
st.info("No sections have been added to this lesson yet.")
|
| 664 |
+
if st.button("⬅ Back to Classes"):
|
| 665 |
+
st.session_state.lesson_route = None
|
| 666 |
+
st.session_state.current_page = "Teacher Link"
|
| 667 |
+
st.rerun()
|
| 668 |
+
return
|
| 669 |
+
|
| 670 |
+
sec = sections[idx]
|
| 671 |
+
sec_title = sec.get("title") or f"Section {idx + 1}"
|
| 672 |
+
content = (sec.get("content") or sec.get("body") or "").strip()
|
| 673 |
+
|
| 674 |
+
st.subheader(sec_title)
|
| 675 |
+
if content:
|
| 676 |
+
st.markdown(content, unsafe_allow_html=True)
|
| 677 |
+
else:
|
| 678 |
+
st.info("Content coming soon.")
|
| 679 |
+
|
| 680 |
+
col1, col2, col3 = st.columns([1, 1, 1])
|
| 681 |
+
with col1:
|
| 682 |
+
if st.button("← Previous", disabled=idx == 0):
|
| 683 |
+
st.session_state[key] = max(0, idx - 1)
|
| 684 |
+
st.rerun()
|
| 685 |
+
with col2:
|
| 686 |
+
if st.button("Back to Classes"):
|
| 687 |
+
st.session_state.lesson_route = None
|
| 688 |
+
st.session_state.current_page = "Teacher Link"
|
| 689 |
+
st.rerun()
|
| 690 |
+
with col3:
|
| 691 |
+
is_last = idx >= len(sections) - 1
|
| 692 |
+
|
| 693 |
+
# If your DB has a quiz section type, you can detect and branch here.
|
| 694 |
+
# For now we just advance or finish.
|
| 695 |
+
if is_last:
|
| 696 |
+
if st.button("Finish"):
|
| 697 |
+
st.success("Lesson complete.")
|
| 698 |
+
st.session_state.lesson_route = None
|
| 699 |
+
st.session_state.current_page = "Teacher Link"
|
| 700 |
+
st.rerun()
|
| 701 |
+
else:
|
| 702 |
+
if st.button("Next →"):
|
| 703 |
+
st.session_state[key] = idx + 1
|
| 704 |
+
st.rerun()
|
| 705 |
+
|
| 706 |
|
| 707 |
def render():
|
| 708 |
_ensure_state()
|
| 709 |
+
# If we were routed here from the Teacher Link, skip the catalog flow.
|
| 710 |
+
route = st.session_state.get("lesson_route")
|
| 711 |
+
if route and route.get("source") == "teacher":
|
| 712 |
+
_render_assigned_lesson(int(route.get("lesson_id", 0)), route.get("assignment_id"))
|
| 713 |
+
return
|
| 714 |
+
|
| 715 |
|
| 716 |
# Breadcrumb
|
| 717 |
st.caption("Learning Path · " + st.session_state.level.capitalize())
|
phase/Student_view/teacherlink.py
CHANGED
|
@@ -204,8 +204,16 @@ def show_code():
|
|
| 204 |
with c1:
|
| 205 |
# pass lesson & assignment to the Lessons page
|
| 206 |
if st.button("▶️ Start Lesson", key=f"start_lesson_{r.get('assignment_id')}"):
|
| 207 |
-
st.session_state.selected_lesson = r.get("lesson_id")
|
| 208 |
-
st.session_state.selected_assignment = r.get("assignment_id")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
st.session_state.current_page = "Lessons"
|
| 210 |
st.rerun()
|
| 211 |
with c2:
|
|
|
|
| 204 |
with c1:
|
| 205 |
# pass lesson & assignment to the Lessons page
|
| 206 |
if st.button("▶️ Start Lesson", key=f"start_lesson_{r.get('assignment_id')}"):
|
| 207 |
+
st.session_state.selected_lesson = int(r.get("lesson_id")) # keep for backward compat
|
| 208 |
+
st.session_state.selected_assignment = int(r.get("assignment_id")) # keep for backward compat
|
| 209 |
+
|
| 210 |
+
# New explicit route hint. This is what fixes your navigation.
|
| 211 |
+
st.session_state.lesson_route = {
|
| 212 |
+
"source": "teacher",
|
| 213 |
+
"lesson_id": int(r.get("lesson_id")),
|
| 214 |
+
"assignment_id": int(r.get("assignment_id") or 0),
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
st.session_state.current_page = "Lessons"
|
| 218 |
st.rerun()
|
| 219 |
with c2:
|