lanna_lalala;- commited on
Commit ·
5b4001f
1
Parent(s): b413450
updating
Browse files- phase/Student_view/lesson.py +48 -21
phase/Student_view/lesson.py
CHANGED
|
@@ -119,9 +119,37 @@ MODULES_META: Dict[str, List[Dict[str, Any]]] = {
|
|
| 119 |
|
| 120 |
# Helper to read topic titles regardless of whether metadata uses `topics` or `topic_labels`
|
| 121 |
|
| 122 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
mod = next(m for m in MODULES_META[level] if m["id"] == module_id)
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
# ---------------------------------------------
|
| 127 |
# Backend integrations
|
|
@@ -132,10 +160,12 @@ def _fetch_topic_from_backend(level: str, module_id: int, topic_idx: int) -> Tup
|
|
| 132 |
Calls the backend to read the topic text from the lessons folder.
|
| 133 |
Returns (title, content). Title uses the configured UI label as a fallback.
|
| 134 |
"""
|
| 135 |
-
|
|
|
|
|
|
|
| 136 |
lesson_label = MODULES_META[level][[m["id"] for m in MODULES_META[level]].index(module_id)]["title"]
|
| 137 |
-
module_label = str(module_id)
|
| 138 |
-
topic_label = str(
|
| 139 |
|
| 140 |
payload = {"lesson": lesson_label, "module": module_label, "topic": topic_label}
|
| 141 |
|
|
@@ -232,13 +262,13 @@ def _render_catalog():
|
|
| 232 |
st.header("Financial Education")
|
| 233 |
st.caption("Build your financial knowledge with structured paths for every skill level.")
|
| 234 |
|
| 235 |
-
level = st.segmented_control(
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
)
|
| 242 |
|
| 243 |
cols = st.columns(3)
|
| 244 |
for i, mod in enumerate(MODULES_META[level]):
|
|
@@ -248,7 +278,7 @@ def _render_catalog():
|
|
| 248 |
st.caption(mod["description"])
|
| 249 |
st.caption(f"Duration: {mod.get('duration','—')} · Difficulty: {mod.get('difficulty','—')}")
|
| 250 |
with st.expander("Topics include"):
|
| 251 |
-
for t in (mod
|
| 252 |
st.write("• ", t)
|
| 253 |
if st.button("Start Learning", key=f"start_{level}_{mod['id']}"):
|
| 254 |
st.session_state.module_id = mod["id"]
|
|
@@ -259,18 +289,14 @@ def _render_catalog():
|
|
| 259 |
|
| 260 |
|
| 261 |
def _get_topics(level: str, module_id: int) -> List[Tuple[str, str]]:
|
| 262 |
-
"""Load and cache all available topics for a module by probing sequentially."""
|
| 263 |
cache_key = (level, module_id)
|
| 264 |
if cache_key in st.session_state.topics_cache:
|
| 265 |
return st.session_state.topics_cache[cache_key]
|
| 266 |
|
| 267 |
-
|
| 268 |
-
out
|
| 269 |
misses = 0
|
| 270 |
-
|
| 271 |
-
planned = len(_topic_titles(level, module_id)) + 2
|
| 272 |
-
N = max(3, planned)
|
| 273 |
-
for idx in range(N):
|
| 274 |
title, text = _fetch_topic_from_backend(level, module_id, idx)
|
| 275 |
if text and not text.startswith("(Failed"):
|
| 276 |
out.append((title, text))
|
|
@@ -279,11 +305,12 @@ def _get_topics(level: str, module_id: int) -> List[Tuple[str, str]]:
|
|
| 279 |
misses += 1
|
| 280 |
if misses >= 2:
|
| 281 |
break
|
| 282 |
-
|
| 283 |
st.session_state.topics_cache[cache_key] = out
|
| 284 |
return out
|
| 285 |
|
| 286 |
|
|
|
|
| 287 |
def _render_lesson():
|
| 288 |
level = st.session_state.level
|
| 289 |
module_id = st.session_state.module_id
|
|
|
|
| 119 |
|
| 120 |
# Helper to read topic titles regardless of whether metadata uses `topics` or `topic_labels`
|
| 121 |
|
| 122 |
+
def _topic_plan(level: str, module_id: int):
|
| 123 |
+
"""
|
| 124 |
+
Returns a list of (title, backend_ordinal) after filtering:
|
| 125 |
+
- drop any 'Play:' topic
|
| 126 |
+
- drop 'Quiz'
|
| 127 |
+
- keep first five + the Summary (6 total)
|
| 128 |
+
backend_ordinal is the 1-based index in the original metadata (so backend files line up).
|
| 129 |
+
"""
|
| 130 |
mod = next(m for m in MODULES_META[level] if m["id"] == module_id)
|
| 131 |
+
raw = (mod.get("topics") or mod.get("topic_labels") or [])
|
| 132 |
+
plan = []
|
| 133 |
+
for i, t in enumerate(raw, start=1):
|
| 134 |
+
tl = t.strip().lower()
|
| 135 |
+
if tl == "quiz" or tl.startswith("play:"):
|
| 136 |
+
continue
|
| 137 |
+
plan.append((t, i))
|
| 138 |
+
|
| 139 |
+
# Ensure at most 6 topics: first five + Summary if present
|
| 140 |
+
if len(plan) > 6:
|
| 141 |
+
# Prefer keeping a 'Summary' entry last if it exists
|
| 142 |
+
summary_pos = next((idx for idx, (title, _) in enumerate(plan)
|
| 143 |
+
if title.strip().lower().startswith("summary")), None)
|
| 144 |
+
if summary_pos is not None:
|
| 145 |
+
plan = plan[:5] + [plan[summary_pos]]
|
| 146 |
+
else:
|
| 147 |
+
plan = plan[:6]
|
| 148 |
+
return plan
|
| 149 |
+
|
| 150 |
+
def _topic_titles(level: str, module_id: int):
|
| 151 |
+
return [t for (t, _) in _topic_plan(level, module_id)]
|
| 152 |
+
|
| 153 |
|
| 154 |
# ---------------------------------------------
|
| 155 |
# Backend integrations
|
|
|
|
| 160 |
Calls the backend to read the topic text from the lessons folder.
|
| 161 |
Returns (title, content). Title uses the configured UI label as a fallback.
|
| 162 |
"""
|
| 163 |
+
plan = _topic_plan(level, module_id)
|
| 164 |
+
title, backend_ordinal = plan[topic_idx] # 1-based ordinal in original list
|
| 165 |
+
# this keeps your filesystem mapping correct: topic_{backend_ordinal}.txt
|
| 166 |
lesson_label = MODULES_META[level][[m["id"] for m in MODULES_META[level]].index(module_id)]["title"]
|
| 167 |
+
module_label = str(module_id)
|
| 168 |
+
topic_label = str(backend_ordinal)
|
| 169 |
|
| 170 |
payload = {"lesson": lesson_label, "module": module_label, "topic": topic_label}
|
| 171 |
|
|
|
|
| 262 |
st.header("Financial Education")
|
| 263 |
st.caption("Build your financial knowledge with structured paths for every skill level.")
|
| 264 |
|
| 265 |
+
# level = st.segmented_control(
|
| 266 |
+
# "Level",
|
| 267 |
+
# options=["beginner", "intermediate", "advanced"],
|
| 268 |
+
# default=st.session_state.level,
|
| 269 |
+
# format_func=lambda s: s.capitalize(),
|
| 270 |
+
# key="level",
|
| 271 |
+
# )
|
| 272 |
|
| 273 |
cols = st.columns(3)
|
| 274 |
for i, mod in enumerate(MODULES_META[level]):
|
|
|
|
| 278 |
st.caption(mod["description"])
|
| 279 |
st.caption(f"Duration: {mod.get('duration','—')} · Difficulty: {mod.get('difficulty','—')}")
|
| 280 |
with st.expander("Topics include"):
|
| 281 |
+
for t, _ord in _topic_plan(level, mod["id"]):
|
| 282 |
st.write("• ", t)
|
| 283 |
if st.button("Start Learning", key=f"start_{level}_{mod['id']}"):
|
| 284 |
st.session_state.module_id = mod["id"]
|
|
|
|
| 289 |
|
| 290 |
|
| 291 |
def _get_topics(level: str, module_id: int) -> List[Tuple[str, str]]:
|
|
|
|
| 292 |
cache_key = (level, module_id)
|
| 293 |
if cache_key in st.session_state.topics_cache:
|
| 294 |
return st.session_state.topics_cache[cache_key]
|
| 295 |
|
| 296 |
+
plan = _topic_plan(level, module_id)
|
| 297 |
+
out = []
|
| 298 |
misses = 0
|
| 299 |
+
for idx in range(len(plan)): # exactly the filtered six
|
|
|
|
|
|
|
|
|
|
| 300 |
title, text = _fetch_topic_from_backend(level, module_id, idx)
|
| 301 |
if text and not text.startswith("(Failed"):
|
| 302 |
out.append((title, text))
|
|
|
|
| 305 |
misses += 1
|
| 306 |
if misses >= 2:
|
| 307 |
break
|
| 308 |
+
|
| 309 |
st.session_state.topics_cache[cache_key] = out
|
| 310 |
return out
|
| 311 |
|
| 312 |
|
| 313 |
+
|
| 314 |
def _render_lesson():
|
| 315 |
level = st.session_state.level
|
| 316 |
module_id = st.session_state.module_id
|