Spaces:
Sleeping
Sleeping
# streamlit_app.py | |
import json, random, streamlit as st | |
from pathlib import Path | |
DATA_FILE = Path(__file__).parent / "master_evaluacion_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"], 38) | |
casos = random.sample(data["casos_clinicos"], 1) | |
items = [{"grupo": "normal", "data": q} for q in preguntas] | |
for c in casos: # bloques de caso completos | |
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("Evaluación") | |
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 (38 + a caso)"): | |
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): | |
# Fin examen | |
pts_preg = st.session_state.hits_norm * (12.8 / 38) | |
pts_casos = 0 | |
detalles = [] | |
for cid, tot in st.session_state.total_case.items(): | |
peso_por_preg = 3.2 / tot | |
aciertos = st.session_state.hits_case.get(cid, 0) | |
pts = aciertos * peso_por_preg | |
pts_casos += pts | |
detalles.append(f"{cid}: {pts:.2f} / 3.2") | |
total = pts_preg + pts_casos | |
aprobado = total >= 7.5 | |
st.balloons() | |
st.markdown( | |
f"## 🏁 Examen finalizado\n\n" | |
f"**Preguntas (máx 12.8):** {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() | |