Spaces:
Sleeping
Sleeping
| ############## | |
| ###modules/studentact/student_activities_v2.py | |
| import streamlit as st | |
| import re | |
| import io | |
| from io import BytesIO | |
| import pandas as pd | |
| import numpy as np | |
| import time | |
| import matplotlib.pyplot as plt | |
| from datetime import datetime | |
| from spacy import displacy | |
| import random | |
| import base64 | |
| import seaborn as sns | |
| import logging | |
| # Importaciones de la base de datos | |
| from ..database.morphosintax_mongo_db import get_student_morphosyntax_analysis | |
| from ..database.semantic_mongo_db import get_student_semantic_analysis | |
| from ..database.discourse_mongo_db import get_student_discourse_analysis | |
| from ..database.chat_mongo_db import get_chat_history | |
| from ..database.current_situation_mongo_db import get_current_situation_analysis # Nueva importación | |
| from ..database.claude_recommendations_mongo_db import get_claude_recommendations # Actualizada | |
| logger = logging.getLogger(__name__) | |
| ################################################################################### | |
| def display_student_activities(username: str, lang_code: str, t: dict): | |
| """ | |
| Muestra todas las actividades del estudiante | |
| Args: | |
| username: Nombre del estudiante | |
| lang_code: Código del idioma | |
| t: Diccionario de traducciones | |
| """ | |
| try: | |
| st.header(t.get('activities_title', 'Mis Actividades')) | |
| # Tabs para diferentes tipos de análisis (añadimos la nueva tab) | |
| tabs = st.tabs([ | |
| t.get('current_situation_activities', 'Mi Situación Actual'), # Nueva pestaña | |
| t.get('morpho_activities', 'Análisis Morfosintáctico'), | |
| t.get('semantic_activities', 'Análisis Semántico'), | |
| t.get('discourse_activities', 'Análisis del Discurso'), | |
| t.get('chat_activities', 'Conversaciones con el Asistente') | |
| ]) | |
| # Tab de Situación Actual (nueva) | |
| with tabs[0]: | |
| display_current_situation_activities(username, t) | |
| # Tab de Análisis Morfosintáctico | |
| with tabs[1]: | |
| display_morphosyntax_activities(username, t) | |
| # Tab de Análisis Semántico | |
| with tabs[2]: | |
| display_semantic_activities(username, t) | |
| # Tab de Análisis del Discurso | |
| with tabs[3]: | |
| display_discourse_activities(username, t) | |
| # Tab de Conversaciones del Chat | |
| with tabs[4]: | |
| display_chat_activities(username, t) | |
| except Exception as e: | |
| logger.error(f"Error mostrando actividades: {str(e)}") | |
| st.error(t.get('error_loading_activities', 'Error al cargar las actividades')) | |
| ############################################################################################### | |
| def display_morphosyntax_activities(username: str, t: dict): | |
| """Muestra actividades de análisis morfosintáctico""" | |
| try: | |
| analyses = get_student_morphosyntax_analysis(username) | |
| if not analyses: | |
| st.info(t.get('no_morpho_analyses', 'No hay análisis morfosintácticos registrados')) | |
| return | |
| for analysis in analyses: | |
| with st.expander( | |
| f"{t.get('analysis_date', 'Fecha')}: {analysis['timestamp']}", | |
| expanded=False | |
| ): | |
| st.text(f"{t.get('analyzed_text', 'Texto analizado')}:") | |
| st.write(analysis['text']) | |
| if 'arc_diagrams' in analysis: | |
| st.subheader(t.get('syntactic_diagrams', 'Diagramas sintácticos')) | |
| for diagram in analysis['arc_diagrams']: | |
| st.write(diagram, unsafe_allow_html=True) | |
| except Exception as e: | |
| logger.error(f"Error mostrando análisis morfosintáctico: {str(e)}") | |
| st.error(t.get('error_morpho', 'Error al mostrar análisis morfosintáctico')) | |
| ############################################################################################### | |
| def display_semantic_activities(username: str, t: dict): | |
| """Muestra actividades de análisis semántico""" | |
| try: | |
| logger.info(f"Recuperando análisis semántico para {username}") | |
| analyses = get_student_semantic_analysis(username) | |
| if not analyses: | |
| logger.info("No se encontraron análisis semánticos") | |
| st.info(t.get('no_semantic_analyses', 'No hay análisis semánticos registrados')) | |
| return | |
| logger.info(f"Procesando {len(analyses)} análisis semánticos") | |
| for analysis in analyses: | |
| try: | |
| # Verificar campos necesarios | |
| if not all(key in analysis for key in ['timestamp', 'concept_graph']): | |
| logger.warning(f"Análisis incompleto: {analysis.keys()}") | |
| continue | |
| # Formatear fecha | |
| timestamp = datetime.fromisoformat(analysis['timestamp'].replace('Z', '+00:00')) | |
| formatted_date = timestamp.strftime("%d/%m/%Y %H:%M:%S") | |
| # Crear expander | |
| with st.expander(f"{t.get('analysis_date', 'Fecha')}: {formatted_date}", expanded=False): | |
| # Procesar y mostrar gráfico | |
| if analysis.get('concept_graph'): | |
| try: | |
| # Convertir de base64 a bytes | |
| logger.debug("Decodificando gráfico de conceptos") | |
| image_data = analysis['concept_graph'] | |
| # Si el gráfico ya es bytes, usarlo directamente | |
| if isinstance(image_data, bytes): | |
| image_bytes = image_data | |
| else: | |
| # Si es string base64, decodificar | |
| image_bytes = base64.b64decode(image_data) | |
| logger.debug(f"Longitud de bytes de imagen: {len(image_bytes)}") | |
| # Mostrar imagen | |
| st.image( | |
| image_bytes, | |
| caption=t.get('concept_network', 'Red de Conceptos'), | |
| use_column_width=True | |
| ) | |
| logger.debug("Gráfico mostrado exitosamente") | |
| except Exception as img_error: | |
| logger.error(f"Error procesando gráfico: {str(img_error)}") | |
| st.error(t.get('error_loading_graph', 'Error al cargar el gráfico')) | |
| else: | |
| st.info(t.get('no_graph', 'No hay visualización disponible')) | |
| except Exception as e: | |
| logger.error(f"Error procesando análisis individual: {str(e)}") | |
| continue | |
| except Exception as e: | |
| logger.error(f"Error mostrando análisis semántico: {str(e)}") | |
| st.error(t.get('error_semantic', 'Error al mostrar análisis semántico')) | |
| ################################################################################################### | |
| def display_discourse_activities(username: str, t: dict): | |
| """Muestra actividades de análisis del discurso""" | |
| try: | |
| logger.info(f"Recuperando análisis del discurso para {username}") | |
| analyses = get_student_discourse_analysis(username) | |
| if not analyses: | |
| logger.info("No se encontraron análisis del discurso") | |
| st.info(t.get('no_discourse_analyses', 'No hay análisis del discurso registrados')) | |
| return | |
| logger.info(f"Procesando {len(analyses)} análisis del discurso") | |
| for analysis in analyses: | |
| try: | |
| # Verificar campos mínimos necesarios | |
| if not all(key in analysis for key in ['timestamp', 'combined_graph']): | |
| logger.warning(f"Análisis incompleto: {analysis.keys()}") | |
| continue | |
| # Formatear fecha | |
| timestamp = datetime.fromisoformat(analysis['timestamp'].replace('Z', '+00:00')) | |
| formatted_date = timestamp.strftime("%d/%m/%Y %H:%M:%S") | |
| with st.expander(f"{t.get('analysis_date', 'Fecha')}: {formatted_date}", expanded=False): | |
| if analysis['combined_graph']: | |
| logger.debug("Decodificando gráfico combinado") | |
| try: | |
| image_bytes = base64.b64decode(analysis['combined_graph']) | |
| st.image(image_bytes, use_column_width=True) | |
| logger.debug("Gráfico mostrado exitosamente") | |
| except Exception as img_error: | |
| logger.error(f"Error decodificando imagen: {str(img_error)}") | |
| st.error(t.get('error_loading_graph', 'Error al cargar el gráfico')) | |
| else: | |
| st.info(t.get('no_visualization', 'No hay visualización comparativa disponible')) | |
| except Exception as e: | |
| logger.error(f"Error procesando análisis individual: {str(e)}") | |
| continue | |
| except Exception as e: | |
| logger.error(f"Error mostrando análisis del discurso: {str(e)}") | |
| st.error(t.get('error_discourse', 'Error al mostrar análisis del discurso')) | |
| ################################################################################### | |
| # Nueva función para mostrar las actividades de Situación Actual | |
| def display_current_situation_activities(username: str, t: dict): | |
| """Muestra actividades de análisis de situación actual con recomendaciones de Claude""" | |
| try: | |
| logger.info(f"Recuperando análisis de situación actual para {username}") | |
| analyses = get_current_situation_analysis(username) | |
| if not analyses: | |
| logger.info("No se encontraron análisis de situación actual") | |
| st.info(t.get('no_current_situation', 'No hay análisis de situación actual registrados')) | |
| return | |
| logger.info(f"Procesando {len(analyses)} análisis de situación actual") | |
| for analysis in analyses: | |
| try: | |
| # Verificar campos necesarios | |
| if not all(key in analysis for key in ['timestamp', 'feedback']): | |
| logger.warning(f"Análisis incompleto: {analysis.keys()}") | |
| continue | |
| # Formatear fecha | |
| timestamp = datetime.fromisoformat(analysis['timestamp'].replace('Z', '+00:00')) | |
| formatted_date = timestamp.strftime("%d/%m/%Y %H:%M:%S") | |
| # Crear expander con título que incluye información del tipo de texto si está disponible | |
| title = f"{t.get('analysis_date', 'Fecha')}: {formatted_date}" | |
| if 'text_type' in analysis: | |
| text_type_display = { | |
| 'academic_article': t.get('academic_article', 'Artículo académico'), | |
| 'university_work': t.get('university_work', 'Trabajo universitario'), | |
| 'general_communication': t.get('general_communication', 'Comunicación general') | |
| }.get(analysis['text_type'], analysis['text_type']) | |
| title += f" - {text_type_display}" | |
| with st.expander(title, expanded=False): | |
| # Mostrar el texto original analizado | |
| st.subheader(t.get('analyzed_text', 'Texto analizado')) | |
| st.text_area( | |
| "", | |
| value=analysis.get('text', ''), | |
| height=100, | |
| disabled=True, | |
| label_visibility="collapsed" | |
| ) | |
| # Mostrar las recomendaciones generadas por Claude | |
| st.subheader(t.get('recommendations', 'Recomendaciones')) | |
| # Dar formato a las recomendaciones en un contenedor estilizado | |
| st.markdown(f""" | |
| <div style="padding: 20px; border-radius: 10px; | |
| background-color: #f8f9fa; margin-bottom: 20px;"> | |
| {analysis['feedback']} | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Mostrar métricas adicionales si están disponibles | |
| if 'metrics' in analysis and analysis['metrics']: | |
| with st.expander(t.get('metrics_details', 'Detalles de métricas')): | |
| # Convertir métricas a dataframe para mejor visualización | |
| metrics_df = pd.DataFrame([ | |
| {"Métrica": k, "Valor": v} | |
| for k, v in analysis['metrics'].items() | |
| if k not in ['test_type', 'timestamp'] and not isinstance(v, dict) | |
| ]) | |
| st.dataframe(metrics_df, use_container_width=True) | |
| except Exception as e: | |
| logger.error(f"Error procesando análisis individual de situación actual: {str(e)}") | |
| continue | |
| except Exception as e: | |
| logger.error(f"Error mostrando análisis de situación actual: {str(e)}") | |
| st.error(t.get('error_current_situation', 'Error al mostrar análisis de situación actual')) | |
| ################################################################################# | |
| def display_discourse_comparison(analysis: dict, t: dict): | |
| """Muestra la comparación de análisis del discurso""" | |
| st.subheader(t.get('comparison_results', 'Resultados de la comparación')) | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown(f"**{t.get('concepts_text_1', 'Conceptos Texto 1')}**") | |
| df1 = pd.DataFrame(analysis['key_concepts1']) | |
| st.dataframe(df1) | |
| with col2: | |
| st.markdown(f"**{t.get('concepts_text_2', 'Conceptos Texto 2')}**") | |
| df2 = pd.DataFrame(analysis['key_concepts2']) | |
| st.dataframe(df2) | |
| ################################################################################# | |
| def display_chat_activities(username: str, t: dict): | |
| """ | |
| Muestra historial de conversaciones del chat | |
| """ | |
| try: | |
| # Obtener historial del chat | |
| chat_history = get_chat_history( | |
| username=username, | |
| analysis_type='sidebar', | |
| limit=50 | |
| ) | |
| if not chat_history: | |
| st.info(t.get('no_chat_history', 'No hay conversaciones registradas')) | |
| return | |
| for chat in reversed(chat_history): # Mostrar las más recientes primero | |
| try: | |
| # Convertir timestamp a datetime para formato | |
| timestamp = datetime.fromisoformat(chat['timestamp'].replace('Z', '+00:00')) | |
| formatted_date = timestamp.strftime("%d/%m/%Y %H:%M:%S") | |
| with st.expander( | |
| f"{t.get('chat_date', 'Fecha de conversación')}: {formatted_date}", | |
| expanded=False | |
| ): | |
| if 'messages' in chat and chat['messages']: | |
| # Mostrar cada mensaje en la conversación | |
| for message in chat['messages']: | |
| role = message.get('role', 'unknown') | |
| content = message.get('content', '') | |
| # Usar el componente de chat de Streamlit | |
| with st.chat_message(role): | |
| st.markdown(content) | |
| # Agregar separador entre mensajes | |
| st.divider() | |
| else: | |
| st.warning(t.get('invalid_chat_format', 'Formato de chat no válido')) | |
| except Exception as e: | |
| logger.error(f"Error mostrando conversación: {str(e)}") | |
| continue | |
| except Exception as e: | |
| logger.error(f"Error mostrando historial del chat: {str(e)}") | |
| st.error(t.get('error_chat', 'Error al mostrar historial del chat')) | |
| ############################################################################################## | |
| # Nueva función para mostrar las actividades de Situación Actual | |
| def display_current_situation_activities(username: str, t: dict): | |
| """Muestra actividades de análisis de situación actual con recomendaciones de Claude""" | |
| try: | |
| logger.info(f"Recuperando recomendaciones de Claude para {username}") | |
| recommendations = get_claude_recommendations(username) | |
| if not recommendations: | |
| logger.info("No se encontraron recomendaciones de Claude") | |
| st.info(t.get('no_recommendations', 'No hay recomendaciones de Claude registradas')) | |
| return | |
| logger.info(f"Procesando {len(recommendations)} recomendaciones de Claude") | |
| for recommendation in recommendations: | |
| try: | |
| # Verificar campos necesarios | |
| if not all(key in recommendation for key in ['timestamp', 'feedback']): | |
| logger.warning(f"Recomendación incompleta: {recommendation.keys()}") | |
| continue | |
| # Formatear fecha | |
| timestamp = datetime.fromisoformat(recommendation['timestamp'].replace('Z', '+00:00')) | |
| formatted_date = timestamp.strftime("%d/%m/%Y %H:%M:%S") | |
| # Crear expander con título que incluye información del tipo de texto si está disponible | |
| title = f"{t.get('recommendation_date', 'Fecha')}: {formatted_date}" | |
| if 'text_type' in recommendation: | |
| text_type_display = { | |
| 'academic_article': t.get('academic_article', 'Artículo académico'), | |
| 'university_work': t.get('university_work', 'Trabajo universitario'), | |
| 'general_communication': t.get('general_communication', 'Comunicación general') | |
| }.get(recommendation['text_type'], recommendation['text_type']) | |
| title += f" - {text_type_display}" | |
| with st.expander(title, expanded=False): | |
| # Mostrar el texto original analizado | |
| st.subheader(t.get('analyzed_text', 'Texto analizado')) | |
| st.text_area( | |
| "", | |
| value=recommendation.get('text', ''), | |
| height=100, | |
| disabled=True, | |
| label_visibility="collapsed" | |
| ) | |
| # Mostrar las recomendaciones generadas por Claude | |
| st.subheader(t.get('recommendations', 'Recomendaciones de Claude')) | |
| # Dar formato a las recomendaciones en un contenedor estilizado | |
| st.markdown(f""" | |
| <div style="padding: 20px; border-radius: 10px; | |
| background-color: #f8f9fa; margin-bottom: 20px;"> | |
| {recommendation.get('feedback', 'No hay recomendaciones disponibles')} | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Mostrar métricas adicionales si están disponibles | |
| if 'metrics' in recommendation and recommendation['metrics']: | |
| with st.expander(t.get('metrics_details', 'Detalles de métricas')): | |
| # Crear un DataFrame para mejor visualización | |
| metrics_data = [] | |
| for key, value in recommendation['metrics'].items(): | |
| if not isinstance(value, dict) and key not in ['test_type', 'timestamp']: | |
| metrics_data.append({"Métrica": key, "Valor": value}) | |
| if metrics_data: | |
| metrics_df = pd.DataFrame(metrics_data) | |
| st.dataframe(metrics_df, use_container_width=True) | |
| else: | |
| st.info(t.get('no_metrics', 'No hay métricas disponibles')) | |
| except Exception as e: | |
| logger.error(f"Error procesando recomendación individual: {str(e)}") | |
| continue | |
| except Exception as e: | |
| logger.error(f"Error mostrando recomendaciones de Claude: {str(e)}") | |
| st.error(t.get('error_recommendations', 'Error al mostrar recomendaciones de Claude')) | |