andrespm commited on
Commit
0a30df7
·
verified ·
1 Parent(s): af0f559

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +188 -40
src/streamlit_app.py CHANGED
@@ -1,40 +1,188 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
- import streamlit as st
5
-
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # streamlit_app.py
2
+ import json, random, streamlit as st
3
+ from pathlib import Path
4
+
5
+ DATA_FILE = Path(__file__).parent / "master_fundamentos_evaluado.json"
6
+
7
+
8
+ @st.cache_data
9
+ def load_data(path):
10
+ with open(path, encoding="utf-8") as f:
11
+ return json.load(f)
12
+
13
+ data = load_data(DATA_FILE)
14
+
15
+ # ───────── Helpers ─────────
16
+ def random_question():
17
+ return random.choice(data["preguntas"])
18
+
19
+ def random_case():
20
+ return random.choice(data["casos_clinicos"])
21
+
22
+ def build_exam():
23
+ preguntas = random.sample(data["preguntas"], 25)
24
+ casos = random.sample(data["casos_clinicos"], 2)
25
+ items = [{"grupo": "normal", "data": q} for q in preguntas]
26
+ for c in casos: # bloques de caso completos
27
+ for q in c["preguntas"]:
28
+ items.append({"grupo": c["id"],
29
+ "data": {**q, "caso": c["titulo"], "context": c["contexto"]}})
30
+ return items
31
+
32
+ # ───────── Estado inicial ─────────
33
+ if "mode" not in st.session_state:
34
+ st.session_state.mode = "preguntas" # preguntas | caso | examen
35
+ st.session_state.question = random_question()
36
+ st.session_state.case_list = []
37
+ st.session_state.case_idx = 0
38
+ st.session_state.exam_items = []
39
+ st.session_state.exam_idx = 0
40
+ st.session_state.hits_norm = 0
41
+ st.session_state.hits_case = {}
42
+ st.session_state.total_case = {}
43
+
44
+ # ───────── Barra lateral ─────────
45
+ st.sidebar.title("Fundamentos")
46
+
47
+ if st.sidebar.button("Preguntas sueltas"):
48
+ st.session_state.mode = "preguntas"
49
+ st.session_state.question = random_question()
50
+ st.rerun()
51
+
52
+ if st.sidebar.button("Casos clínicos"):
53
+ st.session_state.mode = "caso"
54
+ caso = random_case()
55
+ st.session_state.case_list = [
56
+ {**q, "caso": caso["titulo"], "context": caso["contexto"]}
57
+ for q in caso["preguntas"]
58
+ ]
59
+ st.session_state.case_idx = 0
60
+ st.session_state.question = st.session_state.case_list[0]
61
+ st.rerun()
62
+
63
+ if st.sidebar.button("Examen (25 + 2 casos)"):
64
+ st.session_state.mode = "examen"
65
+ st.session_state.exam_items = build_exam()
66
+ st.session_state.exam_idx = 0
67
+ st.session_state.hits_norm = 0
68
+ st.session_state.hits_case.clear()
69
+ st.session_state.total_case = {}
70
+ for it in st.session_state.exam_items:
71
+ if it["grupo"] != "normal":
72
+ st.session_state.total_case[it["grupo"]] = \
73
+ st.session_state.total_case.get(it["grupo"], 0) + 1
74
+ st.session_state.question = st.session_state.exam_items[0]["data"]
75
+ st.rerun()
76
+
77
+ st.sidebar.markdown("---")
78
+ st.sidebar.markdown("Creado con ❤️ por Requetito")
79
+
80
+ # ───────── Encabezado: modo + progreso ─────────
81
+ mode_label = {
82
+ "preguntas": "Práctica – Preguntas sueltas",
83
+ "caso": "Práctica – Casos clínicos",
84
+ "examen": "Examen final"
85
+ }
86
+
87
+ st.subheader(mode_label[st.session_state.mode])
88
+
89
+ progreso = ""
90
+ if st.session_state.mode == "examen":
91
+ progreso = f"{st.session_state.exam_idx + 1} / {len(st.session_state.exam_items)}"
92
+ elif st.session_state.mode == "caso":
93
+ progreso = f"{st.session_state.case_idx + 1} / {len(st.session_state.case_list)}"
94
+
95
+ if progreso:
96
+ st.caption(progreso)
97
+
98
+ # ───────── Pregunta ─────────
99
+ q = st.session_state.question
100
+
101
+ if "context" in q:
102
+ st.info(f"**CONTEXTO ({q['caso']})**\n\n{q['context']}")
103
+ st.markdown(f"### {q['pregunta']}")
104
+
105
+ selected = st.radio(
106
+ "Elige una respuesta:",
107
+ options=list(q["opciones"].keys()),
108
+ format_func=lambda k: f"{k}. {q['opciones'][k]}",
109
+ index=None,
110
+ key=f"radio_{q['id']}_{st.session_state.mode}"
111
+ )
112
+
113
+ col1, col2 = st.columns(2)
114
+ comprobar = col1.button("✅ Comprobar")
115
+ siguiente = col2.button("➡️ Siguiente")
116
+
117
+ # contenedores feedback
118
+ feedback_box = st.empty()
119
+ justif_box = st.empty()
120
+
121
+ # ───────── Comprobar ─────────
122
+ if comprobar and selected:
123
+ correcta = q["respuesta_correcta"]
124
+ ok = selected == correcta
125
+ if ok:
126
+ feedback_box.success("✅ ¡Correcto!")
127
+ else:
128
+ feedback_box.error(f"❌ Incorrecto. La respuesta correcta era **{correcta}**.")
129
+ justif_box.markdown(f"**Justificación:**\n{q['justificacion']}")
130
+
131
+ if st.session_state.mode == "examen":
132
+ grp = st.session_state.exam_items[st.session_state.exam_idx]["grupo"]
133
+ if grp == "normal" and ok:
134
+ st.session_state.hits_norm += 1
135
+ elif grp != "normal" and ok:
136
+ st.session_state.hits_case[grp] = \
137
+ st.session_state.hits_case.get(grp, 0) + 1
138
+
139
+ # ───────── Siguiente ─────────
140
+ if siguiente:
141
+ feedback_box.empty(); justif_box.empty() # limpiar
142
+
143
+ if st.session_state.mode == "preguntas":
144
+ st.session_state.question = random_question()
145
+
146
+ elif st.session_state.mode == "caso":
147
+ st.session_state.case_idx += 1
148
+ if st.session_state.case_idx == len(st.session_state.case_list):
149
+ caso = random_case()
150
+ st.session_state.case_list = [
151
+ {**q, "caso": caso["titulo"], "context": caso["contexto"]}
152
+ for q in caso["preguntas"]
153
+ ]
154
+ st.session_state.case_idx = 0
155
+ st.session_state.question = st.session_state.case_list[st.session_state.case_idx]
156
+
157
+ elif st.session_state.mode == "examen":
158
+ st.session_state.exam_idx += 1
159
+ if st.session_state.exam_idx == len(st.session_state.exam_items):
160
+ # Fin examen
161
+ # NUEVA PONDERACIÓN
162
+ pts_preg = st.session_state.hits_norm * (4 / 25)
163
+ pts_casos = 0; detalles = []
164
+
165
+ for cid, tot in st.session_state.total_case.items():
166
+ peso_por_preg = 6 / tot # Cada caso vale 6 puntos en total
167
+ aciertos = st.session_state.hits_case.get(cid, 0)
168
+ pts = aciertos * peso_por_preg
169
+ pts_casos += pts
170
+ detalles.append(f"{cid}: {pts:.2f} / 6")
171
+
172
+ total = pts_preg + pts_casos
173
+
174
+ aprobado = total >= 7.5
175
+ st.balloons()
176
+ st.markdown(
177
+ f"## 🏁 Examen finalizado\n\n"
178
+ f"**Preguntas (máx 4):** {pts_preg:.2f}\n\n"
179
+ + "\n".join(detalles) +
180
+ f"\n\n**TOTAL:** {total:.2f} / 16 → "
181
+ + ("✅ Aprobado" if total >= 7.5 else "❌ No aprobado")
182
+ )
183
+ st.stop()
184
+
185
+ st.session_state.question = \
186
+ st.session_state.exam_items[st.session_state.exam_idx]["data"]
187
+
188
+ st.rerun()