Update modules/studentact/student_activities_v2.py
Browse files
modules/studentact/student_activities_v2.py
CHANGED
@@ -81,12 +81,12 @@ def display_student_activities(username: str, lang_code: str, t: dict):
|
|
81 |
def display_current_situation_activities(username: str, t: dict):
|
82 |
"""
|
83 |
Muestra an谩lisis de situaci贸n actual junto con las recomendaciones de Claude
|
84 |
-
unificando la informaci贸n de ambas colecciones.
|
85 |
"""
|
86 |
try:
|
87 |
# Recuperar datos de ambas colecciones
|
88 |
logger.info(f"Recuperando an谩lisis de situaci贸n actual para {username}")
|
89 |
-
situation_analyses = get_current_situation_analysis(username, limit=
|
90 |
|
91 |
# Verificar si hay datos
|
92 |
if situation_analyses:
|
@@ -113,49 +113,90 @@ def display_current_situation_activities(username: str, t: dict):
|
|
113 |
st.info(t.get('no_current_situation', 'No hay an谩lisis de situaci贸n actual registrados'))
|
114 |
return
|
115 |
|
116 |
-
# Crear
|
117 |
-
|
118 |
-
logger.info("Creando 铆ndice temporal de an谩lisis")
|
119 |
-
analyses_by_timestamp = {}
|
120 |
|
121 |
-
#
|
|
|
122 |
for analysis in situation_analyses:
|
123 |
if 'timestamp' in analysis:
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
|
|
|
|
128 |
|
129 |
-
|
130 |
for recommendation in claude_recommendations:
|
131 |
if 'timestamp' in recommendation:
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
|
139 |
-
#
|
140 |
-
|
141 |
-
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
|
151 |
-
logger.info(f"Procesando {len(
|
152 |
|
153 |
-
# Mostrar cada par
|
154 |
-
for i, (timestamp_key, analysis_pair) in enumerate(
|
155 |
try:
|
156 |
# Obtener datos de situaci贸n y recomendaci贸n
|
157 |
situation_data = analysis_pair.get('situation', {})
|
158 |
recommendation_data = analysis_pair.get('recommendation', {})
|
|
|
159 |
|
160 |
# Si no hay ning煤n dato, continuar al siguiente
|
161 |
if not situation_data and not recommendation_data:
|
@@ -167,10 +208,9 @@ def display_current_situation_activities(username: str, t: dict):
|
|
167 |
|
168 |
# Formatear fecha para mostrar
|
169 |
try:
|
170 |
-
# Usar timestamp
|
171 |
-
|
172 |
-
|
173 |
-
formatted_date = timestamp.strftime("%d/%m/%Y %H:%M:%S")
|
174 |
except Exception as date_error:
|
175 |
logger.error(f"Error formateando fecha: {str(date_error)}")
|
176 |
formatted_date = timestamp_key
|
@@ -185,8 +225,15 @@ def display_current_situation_activities(username: str, t: dict):
|
|
185 |
}.get(text_type, text_type)
|
186 |
title += f" - {text_type_display}"
|
187 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
# Usar un ID 煤nico para cada expander
|
189 |
-
expander_id = f"analysis_{i}_{timestamp_key}"
|
190 |
|
191 |
# Mostrar el an谩lisis en un expander
|
192 |
with st.expander(title, expanded=False):
|
@@ -212,9 +259,6 @@ def display_current_situation_activities(username: str, t: dict):
|
|
212 |
if situation_data and 'metrics' in situation_data:
|
213 |
metrics = situation_data['metrics']
|
214 |
|
215 |
-
# Imprimir estructura real para depuraci贸n
|
216 |
-
st.write("Estructura de m茅tricas:", metrics)
|
217 |
-
|
218 |
# Dividir en dos columnas
|
219 |
col1, col2 = st.columns(2)
|
220 |
|
@@ -235,9 +279,8 @@ def display_current_situation_activities(username: str, t: dict):
|
|
235 |
score = metric_data['score']
|
236 |
elif 'value' in metric_data:
|
237 |
score = metric_data['value']
|
238 |
-
|
239 |
-
|
240 |
-
score = metric_data
|
241 |
|
242 |
if score is not None:
|
243 |
# Asegurarse de que score es num茅rico
|
@@ -263,11 +306,10 @@ def display_current_situation_activities(username: str, t: dict):
|
|
263 |
# Si no es num茅rico, mostrar como texto
|
264 |
st.markdown(f"""
|
265 |
<div style="background-color:#f0f0f0; padding:10px; border-radius:5px; margin-bottom:10px;">
|
266 |
-
<b>鈩癸笍 {metric_name.capitalize()}:</b> {score}
|
267 |
</div>
|
268 |
""", unsafe_allow_html=True)
|
269 |
except Exception as e:
|
270 |
-
st.error(f"Error procesando m茅trica {metric_name}: {str(e)}")
|
271 |
logger.error(f"Error procesando m茅trica {metric_name}: {str(e)}")
|
272 |
|
273 |
# Mostrar detalles adicionales si est谩n disponibles
|
@@ -288,16 +330,12 @@ def display_current_situation_activities(username: str, t: dict):
|
|
288 |
if k not in ['normalized_score', 'score', 'value']}
|
289 |
|
290 |
if details:
|
291 |
-
detail_key = f"details_{metric_name}_{timestamp_key}_{i}"
|
292 |
st.write(f"**{metric_name.capitalize()}**")
|
293 |
st.json(details, expanded=False)
|
294 |
except Exception as e:
|
295 |
-
st.error(f"Error mostrando detalles de {metric_name}: {str(e)}")
|
296 |
logger.error(f"Error mostrando detalles de {metric_name}: {str(e)}")
|
297 |
else:
|
298 |
st.info(t.get('no_diagnosis', 'No hay datos de diagn贸stico disponibles'))
|
299 |
-
if situation_data:
|
300 |
-
logger.warning(f"Claves disponibles en situation_data: {list(situation_data.keys())}")
|
301 |
|
302 |
# Tab de recomendaciones
|
303 |
with recommendations_tab:
|
|
|
81 |
def display_current_situation_activities(username: str, t: dict):
|
82 |
"""
|
83 |
Muestra an谩lisis de situaci贸n actual junto con las recomendaciones de Claude
|
84 |
+
unificando la informaci贸n de ambas colecciones y emparej谩ndolas por cercan铆a temporal.
|
85 |
"""
|
86 |
try:
|
87 |
# Recuperar datos de ambas colecciones
|
88 |
logger.info(f"Recuperando an谩lisis de situaci贸n actual para {username}")
|
89 |
+
situation_analyses = get_current_situation_analysis(username, limit=10)
|
90 |
|
91 |
# Verificar si hay datos
|
92 |
if situation_analyses:
|
|
|
113 |
st.info(t.get('no_current_situation', 'No hay an谩lisis de situaci贸n actual registrados'))
|
114 |
return
|
115 |
|
116 |
+
# Crear pares combinados emparejando diagn贸sticos y recomendaciones cercanos en tiempo
|
117 |
+
logger.info("Creando emparejamientos temporales de an谩lisis")
|
|
|
|
|
118 |
|
119 |
+
# Convertir timestamps a objetos datetime para comparaci贸n
|
120 |
+
situation_times = []
|
121 |
for analysis in situation_analyses:
|
122 |
if 'timestamp' in analysis:
|
123 |
+
try:
|
124 |
+
timestamp_str = analysis['timestamp']
|
125 |
+
dt = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
|
126 |
+
situation_times.append((dt, analysis))
|
127 |
+
except Exception as e:
|
128 |
+
logger.error(f"Error parseando timestamp de situaci贸n: {str(e)}")
|
129 |
|
130 |
+
recommendation_times = []
|
131 |
for recommendation in claude_recommendations:
|
132 |
if 'timestamp' in recommendation:
|
133 |
+
try:
|
134 |
+
timestamp_str = recommendation['timestamp']
|
135 |
+
dt = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
|
136 |
+
recommendation_times.append((dt, recommendation))
|
137 |
+
except Exception as e:
|
138 |
+
logger.error(f"Error parseando timestamp de recomendaci贸n: {str(e)}")
|
139 |
|
140 |
+
# Ordenar por tiempo
|
141 |
+
situation_times.sort(key=lambda x: x[0], reverse=True)
|
142 |
+
recommendation_times.sort(key=lambda x: x[0], reverse=True)
|
143 |
+
|
144 |
+
# Crear pares combinados
|
145 |
+
combined_items = []
|
146 |
+
|
147 |
+
# Primero, procesar todas las situaciones encontrando la recomendaci贸n m谩s cercana
|
148 |
+
for sit_time, situation in situation_times:
|
149 |
+
# Buscar la recomendaci贸n m谩s cercana en tiempo
|
150 |
+
best_match = None
|
151 |
+
min_diff = timedelta(minutes=30) # M谩xima diferencia de tiempo aceptable (30 minutos)
|
152 |
+
best_rec_time = None
|
153 |
|
154 |
+
for rec_time, recommendation in recommendation_times:
|
155 |
+
time_diff = abs(sit_time - rec_time)
|
156 |
+
if time_diff < min_diff:
|
157 |
+
min_diff = time_diff
|
158 |
+
best_match = recommendation
|
159 |
+
best_rec_time = rec_time
|
160 |
+
|
161 |
+
# Crear un elemento combinado
|
162 |
+
if best_match:
|
163 |
+
timestamp_key = sit_time.isoformat()
|
164 |
+
combined_items.append((timestamp_key, {
|
165 |
+
'situation': situation,
|
166 |
+
'recommendation': best_match,
|
167 |
+
'time_diff': min_diff.total_seconds()
|
168 |
+
}))
|
169 |
+
# Eliminar la recomendaci贸n usada para no reutilizarla
|
170 |
+
recommendation_times = [(t, r) for t, r in recommendation_times if t != best_rec_time]
|
171 |
+
logger.info(f"Emparejado: Diagn贸stico {sit_time} con Recomendaci贸n {best_rec_time} (diferencia: {min_diff})")
|
172 |
+
else:
|
173 |
+
# Si no hay recomendaci贸n cercana, solo incluir la situaci贸n
|
174 |
+
timestamp_key = sit_time.isoformat()
|
175 |
+
combined_items.append((timestamp_key, {
|
176 |
+
'situation': situation
|
177 |
+
}))
|
178 |
+
logger.info(f"Sin emparejar: Diagn贸stico {sit_time} sin recomendaci贸n cercana")
|
179 |
+
|
180 |
+
# Agregar recomendaciones restantes sin situaci贸n
|
181 |
+
for rec_time, recommendation in recommendation_times:
|
182 |
+
timestamp_key = rec_time.isoformat()
|
183 |
+
combined_items.append((timestamp_key, {
|
184 |
+
'recommendation': recommendation
|
185 |
+
}))
|
186 |
+
logger.info(f"Sin emparejar: Recomendaci贸n {rec_time} sin diagn贸stico cercano")
|
187 |
+
|
188 |
+
# Ordenar por tiempo (m谩s reciente primero)
|
189 |
+
combined_items.sort(key=lambda x: x[0], reverse=True)
|
190 |
|
191 |
+
logger.info(f"Procesando {len(combined_items)} elementos combinados")
|
192 |
|
193 |
+
# Mostrar cada par combinado
|
194 |
+
for i, (timestamp_key, analysis_pair) in enumerate(combined_items):
|
195 |
try:
|
196 |
# Obtener datos de situaci贸n y recomendaci贸n
|
197 |
situation_data = analysis_pair.get('situation', {})
|
198 |
recommendation_data = analysis_pair.get('recommendation', {})
|
199 |
+
time_diff = analysis_pair.get('time_diff')
|
200 |
|
201 |
# Si no hay ning煤n dato, continuar al siguiente
|
202 |
if not situation_data and not recommendation_data:
|
|
|
208 |
|
209 |
# Formatear fecha para mostrar
|
210 |
try:
|
211 |
+
# Usar timestamp del key que ya es un formato ISO
|
212 |
+
dt = datetime.fromisoformat(timestamp_key)
|
213 |
+
formatted_date = dt.strftime("%d/%m/%Y %H:%M:%S")
|
|
|
214 |
except Exception as date_error:
|
215 |
logger.error(f"Error formateando fecha: {str(date_error)}")
|
216 |
formatted_date = timestamp_key
|
|
|
225 |
}.get(text_type, text_type)
|
226 |
title += f" - {text_type_display}"
|
227 |
|
228 |
+
# A帽adir indicador de emparejamiento si existe
|
229 |
+
if time_diff is not None:
|
230 |
+
if time_diff < 60: # menos de un minuto
|
231 |
+
title += f" 馃攧 (emparejados)"
|
232 |
+
else:
|
233 |
+
title += f" 馃攧 (emparejados, diferencia: {int(time_diff//60)} min)"
|
234 |
+
|
235 |
# Usar un ID 煤nico para cada expander
|
236 |
+
expander_id = f"analysis_{i}_{timestamp_key.replace(':', '_')}"
|
237 |
|
238 |
# Mostrar el an谩lisis en un expander
|
239 |
with st.expander(title, expanded=False):
|
|
|
259 |
if situation_data and 'metrics' in situation_data:
|
260 |
metrics = situation_data['metrics']
|
261 |
|
|
|
|
|
|
|
262 |
# Dividir en dos columnas
|
263 |
col1, col2 = st.columns(2)
|
264 |
|
|
|
279 |
score = metric_data['score']
|
280 |
elif 'value' in metric_data:
|
281 |
score = metric_data['value']
|
282 |
+
elif isinstance(metric_data, (int, float)):
|
283 |
+
score = metric_data
|
|
|
284 |
|
285 |
if score is not None:
|
286 |
# Asegurarse de que score es num茅rico
|
|
|
306 |
# Si no es num茅rico, mostrar como texto
|
307 |
st.markdown(f"""
|
308 |
<div style="background-color:#f0f0f0; padding:10px; border-radius:5px; margin-bottom:10px;">
|
309 |
+
<b>鈩癸笍 {metric_name.capitalize()}:</b> {str(score)}
|
310 |
</div>
|
311 |
""", unsafe_allow_html=True)
|
312 |
except Exception as e:
|
|
|
313 |
logger.error(f"Error procesando m茅trica {metric_name}: {str(e)}")
|
314 |
|
315 |
# Mostrar detalles adicionales si est谩n disponibles
|
|
|
330 |
if k not in ['normalized_score', 'score', 'value']}
|
331 |
|
332 |
if details:
|
|
|
333 |
st.write(f"**{metric_name.capitalize()}**")
|
334 |
st.json(details, expanded=False)
|
335 |
except Exception as e:
|
|
|
336 |
logger.error(f"Error mostrando detalles de {metric_name}: {str(e)}")
|
337 |
else:
|
338 |
st.info(t.get('no_diagnosis', 'No hay datos de diagn贸stico disponibles'))
|
|
|
|
|
339 |
|
340 |
# Tab de recomendaciones
|
341 |
with recommendations_tab:
|