Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -323,6 +323,7 @@ st.plotly_chart(fig, use_container_width=True)
|
|
| 323 |
|
| 324 |
st.caption(f"{len(df_final)} line(s) aggregated." if not df_final.empty else "No data.")
|
| 325 |
|
|
|
|
| 326 |
# ================== Dynamic Stage Summaries (only if student answered that week) ==================
|
| 327 |
import re
|
| 328 |
import unicodedata
|
|
@@ -554,82 +555,72 @@ if mongo_uri and student_choice != "(All)" and source_choice != "(All)":
|
|
| 554 |
for q in (summary.get("notable_quotes") or [])[:3]:
|
| 555 |
st.markdown(f"> {q}")
|
| 556 |
# # ------------------- Output (Tabs) -------------------
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
#
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
#
|
| 575 |
-
|
| 576 |
-
#
|
| 577 |
-
|
| 578 |
-
#
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
#
|
| 588 |
-
|
| 589 |
-
|
| 590 |
-
|
| 591 |
-
|
| 592 |
-
#
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
#
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
#
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
# "Download analysis (.md)", f,
|
| 624 |
-
# file_name=os.path.basename(path), mime="text/markdown"
|
| 625 |
-
# )
|
| 626 |
-
# else:
|
| 627 |
-
# st.warning("Analysis file found but empty.")
|
| 628 |
-
# else:
|
| 629 |
-
# st.warning(f"No analysis found for **{student_choice}** in `{ANALYSES_DIR}` yet.")
|
| 630 |
-
# if idx:
|
| 631 |
-
# st.caption("Available analyses:")
|
| 632 |
-
# st.write(", ".join(sorted({name.title() for name in idx.keys()})))
|
| 633 |
|
| 634 |
|
| 635 |
|
|
|
|
| 323 |
|
| 324 |
st.caption(f"{len(df_final)} line(s) aggregated." if not df_final.empty else "No data.")
|
| 325 |
|
| 326 |
+
|
| 327 |
# ================== Dynamic Stage Summaries (only if student answered that week) ==================
|
| 328 |
import re
|
| 329 |
import unicodedata
|
|
|
|
| 555 |
for q in (summary.get("notable_quotes") or [])[:3]:
|
| 556 |
st.markdown(f"> {q}")
|
| 557 |
# # ------------------- Output (Tabs) -------------------
|
| 558 |
+
tab_radar, tab_analyses = st.tabs(["π Radar", "π Analyses"])
|
| 559 |
+
|
| 560 |
+
with tab_radar:
|
| 561 |
+
# Recompute a local copy for the tab (prevents reusing the same figure object)
|
| 562 |
+
df_plot_local = df_final.copy()
|
| 563 |
+
avg_label_local = None
|
| 564 |
+
|
| 565 |
+
if not df_plot_local.empty:
|
| 566 |
+
cols = list(SKILL_GROUPS.keys()) if grouped else SKILLS
|
| 567 |
+
show_cohort_avg = st.toggle("Show cohort average (all students)", value=True, key="toggle_avg_radar")
|
| 568 |
+
if show_cohort_avg:
|
| 569 |
+
avg_vals = df_plot_local[cols].mean()
|
| 570 |
+
avg_row = {"label": "Average (All Students)"}
|
| 571 |
+
avg_row.update({k: float(avg_vals[k]) for k in cols})
|
| 572 |
+
df_plot_local = pd.concat([df_plot_local, pd.DataFrame([avg_row])], ignore_index=True)
|
| 573 |
+
avg_label_local = "Average (All Students)"
|
| 574 |
+
|
| 575 |
+
# Build a fresh figure for the tab or copy if you already built one above:
|
| 576 |
+
fig_tab = plot_radar(df_plot_local, grouped, chart_title, avg_label=avg_label_local)
|
| 577 |
+
# fig_tab = go.Figure(fig) # <- alternative if you already computed `fig` above
|
| 578 |
+
|
| 579 |
+
# Unique key to avoid Plotly id collisions on reruns/tab switches
|
| 580 |
+
key_suffix = f"{student_choice}|{source_choice}|{int(grouped)}|{int(overlay_sources)}"
|
| 581 |
+
st.plotly_chart(fig_tab, use_container_width=True, key=f"radar_tab_{key_suffix}")
|
| 582 |
+
|
| 583 |
+
st.caption(f"{len(df_final)} line(s) aggregated." if not df_final.empty else "No data.")
|
| 584 |
+
|
| 585 |
+
with tab_analyses:
|
| 586 |
+
st.subheader("Student Analysis (Markdown)")
|
| 587 |
+
|
| 588 |
+
# Use the folder you defined at top (ANALYSES_DIR), or expose it in the sidebar if you prefer.
|
| 589 |
+
idx = _build_analysis_index(ANALYSES_DIR)
|
| 590 |
+
|
| 591 |
+
if student_choice == "(All)":
|
| 592 |
+
st.info("Pick a specific student on the left to view their analysis.")
|
| 593 |
+
# (Optional) show what's available so you can browse:
|
| 594 |
+
if idx:
|
| 595 |
+
st.caption("Available analyses:")
|
| 596 |
+
st.write(", ".join(sorted({name.title() for name in idx.keys()})))
|
| 597 |
+
else:
|
| 598 |
+
# Normalize the selected student name to match filenames
|
| 599 |
+
norm = _normalize_name(student_choice)
|
| 600 |
+
path = idx.get(norm)
|
| 601 |
+
|
| 602 |
+
# If exact match not found, try simple underscore variant
|
| 603 |
+
if not path:
|
| 604 |
+
alt = student_choice.replace(" ", "_")
|
| 605 |
+
path = idx.get(_normalize_name(alt))
|
| 606 |
+
|
| 607 |
+
if path:
|
| 608 |
+
md = _load_markdown(path)
|
| 609 |
+
if md.strip():
|
| 610 |
+
st.markdown(md, unsafe_allow_html=False)
|
| 611 |
+
# Optional download button
|
| 612 |
+
with open(path, "rb") as f:
|
| 613 |
+
st.download_button(
|
| 614 |
+
"Download analysis (.md)", f,
|
| 615 |
+
file_name=os.path.basename(path), mime="text/markdown"
|
| 616 |
+
)
|
| 617 |
+
else:
|
| 618 |
+
st.warning("Analysis file found but empty.")
|
| 619 |
+
else:
|
| 620 |
+
st.warning(f"No analysis found for **{student_choice}** in `{ANALYSES_DIR}` yet.")
|
| 621 |
+
if idx:
|
| 622 |
+
st.caption("Available analyses:")
|
| 623 |
+
st.write(", ".join(sorted({name.title() for name in idx.keys()})))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 624 |
|
| 625 |
|
| 626 |
|