Spaces:
Sleeping
Sleeping
| # streamlit_app.py | |
| import json, random, streamlit as st | |
| from pathlib import Path | |
| DATA_FILE = Path(__file__).parent / "master_fundamentos_evaluado.json" | |
| def load_data(path): | |
| with open(path, encoding="utf-8") as f: | |
| return json.load(f) | |
| data = load_data(DATA_FILE) | |
| # ───────── Helpers ───────── | |
| def random_question(): | |
| return random.choice(data["preguntas"]) | |
| def random_case(): | |
| return random.choice(data["casos_clinicos"]) | |
| def build_exam(): | |
| preguntas = random.sample(data["preguntas"], 25) | |
| casos = random.sample(data["casos_clinicos"], 2) | |
| items = [{"grupo": "normal", "data": q} for q in preguntas] | |
| for c in casos: | |
| for q in c["preguntas"]: | |
| items.append({ | |
| "grupo": c["id"], | |
| "data": {**q, "caso": c["titulo"], "context": c["contexto"]} | |
| }) | |
| return items | |
| # ───────── Estado inicial ───────── | |
| if "mode" not in st.session_state: | |
| st.session_state.mode = "preguntas" | |
| st.session_state.question = random_question() | |
| st.session_state.case_list = [] | |
| st.session_state.case_idx = 0 | |
| st.session_state.exam_items = [] | |
| st.session_state.exam_idx = 0 | |
| st.session_state.hits_norm = 0 | |
| st.session_state.hits_case = {} | |
| st.session_state.total_case = {} | |
| st.session_state.recorrido_idx = 0 # nuevo | |
| # ───────── Barra lateral ───────── | |
| st.sidebar.title("Fundamentos") | |
| if st.sidebar.button("Preguntas sueltas"): | |
| st.session_state.mode = "preguntas" | |
| st.session_state.question = random_question() | |
| st.rerun() | |
| if st.sidebar.button("Casos clínicos"): | |
| st.session_state.mode = "caso" | |
| caso = random_case() | |
| st.session_state.case_list = [ | |
| {**q, "caso": caso["titulo"], "context": caso["contexto"]} | |
| for q in caso["preguntas"] | |
| ] | |
| st.session_state.case_idx = 0 | |
| st.session_state.question = st.session_state.case_list[0] | |
| st.rerun() | |
| if st.sidebar.button("Examen (25 + 2 casos)"): | |
| st.session_state.mode = "examen" | |
| st.session_state.exam_items = build_exam() | |
| st.session_state.exam_idx = 0 | |
| st.session_state.hits_norm = 0 | |
| st.session_state.hits_case.clear() | |
| st.session_state.total_case = {} | |
| for it in st.session_state.exam_items: | |
| if it["grupo"] != "normal": | |
| st.session_state.total_case[it["grupo"]] = \ | |
| st.session_state.total_case.get(it["grupo"], 0) + 1 | |
| st.session_state.question = st.session_state.exam_items[0]["data"] | |
| st.rerun() | |
| if st.sidebar.button("Recorrido completo"): | |
| st.session_state.mode = "recorrido" | |
| st.session_state.recorrido_idx = 0 | |
| st.session_state.question = data["preguntas"][0] | |
| st.rerun() | |
| st.sidebar.markdown("---") | |
| st.sidebar.markdown("Creado con ❤️ por Requetito") | |
| # ───────── Encabezado: modo + progreso ───────── | |
| mode_label = { | |
| "preguntas": "Práctica – Preguntas sueltas", | |
| "caso": "Práctica – Casos clínicos", | |
| "examen": "Examen final", | |
| "recorrido": "Recorrido completo – Todas las preguntas" | |
| } | |
| st.subheader(mode_label[st.session_state.mode]) | |
| progreso = "" | |
| if st.session_state.mode == "examen": | |
| progreso = f"{st.session_state.exam_idx + 1} / {len(st.session_state.exam_items)}" | |
| elif st.session_state.mode == "caso": | |
| progreso = f"{st.session_state.case_idx + 1} / {len(st.session_state.case_list)}" | |
| elif st.session_state.mode == "recorrido": | |
| total = len(data["preguntas"]) | |
| idx = st.session_state.recorrido_idx | |
| progreso = f"{idx + 1} / {total}" | |
| st.caption(progreso) | |
| nuevo_idx = st.number_input("Ir a la pregunta n.º:", min_value=1, max_value=total, value=idx + 1, step=1) | |
| if nuevo_idx - 1 != idx: | |
| st.session_state.recorrido_idx = nuevo_idx - 1 | |
| st.session_state.question = data["preguntas"][nuevo_idx - 1] | |
| st.rerun() | |
| if progreso and st.session_state.mode != "recorrido": | |
| st.caption(progreso) | |
| # ───────── Pregunta ───────── | |
| q = st.session_state.question | |
| if "context" in q: | |
| st.info(f"**CONTEXTO ({q['caso']})**\n\n{q['context']}") | |
| st.markdown(f"### {q['pregunta']}") | |
| selected = st.radio( | |
| "Elige una respuesta:", | |
| options=list(q["opciones"].keys()), | |
| format_func=lambda k: f"{k}. {q['opciones'][k]}", | |
| index=None, | |
| key=f"radio_{q['id']}_{st.session_state.mode}" | |
| ) | |
| col1, col2 = st.columns(2) | |
| comprobar = col1.button("✅ Comprobar") | |
| siguiente = col2.button("➡️ Siguiente") | |
| # contenedores feedback | |
| feedback_box = st.empty() | |
| justif_box = st.empty() | |
| # ───────── Comprobar ───────── | |
| if comprobar and selected: | |
| correcta = q["respuesta_correcta"] | |
| ok = selected == correcta | |
| if ok: | |
| feedback_box.success("✅ ¡Correcto!") | |
| else: | |
| feedback_box.error(f"❌ Incorrecto. La respuesta correcta era **{correcta}**.") | |
| justif_box.markdown(f"**Justificación:**\n{q['justificacion']}") | |
| if st.session_state.mode == "examen": | |
| grp = st.session_state.exam_items[st.session_state.exam_idx]["grupo"] | |
| if grp == "normal" and ok: | |
| st.session_state.hits_norm += 1 | |
| elif grp != "normal" and ok: | |
| st.session_state.hits_case[grp] = \ | |
| st.session_state.hits_case.get(grp, 0) + 1 | |
| # ───────── Siguiente ───────── | |
| if siguiente: | |
| feedback_box.empty(); justif_box.empty() | |
| if st.session_state.mode == "preguntas": | |
| st.session_state.question = random_question() | |
| elif st.session_state.mode == "caso": | |
| st.session_state.case_idx += 1 | |
| if st.session_state.case_idx == len(st.session_state.case_list): | |
| caso = random_case() | |
| st.session_state.case_list = [ | |
| {**q, "caso": caso["titulo"], "context": caso["contexto"]} | |
| for q in caso["preguntas"] | |
| ] | |
| st.session_state.case_idx = 0 | |
| st.session_state.question = st.session_state.case_list[st.session_state.case_idx] | |
| elif st.session_state.mode == "examen": | |
| st.session_state.exam_idx += 1 | |
| if st.session_state.exam_idx == len(st.session_state.exam_items): | |
| pts_preg = st.session_state.hits_norm * (4 / 25) | |
| pts_casos = 0 | |
| detalles = [] | |
| for cid, tot in st.session_state.total_case.items(): | |
| peso_por_preg = 6 / tot | |
| aciertos = st.session_state.hits_case.get(cid, 0) | |
| pts = aciertos * peso_por_preg | |
| pts_casos += pts | |
| detalles.append(f"{cid}: {pts:.2f} / 6") | |
| total = pts_preg + pts_casos | |
| aprobado = total >= 7.5 | |
| st.balloons() | |
| st.markdown( | |
| f"## 🏁 Examen finalizado\n\n" | |
| f"**Preguntas (máx 4):** {pts_preg:.2f}\n\n" | |
| + "\n".join(detalles) + | |
| f"\n\n**TOTAL:** {total:.2f} / 16 → " | |
| + ("✅ Aprobado" if aprobado else "❌ No aprobado") | |
| ) | |
| st.stop() | |
| st.session_state.question = \ | |
| st.session_state.exam_items[st.session_state.exam_idx]["data"] | |
| elif st.session_state.mode == "recorrido": | |
| st.session_state.recorrido_idx += 1 | |
| if st.session_state.recorrido_idx >= len(data["preguntas"]): | |
| st.session_state.recorrido_idx = 0 | |
| st.session_state.question = data["preguntas"][st.session_state.recorrido_idx] | |
| st.rerun() | |