Spaces:
Sleeping
Sleeping
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:
|