- {get_translation(t, 'semantic_initial_message', 'Welcome to the semantic analysis interface.')}
-
- """, unsafe_allow_html=True)
-
- # Inicializar el chatbot si no existe
- if 'semantic_chatbot' not in st.session_state:
- st.session_state.semantic_chatbot = initialize_chatbot('semantic')
-
- # Contenedor para la gestión de archivos
- with st.container():
- st.markdown('', unsafe_allow_html=True)
- col1, col2, col3, col4 = st.columns(4)
-
- with col1:
- if st.button(get_translation(t, 'upload_file', 'Upload File'), key=generate_unique_key('semantic', 'upload_button')):
- uploaded_file = st.file_uploader(get_translation(t, 'file_uploader', 'Choose a file'), type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
- if uploaded_file is not None:
- file_contents = uploaded_file.getvalue().decode('utf-8')
- if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
- st.success(get_translation(t, 'file_uploaded_success', 'File uploaded and saved to database successfully'))
- st.session_state.file_contents = file_contents
- st.rerun()
- else:
- st.error(get_translation(t, 'file_upload_error', 'Error uploading file'))
-
- with col2:
- user_files = get_user_files(st.session_state.username, 'semantic')
- file_options = [get_translation(t, 'select_file', 'Select a file')] + [file['file_name'] for file in user_files]
- selected_file = st.selectbox(get_translation(t, 'file_list', 'File List'), options=file_options, key=generate_unique_key('semantic', 'file_selector'))
- if selected_file != get_translation(t, 'select_file', 'Select a file'):
- if st.button(get_translation(t, 'load_file', 'Load File'), key=generate_unique_key('semantic', 'load_file')):
- file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
- if file_contents:
- st.session_state.file_contents = file_contents
- st.success(get_translation(t, 'file_loaded_success', 'File loaded successfully'))
- else:
- st.error(get_translation(t, 'file_load_error', 'Error loading file'))
-
- with col3:
- if st.button(get_translation(t, 'analyze_document', 'Analyze Document'), key=generate_unique_key('semantic', 'analyze_document')):
- if 'file_contents' in st.session_state:
- with st.spinner(get_translation(t, 'analyzing', 'Analyzing...')):
- graph, key_concepts = process_semantic_analysis(st.session_state.file_contents, nlp_models[lang_code], lang_code)
- st.session_state.graph = graph
- st.session_state.key_concepts = key_concepts
- st.success(get_translation(t, 'analysis_completed', 'Analysis completed'))
- else:
- st.error(get_translation(t, 'no_file_uploaded', 'No file uploaded'))
-
- with col4:
- if st.button(get_translation(t, 'delete_file', 'Delete File'), key=generate_unique_key('semantic', 'delete_file')):
- if selected_file and selected_file != get_translation(t, 'select_file', 'Select a file'):
- if delete_file(st.session_state.username, selected_file, 'semantic'):
- st.success(get_translation(t, 'file_deleted_success', 'File deleted successfully'))
- if 'file_contents' in st.session_state:
- del st.session_state.file_contents
- st.rerun()
- else:
- st.error(get_translation(t, 'file_delete_error', 'Error deleting file'))
- else:
- st.error(get_translation(t, 'no_file_selected', 'No file selected'))
-
- st.markdown('
', unsafe_allow_html=True)
-
- # Crear dos columnas: una para el chat y otra para la visualización
- col_chat, col_graph = st.columns([1, 1])
-
- with col_chat:
- st.subheader(get_translation(t, 'chat_title', 'Semantic Analysis Chat'))
- # Chat interface
- chat_container = st.container()
-
- with chat_container:
- # Mostrar el historial del chat
- chat_history = st.session_state.get('semantic_chat_history', [])
- for message in chat_history:
- with st.chat_message(message["role"]):
- st.write(message["content"])
-
- # Input del usuario
- user_input = st.chat_input(get_translation(t, 'semantic_chat_input', 'Type your message here...'), key=generate_unique_key('semantic', 'chat_input'))
-
- if user_input:
- # Añadir el mensaje del usuario al historial
- chat_history.append({"role": "user", "content": user_input})
-
- # Generar respuesta del chatbot
- chatbot = st.session_state.semantic_chatbot
- response = chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents'))
-
- # Añadir la respuesta del chatbot al historial
- chat_history.append({"role": "assistant", "content": response})
-
- # Actualizar el historial en session_state
- st.session_state.semantic_chat_history = chat_history
-
- # Forzar la actualización de la interfaz
- st.rerun()
-
- with col_graph:
- st.subheader(get_translation(t, 'graph_title', 'Semantic Graph'))
-
- # Mostrar conceptos clave en un expander horizontal
- with st.expander(get_translation(t, 'key_concepts_title', 'Key Concepts'), expanded=True):
- if 'key_concepts' in st.session_state:
- st.markdown('', unsafe_allow_html=True)
- for concept, freq in st.session_state.key_concepts:
- st.markdown(f'{concept}: {freq:.2f}', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
-
- if 'graph' in st.session_state:
- st.pyplot(st.session_state.graph)
-
- # Botón para limpiar el historial del chat
- if st.button(get_translation(t, 'clear_chat', 'Clear chat'), key=generate_unique_key('semantic', 'clear_chat')):
- st.session_state.semantic_chat_history = []
+import streamlit as st
+from .semantic_process import process_semantic_analysis
+from ..chatbot.chatbot import initialize_chatbot
+from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
+from ..utils.widget_utils import generate_unique_key
+
+def get_translation(t, key, default):
+ return t.get(key, default)
+
+def display_semantic_interface(lang_code, nlp_models, t):
+ #st.set_page_config(layout="wide")
+
+ # Estilo CSS personalizado
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # Mostrar el mensaje inicial como un párrafo estilizado
+ st.markdown(f"""
+
+ {get_translation(t, 'semantic_initial_message', 'Welcome to the semantic analysis interface.')}
+
+ """, unsafe_allow_html=True)
+
+ # Inicializar el chatbot si no existe
+ if 'semantic_chatbot' not in st.session_state:
+ st.session_state.semantic_chatbot = initialize_chatbot('semantic')
+
+ # Contenedor para la gestión de archivos
+ with st.container():
+ st.markdown('', unsafe_allow_html=True)
+ col1, col2, col3, col4 = st.columns(4)
+
+ with col1:
+ if st.button(get_translation(t, 'upload_file', 'Upload File'), key=generate_unique_key('semantic', 'upload_button')):
+ uploaded_file = st.file_uploader(get_translation(t, 'file_uploader', 'Choose a file'), type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
+ if uploaded_file is not None:
+ file_contents = uploaded_file.getvalue().decode('utf-8')
+ if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
+ st.success(get_translation(t, 'file_uploaded_success', 'File uploaded and saved to database successfully'))
+ st.session_state.file_contents = file_contents
+ st.rerun()
+ else:
+ st.error(get_translation(t, 'file_upload_error', 'Error uploading file'))
+
+ with col2:
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ file_options = [get_translation(t, 'select_file', 'Select a file')] + [file['file_name'] for file in user_files]
+ selected_file = st.selectbox(get_translation(t, 'file_list', 'File List'), options=file_options, key=generate_unique_key('semantic', 'file_selector'))
+ if selected_file != get_translation(t, 'select_file', 'Select a file'):
+ if st.button(get_translation(t, 'load_file', 'Load File'), key=generate_unique_key('semantic', 'load_file')):
+ file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
+ if file_contents:
+ st.session_state.file_contents = file_contents
+ st.success(get_translation(t, 'file_loaded_success', 'File loaded successfully'))
+ else:
+ st.error(get_translation(t, 'file_load_error', 'Error loading file'))
+
+ with col3:
+ if st.button(get_translation(t, 'analyze_document', 'Analyze Document'), key=generate_unique_key('semantic', 'analyze_document')):
+ if 'file_contents' in st.session_state:
+ with st.spinner(get_translation(t, 'analyzing', 'Analyzing...')):
+ graph, key_concepts = process_semantic_analysis(st.session_state.file_contents, nlp_models[lang_code], lang_code)
+ st.session_state.graph = graph
+ st.session_state.key_concepts = key_concepts
+ st.success(get_translation(t, 'analysis_completed', 'Analysis completed'))
+ else:
+ st.error(get_translation(t, 'no_file_uploaded', 'No file uploaded'))
+
+ with col4:
+ if st.button(get_translation(t, 'delete_file', 'Delete File'), key=generate_unique_key('semantic', 'delete_file')):
+ if selected_file and selected_file != get_translation(t, 'select_file', 'Select a file'):
+ if delete_file(st.session_state.username, selected_file, 'semantic'):
+ st.success(get_translation(t, 'file_deleted_success', 'File deleted successfully'))
+ if 'file_contents' in st.session_state:
+ del st.session_state.file_contents
+ st.rerun()
+ else:
+ st.error(get_translation(t, 'file_delete_error', 'Error deleting file'))
+ else:
+ st.error(get_translation(t, 'no_file_selected', 'No file selected'))
+
+ st.markdown('
', unsafe_allow_html=True)
+
+ # Crear dos columnas: una para el chat y otra para la visualización
+ col_chat, col_graph = st.columns([1, 1])
+
+ with col_chat:
+ st.subheader(get_translation(t, 'chat_title', 'Semantic Analysis Chat'))
+ # Chat interface
+ chat_container = st.container()
+
+ with chat_container:
+ # Mostrar el historial del chat
+ chat_history = st.session_state.get('semantic_chat_history', [])
+ for message in chat_history:
+ with st.chat_message(message["role"]):
+ st.write(message["content"])
+
+ # Input del usuario
+ user_input = st.chat_input(get_translation(t, 'semantic_chat_input', 'Type your message here...'), key=generate_unique_key('semantic', 'chat_input'))
+
+ if user_input:
+ # Añadir el mensaje del usuario al historial
+ chat_history.append({"role": "user", "content": user_input})
+
+ # Generar respuesta del chatbot
+ chatbot = st.session_state.semantic_chatbot
+ response = chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents'))
+
+ # Añadir la respuesta del chatbot al historial
+ chat_history.append({"role": "assistant", "content": response})
+
+ # Actualizar el historial en session_state
+ st.session_state.semantic_chat_history = chat_history
+
+ # Forzar la actualización de la interfaz
+ st.rerun()
+
+ with col_graph:
+ st.subheader(get_translation(t, 'graph_title', 'Semantic Graph'))
+
+ # Mostrar conceptos clave en un expander horizontal
+ with st.expander(get_translation(t, 'key_concepts_title', 'Key Concepts'), expanded=True):
+ if 'key_concepts' in st.session_state:
+ st.markdown('', unsafe_allow_html=True)
+ for concept, freq in st.session_state.key_concepts:
+ st.markdown(f'{concept}: {freq:.2f}', unsafe_allow_html=True)
+ st.markdown('
', unsafe_allow_html=True)
+
+ if 'graph' in st.session_state:
+ st.pyplot(st.session_state.graph)
+
+ # Botón para limpiar el historial del chat
+ if st.button(get_translation(t, 'clear_chat', 'Clear chat'), key=generate_unique_key('semantic', 'clear_chat')):
+ st.session_state.semantic_chat_history = []
st.rerun()
\ No newline at end of file
diff --git a/modules/semantic/semantic_interface_3.py b/modules/semantic/semantic_interface_3.py
index a0f4b7392ea4e041d6885d07aa76e8209c6d03a9..b42b4101804f09c8cf78d9458a9c4ad20f2ece4d 100644
--- a/modules/semantic/semantic_interface_3.py
+++ b/modules/semantic/semantic_interface_3.py
@@ -1,182 +1,182 @@
-import streamlit as st
-import logging
-from .semantic_process import process_semantic_analysis
-from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
-from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
-from ..utils.widget_utils import generate_unique_key
-
-logger = logging.getLogger(__name__)
-
-def get_translation(t, key, default):
- return t.get(key, default)
-
-def display_semantic_interface(lang_code, nlp_models, t):
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- st.markdown(f"""
-
- {get_translation(t, 'semantic_initial_message', 'Welcome to the semantic analysis interface.')}
-
- """, unsafe_allow_html=True)
-
- if 'semantic_chatbot' not in st.session_state:
- st.session_state.semantic_chatbot = initialize_chatbot('semantic')
-
- # Contenedor para la gestión de archivos
- with st.container():
- st.markdown('', unsafe_allow_html=True)
- col1, col2, col3, col4 = st.columns(4)
-
- with col1:
- uploaded_file = st.file_uploader(get_translation(t, 'upload_file', 'Upload File'), type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
- if uploaded_file is not None:
- file_contents = uploaded_file.getvalue().decode('utf-8')
- if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
- st.session_state.file_contents = file_contents
- st.success(get_translation(t, 'file_uploaded_success', 'File uploaded and saved successfully'))
- st.rerun()
- else:
- st.error(get_translation(t, 'file_upload_error', 'Error uploading file'))
-
- with col2:
- user_files = get_user_files(st.session_state.username, 'semantic')
- file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
- selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
- if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
- file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
- if file_contents:
- st.session_state.file_contents = file_contents
- st.success(get_translation(t, 'file_loaded_success', 'File loaded successfully'))
- else:
- st.error(get_translation(t, 'file_load_error', 'Error loading file'))
-
- with col3:
- if st.button(get_translation(t, 'analyze_document', 'Analyze Document'), key=generate_unique_key('semantic', 'analyze_document')):
- if 'file_contents' in st.session_state:
- with st.spinner(get_translation(t, 'analyzing', 'Analyzing...')):
- try:
- nlp_model = nlp_models[lang_code]
- concept_graph, entity_graph, key_concepts = process_semantic_analysis(st.session_state.file_contents, nlp_model, lang_code)
- st.session_state.concept_graph = concept_graph
- st.session_state.entity_graph = entity_graph
- st.session_state.key_concepts = key_concepts
- st.success(get_translation(t, 'analysis_completed', 'Analysis completed'))
- except Exception as e:
- logger.error(f"Error during analysis: {str(e)}")
- st.error(f"Error during analysis: {str(e)}")
- else:
- st.error(get_translation(t, 'no_file_uploaded', 'No file uploaded'))
-
- with col4:
- if st.button(get_translation(t, 'delete_file', 'Delete File'), key=generate_unique_key('semantic', 'delete_file')):
- if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
- if delete_file(st.session_state.username, selected_file, 'semantic'):
- st.success(get_translation(t, 'file_deleted_success', 'File deleted successfully'))
- if 'file_contents' in st.session_state:
- del st.session_state.file_contents
- st.rerun()
- else:
- st.error(get_translation(t, 'file_delete_error', 'Error deleting file'))
- else:
- st.error(get_translation(t, 'no_file_selected', 'No file selected'))
-
- st.markdown('
', unsafe_allow_html=True)
-
- # Contenedor para la sección de análisis
- st.markdown('
+ {get_translation(t, 'semantic_initial_message', 'Welcome to the semantic analysis interface.')}
+
+ """, unsafe_allow_html=True)
+
+ if 'semantic_chatbot' not in st.session_state:
+ st.session_state.semantic_chatbot = initialize_chatbot('semantic')
+
+ # Contenedor para la gestión de archivos
+ with st.container():
+ st.markdown('', unsafe_allow_html=True)
+ col1, col2, col3, col4 = st.columns(4)
+
+ with col1:
+ uploaded_file = st.file_uploader(get_translation(t, 'upload_file', 'Upload File'), type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
+ if uploaded_file is not None:
+ file_contents = uploaded_file.getvalue().decode('utf-8')
+ if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
+ st.session_state.file_contents = file_contents
+ st.success(get_translation(t, 'file_uploaded_success', 'File uploaded and saved successfully'))
+ st.rerun()
+ else:
+ st.error(get_translation(t, 'file_upload_error', 'Error uploading file'))
+
+ with col2:
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
+ selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
+ if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
+ file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
+ if file_contents:
+ st.session_state.file_contents = file_contents
+ st.success(get_translation(t, 'file_loaded_success', 'File loaded successfully'))
+ else:
+ st.error(get_translation(t, 'file_load_error', 'Error loading file'))
+
+ with col3:
+ if st.button(get_translation(t, 'analyze_document', 'Analyze Document'), key=generate_unique_key('semantic', 'analyze_document')):
+ if 'file_contents' in st.session_state:
+ with st.spinner(get_translation(t, 'analyzing', 'Analyzing...')):
+ try:
+ nlp_model = nlp_models[lang_code]
+ concept_graph, entity_graph, key_concepts = process_semantic_analysis(st.session_state.file_contents, nlp_model, lang_code)
+ st.session_state.concept_graph = concept_graph
+ st.session_state.entity_graph = entity_graph
+ st.session_state.key_concepts = key_concepts
+ st.success(get_translation(t, 'analysis_completed', 'Analysis completed'))
+ except Exception as e:
+ logger.error(f"Error during analysis: {str(e)}")
+ st.error(f"Error during analysis: {str(e)}")
+ else:
+ st.error(get_translation(t, 'no_file_uploaded', 'No file uploaded'))
+
+ with col4:
+ if st.button(get_translation(t, 'delete_file', 'Delete File'), key=generate_unique_key('semantic', 'delete_file')):
+ if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
+ if delete_file(st.session_state.username, selected_file, 'semantic'):
+ st.success(get_translation(t, 'file_deleted_success', 'File deleted successfully'))
+ if 'file_contents' in st.session_state:
+ del st.session_state.file_contents
+ st.rerun()
+ else:
+ st.error(get_translation(t, 'file_delete_error', 'Error deleting file'))
+ else:
+ st.error(get_translation(t, 'no_file_selected', 'No file selected'))
+
+ st.markdown('
', unsafe_allow_html=True)
+
+ # Contenedor para la sección de análisis
+ st.markdown('', unsafe_allow_html=True)
- chat_history = st.session_state.get('semantic_chat_history', [])
- for message in chat_history:
- with st.chat_message(message["role"]):
- st.write(message["content"])
- st.markdown('
', unsafe_allow_html=True)
-
- user_input = st.chat_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
-
- if user_input:
- chat_history.append({"role": "user", "content": user_input})
-
- if user_input.startswith('/analyze_current'):
- response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
- else:
- response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code)
-
- chat_history.append({"role": "assistant", "content": response})
- st.session_state.semantic_chat_history = chat_history
-
- if st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat')):
- st.session_state.semantic_chat_history = []
- st.rerun()
-
- with col_graph:
- st.subheader("Visualization")
- st.markdown('', unsafe_allow_html=True)
- if 'key_concepts' in st.session_state:
- st.write("Key Concepts:")
- st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
-
- tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
-
- with tab_concept:
- if 'concept_graph' in st.session_state:
- st.pyplot(st.session_state.concept_graph)
- else:
- st.info("No concept graph available. Please analyze a document first.")
-
- with tab_entity:
- if 'entity_graph' in st.session_state:
- st.pyplot(st.session_state.entity_graph)
- else:
- st.info("No entity graph available. Please analyze a document first.")
+import streamlit as st
+import logging
+from .semantic_process import process_semantic_analysis
+from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
+from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
+from ..utils.widget_utils import generate_unique_key
+
+logger = logging.getLogger(__name__)
+
+def get_translation(t, key, default):
+ return t.get(key, default)
+
+def display_semantic_interface(lang_code, nlp_models, t):
+ # Inicializar el chatbot al principio de la función
+ if 'semantic_chatbot' not in st.session_state:
+ st.session_state.semantic_chatbot = initialize_chatbot('semantic')
+
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # Mostrar el mensaje inicial como un párrafo estilizado
+ st.markdown(f"""
+
+ {t['semantic_initial_message']}
+
+ """, unsafe_allow_html=True)
+
+ tab1, tab2 = st.tabs(["Upload", "Analyze"])
+
+ with tab1:
+ st.subheader("File Management")
+ uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
+ if uploaded_file is not None:
+ file_contents = uploaded_file.getvalue().decode('utf-8')
+ if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
+ st.success(f"File {uploaded_file.name} uploaded and saved successfully")
+ else:
+ st.error("Error uploading file")
+
+ st.markdown("---") # Línea separadora
+
+ st.subheader("Manage Uploaded Files")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ if user_files:
+ for file in user_files:
+ col1, col2 = st.columns([3, 1])
+ with col1:
+ st.write(file['file_name'])
+ with col2:
+ if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
+ if delete_file(st.session_state.username, file['file_name'], 'semantic'):
+ st.success(f"File {file['file_name']} deleted successfully")
+ st.rerun()
+ else:
+ st.error(f"Error deleting file {file['file_name']}")
+ else:
+ st.info("No files uploaded yet.")
+
+ with tab2:
+ st.subheader("Select File for Analysis")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
+ selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
+
+ if st.button("Analyze Document", key=generate_unique_key('semantic', 'analyze_document')):
+ if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
+ file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
+ if file_contents:
+ st.session_state.file_contents = file_contents
+ with st.spinner("Analyzing..."):
+ try:
+ nlp_model = nlp_models[lang_code]
+ concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
+ st.session_state.concept_graph = concept_graph
+ st.session_state.entity_graph = entity_graph
+ st.session_state.key_concepts = key_concepts
+ st.success("Analysis completed successfully")
+ except Exception as e:
+ logger.error(f"Error during analysis: {str(e)}")
+ st.error(f"Error during analysis: {str(e)}")
+ else:
+ st.error("Error loading file contents")
+ else:
+ st.error("Please select a file to analyze")
+
+ # Chat and Visualization
+ col_chat, col_graph = st.columns([1, 1])
+
+ with col_chat:
+ st.subheader("Chat with AI")
+ st.markdown('
', unsafe_allow_html=True)
+ chat_history = st.session_state.get('semantic_chat_history', [])
+ for message in chat_history:
+ with st.chat_message(message["role"]):
+ st.write(message["content"])
+ st.markdown('
', unsafe_allow_html=True)
+
+ user_input = st.chat_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
+
+ if user_input:
+ chat_history.append({"role": "user", "content": user_input})
+
+ if user_input.startswith('/analyze_current'):
+ response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
+ else:
+ response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code)
+
+ chat_history.append({"role": "assistant", "content": response})
+ st.session_state.semantic_chat_history = chat_history
+
+ if st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat')):
+ st.session_state.semantic_chat_history = []
+ st.rerun()
+
+ with col_graph:
+ st.subheader("Visualization")
+ st.markdown('
', unsafe_allow_html=True)
+ if 'key_concepts' in st.session_state:
+ st.write("Key Concepts:")
+ st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
+
+ tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
+
+ with tab_concept:
+ if 'concept_graph' in st.session_state:
+ st.pyplot(st.session_state.concept_graph)
+ else:
+ st.info("No concept graph available. Please analyze a document first.")
+
+ with tab_entity:
+ if 'entity_graph' in st.session_state:
+ st.pyplot(st.session_state.entity_graph)
+ else:
+ st.info("No entity graph available. Please analyze a document first.")
st.markdown('
', unsafe_allow_html=True)
\ No newline at end of file
diff --git a/modules/semantic/semantic_interface_5.py b/modules/semantic/semantic_interface_5.py
index b9d8a0e565a92c0c140a7af0f672cf489b50ddb9..b9c2c13e29ee1fe2b8048e233b65bcaaa02af6fc 100644
--- a/modules/semantic/semantic_interface_5.py
+++ b/modules/semantic/semantic_interface_5.py
@@ -1,195 +1,195 @@
-import streamlit as st
-import logging
-from .semantic_process import process_semantic_analysis
-from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
-from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
-from ..utils.widget_utils import generate_unique_key
-
-logger = logging.getLogger(__name__)
-
-def get_translation(t, key, default):
- return t.get(key, default)
-
-def display_semantic_interface(lang_code, nlp_models, t):
- # Inicializar el chatbot y el historial del chat al principio de la función
- if 'semantic_chatbot' not in st.session_state:
- st.session_state.semantic_chatbot = initialize_chatbot('semantic')
-
- if 'semantic_chat_history' not in st.session_state:
- st.session_state.semantic_chat_history = []
-
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- # Estilo CSS personalizado
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- tab1, tab2 = st.tabs(["Upload", "Analyze"])
-
- with tab1:
- st.subheader("File Management")
- uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
- if uploaded_file is not None:
- file_contents = uploaded_file.getvalue().decode('utf-8')
- if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
- st.success(f"File {uploaded_file.name} uploaded and saved successfully")
- else:
- st.error("Error uploading file")
-
- st.markdown("---") # Línea separadora
-
- st.subheader("Manage Uploaded Files")
- user_files = get_user_files(st.session_state.username, 'semantic')
- if user_files:
- for file in user_files:
- col1, col2 = st.columns([3, 1])
- with col1:
- st.write(file['file_name'])
- with col2:
- if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
- if delete_file(st.session_state.username, file['file_name'], 'semantic'):
- st.success(f"File {file['file_name']} deleted successfully")
- st.rerun()
- else:
- st.error(f"Error deleting file {file['file_name']}")
- else:
- st.info("No files uploaded yet.")
-
- with tab2:
- st.subheader("Select File for Analysis")
- user_files = get_user_files(st.session_state.username, 'semantic')
- file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
- selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
-
- if st.button("Analyze Document", key=generate_unique_key('semantic', 'analyze_document')):
- if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
- file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
- if file_contents:
- st.session_state.file_contents = file_contents
- with st.spinner("Analyzing..."):
- try:
- nlp_model = nlp_models[lang_code]
- concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
- st.session_state.concept_graph = concept_graph
- st.session_state.entity_graph = entity_graph
- st.session_state.key_concepts = key_concepts
- st.success("Analysis completed successfully")
- except Exception as e:
- logger.error(f"Error during analysis: {str(e)}")
- st.error(f"Error during analysis: {str(e)}")
- else:
- st.error("Error loading file contents")
- else:
- st.error("Please select a file to analyze")
-
- # Chat and Visualization
- with st.container():
- col_chat, col_graph = st.columns([1, 1])
-
- with col_chat:
- st.subheader("Chat with AI")
-
- chat_container = st.container()
- with chat_container:
- for message in st.session_state.semantic_chat_history:
- with st.chat_message(message["role"]):
- st.markdown(message["content"])
-
- user_input = st.text_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
- col1, col2 = st.columns([3, 1])
- with col1:
- send_button = st.button("Send", key=generate_unique_key('semantic', 'send_message'))
- with col2:
- clear_button = st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat'))
-
- if send_button and user_input:
- st.session_state.semantic_chat_history.append({"role": "user", "content": user_input})
-
- if user_input.startswith('/analyze_current'):
- response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
- else:
- response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents', ''))
-
- st.session_state.semantic_chat_history.append({"role": "assistant", "content": response})
- st.rerun()
-
- if clear_button:
- st.session_state.semantic_chat_history = []
- st.rerun()
-
- with col_graph:
- st.subheader("Visualization")
- if 'key_concepts' in st.session_state:
- st.write("Key Concepts:")
- st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
-
- tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
-
- with tab_concept:
- if 'concept_graph' in st.session_state:
- st.pyplot(st.session_state.concept_graph)
- else:
- st.info("No concept graph available. Please analyze a document first.")
-
- with tab_entity:
- if 'entity_graph' in st.session_state:
- st.pyplot(st.session_state.entity_graph)
- else:
+import streamlit as st
+import logging
+from .semantic_process import process_semantic_analysis
+from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
+from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
+from ..utils.widget_utils import generate_unique_key
+
+logger = logging.getLogger(__name__)
+
+def get_translation(t, key, default):
+ return t.get(key, default)
+
+def display_semantic_interface(lang_code, nlp_models, t):
+ # Inicializar el chatbot y el historial del chat al principio de la función
+ if 'semantic_chatbot' not in st.session_state:
+ st.session_state.semantic_chatbot = initialize_chatbot('semantic')
+
+ if 'semantic_chat_history' not in st.session_state:
+ st.session_state.semantic_chat_history = []
+
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # Estilo CSS personalizado
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ tab1, tab2 = st.tabs(["Upload", "Analyze"])
+
+ with tab1:
+ st.subheader("File Management")
+ uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
+ if uploaded_file is not None:
+ file_contents = uploaded_file.getvalue().decode('utf-8')
+ if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
+ st.success(f"File {uploaded_file.name} uploaded and saved successfully")
+ else:
+ st.error("Error uploading file")
+
+ st.markdown("---") # Línea separadora
+
+ st.subheader("Manage Uploaded Files")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ if user_files:
+ for file in user_files:
+ col1, col2 = st.columns([3, 1])
+ with col1:
+ st.write(file['file_name'])
+ with col2:
+ if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
+ if delete_file(st.session_state.username, file['file_name'], 'semantic'):
+ st.success(f"File {file['file_name']} deleted successfully")
+ st.rerun()
+ else:
+ st.error(f"Error deleting file {file['file_name']}")
+ else:
+ st.info("No files uploaded yet.")
+
+ with tab2:
+ st.subheader("Select File for Analysis")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
+ selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
+
+ if st.button("Analyze Document", key=generate_unique_key('semantic', 'analyze_document')):
+ if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
+ file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
+ if file_contents:
+ st.session_state.file_contents = file_contents
+ with st.spinner("Analyzing..."):
+ try:
+ nlp_model = nlp_models[lang_code]
+ concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
+ st.session_state.concept_graph = concept_graph
+ st.session_state.entity_graph = entity_graph
+ st.session_state.key_concepts = key_concepts
+ st.success("Analysis completed successfully")
+ except Exception as e:
+ logger.error(f"Error during analysis: {str(e)}")
+ st.error(f"Error during analysis: {str(e)}")
+ else:
+ st.error("Error loading file contents")
+ else:
+ st.error("Please select a file to analyze")
+
+ # Chat and Visualization
+ with st.container():
+ col_chat, col_graph = st.columns([1, 1])
+
+ with col_chat:
+ st.subheader("Chat with AI")
+
+ chat_container = st.container()
+ with chat_container:
+ for message in st.session_state.semantic_chat_history:
+ with st.chat_message(message["role"]):
+ st.markdown(message["content"])
+
+ user_input = st.text_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
+ col1, col2 = st.columns([3, 1])
+ with col1:
+ send_button = st.button("Send", key=generate_unique_key('semantic', 'send_message'))
+ with col2:
+ clear_button = st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat'))
+
+ if send_button and user_input:
+ st.session_state.semantic_chat_history.append({"role": "user", "content": user_input})
+
+ if user_input.startswith('/analyze_current'):
+ response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
+ else:
+ response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents', ''))
+
+ st.session_state.semantic_chat_history.append({"role": "assistant", "content": response})
+ st.rerun()
+
+ if clear_button:
+ st.session_state.semantic_chat_history = []
+ st.rerun()
+
+ with col_graph:
+ st.subheader("Visualization")
+ if 'key_concepts' in st.session_state:
+ st.write("Key Concepts:")
+ st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
+
+ tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
+
+ with tab_concept:
+ if 'concept_graph' in st.session_state:
+ st.pyplot(st.session_state.concept_graph)
+ else:
+ st.info("No concept graph available. Please analyze a document first.")
+
+ with tab_entity:
+ if 'entity_graph' in st.session_state:
+ st.pyplot(st.session_state.entity_graph)
+ else:
st.info("No entity graph available. Please analyze a document first.")
\ No newline at end of file
diff --git a/modules/semantic/semantic_interface_6.py b/modules/semantic/semantic_interface_6.py
index 38df59957615774686e19ed33325ff346f948c7b..6b9e483a32c03f1fc3dbf0a6aa2e65f71a284e35 100644
--- a/modules/semantic/semantic_interface_6.py
+++ b/modules/semantic/semantic_interface_6.py
@@ -1,223 +1,223 @@
-import streamlit as st
-import logging
-from .semantic_process import process_semantic_analysis
-from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
-from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
-from ..utils.widget_utils import generate_unique_key
-from .semantic_float_reset import semantic_float_init, float_graph, toggle_float_visibility, update_float_content
-
-logger = logging.getLogger(__name__)
-semantic_float_init()
-
-def get_translation(t, key, default):
- return t.get(key, default)
-
-def display_semantic_interface(lang_code, nlp_models, t):
- # Inicializar el chatbot y el historial del chat al principio de la función
- if 'semantic_chatbot' not in st.session_state:
- st.session_state.semantic_chatbot = initialize_chatbot('semantic')
-
- if 'semantic_chat_history' not in st.session_state:
- st.session_state.semantic_chat_history = []
-
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- # Mostrar el mensaje inicial como un párrafo estilizado
- st.markdown(f"""
-
- {t['semantic_initial_message']}
-
- """, unsafe_allow_html=True)
-
- tab1, tab2 = st.tabs(["Upload", "Analyze"])
-
- with tab1:
- st.subheader("File Management")
- uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
- if uploaded_file is not None:
- file_contents = uploaded_file.getvalue().decode('utf-8')
- if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
- st.success(f"File {uploaded_file.name} uploaded and saved successfully")
- else:
- st.error("Error uploading file")
-
- st.markdown("---") # Línea separadora
-
- st.subheader("Manage Uploaded Files")
- user_files = get_user_files(st.session_state.username, 'semantic')
- if user_files:
- for file in user_files:
- col1, col2 = st.columns([3, 1])
- with col1:
- st.write(file['file_name'])
- with col2:
- if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
- if delete_file(st.session_state.username, file['file_name'], 'semantic'):
- st.success(f"File {file['file_name']} deleted successfully")
- st.rerun()
- else:
- st.error(f"Error deleting file {file['file_name']}")
- else:
- st.info("No files uploaded yet.")
-
- with tab2:
- st.subheader("Select File for Analysis")
- user_files = get_user_files(st.session_state.username, 'semantic')
- file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
- selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
-
- if st.button("Analyze Document"):
- if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
- file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
- if file_contents:
- with st.spinner("Analyzing..."):
- try:
- nlp_model = nlp_models[lang_code]
- concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
- st.session_state.concept_graph = concept_graph
- st.session_state.entity_graph = entity_graph
- st.session_state.key_concepts = key_concepts
- st.success("Analysis completed successfully")
-
- # Crear el grafo flotante
- if 'graph_id' not in st.session_state:
- st.session_state.graph_id = float_graph(
- content="
Loading graph...
",
- width="40%",
- height="60%",
- position="bottom-right",
- shadow=2,
- transition=1
- )
-
- # Actualizar el contenido del grafo flotante
- update_float_content(st.session_state.graph_id, f"""
-
Key Concepts:
-
{', '.join([f"{concept}: {freq:.2f}" for concept, freq in key_concepts])}
-

- """)
-
- except Exception as e:
- logger.error(f"Error during analysis: {str(e)}")
- st.error(f"Error during analysis: {str(e)}")
- st.session_state.concept_graph = None
- st.session_state.entity_graph = None
- st.session_state.key_concepts = []
- else:
- st.error("Error loading file contents")
- else:
- st.error("Please select a file to analyze")
-
- # Chat and Visualization
- with st.container():
- col_chat, col_graph = st.columns([1, 1])
-
- with col_chat:
- with st.expander("Chat with AI", expanded=True):
- chat_container = st.container()
-
- with chat_container:
- for message in st.session_state.semantic_chat_history:
- with st.chat_message(message["role"]):
- st.markdown(message["content"])
-
- user_input = st.text_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
- col1, col2 = st.columns([3, 1])
- with col1:
- send_button = st.button("Send", key=generate_unique_key('semantic', 'send_message'))
- with col2:
- clear_button = st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat'))
-
- if send_button and user_input:
- st.session_state.semantic_chat_history.append({"role": "user", "content": user_input})
-
- if user_input.startswith('/analyze_current'):
- response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
- else:
- response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents', ''))
-
- st.session_state.semantic_chat_history.append({"role": "assistant", "content": response})
- st.rerun()
-
- if clear_button:
- st.session_state.semantic_chat_history = []
- st.rerun()
-
- with col_graph:
- st.subheader("Visualization")
- if 'key_concepts' in st.session_state:
- st.write("Key Concepts:")
- st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
-
- tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
-
- with tab_concept:
- if 'concept_graph' in st.session_state and st.session_state.concept_graph:
- st.image(st.session_state.concept_graph)
- else:
- st.info("No concept graph available. Please analyze a document first.")
-
- with tab_entity:
- if 'entity_graph' in st.session_state and st.session_state.entity_graph:
- st.image(st.session_state.entity_graph)
- else:
- st.info("No entity graph available. Please analyze a document first.")
-
- # Botón para cerrar el grafo flotante
- if st.button("Close Graph", key="close_graph"):
- if 'graph_id' in st.session_state:
- toggle_float_visibility(st.session_state.graph_id, False)
+import streamlit as st
+import logging
+from .semantic_process import process_semantic_analysis
+from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
+from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
+from ..utils.widget_utils import generate_unique_key
+from .semantic_float_reset import semantic_float_init, float_graph, toggle_float_visibility, update_float_content
+
+logger = logging.getLogger(__name__)
+semantic_float_init()
+
+def get_translation(t, key, default):
+ return t.get(key, default)
+
+def display_semantic_interface(lang_code, nlp_models, t):
+ # Inicializar el chatbot y el historial del chat al principio de la función
+ if 'semantic_chatbot' not in st.session_state:
+ st.session_state.semantic_chatbot = initialize_chatbot('semantic')
+
+ if 'semantic_chat_history' not in st.session_state:
+ st.session_state.semantic_chat_history = []
+
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # Mostrar el mensaje inicial como un párrafo estilizado
+ st.markdown(f"""
+
+ {t['semantic_initial_message']}
+
+ """, unsafe_allow_html=True)
+
+ tab1, tab2 = st.tabs(["Upload", "Analyze"])
+
+ with tab1:
+ st.subheader("File Management")
+ uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
+ if uploaded_file is not None:
+ file_contents = uploaded_file.getvalue().decode('utf-8')
+ if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
+ st.success(f"File {uploaded_file.name} uploaded and saved successfully")
+ else:
+ st.error("Error uploading file")
+
+ st.markdown("---") # Línea separadora
+
+ st.subheader("Manage Uploaded Files")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ if user_files:
+ for file in user_files:
+ col1, col2 = st.columns([3, 1])
+ with col1:
+ st.write(file['file_name'])
+ with col2:
+ if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
+ if delete_file(st.session_state.username, file['file_name'], 'semantic'):
+ st.success(f"File {file['file_name']} deleted successfully")
+ st.rerun()
+ else:
+ st.error(f"Error deleting file {file['file_name']}")
+ else:
+ st.info("No files uploaded yet.")
+
+ with tab2:
+ st.subheader("Select File for Analysis")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
+ selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
+
+ if st.button("Analyze Document"):
+ if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
+ file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
+ if file_contents:
+ with st.spinner("Analyzing..."):
+ try:
+ nlp_model = nlp_models[lang_code]
+ concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
+ st.session_state.concept_graph = concept_graph
+ st.session_state.entity_graph = entity_graph
+ st.session_state.key_concepts = key_concepts
+ st.success("Analysis completed successfully")
+
+ # Crear el grafo flotante
+ if 'graph_id' not in st.session_state:
+ st.session_state.graph_id = float_graph(
+ content="
Loading graph...
",
+ width="40%",
+ height="60%",
+ position="bottom-right",
+ shadow=2,
+ transition=1
+ )
+
+ # Actualizar el contenido del grafo flotante
+ update_float_content(st.session_state.graph_id, f"""
+
Key Concepts:
+
{', '.join([f"{concept}: {freq:.2f}" for concept, freq in key_concepts])}
+

+ """)
+
+ except Exception as e:
+ logger.error(f"Error during analysis: {str(e)}")
+ st.error(f"Error during analysis: {str(e)}")
+ st.session_state.concept_graph = None
+ st.session_state.entity_graph = None
+ st.session_state.key_concepts = []
+ else:
+ st.error("Error loading file contents")
+ else:
+ st.error("Please select a file to analyze")
+
+ # Chat and Visualization
+ with st.container():
+ col_chat, col_graph = st.columns([1, 1])
+
+ with col_chat:
+ with st.expander("Chat with AI", expanded=True):
+ chat_container = st.container()
+
+ with chat_container:
+ for message in st.session_state.semantic_chat_history:
+ with st.chat_message(message["role"]):
+ st.markdown(message["content"])
+
+ user_input = st.text_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
+ col1, col2 = st.columns([3, 1])
+ with col1:
+ send_button = st.button("Send", key=generate_unique_key('semantic', 'send_message'))
+ with col2:
+ clear_button = st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat'))
+
+ if send_button and user_input:
+ st.session_state.semantic_chat_history.append({"role": "user", "content": user_input})
+
+ if user_input.startswith('/analyze_current'):
+ response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
+ else:
+ response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents', ''))
+
+ st.session_state.semantic_chat_history.append({"role": "assistant", "content": response})
+ st.rerun()
+
+ if clear_button:
+ st.session_state.semantic_chat_history = []
+ st.rerun()
+
+ with col_graph:
+ st.subheader("Visualization")
+ if 'key_concepts' in st.session_state:
+ st.write("Key Concepts:")
+ st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
+
+ tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
+
+ with tab_concept:
+ if 'concept_graph' in st.session_state and st.session_state.concept_graph:
+ st.image(st.session_state.concept_graph)
+ else:
+ st.info("No concept graph available. Please analyze a document first.")
+
+ with tab_entity:
+ if 'entity_graph' in st.session_state and st.session_state.entity_graph:
+ st.image(st.session_state.entity_graph)
+ else:
+ st.info("No entity graph available. Please analyze a document first.")
+
+ # Botón para cerrar el grafo flotante
+ if st.button("Close Graph", key="close_graph"):
+ if 'graph_id' in st.session_state:
+ toggle_float_visibility(st.session_state.graph_id, False)
del st.session_state.graph_id
\ No newline at end of file
diff --git a/modules/semantic/semantic_interface_6_Ok-23-9-24.py b/modules/semantic/semantic_interface_6_Ok-23-9-24.py
index bcefdcf92a7c7f4f828d3ca74a88cd0132c3c27a..c56fcc1da26f832d7e3e5037453ed17469943284 100644
--- a/modules/semantic/semantic_interface_6_Ok-23-9-24.py
+++ b/modules/semantic/semantic_interface_6_Ok-23-9-24.py
@@ -1,223 +1,223 @@
-import streamlit as st
-import logging
-from .semantic_process import process_semantic_analysis
-from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
-from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
-from ..utils.widget_utils import generate_unique_key
-from .semantic_float_reset import semantic_float_init, float_graph, toggle_float_visibility, update_float_content
-
-logger = logging.getLogger(__name__)
-semantic_float_init()
-
-def get_translation(t, key, default):
- return t.get(key, default)
-
-def display_semantic_interface(lang_code, nlp_models, t):
- # Inicializar el chatbot y el historial del chat al principio de la función
- if 'semantic_chatbot' not in st.session_state:
- st.session_state.semantic_chatbot = initialize_chatbot('semantic')
-
- if 'semantic_chat_history' not in st.session_state:
- st.session_state.semantic_chat_history = []
-
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- # Mostrar el mensaje inicial como un párrafo estilizado
- st.markdown(f"""
-
- {t['semantic_initial_message']}
-
- """, unsafe_allow_html=True)
-
- tab1, tab2 = st.tabs(["Upload", "Analyze"])
-
- with tab1:
- st.subheader("File Management")
- uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
- if uploaded_file is not None:
- file_contents = uploaded_file.getvalue().decode('utf-8')
- if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
- st.success(f"File {uploaded_file.name} uploaded and saved successfully")
- else:
- st.error("Error uploading file")
-
- st.markdown("---") # Línea separadora
-
- st.subheader("Manage Uploaded Files")
- user_files = get_user_files(st.session_state.username, 'semantic')
- if user_files:
- for file in user_files:
- col1, col2 = st.columns([3, 1])
- with col1:
- st.write(file['file_name'])
- with col2:
- if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
- if delete_file(st.session_state.username, file['file_name'], 'semantic'):
- st.success(f"File {file['file_name']} deleted successfully")
- st.rerun()
- else:
- st.error(f"Error deleting file {file['file_name']}")
- else:
- st.info("No files uploaded yet.")
-
- with tab2:
- st.subheader("Select File for Analysis")
- user_files = get_user_files(st.session_state.username, 'semantic')
- file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
- selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
-
- if st.button("Analyze Document"):
- if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
- file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
- if file_contents:
- with st.spinner("Analyzing..."):
- try:
- nlp_model = nlp_models[lang_code]
- concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
- st.session_state.concept_graph = concept_graph
- st.session_state.entity_graph = entity_graph
- st.session_state.key_concepts = key_concepts
- st.success("Analysis completed successfully")
-
- # Crear el grafo flotante
- if 'graph_id' not in st.session_state:
- st.session_state.graph_id = float_graph(
- content="
Loading graph...
",
- width="40%",
- height="60%",
- position="bottom-right",
- shadow=2,
- transition=1
- )
-
- # Actualizar el contenido del grafo flotante
- update_float_content(st.session_state.graph_id, f"""
-
Key Concepts:
-
{', '.join([f"{concept}: {freq:.2f}" for concept, freq in key_concepts])}
-

- """)
-
- except Exception as e:
- logger.error(f"Error during analysis: {str(e)}")
- st.error(f"Error during analysis: {str(e)}")
- st.session_state.concept_graph = None
- st.session_state.entity_graph = None
- st.session_state.key_concepts = []
- else:
- st.error("Error loading file contents")
- else:
- st.error("Please select a file to analyze")
-
- # Chat and Visualization
- with st.container():
- col_chat, col_graph = st.columns([1, 1])
-
- with col_chat:
- with st.expander("Chat with AI", expanded=True):
- chat_container = st.container()
-
- with chat_container:
- for message in st.session_state.semantic_chat_history:
- with st.chat_message(message["role"]):
- st.markdown(message["content"])
-
- user_input = st.text_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
- col1, col2 = st.columns([3, 1])
- with col1:
- send_button = st.button("Send", key=generate_unique_key('semantic', 'send_message'))
- with col2:
- clear_button = st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat'))
-
- if send_button and user_input:
- st.session_state.semantic_chat_history.append({"role": "user", "content": user_input})
-
- if user_input.startswith('/analyze_current'):
- response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
- else:
- response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents', ''))
-
- st.session_state.semantic_chat_history.append({"role": "assistant", "content": response})
- st.rerun()
-
- if clear_button:
- st.session_state.semantic_chat_history = []
- st.rerun()
-
- with col_graph:
- st.subheader("Visualization")
- if 'key_concepts' in st.session_state:
- st.write("Key Concepts:")
- st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
-
- tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
-
- with tab_concept:
- if 'concept_graph' in st.session_state and st.session_state.concept_graph:
- st.image(st.session_state.concept_graph)
- else:
- st.info("No concept graph available. Please analyze a document first.")
-
- with tab_entity:
- if 'entity_graph' in st.session_state and st.session_state.entity_graph:
- st.image(st.session_state.entity_graph)
- else:
- st.info("No entity graph available. Please analyze a document first.")
-
- # Botón para cerrar el grafo flotante
- if st.button("Close Graph", key="close_graph"):
- if 'graph_id' in st.session_state:
- toggle_float_visibility(st.session_state.graph_id, False)
+import streamlit as st
+import logging
+from .semantic_process import process_semantic_analysis
+from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
+from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
+from ..utils.widget_utils import generate_unique_key
+from .semantic_float_reset import semantic_float_init, float_graph, toggle_float_visibility, update_float_content
+
+logger = logging.getLogger(__name__)
+semantic_float_init()
+
+def get_translation(t, key, default):
+ return t.get(key, default)
+
+def display_semantic_interface(lang_code, nlp_models, t):
+ # Inicializar el chatbot y el historial del chat al principio de la función
+ if 'semantic_chatbot' not in st.session_state:
+ st.session_state.semantic_chatbot = initialize_chatbot('semantic')
+
+ if 'semantic_chat_history' not in st.session_state:
+ st.session_state.semantic_chat_history = []
+
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # Mostrar el mensaje inicial como un párrafo estilizado
+ st.markdown(f"""
+
+ {t['semantic_initial_message']}
+
+ """, unsafe_allow_html=True)
+
+ tab1, tab2 = st.tabs(["Upload", "Analyze"])
+
+ with tab1:
+ st.subheader("File Management")
+ uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
+ if uploaded_file is not None:
+ file_contents = uploaded_file.getvalue().decode('utf-8')
+ if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
+ st.success(f"File {uploaded_file.name} uploaded and saved successfully")
+ else:
+ st.error("Error uploading file")
+
+ st.markdown("---") # Línea separadora
+
+ st.subheader("Manage Uploaded Files")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ if user_files:
+ for file in user_files:
+ col1, col2 = st.columns([3, 1])
+ with col1:
+ st.write(file['file_name'])
+ with col2:
+ if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
+ if delete_file(st.session_state.username, file['file_name'], 'semantic'):
+ st.success(f"File {file['file_name']} deleted successfully")
+ st.rerun()
+ else:
+ st.error(f"Error deleting file {file['file_name']}")
+ else:
+ st.info("No files uploaded yet.")
+
+ with tab2:
+ st.subheader("Select File for Analysis")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
+ selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
+
+ if st.button("Analyze Document"):
+ if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
+ file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
+ if file_contents:
+ with st.spinner("Analyzing..."):
+ try:
+ nlp_model = nlp_models[lang_code]
+ concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
+ st.session_state.concept_graph = concept_graph
+ st.session_state.entity_graph = entity_graph
+ st.session_state.key_concepts = key_concepts
+ st.success("Analysis completed successfully")
+
+ # Crear el grafo flotante
+ if 'graph_id' not in st.session_state:
+ st.session_state.graph_id = float_graph(
+ content="
Loading graph...
",
+ width="40%",
+ height="60%",
+ position="bottom-right",
+ shadow=2,
+ transition=1
+ )
+
+ # Actualizar el contenido del grafo flotante
+ update_float_content(st.session_state.graph_id, f"""
+
Key Concepts:
+
{', '.join([f"{concept}: {freq:.2f}" for concept, freq in key_concepts])}
+

+ """)
+
+ except Exception as e:
+ logger.error(f"Error during analysis: {str(e)}")
+ st.error(f"Error during analysis: {str(e)}")
+ st.session_state.concept_graph = None
+ st.session_state.entity_graph = None
+ st.session_state.key_concepts = []
+ else:
+ st.error("Error loading file contents")
+ else:
+ st.error("Please select a file to analyze")
+
+ # Chat and Visualization
+ with st.container():
+ col_chat, col_graph = st.columns([1, 1])
+
+ with col_chat:
+ with st.expander("Chat with AI", expanded=True):
+ chat_container = st.container()
+
+ with chat_container:
+ for message in st.session_state.semantic_chat_history:
+ with st.chat_message(message["role"]):
+ st.markdown(message["content"])
+
+ user_input = st.text_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
+ col1, col2 = st.columns([3, 1])
+ with col1:
+ send_button = st.button("Send", key=generate_unique_key('semantic', 'send_message'))
+ with col2:
+ clear_button = st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat'))
+
+ if send_button and user_input:
+ st.session_state.semantic_chat_history.append({"role": "user", "content": user_input})
+
+ if user_input.startswith('/analyze_current'):
+ response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
+ else:
+ response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents', ''))
+
+ st.session_state.semantic_chat_history.append({"role": "assistant", "content": response})
+ st.rerun()
+
+ if clear_button:
+ st.session_state.semantic_chat_history = []
+ st.rerun()
+
+ with col_graph:
+ st.subheader("Visualization")
+ if 'key_concepts' in st.session_state:
+ st.write("Key Concepts:")
+ st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
+
+ tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
+
+ with tab_concept:
+ if 'concept_graph' in st.session_state and st.session_state.concept_graph:
+ st.image(st.session_state.concept_graph)
+ else:
+ st.info("No concept graph available. Please analyze a document first.")
+
+ with tab_entity:
+ if 'entity_graph' in st.session_state and st.session_state.entity_graph:
+ st.image(st.session_state.entity_graph)
+ else:
+ st.info("No entity graph available. Please analyze a document first.")
+
+ # Botón para cerrar el grafo flotante
+ if st.button("Close Graph", key="close_graph"):
+ if 'graph_id' in st.session_state:
+ toggle_float_visibility(st.session_state.graph_id, False)
del st.session_state.graph_id
\ No newline at end of file
diff --git a/modules/semantic/semantic_interface_6_StarPoint.py b/modules/semantic/semantic_interface_6_StarPoint.py
index b251f023c01e4c0d042605a5a97477c2269670e9..128c21dd4422f723c9b35a7484ab0b2af79f69d2 100644
--- a/modules/semantic/semantic_interface_6_StarPoint.py
+++ b/modules/semantic/semantic_interface_6_StarPoint.py
@@ -1,196 +1,196 @@
-import streamlit as st
-import logging
-from .semantic_process import process_semantic_analysis
-from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
-from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
-from ..utils.widget_utils import generate_unique_key
-
-logger = logging.getLogger(__name__)
-
-def get_translation(t, key, default):
- return t.get(key, default)
-
-def display_semantic_interface(lang_code, nlp_models, t):
- # Inicializar el chatbot y el historial del chat al principio de la función
- if 'semantic_chatbot' not in st.session_state:
- st.session_state.semantic_chatbot = initialize_chatbot('semantic')
-
- if 'semantic_chat_history' not in st.session_state:
- st.session_state.semantic_chat_history = []
-
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- # Mostrar el mensaje inicial como un párrafo estilizado
- st.markdown(f"""
-
- {t['semantic_initial_message']}
-
- """, unsafe_allow_html=True)
-
- tab1, tab2 = st.tabs(["Upload", "Analyze"])
-
- with tab1:
- st.subheader("File Management")
- uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
- if uploaded_file is not None:
- file_contents = uploaded_file.getvalue().decode('utf-8')
- if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
- st.success(f"File {uploaded_file.name} uploaded and saved successfully")
- else:
- st.error("Error uploading file")
-
- st.markdown("---") # Línea separadora
-
- st.subheader("Manage Uploaded Files")
- user_files = get_user_files(st.session_state.username, 'semantic')
- if user_files:
- for file in user_files:
- col1, col2 = st.columns([3, 1])
- with col1:
- st.write(file['file_name'])
- with col2:
- if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
- if delete_file(st.session_state.username, file['file_name'], 'semantic'):
- st.success(f"File {file['file_name']} deleted successfully")
- st.rerun()
- else:
- st.error(f"Error deleting file {file['file_name']}")
- else:
- st.info("No files uploaded yet.")
-
- with tab2:
- st.subheader("Select File for Analysis")
- user_files = get_user_files(st.session_state.username, 'semantic')
- file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
- selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
-
- if st.button("Analyze Document", key=generate_unique_key('semantic', 'analyze_document')):
- if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
- file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
- if file_contents:
- st.session_state.file_contents = file_contents
- with st.spinner("Analyzing..."):
- try:
- nlp_model = nlp_models[lang_code]
- concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
- st.session_state.concept_graph = concept_graph
- st.session_state.entity_graph = entity_graph
- st.session_state.key_concepts = key_concepts
- st.success("Analysis completed successfully")
- except Exception as e:
- logger.error(f"Error during analysis: {str(e)}")
- st.error(f"Error during analysis: {str(e)}")
- else:
- st.error("Error loading file contents")
- else:
- st.error("Please select a file to analyze")
-
- # Chat and Visualization
- with st.container():
- col_chat, col_graph = st.columns([1, 1])
-
- with col_chat:
- with st.expander("Chat with AI", expanded=True):
- chat_container = st.container()
-
- with chat_container:
- for message in st.session_state.semantic_chat_history:
- with st.chat_message(message["role"]):
- st.markdown(message["content"])
-
- user_input = st.text_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
-
- col1, col2 = st.columns([3, 1])
-
- with col1:
- send_button = st.button("Send", key=generate_unique_key('semantic', 'send_message'))
- with col2:
- clear_button = st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat'))
-
- if send_button and user_input:
- st.session_state.semantic_chat_history.append({"role": "user", "content": user_input})
-
- if user_input.startswith('/analyze_current'):
- response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
- else:
- response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents', ''))
-
- st.session_state.semantic_chat_history.append({"role": "assistant", "content": response})
- st.rerun()
-
- if clear_button:
- st.session_state.semantic_chat_history = []
- st.rerun()
-
- with col_graph:
- st.subheader("Visualization")
- if 'key_concepts' in st.session_state:
- st.write("Key Concepts:")
- st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
-
- tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
-
- with tab_concept:
- if 'concept_graph' in st.session_state:
- st.pyplot(st.session_state.concept_graph)
- else:
- st.info("No concept graph available. Please analyze a document first.")
-
- with tab_entity:
- if 'entity_graph' in st.session_state:
- st.pyplot(st.session_state.entity_graph)
- else:
+import streamlit as st
+import logging
+from .semantic_process import process_semantic_analysis
+from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
+from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
+from ..utils.widget_utils import generate_unique_key
+
+logger = logging.getLogger(__name__)
+
+def get_translation(t, key, default):
+ return t.get(key, default)
+
+def display_semantic_interface(lang_code, nlp_models, t):
+ # Inicializar el chatbot y el historial del chat al principio de la función
+ if 'semantic_chatbot' not in st.session_state:
+ st.session_state.semantic_chatbot = initialize_chatbot('semantic')
+
+ if 'semantic_chat_history' not in st.session_state:
+ st.session_state.semantic_chat_history = []
+
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # Mostrar el mensaje inicial como un párrafo estilizado
+ st.markdown(f"""
+
+ {t['semantic_initial_message']}
+
+ """, unsafe_allow_html=True)
+
+ tab1, tab2 = st.tabs(["Upload", "Analyze"])
+
+ with tab1:
+ st.subheader("File Management")
+ uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
+ if uploaded_file is not None:
+ file_contents = uploaded_file.getvalue().decode('utf-8')
+ if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
+ st.success(f"File {uploaded_file.name} uploaded and saved successfully")
+ else:
+ st.error("Error uploading file")
+
+ st.markdown("---") # Línea separadora
+
+ st.subheader("Manage Uploaded Files")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ if user_files:
+ for file in user_files:
+ col1, col2 = st.columns([3, 1])
+ with col1:
+ st.write(file['file_name'])
+ with col2:
+ if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
+ if delete_file(st.session_state.username, file['file_name'], 'semantic'):
+ st.success(f"File {file['file_name']} deleted successfully")
+ st.rerun()
+ else:
+ st.error(f"Error deleting file {file['file_name']}")
+ else:
+ st.info("No files uploaded yet.")
+
+ with tab2:
+ st.subheader("Select File for Analysis")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
+ selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
+
+ if st.button("Analyze Document", key=generate_unique_key('semantic', 'analyze_document')):
+ if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
+ file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
+ if file_contents:
+ st.session_state.file_contents = file_contents
+ with st.spinner("Analyzing..."):
+ try:
+ nlp_model = nlp_models[lang_code]
+ concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
+ st.session_state.concept_graph = concept_graph
+ st.session_state.entity_graph = entity_graph
+ st.session_state.key_concepts = key_concepts
+ st.success("Analysis completed successfully")
+ except Exception as e:
+ logger.error(f"Error during analysis: {str(e)}")
+ st.error(f"Error during analysis: {str(e)}")
+ else:
+ st.error("Error loading file contents")
+ else:
+ st.error("Please select a file to analyze")
+
+ # Chat and Visualization
+ with st.container():
+ col_chat, col_graph = st.columns([1, 1])
+
+ with col_chat:
+ with st.expander("Chat with AI", expanded=True):
+ chat_container = st.container()
+
+ with chat_container:
+ for message in st.session_state.semantic_chat_history:
+ with st.chat_message(message["role"]):
+ st.markdown(message["content"])
+
+ user_input = st.text_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
+
+ col1, col2 = st.columns([3, 1])
+
+ with col1:
+ send_button = st.button("Send", key=generate_unique_key('semantic', 'send_message'))
+ with col2:
+ clear_button = st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat'))
+
+ if send_button and user_input:
+ st.session_state.semantic_chat_history.append({"role": "user", "content": user_input})
+
+ if user_input.startswith('/analyze_current'):
+ response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
+ else:
+ response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents', ''))
+
+ st.session_state.semantic_chat_history.append({"role": "assistant", "content": response})
+ st.rerun()
+
+ if clear_button:
+ st.session_state.semantic_chat_history = []
+ st.rerun()
+
+ with col_graph:
+ st.subheader("Visualization")
+ if 'key_concepts' in st.session_state:
+ st.write("Key Concepts:")
+ st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
+
+ tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
+
+ with tab_concept:
+ if 'concept_graph' in st.session_state:
+ st.pyplot(st.session_state.concept_graph)
+ else:
+ st.info("No concept graph available. Please analyze a document first.")
+
+ with tab_entity:
+ if 'entity_graph' in st.session_state:
+ st.pyplot(st.session_state.entity_graph)
+ else:
st.info("No entity graph available. Please analyze a document first.")
\ No newline at end of file
diff --git a/modules/semantic/semantic_interface_7.py b/modules/semantic/semantic_interface_7.py
index 650182ceb40ce32885615517efeb32786009d996..26893a836e36378aad6d6fbc4a259bb7a9126b22 100644
--- a/modules/semantic/semantic_interface_7.py
+++ b/modules/semantic/semantic_interface_7.py
@@ -1,201 +1,201 @@
-import streamlit as st
-import logging
-from .semantic_process import process_semantic_analysis
-from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
-from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
-from ..utils.widget_utils import generate_unique_key
-
-logger = logging.getLogger(__name__)
-
-def get_translation(t, key, default):
- return t.get(key, default)
-
-def display_semantic_interface(lang_code, nlp_models, t):
- # Inicializar el chatbot y el historial del chat al principio de la función
- if 'semantic_chatbot' not in st.session_state:
- st.session_state.semantic_chatbot = initialize_chatbot('semantic')
-
- if 'semantic_chat_history' not in st.session_state:
- st.session_state.semantic_chat_history = []
-
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- # Mostrar el mensaje inicial como un párrafo estilizado
- st.markdown(f"""
-
- {t['semantic_initial_message']}
-
- """, unsafe_allow_html=True)
-
- tab1, tab2 = st.tabs(["Upload", "Analyze"])
-
- with tab1:
- st.subheader("File Management")
- uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
- if uploaded_file is not None:
- file_contents = uploaded_file.getvalue().decode('utf-8')
- if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
- st.success(f"File {uploaded_file.name} uploaded and saved successfully")
- else:
- st.error("Error uploading file")
-
- st.markdown("---") # Línea separadora
-
- st.subheader("Manage Uploaded Files")
- user_files = get_user_files(st.session_state.username, 'semantic')
- if user_files:
- for file in user_files:
- col1, col2 = st.columns([3, 1])
- with col1:
- st.write(file['file_name'])
- with col2:
- if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
- if delete_file(st.session_state.username, file['file_name'], 'semantic'):
- st.success(f"File {file['file_name']} deleted successfully")
- st.rerun()
- else:
- st.error(f"Error deleting file {file['file_name']}")
- else:
- st.info("No files uploaded yet.")
-
- with tab2:
- st.subheader("Select File for Analysis")
- user_files = get_user_files(st.session_state.username, 'semantic')
- file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
- selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
-
- if st.button("Analyze Document", key=generate_unique_key('semantic', 'analyze_document')):
- if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
- file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
- if file_contents:
- st.session_state.file_contents = file_contents
- with st.spinner("Analyzing..."):
- try:
- nlp_model = nlp_models[lang_code]
- concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
- st.session_state.concept_graph = concept_graph
- st.session_state.entity_graph = entity_graph
- st.session_state.key_concepts = key_concepts
- st.success("Analysis completed successfully")
- except Exception as e:
- logger.error(f"Error during analysis: {str(e)}")
- st.error(f"Error during analysis: {str(e)}")
- else:
- st.error("Error loading file contents")
- else:
- st.error("Please select a file to analyze")
-
- # Chat and Visualization
- with st.container():
- col_chat, col_graph = st.columns([1, 1])
-
- with col_chat:
- st.subheader("Chat with AI")
-
- chat_container = st.container()
- with chat_container:
- for message in st.session_state.semantic_chat_history:
- with st.chat_message(message["role"]):
- st.markdown(message["content"])
-
- user_input = st.text_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
- col1, col2 = st.columns([3, 1])
- with col1:
- send_button = st.button("Send", key=generate_unique_key('semantic', 'send_message'))
- with col2:
- clear_button = st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat'))
-
- if send_button and user_input:
- st.session_state.semantic_chat_history.append({"role": "user", "content": user_input})
-
- if user_input.startswith('/analyze_current'):
- response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
- else:
- response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents', ''))
-
- st.session_state.semantic_chat_history.append({"role": "assistant", "content": response})
- st.rerun()
-
- if clear_button:
- st.session_state.semantic_chat_history = []
- st.rerun()
-
- with col_graph:
- st.subheader("Visualization")
- if 'key_concepts' in st.session_state:
- st.write("Key Concepts:")
- st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
-
- tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
-
- with tab_concept:
- if 'concept_graph' in st.session_state:
- st.pyplot(st.session_state.concept_graph)
- else:
- st.info("No concept graph available. Please analyze a document first.")
-
- with tab_entity:
- if 'entity_graph' in st.session_state:
- st.pyplot(st.session_state.entity_graph)
- else:
+import streamlit as st
+import logging
+from .semantic_process import process_semantic_analysis
+from ..chatbot.chatbot import initialize_chatbot, process_semantic_chat_input
+from ..database.database_oldFromV2 import store_file_semantic_contents, retrieve_file_contents, delete_file, get_user_files
+from ..utils.widget_utils import generate_unique_key
+
+logger = logging.getLogger(__name__)
+
+def get_translation(t, key, default):
+ return t.get(key, default)
+
+def display_semantic_interface(lang_code, nlp_models, t):
+ # Inicializar el chatbot y el historial del chat al principio de la función
+ if 'semantic_chatbot' not in st.session_state:
+ st.session_state.semantic_chatbot = initialize_chatbot('semantic')
+
+ if 'semantic_chat_history' not in st.session_state:
+ st.session_state.semantic_chat_history = []
+
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # Mostrar el mensaje inicial como un párrafo estilizado
+ st.markdown(f"""
+
+ {t['semantic_initial_message']}
+
+ """, unsafe_allow_html=True)
+
+ tab1, tab2 = st.tabs(["Upload", "Analyze"])
+
+ with tab1:
+ st.subheader("File Management")
+ uploaded_file = st.file_uploader("Choose a file to upload", type=['txt', 'pdf', 'docx', 'doc', 'odt'], key=generate_unique_key('semantic', 'file_uploader'))
+ if uploaded_file is not None:
+ file_contents = uploaded_file.getvalue().decode('utf-8')
+ if store_file_semantic_contents(st.session_state.username, uploaded_file.name, file_contents):
+ st.success(f"File {uploaded_file.name} uploaded and saved successfully")
+ else:
+ st.error("Error uploading file")
+
+ st.markdown("---") # Línea separadora
+
+ st.subheader("Manage Uploaded Files")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ if user_files:
+ for file in user_files:
+ col1, col2 = st.columns([3, 1])
+ with col1:
+ st.write(file['file_name'])
+ with col2:
+ if st.button("Delete", key=f"delete_{file['file_name']}", help=f"Delete {file['file_name']}"):
+ if delete_file(st.session_state.username, file['file_name'], 'semantic'):
+ st.success(f"File {file['file_name']} deleted successfully")
+ st.rerun()
+ else:
+ st.error(f"Error deleting file {file['file_name']}")
+ else:
+ st.info("No files uploaded yet.")
+
+ with tab2:
+ st.subheader("Select File for Analysis")
+ user_files = get_user_files(st.session_state.username, 'semantic')
+ file_options = [get_translation(t, 'select_saved_file', 'Select a saved file')] + [file['file_name'] for file in user_files]
+ selected_file = st.selectbox("", options=file_options, key=generate_unique_key('semantic', 'file_selector'))
+
+ if st.button("Analyze Document", key=generate_unique_key('semantic', 'analyze_document')):
+ if selected_file and selected_file != get_translation(t, 'select_saved_file', 'Select a saved file'):
+ file_contents = retrieve_file_contents(st.session_state.username, selected_file, 'semantic')
+ if file_contents:
+ st.session_state.file_contents = file_contents
+ with st.spinner("Analyzing..."):
+ try:
+ nlp_model = nlp_models[lang_code]
+ concept_graph, entity_graph, key_concepts = process_semantic_analysis(file_contents, nlp_model, lang_code)
+ st.session_state.concept_graph = concept_graph
+ st.session_state.entity_graph = entity_graph
+ st.session_state.key_concepts = key_concepts
+ st.success("Analysis completed successfully")
+ except Exception as e:
+ logger.error(f"Error during analysis: {str(e)}")
+ st.error(f"Error during analysis: {str(e)}")
+ else:
+ st.error("Error loading file contents")
+ else:
+ st.error("Please select a file to analyze")
+
+ # Chat and Visualization
+ with st.container():
+ col_chat, col_graph = st.columns([1, 1])
+
+ with col_chat:
+ st.subheader("Chat with AI")
+
+ chat_container = st.container()
+ with chat_container:
+ for message in st.session_state.semantic_chat_history:
+ with st.chat_message(message["role"]):
+ st.markdown(message["content"])
+
+ user_input = st.text_input("Type your message here...", key=generate_unique_key('semantic', 'chat_input'))
+ col1, col2 = st.columns([3, 1])
+ with col1:
+ send_button = st.button("Send", key=generate_unique_key('semantic', 'send_message'))
+ with col2:
+ clear_button = st.button("Clear Chat", key=generate_unique_key('semantic', 'clear_chat'))
+
+ if send_button and user_input:
+ st.session_state.semantic_chat_history.append({"role": "user", "content": user_input})
+
+ if user_input.startswith('/analyze_current'):
+ response = process_semantic_chat_input(user_input, lang_code, nlp_models[lang_code], st.session_state.get('file_contents', ''))
+ else:
+ response = st.session_state.semantic_chatbot.generate_response(user_input, lang_code, context=st.session_state.get('file_contents', ''))
+
+ st.session_state.semantic_chat_history.append({"role": "assistant", "content": response})
+ st.rerun()
+
+ if clear_button:
+ st.session_state.semantic_chat_history = []
+ st.rerun()
+
+ with col_graph:
+ st.subheader("Visualization")
+ if 'key_concepts' in st.session_state:
+ st.write("Key Concepts:")
+ st.write(', '.join([f"{concept}: {freq:.2f}" for concept, freq in st.session_state.key_concepts]))
+
+ tab_concept, tab_entity = st.tabs(["Concept Graph", "Entity Graph"])
+
+ with tab_concept:
+ if 'concept_graph' in st.session_state:
+ st.pyplot(st.session_state.concept_graph)
+ else:
+ st.info("No concept graph available. Please analyze a document first.")
+
+ with tab_entity:
+ if 'entity_graph' in st.session_state:
+ st.pyplot(st.session_state.entity_graph)
+ else:
st.info("No entity graph available. Please analyze a document first.")
\ No newline at end of file
diff --git a/modules/semantic/semantic_live_interface.py b/modules/semantic/semantic_live_interface.py
index a6b409a4e43720c5fb99d1aa61126529e1efeabc..d4251d304deda779ea88a7d1a8c784317b58db31 100644
--- a/modules/semantic/semantic_live_interface.py
+++ b/modules/semantic/semantic_live_interface.py
@@ -1,197 +1,197 @@
-# modules/semantic/semantic_live_interface.py
-import streamlit as st
-from streamlit_float import *
-from streamlit_antd_components import *
-import pandas as pd
-import logging
-
-# Configuración del logger
-logger = logging.getLogger(__name__)
-
-# Importaciones locales
-from .semantic_process import (
- process_semantic_input,
- format_semantic_results
-)
-
-from ..utils.widget_utils import generate_unique_key
-from ..database.semantic_mongo_db import store_student_semantic_result
-from ..database.chat_mongo_db import store_chat_history, get_chat_history
-
-def display_semantic_live_interface(lang_code, nlp_models, semantic_t):
- """
- Interfaz para el análisis semántico en vivo con proporciones de columna ajustadas
- """
- try:
- # 1. Inicializar el estado de la sesión de manera más robusta
- if 'semantic_live_state' not in st.session_state:
- st.session_state.semantic_live_state = {
- 'analysis_count': 0,
- 'current_text': '',
- 'last_result': None,
- 'text_changed': False
- }
-
- # 2. Función para manejar cambios en el texto
- def on_text_change():
- current_text = st.session_state.semantic_live_text
- st.session_state.semantic_live_state['current_text'] = current_text
- st.session_state.semantic_live_state['text_changed'] = True
-
- # 3. Crear columnas con nueva proporción (1:3)
- input_col, result_col = st.columns([1, 3])
-
- # Columna izquierda: Entrada de texto
- with input_col:
- st.subheader(semantic_t.get('enter_text', 'Ingrese su texto'))
-
- # Área de texto con manejo de eventos
- text_input = st.text_area(
- semantic_t.get('text_input_label', 'Escriba o pegue su texto aquí'),
- height=500,
- key="semantic_live_text",
- value=st.session_state.semantic_live_state.get('current_text', ''),
- on_change=on_text_change,
- label_visibility="collapsed" # Oculta el label para mayor estabilidad
- )
-
- # Botón de análisis y procesamiento
- analyze_button = st.button(
- semantic_t.get('analyze_button', 'Analizar'),
- key="semantic_live_analyze",
- type="primary",
- icon="🔍",
- disabled=not text_input,
- use_container_width=True
- )
-
- if analyze_button and text_input:
- try:
- with st.spinner(semantic_t.get('processing', 'Procesando...')):
- analysis_result = process_semantic_input(
- text_input,
- lang_code,
- nlp_models,
- semantic_t
- )
-
- if analysis_result['success']:
- st.session_state.semantic_live_state['last_result'] = analysis_result
- st.session_state.semantic_live_state['analysis_count'] += 1
- st.session_state.semantic_live_state['text_changed'] = False
-
- store_student_semantic_result(
- st.session_state.username,
- text_input,
- analysis_result['analysis']
- )
- else:
- st.error(analysis_result.get('message', 'Error en el análisis'))
-
- except Exception as e:
- logger.error(f"Error en análisis: {str(e)}")
- st.error(semantic_t.get('error_processing', 'Error al procesar el texto'))
-
- # Columna derecha: Visualización de resultados
- with result_col:
- st.subheader(semantic_t.get('live_results', 'Resultados en vivo'))
-
- if 'last_result' in st.session_state.semantic_live_state and \
- st.session_state.semantic_live_state['last_result'] is not None:
-
- analysis = st.session_state.semantic_live_state['last_result']['analysis']
-
- if 'key_concepts' in analysis and analysis['key_concepts'] and \
- 'concept_graph' in analysis and analysis['concept_graph'] is not None:
-
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- with st.container():
- # Conceptos en una sola línea
- concepts_html = """
-
-
- """
- concepts_html += ''.join(
- f'
{concept}'
- f'({freq:.2f})
'
- for concept, freq in analysis['key_concepts']
- )
- concepts_html += "
"
- st.markdown(concepts_html, unsafe_allow_html=True)
-
- # Grafo
- if 'concept_graph' in analysis and analysis['concept_graph'] is not None:
- st.image(
- analysis['concept_graph'],
- use_container_width=True
- )
-
- # Botones y controles
- button_col, spacer_col = st.columns([1,5])
- with button_col:
- st.download_button(
- label="📥 " + semantic_t.get('download_graph', "Download"),
- data=analysis['concept_graph'],
- file_name="semantic_live_graph.png",
- mime="image/png",
- use_container_width=True
- )
-
- with st.expander("📊 " + semantic_t.get('graph_help', "Graph Interpretation")):
- st.markdown("""
- - 🔀 Las flechas indican la dirección de la relación entre conceptos
- - 🎨 Los colores más intensos indican conceptos más centrales en el texto
- - ⭕ El tamaño de los nodos representa la frecuencia del concepto
- - ↔️ El grosor de las líneas indica la fuerza de la conexión
- """)
- else:
- st.info(semantic_t.get('no_graph', 'No hay datos para mostrar'))
-
- except Exception as e:
- logger.error(f"Error general en interfaz semántica en vivo: {str(e)}")
- st.error(semantic_t.get('general_error', "Se produjo un error. Por favor, intente de nuevo."))
-
+# modules/semantic/semantic_live_interface.py
+import streamlit as st
+from streamlit_float import *
+from streamlit_antd_components import *
+import pandas as pd
+import logging
+
+# Configuración del logger
+logger = logging.getLogger(__name__)
+
+# Importaciones locales
+from .semantic_process import (
+ process_semantic_input,
+ format_semantic_results
+)
+
+from ..utils.widget_utils import generate_unique_key
+from ..database.semantic_mongo_db import store_student_semantic_result
+from ..database.chat_mongo_db import store_chat_history, get_chat_history
+
+def display_semantic_live_interface(lang_code, nlp_models, semantic_t):
+ """
+ Interfaz para el análisis semántico en vivo con proporciones de columna ajustadas
+ """
+ try:
+ # 1. Inicializar el estado de la sesión de manera más robusta
+ if 'semantic_live_state' not in st.session_state:
+ st.session_state.semantic_live_state = {
+ 'analysis_count': 0,
+ 'current_text': '',
+ 'last_result': None,
+ 'text_changed': False
+ }
+
+ # 2. Función para manejar cambios en el texto
+ def on_text_change():
+ current_text = st.session_state.semantic_live_text
+ st.session_state.semantic_live_state['current_text'] = current_text
+ st.session_state.semantic_live_state['text_changed'] = True
+
+ # 3. Crear columnas con nueva proporción (1:3)
+ input_col, result_col = st.columns([1, 3])
+
+ # Columna izquierda: Entrada de texto
+ with input_col:
+ st.subheader(semantic_t.get('enter_text', 'Ingrese su texto'))
+
+ # Área de texto con manejo de eventos
+ text_input = st.text_area(
+ semantic_t.get('text_input_label', 'Escriba o pegue su texto aquí'),
+ height=500,
+ key="semantic_live_text",
+ value=st.session_state.semantic_live_state.get('current_text', ''),
+ on_change=on_text_change,
+ label_visibility="collapsed" # Oculta el label para mayor estabilidad
+ )
+
+ # Botón de análisis y procesamiento
+ analyze_button = st.button(
+ semantic_t.get('analyze_button', 'Analizar'),
+ key="semantic_live_analyze",
+ type="primary",
+ icon="🔍",
+ disabled=not text_input,
+ use_container_width=True
+ )
+
+ if analyze_button and text_input:
+ try:
+ with st.spinner(semantic_t.get('processing', 'Procesando...')):
+ analysis_result = process_semantic_input(
+ text_input,
+ lang_code,
+ nlp_models,
+ semantic_t
+ )
+
+ if analysis_result['success']:
+ st.session_state.semantic_live_state['last_result'] = analysis_result
+ st.session_state.semantic_live_state['analysis_count'] += 1
+ st.session_state.semantic_live_state['text_changed'] = False
+
+ store_student_semantic_result(
+ st.session_state.username,
+ text_input,
+ analysis_result['analysis']
+ )
+ else:
+ st.error(analysis_result.get('message', 'Error en el análisis'))
+
+ except Exception as e:
+ logger.error(f"Error en análisis: {str(e)}")
+ st.error(semantic_t.get('error_processing', 'Error al procesar el texto'))
+
+ # Columna derecha: Visualización de resultados
+ with result_col:
+ st.subheader(semantic_t.get('live_results', 'Resultados en vivo'))
+
+ if 'last_result' in st.session_state.semantic_live_state and \
+ st.session_state.semantic_live_state['last_result'] is not None:
+
+ analysis = st.session_state.semantic_live_state['last_result']['analysis']
+
+ if 'key_concepts' in analysis and analysis['key_concepts'] and \
+ 'concept_graph' in analysis and analysis['concept_graph'] is not None:
+
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ with st.container():
+ # Conceptos en una sola línea
+ concepts_html = """
+
+
+ """
+ concepts_html += ''.join(
+ f'
{concept}'
+ f'({freq:.2f})
'
+ for concept, freq in analysis['key_concepts']
+ )
+ concepts_html += "
"
+ st.markdown(concepts_html, unsafe_allow_html=True)
+
+ # Grafo
+ if 'concept_graph' in analysis and analysis['concept_graph'] is not None:
+ st.image(
+ analysis['concept_graph'],
+ use_container_width=True
+ )
+
+ # Botones y controles
+ button_col, spacer_col = st.columns([1,5])
+ with button_col:
+ st.download_button(
+ label="📥 " + semantic_t.get('download_graph', "Download"),
+ data=analysis['concept_graph'],
+ file_name="semantic_live_graph.png",
+ mime="image/png",
+ use_container_width=True
+ )
+
+ with st.expander("📊 " + semantic_t.get('graph_help', "Graph Interpretation")):
+ st.markdown("""
+ - 🔀 Las flechas indican la dirección de la relación entre conceptos
+ - 🎨 Los colores más intensos indican conceptos más centrales en el texto
+ - ⭕ El tamaño de los nodos representa la frecuencia del concepto
+ - ↔️ El grosor de las líneas indica la fuerza de la conexión
+ """)
+ else:
+ st.info(semantic_t.get('no_graph', 'No hay datos para mostrar'))
+
+ except Exception as e:
+ logger.error(f"Error general en interfaz semántica en vivo: {str(e)}")
+ st.error(semantic_t.get('general_error', "Se produjo un error. Por favor, intente de nuevo."))
+
diff --git a/modules/semantic/semantic_process.py b/modules/semantic/semantic_process.py
index 35e6d4c877cb89066e81b62a3df4a64ffd64b86f..421dc7fc03afc4d71f1bcdf485ea054bd6972e6a 100644
--- a/modules/semantic/semantic_process.py
+++ b/modules/semantic/semantic_process.py
@@ -1,108 +1,108 @@
-# modules/semantic/semantic_process.py
-import streamlit as st
-import matplotlib.pyplot as plt
-import io
-import base64
-import logging
-
-from ..text_analysis.semantic_analysis import (
- perform_semantic_analysis,
- identify_key_concepts,
- create_concept_graph,
- visualize_concept_graph
-)
-from ..database.semantic_mongo_db import store_student_semantic_result
-
-logger = logging.getLogger(__name__)
-
-def process_semantic_input(text, lang_code, nlp_models, t):
- """
- Procesa el texto ingresado para realizar el análisis semántico.
- """
- try:
- logger.info(f"Iniciando análisis semántico para texto de {len(text)} caracteres")
-
- # Realizar el análisis semántico
- nlp = nlp_models[lang_code]
- analysis_result = perform_semantic_analysis(text, nlp, lang_code)
-
- if not analysis_result['success']:
- return {
- 'success': False,
- 'message': analysis_result['error'],
- 'analysis': None
- }
-
- logger.info("Análisis semántico completado. Guardando resultados...")
-
- # Intentar guardar en la base de datos
- try:
- store_result = store_student_semantic_result(
- st.session_state.username,
- text,
- analysis_result
- )
- if not store_result:
- logger.warning("No se pudo guardar el análisis en la base de datos")
- except Exception as db_error:
- logger.error(f"Error al guardar en base de datos: {str(db_error)}")
-
- # Devolver el resultado incluso si falla el guardado
- return {
- 'success': True,
- 'message': t.get('success_message', 'Analysis completed successfully'),
- 'analysis': {
- 'key_concepts': analysis_result['key_concepts'],
- 'concept_graph': analysis_result['concept_graph']
- }
- }
-
- except Exception as e:
- logger.error(f"Error en process_semantic_input: {str(e)}")
- return {
- 'success': False,
- 'message': str(e),
- 'analysis': None
- }
-
-def format_semantic_results(analysis_result, t):
- """
- Formatea los resultados del análisis para su visualización.
- """
- try:
- if not analysis_result['success']:
- return {
- 'formatted_text': analysis_result['message'],
- 'visualizations': None
- }
-
- formatted_sections = []
- analysis = analysis_result['analysis']
-
- # Formatear conceptos clave
- if 'key_concepts' in analysis:
- concepts_section = [f"### {t.get('key_concepts', 'Key Concepts')}"]
- concepts_section.extend([
- f"- {concept}: {frequency:.2f}"
- for concept, frequency in analysis['key_concepts']
- ])
- formatted_sections.append('\n'.join(concepts_section))
-
- return {
- 'formatted_text': '\n\n'.join(formatted_sections),
- 'visualizations': {
- 'concept_graph': analysis.get('concept_graph')
- }
- }
-
- except Exception as e:
- logger.error(f"Error en format_semantic_results: {str(e)}")
- return {
- 'formatted_text': str(e),
- 'visualizations': None
- }
-
-__all__ = [
- 'process_semantic_input',
- 'format_semantic_results'
+# modules/semantic/semantic_process.py
+import streamlit as st
+import matplotlib.pyplot as plt
+import io
+import base64
+import logging
+
+from ..text_analysis.semantic_analysis import (
+ perform_semantic_analysis,
+ identify_key_concepts,
+ create_concept_graph,
+ visualize_concept_graph
+)
+from ..database.semantic_mongo_db import store_student_semantic_result
+
+logger = logging.getLogger(__name__)
+
+def process_semantic_input(text, lang_code, nlp_models, t):
+ """
+ Procesa el texto ingresado para realizar el análisis semántico.
+ """
+ try:
+ logger.info(f"Iniciando análisis semántico para texto de {len(text)} caracteres")
+
+ # Realizar el análisis semántico
+ nlp = nlp_models[lang_code]
+ analysis_result = perform_semantic_analysis(text, nlp, lang_code)
+
+ if not analysis_result['success']:
+ return {
+ 'success': False,
+ 'message': analysis_result['error'],
+ 'analysis': None
+ }
+
+ logger.info("Análisis semántico completado. Guardando resultados...")
+
+ # Intentar guardar en la base de datos
+ try:
+ store_result = store_student_semantic_result(
+ st.session_state.username,
+ text,
+ analysis_result
+ )
+ if not store_result:
+ logger.warning("No se pudo guardar el análisis en la base de datos")
+ except Exception as db_error:
+ logger.error(f"Error al guardar en base de datos: {str(db_error)}")
+
+ # Devolver el resultado incluso si falla el guardado
+ return {
+ 'success': True,
+ 'message': t.get('success_message', 'Analysis completed successfully'),
+ 'analysis': {
+ 'key_concepts': analysis_result['key_concepts'],
+ 'concept_graph': analysis_result['concept_graph']
+ }
+ }
+
+ except Exception as e:
+ logger.error(f"Error en process_semantic_input: {str(e)}")
+ return {
+ 'success': False,
+ 'message': str(e),
+ 'analysis': None
+ }
+
+def format_semantic_results(analysis_result, t):
+ """
+ Formatea los resultados del análisis para su visualización.
+ """
+ try:
+ if not analysis_result['success']:
+ return {
+ 'formatted_text': analysis_result['message'],
+ 'visualizations': None
+ }
+
+ formatted_sections = []
+ analysis = analysis_result['analysis']
+
+ # Formatear conceptos clave
+ if 'key_concepts' in analysis:
+ concepts_section = [f"### {t.get('key_concepts', 'Key Concepts')}"]
+ concepts_section.extend([
+ f"- {concept}: {frequency:.2f}"
+ for concept, frequency in analysis['key_concepts']
+ ])
+ formatted_sections.append('\n'.join(concepts_section))
+
+ return {
+ 'formatted_text': '\n\n'.join(formatted_sections),
+ 'visualizations': {
+ 'concept_graph': analysis.get('concept_graph')
+ }
+ }
+
+ except Exception as e:
+ logger.error(f"Error en format_semantic_results: {str(e)}")
+ return {
+ 'formatted_text': str(e),
+ 'visualizations': None
+ }
+
+__all__ = [
+ 'process_semantic_input',
+ 'format_semantic_results'
]
\ No newline at end of file
diff --git a/modules/studentact/6-3-2025_current_situation_interface.py b/modules/studentact/6-3-2025_current_situation_interface.py
index d6374b1722bad26dd5e921dfb66e9248c7d3208d..d0a081ebd3cbb0846071b7ff68bfb5ee5c1e075a 100644
--- a/modules/studentact/6-3-2025_current_situation_interface.py
+++ b/modules/studentact/6-3-2025_current_situation_interface.py
@@ -1,486 +1,486 @@
-# modules/studentact/current_situation_interface-vOK.py
-
-import streamlit as st
-import logging
-from ..utils.widget_utils import generate_unique_key
-import matplotlib.pyplot as plt
-import numpy as np
-from ..database.current_situation_mongo_db import store_current_situation_result
-
-# Importaciones locales
-from translations import get_translations
-
-from .current_situation_analysis import (
- analyze_text_dimensions,
- analyze_clarity,
- analyze_vocabulary_diversity,
- analyze_cohesion,
- analyze_structure,
- get_dependency_depths,
- normalize_score,
- generate_sentence_graphs,
- generate_word_connections,
- generate_connection_paths,
- create_vocabulary_network,
- create_syntax_complexity_graph,
- create_cohesion_heatmap,
- generate_recommendations
-)
-
-# Configuración del estilo de matplotlib para el gráfico de radar
-plt.rcParams['font.family'] = 'sans-serif'
-plt.rcParams['axes.grid'] = True
-plt.rcParams['axes.spines.top'] = False
-plt.rcParams['axes.spines.right'] = False
-
-logger = logging.getLogger(__name__)
-####################################
-
-TEXT_TYPES = {
- 'academic_article': {
- 'name': 'Artículo Académico',
- 'thresholds': {
- 'vocabulary': {'min': 0.70, 'target': 0.85},
- 'structure': {'min': 0.75, 'target': 0.90},
- 'cohesion': {'min': 0.65, 'target': 0.80},
- 'clarity': {'min': 0.70, 'target': 0.85}
- }
- },
- 'student_essay': {
- 'name': 'Trabajo Universitario',
- 'thresholds': {
- 'vocabulary': {'min': 0.60, 'target': 0.75},
- 'structure': {'min': 0.65, 'target': 0.80},
- 'cohesion': {'min': 0.55, 'target': 0.70},
- 'clarity': {'min': 0.60, 'target': 0.75}
- }
- },
- 'general_communication': {
- 'name': 'Comunicación General',
- 'thresholds': {
- 'vocabulary': {'min': 0.50, 'target': 0.65},
- 'structure': {'min': 0.55, 'target': 0.70},
- 'cohesion': {'min': 0.45, 'target': 0.60},
- 'clarity': {'min': 0.50, 'target': 0.65}
- }
- }
-}
-####################################
-
-def display_current_situation_interface(lang_code, nlp_models, t):
- """
- Interfaz simplificada con gráfico de radar para visualizar métricas.
- """
- # Inicializar estados si no existen
- if 'text_input' not in st.session_state:
- st.session_state.text_input = ""
- if 'text_area' not in st.session_state: # Añadir inicialización de text_area
- st.session_state.text_area = ""
- if 'show_results' not in st.session_state:
- st.session_state.show_results = False
- if 'current_doc' not in st.session_state:
- st.session_state.current_doc = None
- if 'current_metrics' not in st.session_state:
- st.session_state.current_metrics = None
-
- try:
- # Container principal con dos columnas
- with st.container():
- input_col, results_col = st.columns([1,2])
-
- with input_col:
- # Text area con manejo de estado
- text_input = st.text_area(
- t.get('input_prompt', "Escribe o pega tu texto aquí:"),
- height=400,
- key="text_area",
- value=st.session_state.text_input,
- help="Este texto será analizado para darte recomendaciones personalizadas"
- )
-
- # Función para manejar cambios de texto
- if text_input != st.session_state.text_input:
- st.session_state.text_input = text_input
- st.session_state.show_results = False
-
- if st.button(
- t.get('analyze_button', "Analizar mi escritura"),
- type="primary",
- disabled=not text_input.strip(),
- use_container_width=True,
- ):
- try:
- with st.spinner(t.get('processing', "Analizando...")):
- doc = nlp_models[lang_code](text_input)
- metrics = analyze_text_dimensions(doc)
-
- storage_success = store_current_situation_result(
- username=st.session_state.username,
- text=text_input,
- metrics=metrics,
- feedback=None
- )
-
- if not storage_success:
- logger.warning("No se pudo guardar el análisis en la base de datos")
-
- st.session_state.current_doc = doc
- st.session_state.current_metrics = metrics
- st.session_state.show_results = True
-
- except Exception as e:
- logger.error(f"Error en análisis: {str(e)}")
- st.error(t.get('analysis_error', "Error al analizar el texto"))
-
- # Mostrar resultados en la columna derecha
- with results_col:
- if st.session_state.show_results and st.session_state.current_metrics is not None:
- # Primero los radio buttons para tipo de texto
- st.markdown("### Tipo de texto")
- text_type = st.radio(
- "",
- options=list(TEXT_TYPES.keys()),
- format_func=lambda x: TEXT_TYPES[x]['name'],
- horizontal=True,
- key="text_type_radio",
- help="Selecciona el tipo de texto para ajustar los criterios de evaluación"
- )
-
- st.session_state.current_text_type = text_type
-
- # Luego mostrar los resultados
- display_results(
- metrics=st.session_state.current_metrics,
- text_type=text_type
- )
-
- except Exception as e:
- logger.error(f"Error en interfaz principal: {str(e)}")
- st.error("Ocurrió un error al cargar la interfaz")
-
-###################################3333
-
-'''
-def display_results(metrics, text_type=None):
- """
- Muestra los resultados del análisis: métricas verticalmente y gráfico radar.
- """
- try:
- # Usar valor por defecto si no se especifica tipo
- text_type = text_type or 'student_essay'
-
- # Obtener umbrales según el tipo de texto
- thresholds = TEXT_TYPES[text_type]['thresholds']
-
- # Crear dos columnas para las métricas y el gráfico
- metrics_col, graph_col = st.columns([1, 1.5])
-
- # Columna de métricas
- with metrics_col:
- metrics_config = [
- {
- 'label': "Vocabulario",
- 'key': 'vocabulary',
- 'value': metrics['vocabulary']['normalized_score'],
- 'help': "Riqueza y variedad del vocabulario",
- 'thresholds': thresholds['vocabulary']
- },
- {
- 'label': "Estructura",
- 'key': 'structure',
- 'value': metrics['structure']['normalized_score'],
- 'help': "Organización y complejidad de oraciones",
- 'thresholds': thresholds['structure']
- },
- {
- 'label': "Cohesión",
- 'key': 'cohesion',
- 'value': metrics['cohesion']['normalized_score'],
- 'help': "Conexión y fluidez entre ideas",
- 'thresholds': thresholds['cohesion']
- },
- {
- 'label': "Claridad",
- 'key': 'clarity',
- 'value': metrics['clarity']['normalized_score'],
- 'help': "Facilidad de comprensión del texto",
- 'thresholds': thresholds['clarity']
- }
- ]
-
- # Mostrar métricas
- for metric in metrics_config:
- value = metric['value']
- if value < metric['thresholds']['min']:
- status = "⚠️ Por mejorar"
- color = "inverse"
- elif value < metric['thresholds']['target']:
- status = "📈 Aceptable"
- color = "off"
- else:
- status = "✅ Óptimo"
- color = "normal"
-
- st.metric(
- metric['label'],
- f"{value:.2f}",
- f"{status} (Meta: {metric['thresholds']['target']:.2f})",
- delta_color=color,
- help=metric['help']
- )
- st.markdown("
", unsafe_allow_html=True)
-
- # Gráfico radar en la columna derecha
- with graph_col:
- display_radar_chart(metrics_config, thresholds)
-
- except Exception as e:
- logger.error(f"Error mostrando resultados: {str(e)}")
- st.error("Error al mostrar los resultados")
-'''
-
-######################################
-######################################
-def display_results(metrics, text_type=None):
- """
- Muestra los resultados del análisis: métricas verticalmente y gráfico radar.
- """
- try:
- # Usar valor por defecto si no se especifica tipo
- text_type = text_type or 'student_essay'
-
- # Obtener umbrales según el tipo de texto
- thresholds = TEXT_TYPES[text_type]['thresholds']
-
- # Crear dos columnas para las métricas y el gráfico
- metrics_col, graph_col = st.columns([1, 1.5])
-
- # Columna de métricas
- with metrics_col:
- metrics_config = [
- {
- 'label': "Vocabulario",
- 'key': 'vocabulary',
- 'value': metrics['vocabulary']['normalized_score'],
- 'help': "Riqueza y variedad del vocabulario",
- 'thresholds': thresholds['vocabulary']
- },
- {
- 'label': "Estructura",
- 'key': 'structure',
- 'value': metrics['structure']['normalized_score'],
- 'help': "Organización y complejidad de oraciones",
- 'thresholds': thresholds['structure']
- },
- {
- 'label': "Cohesión",
- 'key': 'cohesion',
- 'value': metrics['cohesion']['normalized_score'],
- 'help': "Conexión y fluidez entre ideas",
- 'thresholds': thresholds['cohesion']
- },
- {
- 'label': "Claridad",
- 'key': 'clarity',
- 'value': metrics['clarity']['normalized_score'],
- 'help': "Facilidad de comprensión del texto",
- 'thresholds': thresholds['clarity']
- }
- ]
-
- # Mostrar métricas
- for metric in metrics_config:
- value = metric['value']
- if value < metric['thresholds']['min']:
- status = "⚠️ Por mejorar"
- color = "inverse"
- elif value < metric['thresholds']['target']:
- status = "📈 Aceptable"
- color = "off"
- else:
- status = "✅ Óptimo"
- color = "normal"
-
- st.metric(
- metric['label'],
- f"{value:.2f}",
- f"{status} (Meta: {metric['thresholds']['target']:.2f})",
- delta_color=color,
- help=metric['help']
- )
- st.markdown("
", unsafe_allow_html=True)
-
- # Gráfico radar en la columna derecha
- with graph_col:
- display_radar_chart(metrics_config, thresholds)
-
- recommendations = generate_recommendations(
- metrics=metrics,
- text_type=text_type,
- lang_code=st.session_state.lang_code
- )
-
- # Separador visual
- st.markdown("---")
-
- # Título para la sección de recomendaciones
- st.subheader("Recomendaciones para mejorar tu escritura")
-
- # Mostrar las recomendaciones
- display_recommendations(recommendations, get_translations(st.session_state.lang_code))
-
- except Exception as e:
- logger.error(f"Error mostrando resultados: {str(e)}")
- st.error("Error al mostrar los resultados")
-
-
-######################################
-######################################
-def display_radar_chart(metrics_config, thresholds):
- """
- Muestra el gráfico radar con los resultados.
- """
- try:
- # Preparar datos para el gráfico
- categories = [m['label'] for m in metrics_config]
- values_user = [m['value'] for m in metrics_config]
- min_values = [m['thresholds']['min'] for m in metrics_config]
- target_values = [m['thresholds']['target'] for m in metrics_config]
-
- # Crear y configurar gráfico
- fig = plt.figure(figsize=(8, 8))
- ax = fig.add_subplot(111, projection='polar')
-
- # Configurar radar
- angles = [n / float(len(categories)) * 2 * np.pi for n in range(len(categories))]
- angles += angles[:1]
- values_user += values_user[:1]
- min_values += min_values[:1]
- target_values += target_values[:1]
-
- # Configurar ejes
- ax.set_xticks(angles[:-1])
- ax.set_xticklabels(categories, fontsize=10)
- circle_ticks = np.arange(0, 1.1, 0.2)
- ax.set_yticks(circle_ticks)
- ax.set_yticklabels([f'{tick:.1f}' for tick in circle_ticks], fontsize=8)
- ax.set_ylim(0, 1)
-
- # Dibujar áreas de umbrales
- ax.plot(angles, min_values, '#e74c3c', linestyle='--', linewidth=1, label='Mínimo', alpha=0.5)
- ax.plot(angles, target_values, '#2ecc71', linestyle='--', linewidth=1, label='Meta', alpha=0.5)
- ax.fill_between(angles, target_values, [1]*len(angles), color='#2ecc71', alpha=0.1)
- ax.fill_between(angles, [0]*len(angles), min_values, color='#e74c3c', alpha=0.1)
-
- # Dibujar valores del usuario
- ax.plot(angles, values_user, '#3498db', linewidth=2, label='Tu escritura')
- ax.fill(angles, values_user, '#3498db', alpha=0.2)
-
- # Ajustar leyenda
- ax.legend(
- loc='upper right',
- bbox_to_anchor=(1.3, 1.1), # Cambiado de (0.1, 0.1) a (1.3, 1.1)
- fontsize=10,
- frameon=True,
- facecolor='white',
- edgecolor='none',
- shadow=True
- )
-
- plt.tight_layout()
- st.pyplot(fig)
- plt.close()
-
- except Exception as e:
- logger.error(f"Error mostrando gráfico radar: {str(e)}")
- st.error("Error al mostrar el gráfico")
-
-#####################################################
-def display_recommendations(recommendations, t):
- """
- Muestra las recomendaciones con un diseño de tarjetas.
- """
- # Definir colores para cada categoría
- colors = {
- 'vocabulary': '#2E86C1', # Azul
- 'structure': '#28B463', # Verde
- 'cohesion': '#F39C12', # Naranja
- 'clarity': '#9B59B6', # Púrpura
- 'priority': '#E74C3C' # Rojo para la categoría prioritaria
- }
-
- # Iconos para cada categoría
- icons = {
- 'vocabulary': '📚',
- 'structure': '🏗️',
- 'cohesion': '🔄',
- 'clarity': '💡',
- 'priority': '⭐'
- }
-
- # Obtener traducciones para cada dimensión
- dimension_names = {
- 'vocabulary': t.get('SITUATION_ANALYSIS', {}).get('vocabulary', "Vocabulario"),
- 'structure': t.get('SITUATION_ANALYSIS', {}).get('structure', "Estructura"),
- 'cohesion': t.get('SITUATION_ANALYSIS', {}).get('cohesion', "Cohesión"),
- 'clarity': t.get('SITUATION_ANALYSIS', {}).get('clarity', "Claridad"),
- 'priority': t.get('SITUATION_ANALYSIS', {}).get('priority', "Prioridad")
- }
-
- # Título de la sección prioritaria
- priority_focus = t.get('SITUATION_ANALYSIS', {}).get('priority_focus', 'Área prioritaria para mejorar')
- st.markdown(f"### {icons['priority']} {priority_focus}")
-
- # Determinar área prioritaria (la que tiene menor puntuación)
- priority_area = recommendations.get('priority', 'vocabulary')
- priority_title = dimension_names.get(priority_area, "Área prioritaria")
-
- # Determinar el contenido para mostrar
- if isinstance(recommendations[priority_area], dict) and 'title' in recommendations[priority_area]:
- priority_title = recommendations[priority_area]['title']
- priority_content = recommendations[priority_area]['content']
- else:
- priority_content = recommendations[priority_area]
-
- # Mostrar la recomendación prioritaria con un estilo destacado
- with st.container():
- st.markdown(
- f"""
-
-
{priority_title}
-
{priority_content}
-
- """,
- unsafe_allow_html=True
- )
-
- # Crear dos columnas para las tarjetas de recomendaciones restantes
- col1, col2 = st.columns(2)
-
- # Distribuir las recomendaciones en las columnas
- categories = ['vocabulary', 'structure', 'cohesion', 'clarity']
- for i, category in enumerate(categories):
- # Saltar si esta categoría ya es la prioritaria
- if category == priority_area:
- continue
-
- # Determinar título y contenido
- if isinstance(recommendations[category], dict) and 'title' in recommendations[category]:
- category_title = recommendations[category]['title']
- category_content = recommendations[category]['content']
- else:
- category_title = dimension_names.get(category, category)
- category_content = recommendations[category]
-
- # Alternar entre columnas
- with col1 if i % 2 == 0 else col2:
- # Crear tarjeta para cada recomendación
- st.markdown(
- f"""
-
-
{icons[category]} {category_title}
-
{category_content}
-
- """,
- unsafe_allow_html=True
+# modules/studentact/current_situation_interface-vOK.py
+
+import streamlit as st
+import logging
+from ..utils.widget_utils import generate_unique_key
+import matplotlib.pyplot as plt
+import numpy as np
+from ..database.current_situation_mongo_db import store_current_situation_result
+
+# Importaciones locales
+from translations import get_translations
+
+from .current_situation_analysis import (
+ analyze_text_dimensions,
+ analyze_clarity,
+ analyze_vocabulary_diversity,
+ analyze_cohesion,
+ analyze_structure,
+ get_dependency_depths,
+ normalize_score,
+ generate_sentence_graphs,
+ generate_word_connections,
+ generate_connection_paths,
+ create_vocabulary_network,
+ create_syntax_complexity_graph,
+ create_cohesion_heatmap,
+ generate_recommendations
+)
+
+# Configuración del estilo de matplotlib para el gráfico de radar
+plt.rcParams['font.family'] = 'sans-serif'
+plt.rcParams['axes.grid'] = True
+plt.rcParams['axes.spines.top'] = False
+plt.rcParams['axes.spines.right'] = False
+
+logger = logging.getLogger(__name__)
+####################################
+
+TEXT_TYPES = {
+ 'academic_article': {
+ 'name': 'Artículo Académico',
+ 'thresholds': {
+ 'vocabulary': {'min': 0.70, 'target': 0.85},
+ 'structure': {'min': 0.75, 'target': 0.90},
+ 'cohesion': {'min': 0.65, 'target': 0.80},
+ 'clarity': {'min': 0.70, 'target': 0.85}
+ }
+ },
+ 'student_essay': {
+ 'name': 'Trabajo Universitario',
+ 'thresholds': {
+ 'vocabulary': {'min': 0.60, 'target': 0.75},
+ 'structure': {'min': 0.65, 'target': 0.80},
+ 'cohesion': {'min': 0.55, 'target': 0.70},
+ 'clarity': {'min': 0.60, 'target': 0.75}
+ }
+ },
+ 'general_communication': {
+ 'name': 'Comunicación General',
+ 'thresholds': {
+ 'vocabulary': {'min': 0.50, 'target': 0.65},
+ 'structure': {'min': 0.55, 'target': 0.70},
+ 'cohesion': {'min': 0.45, 'target': 0.60},
+ 'clarity': {'min': 0.50, 'target': 0.65}
+ }
+ }
+}
+####################################
+
+def display_current_situation_interface(lang_code, nlp_models, t):
+ """
+ Interfaz simplificada con gráfico de radar para visualizar métricas.
+ """
+ # Inicializar estados si no existen
+ if 'text_input' not in st.session_state:
+ st.session_state.text_input = ""
+ if 'text_area' not in st.session_state: # Añadir inicialización de text_area
+ st.session_state.text_area = ""
+ if 'show_results' not in st.session_state:
+ st.session_state.show_results = False
+ if 'current_doc' not in st.session_state:
+ st.session_state.current_doc = None
+ if 'current_metrics' not in st.session_state:
+ st.session_state.current_metrics = None
+
+ try:
+ # Container principal con dos columnas
+ with st.container():
+ input_col, results_col = st.columns([1,2])
+
+ with input_col:
+ # Text area con manejo de estado
+ text_input = st.text_area(
+ t.get('input_prompt', "Escribe o pega tu texto aquí:"),
+ height=400,
+ key="text_area",
+ value=st.session_state.text_input,
+ help="Este texto será analizado para darte recomendaciones personalizadas"
+ )
+
+ # Función para manejar cambios de texto
+ if text_input != st.session_state.text_input:
+ st.session_state.text_input = text_input
+ st.session_state.show_results = False
+
+ if st.button(
+ t.get('analyze_button', "Analizar mi escritura"),
+ type="primary",
+ disabled=not text_input.strip(),
+ use_container_width=True,
+ ):
+ try:
+ with st.spinner(t.get('processing', "Analizando...")):
+ doc = nlp_models[lang_code](text_input)
+ metrics = analyze_text_dimensions(doc)
+
+ storage_success = store_current_situation_result(
+ username=st.session_state.username,
+ text=text_input,
+ metrics=metrics,
+ feedback=None
+ )
+
+ if not storage_success:
+ logger.warning("No se pudo guardar el análisis en la base de datos")
+
+ st.session_state.current_doc = doc
+ st.session_state.current_metrics = metrics
+ st.session_state.show_results = True
+
+ except Exception as e:
+ logger.error(f"Error en análisis: {str(e)}")
+ st.error(t.get('analysis_error', "Error al analizar el texto"))
+
+ # Mostrar resultados en la columna derecha
+ with results_col:
+ if st.session_state.show_results and st.session_state.current_metrics is not None:
+ # Primero los radio buttons para tipo de texto
+ st.markdown("### Tipo de texto")
+ text_type = st.radio(
+ "",
+ options=list(TEXT_TYPES.keys()),
+ format_func=lambda x: TEXT_TYPES[x]['name'],
+ horizontal=True,
+ key="text_type_radio",
+ help="Selecciona el tipo de texto para ajustar los criterios de evaluación"
+ )
+
+ st.session_state.current_text_type = text_type
+
+ # Luego mostrar los resultados
+ display_results(
+ metrics=st.session_state.current_metrics,
+ text_type=text_type
+ )
+
+ except Exception as e:
+ logger.error(f"Error en interfaz principal: {str(e)}")
+ st.error("Ocurrió un error al cargar la interfaz")
+
+###################################3333
+
+'''
+def display_results(metrics, text_type=None):
+ """
+ Muestra los resultados del análisis: métricas verticalmente y gráfico radar.
+ """
+ try:
+ # Usar valor por defecto si no se especifica tipo
+ text_type = text_type or 'student_essay'
+
+ # Obtener umbrales según el tipo de texto
+ thresholds = TEXT_TYPES[text_type]['thresholds']
+
+ # Crear dos columnas para las métricas y el gráfico
+ metrics_col, graph_col = st.columns([1, 1.5])
+
+ # Columna de métricas
+ with metrics_col:
+ metrics_config = [
+ {
+ 'label': "Vocabulario",
+ 'key': 'vocabulary',
+ 'value': metrics['vocabulary']['normalized_score'],
+ 'help': "Riqueza y variedad del vocabulario",
+ 'thresholds': thresholds['vocabulary']
+ },
+ {
+ 'label': "Estructura",
+ 'key': 'structure',
+ 'value': metrics['structure']['normalized_score'],
+ 'help': "Organización y complejidad de oraciones",
+ 'thresholds': thresholds['structure']
+ },
+ {
+ 'label': "Cohesión",
+ 'key': 'cohesion',
+ 'value': metrics['cohesion']['normalized_score'],
+ 'help': "Conexión y fluidez entre ideas",
+ 'thresholds': thresholds['cohesion']
+ },
+ {
+ 'label': "Claridad",
+ 'key': 'clarity',
+ 'value': metrics['clarity']['normalized_score'],
+ 'help': "Facilidad de comprensión del texto",
+ 'thresholds': thresholds['clarity']
+ }
+ ]
+
+ # Mostrar métricas
+ for metric in metrics_config:
+ value = metric['value']
+ if value < metric['thresholds']['min']:
+ status = "⚠️ Por mejorar"
+ color = "inverse"
+ elif value < metric['thresholds']['target']:
+ status = "📈 Aceptable"
+ color = "off"
+ else:
+ status = "✅ Óptimo"
+ color = "normal"
+
+ st.metric(
+ metric['label'],
+ f"{value:.2f}",
+ f"{status} (Meta: {metric['thresholds']['target']:.2f})",
+ delta_color=color,
+ help=metric['help']
+ )
+ st.markdown("
", unsafe_allow_html=True)
+
+ # Gráfico radar en la columna derecha
+ with graph_col:
+ display_radar_chart(metrics_config, thresholds)
+
+ except Exception as e:
+ logger.error(f"Error mostrando resultados: {str(e)}")
+ st.error("Error al mostrar los resultados")
+'''
+
+######################################
+######################################
+def display_results(metrics, text_type=None):
+ """
+ Muestra los resultados del análisis: métricas verticalmente y gráfico radar.
+ """
+ try:
+ # Usar valor por defecto si no se especifica tipo
+ text_type = text_type or 'student_essay'
+
+ # Obtener umbrales según el tipo de texto
+ thresholds = TEXT_TYPES[text_type]['thresholds']
+
+ # Crear dos columnas para las métricas y el gráfico
+ metrics_col, graph_col = st.columns([1, 1.5])
+
+ # Columna de métricas
+ with metrics_col:
+ metrics_config = [
+ {
+ 'label': "Vocabulario",
+ 'key': 'vocabulary',
+ 'value': metrics['vocabulary']['normalized_score'],
+ 'help': "Riqueza y variedad del vocabulario",
+ 'thresholds': thresholds['vocabulary']
+ },
+ {
+ 'label': "Estructura",
+ 'key': 'structure',
+ 'value': metrics['structure']['normalized_score'],
+ 'help': "Organización y complejidad de oraciones",
+ 'thresholds': thresholds['structure']
+ },
+ {
+ 'label': "Cohesión",
+ 'key': 'cohesion',
+ 'value': metrics['cohesion']['normalized_score'],
+ 'help': "Conexión y fluidez entre ideas",
+ 'thresholds': thresholds['cohesion']
+ },
+ {
+ 'label': "Claridad",
+ 'key': 'clarity',
+ 'value': metrics['clarity']['normalized_score'],
+ 'help': "Facilidad de comprensión del texto",
+ 'thresholds': thresholds['clarity']
+ }
+ ]
+
+ # Mostrar métricas
+ for metric in metrics_config:
+ value = metric['value']
+ if value < metric['thresholds']['min']:
+ status = "⚠️ Por mejorar"
+ color = "inverse"
+ elif value < metric['thresholds']['target']:
+ status = "📈 Aceptable"
+ color = "off"
+ else:
+ status = "✅ Óptimo"
+ color = "normal"
+
+ st.metric(
+ metric['label'],
+ f"{value:.2f}",
+ f"{status} (Meta: {metric['thresholds']['target']:.2f})",
+ delta_color=color,
+ help=metric['help']
+ )
+ st.markdown("
", unsafe_allow_html=True)
+
+ # Gráfico radar en la columna derecha
+ with graph_col:
+ display_radar_chart(metrics_config, thresholds)
+
+ recommendations = generate_recommendations(
+ metrics=metrics,
+ text_type=text_type,
+ lang_code=st.session_state.lang_code
+ )
+
+ # Separador visual
+ st.markdown("---")
+
+ # Título para la sección de recomendaciones
+ st.subheader("Recomendaciones para mejorar tu escritura")
+
+ # Mostrar las recomendaciones
+ display_recommendations(recommendations, get_translations(st.session_state.lang_code))
+
+ except Exception as e:
+ logger.error(f"Error mostrando resultados: {str(e)}")
+ st.error("Error al mostrar los resultados")
+
+
+######################################
+######################################
+def display_radar_chart(metrics_config, thresholds):
+ """
+ Muestra el gráfico radar con los resultados.
+ """
+ try:
+ # Preparar datos para el gráfico
+ categories = [m['label'] for m in metrics_config]
+ values_user = [m['value'] for m in metrics_config]
+ min_values = [m['thresholds']['min'] for m in metrics_config]
+ target_values = [m['thresholds']['target'] for m in metrics_config]
+
+ # Crear y configurar gráfico
+ fig = plt.figure(figsize=(8, 8))
+ ax = fig.add_subplot(111, projection='polar')
+
+ # Configurar radar
+ angles = [n / float(len(categories)) * 2 * np.pi for n in range(len(categories))]
+ angles += angles[:1]
+ values_user += values_user[:1]
+ min_values += min_values[:1]
+ target_values += target_values[:1]
+
+ # Configurar ejes
+ ax.set_xticks(angles[:-1])
+ ax.set_xticklabels(categories, fontsize=10)
+ circle_ticks = np.arange(0, 1.1, 0.2)
+ ax.set_yticks(circle_ticks)
+ ax.set_yticklabels([f'{tick:.1f}' for tick in circle_ticks], fontsize=8)
+ ax.set_ylim(0, 1)
+
+ # Dibujar áreas de umbrales
+ ax.plot(angles, min_values, '#e74c3c', linestyle='--', linewidth=1, label='Mínimo', alpha=0.5)
+ ax.plot(angles, target_values, '#2ecc71', linestyle='--', linewidth=1, label='Meta', alpha=0.5)
+ ax.fill_between(angles, target_values, [1]*len(angles), color='#2ecc71', alpha=0.1)
+ ax.fill_between(angles, [0]*len(angles), min_values, color='#e74c3c', alpha=0.1)
+
+ # Dibujar valores del usuario
+ ax.plot(angles, values_user, '#3498db', linewidth=2, label='Tu escritura')
+ ax.fill(angles, values_user, '#3498db', alpha=0.2)
+
+ # Ajustar leyenda
+ ax.legend(
+ loc='upper right',
+ bbox_to_anchor=(1.3, 1.1), # Cambiado de (0.1, 0.1) a (1.3, 1.1)
+ fontsize=10,
+ frameon=True,
+ facecolor='white',
+ edgecolor='none',
+ shadow=True
+ )
+
+ plt.tight_layout()
+ st.pyplot(fig)
+ plt.close()
+
+ except Exception as e:
+ logger.error(f"Error mostrando gráfico radar: {str(e)}")
+ st.error("Error al mostrar el gráfico")
+
+#####################################################
+def display_recommendations(recommendations, t):
+ """
+ Muestra las recomendaciones con un diseño de tarjetas.
+ """
+ # Definir colores para cada categoría
+ colors = {
+ 'vocabulary': '#2E86C1', # Azul
+ 'structure': '#28B463', # Verde
+ 'cohesion': '#F39C12', # Naranja
+ 'clarity': '#9B59B6', # Púrpura
+ 'priority': '#E74C3C' # Rojo para la categoría prioritaria
+ }
+
+ # Iconos para cada categoría
+ icons = {
+ 'vocabulary': '📚',
+ 'structure': '🏗️',
+ 'cohesion': '🔄',
+ 'clarity': '💡',
+ 'priority': '⭐'
+ }
+
+ # Obtener traducciones para cada dimensión
+ dimension_names = {
+ 'vocabulary': t.get('SITUATION_ANALYSIS', {}).get('vocabulary', "Vocabulario"),
+ 'structure': t.get('SITUATION_ANALYSIS', {}).get('structure', "Estructura"),
+ 'cohesion': t.get('SITUATION_ANALYSIS', {}).get('cohesion', "Cohesión"),
+ 'clarity': t.get('SITUATION_ANALYSIS', {}).get('clarity', "Claridad"),
+ 'priority': t.get('SITUATION_ANALYSIS', {}).get('priority', "Prioridad")
+ }
+
+ # Título de la sección prioritaria
+ priority_focus = t.get('SITUATION_ANALYSIS', {}).get('priority_focus', 'Área prioritaria para mejorar')
+ st.markdown(f"### {icons['priority']} {priority_focus}")
+
+ # Determinar área prioritaria (la que tiene menor puntuación)
+ priority_area = recommendations.get('priority', 'vocabulary')
+ priority_title = dimension_names.get(priority_area, "Área prioritaria")
+
+ # Determinar el contenido para mostrar
+ if isinstance(recommendations[priority_area], dict) and 'title' in recommendations[priority_area]:
+ priority_title = recommendations[priority_area]['title']
+ priority_content = recommendations[priority_area]['content']
+ else:
+ priority_content = recommendations[priority_area]
+
+ # Mostrar la recomendación prioritaria con un estilo destacado
+ with st.container():
+ st.markdown(
+ f"""
+
+
{priority_title}
+
{priority_content}
+
+ """,
+ unsafe_allow_html=True
+ )
+
+ # Crear dos columnas para las tarjetas de recomendaciones restantes
+ col1, col2 = st.columns(2)
+
+ # Distribuir las recomendaciones en las columnas
+ categories = ['vocabulary', 'structure', 'cohesion', 'clarity']
+ for i, category in enumerate(categories):
+ # Saltar si esta categoría ya es la prioritaria
+ if category == priority_area:
+ continue
+
+ # Determinar título y contenido
+ if isinstance(recommendations[category], dict) and 'title' in recommendations[category]:
+ category_title = recommendations[category]['title']
+ category_content = recommendations[category]['content']
+ else:
+ category_title = dimension_names.get(category, category)
+ category_content = recommendations[category]
+
+ # Alternar entre columnas
+ with col1 if i % 2 == 0 else col2:
+ # Crear tarjeta para cada recomendación
+ st.markdown(
+ f"""
+
+
{icons[category]} {category_title}
+
{category_content}
+
+ """,
+ unsafe_allow_html=True
)
\ No newline at end of file
diff --git a/modules/studentact/current_situation_analysis-FAIL.py b/modules/studentact/current_situation_analysis-FAIL.py
index fbb3f9d9b11a94946f6bb4f3f4a7516ba554a075..873a8de350242c563ff7d0257be106e305927e4e 100644
--- a/modules/studentact/current_situation_analysis-FAIL.py
+++ b/modules/studentact/current_situation_analysis-FAIL.py
@@ -1,810 +1,810 @@
-#v3/modules/studentact/current_situation_analysis.py
-
-import streamlit as st
-import matplotlib.pyplot as plt
-import networkx as nx
-import seaborn as sns
-from collections import Counter
-from itertools import combinations
-import numpy as np
-import matplotlib.patches as patches
-import logging
-
-# 2. Configuración básica del logging
-logging.basicConfig(
- level=logging.INFO,
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
- handlers=[
- logging.StreamHandler(),
- logging.FileHandler('app.log')
- ]
-)
-
-# 3. Obtener el logger específico para este módulo
-logger = logging.getLogger(__name__)
-
-#########################################################################
-
-def correlate_metrics(scores):
- """
- Ajusta los scores para mantener correlaciones lógicas entre métricas.
-
- Args:
- scores: dict con scores iniciales de vocabulario, estructura, cohesión y claridad
-
- Returns:
- dict con scores ajustados
- """
- try:
- # 1. Correlación estructura-cohesión
- # La cohesión no puede ser menor que estructura * 0.7
- min_cohesion = scores['structure']['normalized_score'] * 0.7
- if scores['cohesion']['normalized_score'] < min_cohesion:
- scores['cohesion']['normalized_score'] = min_cohesion
-
- # 2. Correlación vocabulario-cohesión
- # La cohesión léxica depende del vocabulario
- vocab_influence = scores['vocabulary']['normalized_score'] * 0.6
- scores['cohesion']['normalized_score'] = max(
- scores['cohesion']['normalized_score'],
- vocab_influence
- )
-
- # 3. Correlación cohesión-claridad
- # La claridad no puede superar cohesión * 1.2
- max_clarity = scores['cohesion']['normalized_score'] * 1.2
- if scores['clarity']['normalized_score'] > max_clarity:
- scores['clarity']['normalized_score'] = max_clarity
-
- # 4. Correlación estructura-claridad
- # La claridad no puede superar estructura * 1.1
- struct_max_clarity = scores['structure']['normalized_score'] * 1.1
- scores['clarity']['normalized_score'] = min(
- scores['clarity']['normalized_score'],
- struct_max_clarity
- )
-
- # Normalizar todos los scores entre 0 y 1
- for metric in scores:
- scores[metric]['normalized_score'] = max(0.0, min(1.0, scores[metric]['normalized_score']))
-
- return scores
-
- except Exception as e:
- logger.error(f"Error en correlate_metrics: {str(e)}")
- return scores
-
-##########################################################################
-
-def analyze_text_dimensions(doc):
- """
- Analiza las dimensiones principales del texto manteniendo correlaciones lógicas.
- """
- try:
- # Obtener scores iniciales
- vocab_score, vocab_details = analyze_vocabulary_diversity(doc)
- struct_score = analyze_structure(doc)
- cohesion_score = analyze_cohesion(doc)
- clarity_score, clarity_details = analyze_clarity(doc)
-
- # Crear diccionario de scores inicial
- scores = {
- 'vocabulary': {
- 'normalized_score': vocab_score,
- 'details': vocab_details
- },
- 'structure': {
- 'normalized_score': struct_score,
- 'details': None
- },
- 'cohesion': {
- 'normalized_score': cohesion_score,
- 'details': None
- },
- 'clarity': {
- 'normalized_score': clarity_score,
- 'details': clarity_details
- }
- }
-
- # Ajustar correlaciones entre métricas
- adjusted_scores = correlate_metrics(scores)
-
- # Logging para diagnóstico
- logger.info(f"""
- Scores originales vs ajustados:
- Vocabulario: {vocab_score:.2f} -> {adjusted_scores['vocabulary']['normalized_score']:.2f}
- Estructura: {struct_score:.2f} -> {adjusted_scores['structure']['normalized_score']:.2f}
- Cohesión: {cohesion_score:.2f} -> {adjusted_scores['cohesion']['normalized_score']:.2f}
- Claridad: {clarity_score:.2f} -> {adjusted_scores['clarity']['normalized_score']:.2f}
- """)
-
- return adjusted_scores
-
- except Exception as e:
- logger.error(f"Error en analyze_text_dimensions: {str(e)}")
- return {
- 'vocabulary': {'normalized_score': 0.0, 'details': {}},
- 'structure': {'normalized_score': 0.0, 'details': {}},
- 'cohesion': {'normalized_score': 0.0, 'details': {}},
- 'clarity': {'normalized_score': 0.0, 'details': {}}
- }
-
-
-
-#############################################################################################
-
-def analyze_clarity(doc):
- """
- Analiza la claridad del texto considerando múltiples factores.
- """
- try:
- sentences = list(doc.sents)
- if not sentences:
- return 0.0, {}
-
- # 1. Longitud de oraciones
- sentence_lengths = [len(sent) for sent in sentences]
- avg_length = sum(sentence_lengths) / len(sentences)
-
- # Normalizar usando los umbrales definidos para clarity
- length_score = normalize_score(
- value=avg_length,
- metric_type='clarity',
- optimal_length=20, # Una oración ideal tiene ~20 palabras
- min_threshold=0.60, # Consistente con METRIC_THRESHOLDS
- target_threshold=0.75 # Consistente con METRIC_THRESHOLDS
- )
-
- # 2. Análisis de conectores
- connector_count = 0
- connector_weights = {
- 'CCONJ': 1.0, # Coordinantes
- 'SCONJ': 1.2, # Subordinantes
- 'ADV': 0.8 # Adverbios conectivos
- }
-
- for token in doc:
- if token.pos_ in connector_weights and token.dep_ in ['cc', 'mark', 'advmod']:
- connector_count += connector_weights[token.pos_]
-
- # Normalizar conectores por oración
- connectors_per_sentence = connector_count / len(sentences) if sentences else 0
- connector_score = normalize_score(
- value=connectors_per_sentence,
- metric_type='clarity',
- optimal_connections=1.5, # ~1.5 conectores por oración es óptimo
- min_threshold=0.60,
- target_threshold=0.75
- )
-
- # 3. Complejidad estructural
- clause_count = 0
- for sent in sentences:
- verbs = [token for token in sent if token.pos_ == 'VERB']
- clause_count += len(verbs)
-
- complexity_raw = clause_count / len(sentences) if sentences else 0
- complexity_score = normalize_score(
- value=complexity_raw,
- metric_type='clarity',
- optimal_depth=2.0, # ~2 cláusulas por oración es óptimo
- min_threshold=0.60,
- target_threshold=0.75
- )
-
- # 4. Densidad léxica
- content_words = len([token for token in doc if token.pos_ in ['NOUN', 'VERB', 'ADJ', 'ADV']])
- total_words = len([token for token in doc if token.is_alpha])
- density = content_words / total_words if total_words > 0 else 0
-
- density_score = normalize_score(
- value=density,
- metric_type='clarity',
- optimal_connections=0.6, # 60% de palabras de contenido es óptimo
- min_threshold=0.60,
- target_threshold=0.75
- )
-
- # Score final ponderado
- weights = {
- 'length': 0.3,
- 'connectors': 0.3,
- 'complexity': 0.2,
- 'density': 0.2
- }
-
- clarity_score = (
- weights['length'] * length_score +
- weights['connectors'] * connector_score +
- weights['complexity'] * complexity_score +
- weights['density'] * density_score
- )
-
- details = {
- 'length_score': length_score,
- 'connector_score': connector_score,
- 'complexity_score': complexity_score,
- 'density_score': density_score,
- 'avg_sentence_length': avg_length,
- 'connectors_per_sentence': connectors_per_sentence,
- 'density': density
- }
-
- # Agregar logging para diagnóstico
- logger.info(f"""
- Scores de Claridad:
- - Longitud: {length_score:.2f} (avg={avg_length:.1f} palabras)
- - Conectores: {connector_score:.2f} (avg={connectors_per_sentence:.1f} por oración)
- - Complejidad: {complexity_score:.2f} (avg={complexity_raw:.1f} cláusulas)
- - Densidad: {density_score:.2f} ({density*100:.1f}% palabras de contenido)
- - Score Final: {clarity_score:.2f}
- """)
-
- return clarity_score, details
-
- except Exception as e:
- logger.error(f"Error en analyze_clarity: {str(e)}")
- return 0.0, {}
-
-
-def analyze_vocabulary_diversity(doc):
- """Análisis mejorado de la diversidad y calidad del vocabulario"""
- try:
- # 1. Análisis básico de diversidad
- unique_lemmas = {token.lemma_ for token in doc if token.is_alpha}
- total_words = len([token for token in doc if token.is_alpha])
- basic_diversity = len(unique_lemmas) / total_words if total_words > 0 else 0
-
- # 2. Análisis de registro
- academic_words = 0
- narrative_words = 0
- technical_terms = 0
-
- # Clasificar palabras por registro
- for token in doc:
- if token.is_alpha:
- # Detectar términos académicos/técnicos
- if token.pos_ in ['NOUN', 'VERB', 'ADJ']:
- if any(parent.pos_ == 'NOUN' for parent in token.ancestors):
- technical_terms += 1
- # Detectar palabras narrativas
- if token.pos_ in ['VERB', 'ADV'] and token.dep_ in ['ROOT', 'advcl']:
- narrative_words += 1
-
- # 3. Análisis de complejidad sintáctica
- avg_sentence_length = sum(len(sent) for sent in doc.sents) / len(list(doc.sents))
-
- # 4. Calcular score ponderado
- weights = {
- 'diversity': 0.3,
- 'technical': 0.3,
- 'narrative': 0.2,
- 'complexity': 0.2
- }
-
- scores = {
- 'diversity': basic_diversity,
- 'technical': technical_terms / total_words if total_words > 0 else 0,
- 'narrative': narrative_words / total_words if total_words > 0 else 0,
- 'complexity': min(1.0, avg_sentence_length / 20) # Normalizado a 20 palabras
- }
-
- # Score final ponderado
- final_score = sum(weights[key] * scores[key] for key in weights)
-
- # Información adicional para diagnóstico
- details = {
- 'text_type': 'narrative' if scores['narrative'] > scores['technical'] else 'academic',
- 'scores': scores
- }
-
- return final_score, details
-
- except Exception as e:
- logger.error(f"Error en analyze_vocabulary_diversity: {str(e)}")
- return 0.0, {}
-
-def analyze_cohesion(doc):
- """Analiza la cohesión textual"""
- try:
- sentences = list(doc.sents)
- if len(sentences) < 2:
- logger.warning("Texto demasiado corto para análisis de cohesión")
- return 0.0
-
- # 1. Análisis de conexiones léxicas
- lexical_connections = 0
- total_possible_connections = 0
-
- for i in range(len(sentences)-1):
- # Obtener lemmas significativos (no stopwords)
- sent1_words = {token.lemma_ for token in sentences[i]
- if token.is_alpha and not token.is_stop}
- sent2_words = {token.lemma_ for token in sentences[i+1]
- if token.is_alpha and not token.is_stop}
-
- if sent1_words and sent2_words: # Verificar que ambos conjuntos no estén vacíos
- intersection = len(sent1_words.intersection(sent2_words))
- total_possible = min(len(sent1_words), len(sent2_words))
-
- if total_possible > 0:
- lexical_score = intersection / total_possible
- lexical_connections += lexical_score
- total_possible_connections += 1
-
- # 2. Análisis de conectores
- connector_count = 0
- connector_types = {
- 'CCONJ': 1.0, # Coordinantes
- 'SCONJ': 1.2, # Subordinantes
- 'ADV': 0.8 # Adverbios conectivos
- }
-
- for token in doc:
- if (token.pos_ in connector_types and
- token.dep_ in ['cc', 'mark', 'advmod'] and
- not token.is_stop):
- connector_count += connector_types[token.pos_]
-
- # 3. Cálculo de scores normalizados
- if total_possible_connections > 0:
- lexical_cohesion = lexical_connections / total_possible_connections
- else:
- lexical_cohesion = 0
-
- if len(sentences) > 1:
- connector_cohesion = min(1.0, connector_count / (len(sentences) - 1))
- else:
- connector_cohesion = 0
-
- # 4. Score final ponderado
- weights = {
- 'lexical': 0.7,
- 'connectors': 0.3
- }
-
- cohesion_score = (
- weights['lexical'] * lexical_cohesion +
- weights['connectors'] * connector_cohesion
- )
-
- # 5. Logging para diagnóstico
- logger.info(f"""
- Análisis de Cohesión:
- - Conexiones léxicas encontradas: {lexical_connections}
- - Conexiones posibles: {total_possible_connections}
- - Lexical cohesion score: {lexical_cohesion}
- - Conectores encontrados: {connector_count}
- - Connector cohesion score: {connector_cohesion}
- - Score final: {cohesion_score}
- """)
-
- return cohesion_score
-
- except Exception as e:
- logger.error(f"Error en analyze_cohesion: {str(e)}")
- return 0.0
-
-def analyze_structure(doc):
- try:
- if len(doc) == 0:
- return 0.0
-
- structure_scores = []
- for token in doc:
- if token.dep_ == 'ROOT':
- result = get_dependency_depths(token)
- structure_scores.append(result['final_score'])
-
- if not structure_scores:
- return 0.0
-
- return min(1.0, sum(structure_scores) / len(structure_scores))
-
- except Exception as e:
- logger.error(f"Error en analyze_structure: {str(e)}")
- return 0.0
-
-# Funciones auxiliares de análisis
-
-def get_dependency_depths(token, depth=0, analyzed_tokens=None):
- """
- Analiza la profundidad y calidad de las relaciones de dependencia.
-
- Args:
- token: Token a analizar
- depth: Profundidad actual en el árbol
- analyzed_tokens: Set para evitar ciclos en el análisis
-
- Returns:
- dict: Información detallada sobre las dependencias
- - depths: Lista de profundidades
- - relations: Diccionario con tipos de relaciones encontradas
- - complexity_score: Puntuación de complejidad
- """
- if analyzed_tokens is None:
- analyzed_tokens = set()
-
- # Evitar ciclos
- if token.i in analyzed_tokens:
- return {
- 'depths': [],
- 'relations': {},
- 'complexity_score': 0
- }
-
- analyzed_tokens.add(token.i)
-
- # Pesos para diferentes tipos de dependencias
- dependency_weights = {
- # Dependencias principales
- 'nsubj': 1.2, # Sujeto nominal
- 'obj': 1.1, # Objeto directo
- 'iobj': 1.1, # Objeto indirecto
- 'ROOT': 1.3, # Raíz
-
- # Modificadores
- 'amod': 0.8, # Modificador adjetival
- 'advmod': 0.8, # Modificador adverbial
- 'nmod': 0.9, # Modificador nominal
-
- # Estructuras complejas
- 'csubj': 1.4, # Cláusula como sujeto
- 'ccomp': 1.3, # Complemento clausal
- 'xcomp': 1.2, # Complemento clausal abierto
- 'advcl': 1.2, # Cláusula adverbial
-
- # Coordinación y subordinación
- 'conj': 1.1, # Conjunción
- 'cc': 0.7, # Coordinación
- 'mark': 0.8, # Marcador
-
- # Otros
- 'det': 0.5, # Determinante
- 'case': 0.5, # Caso
- 'punct': 0.1 # Puntuación
- }
-
- # Inicializar resultados
- current_result = {
- 'depths': [depth],
- 'relations': {token.dep_: 1},
- 'complexity_score': dependency_weights.get(token.dep_, 0.5) * (depth + 1)
- }
-
- # Analizar hijos recursivamente
- for child in token.children:
- child_result = get_dependency_depths(child, depth + 1, analyzed_tokens)
-
- # Combinar profundidades
- current_result['depths'].extend(child_result['depths'])
-
- # Combinar relaciones
- for rel, count in child_result['relations'].items():
- current_result['relations'][rel] = current_result['relations'].get(rel, 0) + count
-
- # Acumular score de complejidad
- current_result['complexity_score'] += child_result['complexity_score']
-
- # Calcular métricas adicionales
- current_result['max_depth'] = max(current_result['depths'])
- current_result['avg_depth'] = sum(current_result['depths']) / len(current_result['depths'])
- current_result['relation_diversity'] = len(current_result['relations'])
-
- # Calcular score ponderado por tipo de estructura
- structure_bonus = 0
-
- # Bonus por estructuras complejas
- if 'csubj' in current_result['relations'] or 'ccomp' in current_result['relations']:
- structure_bonus += 0.3
-
- # Bonus por coordinación balanceada
- if 'conj' in current_result['relations'] and 'cc' in current_result['relations']:
- structure_bonus += 0.2
-
- # Bonus por modificación rica
- if len(set(['amod', 'advmod', 'nmod']) & set(current_result['relations'])) >= 2:
- structure_bonus += 0.2
-
- current_result['final_score'] = (
- current_result['complexity_score'] * (1 + structure_bonus)
- )
-
- return current_result
-
-def normalize_score(value, metric_type,
- min_threshold=0.0, target_threshold=1.0,
- range_factor=2.0, optimal_length=None,
- optimal_connections=None, optimal_depth=None):
- """
- Normaliza un valor considerando umbrales específicos por tipo de métrica.
-
- Args:
- value: Valor a normalizar
- metric_type: Tipo de métrica ('vocabulary', 'structure', 'cohesion', 'clarity')
- min_threshold: Valor mínimo aceptable
- target_threshold: Valor objetivo
- range_factor: Factor para ajustar el rango
- optimal_length: Longitud óptima (opcional)
- optimal_connections: Número óptimo de conexiones (opcional)
- optimal_depth: Profundidad óptima de estructura (opcional)
-
- Returns:
- float: Valor normalizado entre 0 y 1
- """
- try:
- # Definir umbrales por tipo de métrica
- METRIC_THRESHOLDS = {
- 'vocabulary': {
- 'min': 0.60,
- 'target': 0.75,
- 'range_factor': 1.5
- },
- 'structure': {
- 'min': 0.65,
- 'target': 0.80,
- 'range_factor': 1.8
- },
- 'cohesion': {
- 'min': 0.55,
- 'target': 0.70,
- 'range_factor': 1.6
- },
- 'clarity': {
- 'min': 0.60,
- 'target': 0.75,
- 'range_factor': 1.7
- }
- }
-
- # Validar valores negativos o cero
- if value < 0:
- logger.warning(f"Valor negativo recibido: {value}")
- return 0.0
-
- # Manejar caso donde el valor es cero
- if value == 0:
- logger.warning("Valor cero recibido")
- return 0.0
-
- # Obtener umbrales específicos para el tipo de métrica
- thresholds = METRIC_THRESHOLDS.get(metric_type, {
- 'min': min_threshold,
- 'target': target_threshold,
- 'range_factor': range_factor
- })
-
- # Identificar el valor de referencia a usar
- if optimal_depth is not None:
- reference = optimal_depth
- elif optimal_connections is not None:
- reference = optimal_connections
- elif optimal_length is not None:
- reference = optimal_length
- else:
- reference = thresholds['target']
-
- # Validar valor de referencia
- if reference <= 0:
- logger.warning(f"Valor de referencia inválido: {reference}")
- return 0.0
-
- # Calcular score basado en umbrales
- if value < thresholds['min']:
- # Valor por debajo del mínimo
- score = (value / thresholds['min']) * 0.5 # Máximo 0.5 para valores bajo el mínimo
- elif value < thresholds['target']:
- # Valor entre mínimo y objetivo
- range_size = thresholds['target'] - thresholds['min']
- progress = (value - thresholds['min']) / range_size
- score = 0.5 + (progress * 0.5) # Escala entre 0.5 y 1.0
- else:
- # Valor alcanza o supera el objetivo
- score = 1.0
-
- # Penalizar valores muy por encima del objetivo
- if value > (thresholds['target'] * thresholds['range_factor']):
- excess = (value - thresholds['target']) / (thresholds['target'] * thresholds['range_factor'])
- score = max(0.7, 1.0 - excess) # No bajar de 0.7 para valores altos
-
- # Asegurar que el resultado esté entre 0 y 1
- return max(0.0, min(1.0, score))
-
- except Exception as e:
- logger.error(f"Error en normalize_score: {str(e)}")
- return 0.0
-
-
-# Funciones de generación de gráficos
-def generate_sentence_graphs(doc):
- """Genera visualizaciones de estructura de oraciones"""
- fig, ax = plt.subplots(figsize=(10, 6))
- # Implementar visualización
- plt.close()
- return fig
-
-def generate_word_connections(doc):
- """Genera red de conexiones de palabras"""
- fig, ax = plt.subplots(figsize=(10, 6))
- # Implementar visualización
- plt.close()
- return fig
-
-def generate_connection_paths(doc):
- """Genera patrones de conexión"""
- fig, ax = plt.subplots(figsize=(10, 6))
- # Implementar visualización
- plt.close()
- return fig
-
-def create_vocabulary_network(doc):
- """
- Genera el grafo de red de vocabulario.
- """
- G = nx.Graph()
-
- # Crear nodos para palabras significativas
- words = [token.text.lower() for token in doc if token.is_alpha and not token.is_stop]
- word_freq = Counter(words)
-
- # Añadir nodos con tamaño basado en frecuencia
- for word, freq in word_freq.items():
- G.add_node(word, size=freq)
-
- # Crear conexiones basadas en co-ocurrencia
- window_size = 5
- for i in range(len(words) - window_size):
- window = words[i:i+window_size]
- for w1, w2 in combinations(set(window), 2):
- if G.has_edge(w1, w2):
- G[w1][w2]['weight'] += 1
- else:
- G.add_edge(w1, w2, weight=1)
-
- # Crear visualización
- fig, ax = plt.subplots(figsize=(12, 8))
- pos = nx.spring_layout(G)
-
- # Dibujar nodos
- nx.draw_networkx_nodes(G, pos,
- node_size=[G.nodes[node]['size']*100 for node in G.nodes],
- node_color='lightblue',
- alpha=0.7)
-
- # Dibujar conexiones
- nx.draw_networkx_edges(G, pos,
- width=[G[u][v]['weight']*0.5 for u,v in G.edges],
- alpha=0.5)
-
- # Añadir etiquetas
- nx.draw_networkx_labels(G, pos)
-
- plt.title("Red de Vocabulario")
- plt.axis('off')
- return fig
-
-def create_syntax_complexity_graph(doc):
- """
- Genera el diagrama de arco de complejidad sintáctica.
- Muestra la estructura de dependencias con colores basados en la complejidad.
- """
- try:
- # Preparar datos para la visualización
- sentences = list(doc.sents)
- if not sentences:
- return None
-
- # Crear figura para el gráfico
- fig, ax = plt.subplots(figsize=(12, len(sentences) * 2))
-
- # Colores para diferentes niveles de profundidad
- depth_colors = plt.cm.viridis(np.linspace(0, 1, 6))
-
- y_offset = 0
- max_x = 0
-
- for sent in sentences:
- words = [token.text for token in sent]
- x_positions = range(len(words))
- max_x = max(max_x, len(words))
-
- # Dibujar palabras
- plt.plot(x_positions, [y_offset] * len(words), 'k-', alpha=0.2)
- plt.scatter(x_positions, [y_offset] * len(words), alpha=0)
-
- # Añadir texto
- for i, word in enumerate(words):
- plt.annotate(word, (i, y_offset), xytext=(0, -10),
- textcoords='offset points', ha='center')
-
- # Dibujar arcos de dependencia
- for token in sent:
- if token.dep_ != "ROOT":
- # Calcular profundidad de dependencia
- depth = 0
- current = token
- while current.head != current:
- depth += 1
- current = current.head
-
- # Determinar posiciones para el arco
- start = token.i - sent[0].i
- end = token.head.i - sent[0].i
-
- # Altura del arco basada en la distancia entre palabras
- height = 0.5 * abs(end - start)
-
- # Color basado en la profundidad
- color = depth_colors[min(depth, len(depth_colors)-1)]
-
- # Crear arco
- arc = patches.Arc((min(start, end) + abs(end - start)/2, y_offset),
- width=abs(end - start),
- height=height,
- angle=0,
- theta1=0,
- theta2=180,
- color=color,
- alpha=0.6)
- ax.add_patch(arc)
-
- y_offset -= 2
-
- # Configurar el gráfico
- plt.xlim(-1, max_x)
- plt.ylim(y_offset - 1, 1)
- plt.axis('off')
- plt.title("Complejidad Sintáctica")
-
- return fig
-
- except Exception as e:
- logger.error(f"Error en create_syntax_complexity_graph: {str(e)}")
- return None
-
-
-def create_cohesion_heatmap(doc):
- """Genera un mapa de calor que muestra la cohesión entre párrafos/oraciones."""
- try:
- sentences = list(doc.sents)
- n_sentences = len(sentences)
-
- if n_sentences < 2:
- return None
-
- similarity_matrix = np.zeros((n_sentences, n_sentences))
-
- for i in range(n_sentences):
- for j in range(n_sentences):
- sent1_lemmas = {token.lemma_ for token in sentences[i]
- if token.is_alpha and not token.is_stop}
- sent2_lemmas = {token.lemma_ for token in sentences[j]
- if token.is_alpha and not token.is_stop}
-
- if sent1_lemmas and sent2_lemmas:
- intersection = len(sent1_lemmas & sent2_lemmas) # Corregido aquí
- union = len(sent1_lemmas | sent2_lemmas) # Y aquí
- similarity_matrix[i, j] = intersection / union if union > 0 else 0
-
- # Crear visualización
- fig, ax = plt.subplots(figsize=(10, 8))
-
- sns.heatmap(similarity_matrix,
- cmap='YlOrRd',
- square=True,
- xticklabels=False,
- yticklabels=False,
- cbar_kws={'label': 'Cohesión'},
- ax=ax)
-
- plt.title("Mapa de Cohesión Textual")
- plt.xlabel("Oraciones")
- plt.ylabel("Oraciones")
-
- plt.tight_layout()
- return fig
-
- except Exception as e:
- logger.error(f"Error en create_cohesion_heatmap: {str(e)}")
- return None
+#v3/modules/studentact/current_situation_analysis.py
+
+import streamlit as st
+import matplotlib.pyplot as plt
+import networkx as nx
+import seaborn as sns
+from collections import Counter
+from itertools import combinations
+import numpy as np
+import matplotlib.patches as patches
+import logging
+
+# 2. Configuración básica del logging
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+ handlers=[
+ logging.StreamHandler(),
+ logging.FileHandler('app.log')
+ ]
+)
+
+# 3. Obtener el logger específico para este módulo
+logger = logging.getLogger(__name__)
+
+#########################################################################
+
+def correlate_metrics(scores):
+ """
+ Ajusta los scores para mantener correlaciones lógicas entre métricas.
+
+ Args:
+ scores: dict con scores iniciales de vocabulario, estructura, cohesión y claridad
+
+ Returns:
+ dict con scores ajustados
+ """
+ try:
+ # 1. Correlación estructura-cohesión
+ # La cohesión no puede ser menor que estructura * 0.7
+ min_cohesion = scores['structure']['normalized_score'] * 0.7
+ if scores['cohesion']['normalized_score'] < min_cohesion:
+ scores['cohesion']['normalized_score'] = min_cohesion
+
+ # 2. Correlación vocabulario-cohesión
+ # La cohesión léxica depende del vocabulario
+ vocab_influence = scores['vocabulary']['normalized_score'] * 0.6
+ scores['cohesion']['normalized_score'] = max(
+ scores['cohesion']['normalized_score'],
+ vocab_influence
+ )
+
+ # 3. Correlación cohesión-claridad
+ # La claridad no puede superar cohesión * 1.2
+ max_clarity = scores['cohesion']['normalized_score'] * 1.2
+ if scores['clarity']['normalized_score'] > max_clarity:
+ scores['clarity']['normalized_score'] = max_clarity
+
+ # 4. Correlación estructura-claridad
+ # La claridad no puede superar estructura * 1.1
+ struct_max_clarity = scores['structure']['normalized_score'] * 1.1
+ scores['clarity']['normalized_score'] = min(
+ scores['clarity']['normalized_score'],
+ struct_max_clarity
+ )
+
+ # Normalizar todos los scores entre 0 y 1
+ for metric in scores:
+ scores[metric]['normalized_score'] = max(0.0, min(1.0, scores[metric]['normalized_score']))
+
+ return scores
+
+ except Exception as e:
+ logger.error(f"Error en correlate_metrics: {str(e)}")
+ return scores
+
+##########################################################################
+
+def analyze_text_dimensions(doc):
+ """
+ Analiza las dimensiones principales del texto manteniendo correlaciones lógicas.
+ """
+ try:
+ # Obtener scores iniciales
+ vocab_score, vocab_details = analyze_vocabulary_diversity(doc)
+ struct_score = analyze_structure(doc)
+ cohesion_score = analyze_cohesion(doc)
+ clarity_score, clarity_details = analyze_clarity(doc)
+
+ # Crear diccionario de scores inicial
+ scores = {
+ 'vocabulary': {
+ 'normalized_score': vocab_score,
+ 'details': vocab_details
+ },
+ 'structure': {
+ 'normalized_score': struct_score,
+ 'details': None
+ },
+ 'cohesion': {
+ 'normalized_score': cohesion_score,
+ 'details': None
+ },
+ 'clarity': {
+ 'normalized_score': clarity_score,
+ 'details': clarity_details
+ }
+ }
+
+ # Ajustar correlaciones entre métricas
+ adjusted_scores = correlate_metrics(scores)
+
+ # Logging para diagnóstico
+ logger.info(f"""
+ Scores originales vs ajustados:
+ Vocabulario: {vocab_score:.2f} -> {adjusted_scores['vocabulary']['normalized_score']:.2f}
+ Estructura: {struct_score:.2f} -> {adjusted_scores['structure']['normalized_score']:.2f}
+ Cohesión: {cohesion_score:.2f} -> {adjusted_scores['cohesion']['normalized_score']:.2f}
+ Claridad: {clarity_score:.2f} -> {adjusted_scores['clarity']['normalized_score']:.2f}
+ """)
+
+ return adjusted_scores
+
+ except Exception as e:
+ logger.error(f"Error en analyze_text_dimensions: {str(e)}")
+ return {
+ 'vocabulary': {'normalized_score': 0.0, 'details': {}},
+ 'structure': {'normalized_score': 0.0, 'details': {}},
+ 'cohesion': {'normalized_score': 0.0, 'details': {}},
+ 'clarity': {'normalized_score': 0.0, 'details': {}}
+ }
+
+
+
+#############################################################################################
+
+def analyze_clarity(doc):
+ """
+ Analiza la claridad del texto considerando múltiples factores.
+ """
+ try:
+ sentences = list(doc.sents)
+ if not sentences:
+ return 0.0, {}
+
+ # 1. Longitud de oraciones
+ sentence_lengths = [len(sent) for sent in sentences]
+ avg_length = sum(sentence_lengths) / len(sentences)
+
+ # Normalizar usando los umbrales definidos para clarity
+ length_score = normalize_score(
+ value=avg_length,
+ metric_type='clarity',
+ optimal_length=20, # Una oración ideal tiene ~20 palabras
+ min_threshold=0.60, # Consistente con METRIC_THRESHOLDS
+ target_threshold=0.75 # Consistente con METRIC_THRESHOLDS
+ )
+
+ # 2. Análisis de conectores
+ connector_count = 0
+ connector_weights = {
+ 'CCONJ': 1.0, # Coordinantes
+ 'SCONJ': 1.2, # Subordinantes
+ 'ADV': 0.8 # Adverbios conectivos
+ }
+
+ for token in doc:
+ if token.pos_ in connector_weights and token.dep_ in ['cc', 'mark', 'advmod']:
+ connector_count += connector_weights[token.pos_]
+
+ # Normalizar conectores por oración
+ connectors_per_sentence = connector_count / len(sentences) if sentences else 0
+ connector_score = normalize_score(
+ value=connectors_per_sentence,
+ metric_type='clarity',
+ optimal_connections=1.5, # ~1.5 conectores por oración es óptimo
+ min_threshold=0.60,
+ target_threshold=0.75
+ )
+
+ # 3. Complejidad estructural
+ clause_count = 0
+ for sent in sentences:
+ verbs = [token for token in sent if token.pos_ == 'VERB']
+ clause_count += len(verbs)
+
+ complexity_raw = clause_count / len(sentences) if sentences else 0
+ complexity_score = normalize_score(
+ value=complexity_raw,
+ metric_type='clarity',
+ optimal_depth=2.0, # ~2 cláusulas por oración es óptimo
+ min_threshold=0.60,
+ target_threshold=0.75
+ )
+
+ # 4. Densidad léxica
+ content_words = len([token for token in doc if token.pos_ in ['NOUN', 'VERB', 'ADJ', 'ADV']])
+ total_words = len([token for token in doc if token.is_alpha])
+ density = content_words / total_words if total_words > 0 else 0
+
+ density_score = normalize_score(
+ value=density,
+ metric_type='clarity',
+ optimal_connections=0.6, # 60% de palabras de contenido es óptimo
+ min_threshold=0.60,
+ target_threshold=0.75
+ )
+
+ # Score final ponderado
+ weights = {
+ 'length': 0.3,
+ 'connectors': 0.3,
+ 'complexity': 0.2,
+ 'density': 0.2
+ }
+
+ clarity_score = (
+ weights['length'] * length_score +
+ weights['connectors'] * connector_score +
+ weights['complexity'] * complexity_score +
+ weights['density'] * density_score
+ )
+
+ details = {
+ 'length_score': length_score,
+ 'connector_score': connector_score,
+ 'complexity_score': complexity_score,
+ 'density_score': density_score,
+ 'avg_sentence_length': avg_length,
+ 'connectors_per_sentence': connectors_per_sentence,
+ 'density': density
+ }
+
+ # Agregar logging para diagnóstico
+ logger.info(f"""
+ Scores de Claridad:
+ - Longitud: {length_score:.2f} (avg={avg_length:.1f} palabras)
+ - Conectores: {connector_score:.2f} (avg={connectors_per_sentence:.1f} por oración)
+ - Complejidad: {complexity_score:.2f} (avg={complexity_raw:.1f} cláusulas)
+ - Densidad: {density_score:.2f} ({density*100:.1f}% palabras de contenido)
+ - Score Final: {clarity_score:.2f}
+ """)
+
+ return clarity_score, details
+
+ except Exception as e:
+ logger.error(f"Error en analyze_clarity: {str(e)}")
+ return 0.0, {}
+
+
+def analyze_vocabulary_diversity(doc):
+ """Análisis mejorado de la diversidad y calidad del vocabulario"""
+ try:
+ # 1. Análisis básico de diversidad
+ unique_lemmas = {token.lemma_ for token in doc if token.is_alpha}
+ total_words = len([token for token in doc if token.is_alpha])
+ basic_diversity = len(unique_lemmas) / total_words if total_words > 0 else 0
+
+ # 2. Análisis de registro
+ academic_words = 0
+ narrative_words = 0
+ technical_terms = 0
+
+ # Clasificar palabras por registro
+ for token in doc:
+ if token.is_alpha:
+ # Detectar términos académicos/técnicos
+ if token.pos_ in ['NOUN', 'VERB', 'ADJ']:
+ if any(parent.pos_ == 'NOUN' for parent in token.ancestors):
+ technical_terms += 1
+ # Detectar palabras narrativas
+ if token.pos_ in ['VERB', 'ADV'] and token.dep_ in ['ROOT', 'advcl']:
+ narrative_words += 1
+
+ # 3. Análisis de complejidad sintáctica
+ avg_sentence_length = sum(len(sent) for sent in doc.sents) / len(list(doc.sents))
+
+ # 4. Calcular score ponderado
+ weights = {
+ 'diversity': 0.3,
+ 'technical': 0.3,
+ 'narrative': 0.2,
+ 'complexity': 0.2
+ }
+
+ scores = {
+ 'diversity': basic_diversity,
+ 'technical': technical_terms / total_words if total_words > 0 else 0,
+ 'narrative': narrative_words / total_words if total_words > 0 else 0,
+ 'complexity': min(1.0, avg_sentence_length / 20) # Normalizado a 20 palabras
+ }
+
+ # Score final ponderado
+ final_score = sum(weights[key] * scores[key] for key in weights)
+
+ # Información adicional para diagnóstico
+ details = {
+ 'text_type': 'narrative' if scores['narrative'] > scores['technical'] else 'academic',
+ 'scores': scores
+ }
+
+ return final_score, details
+
+ except Exception as e:
+ logger.error(f"Error en analyze_vocabulary_diversity: {str(e)}")
+ return 0.0, {}
+
+def analyze_cohesion(doc):
+ """Analiza la cohesión textual"""
+ try:
+ sentences = list(doc.sents)
+ if len(sentences) < 2:
+ logger.warning("Texto demasiado corto para análisis de cohesión")
+ return 0.0
+
+ # 1. Análisis de conexiones léxicas
+ lexical_connections = 0
+ total_possible_connections = 0
+
+ for i in range(len(sentences)-1):
+ # Obtener lemmas significativos (no stopwords)
+ sent1_words = {token.lemma_ for token in sentences[i]
+ if token.is_alpha and not token.is_stop}
+ sent2_words = {token.lemma_ for token in sentences[i+1]
+ if token.is_alpha and not token.is_stop}
+
+ if sent1_words and sent2_words: # Verificar que ambos conjuntos no estén vacíos
+ intersection = len(sent1_words.intersection(sent2_words))
+ total_possible = min(len(sent1_words), len(sent2_words))
+
+ if total_possible > 0:
+ lexical_score = intersection / total_possible
+ lexical_connections += lexical_score
+ total_possible_connections += 1
+
+ # 2. Análisis de conectores
+ connector_count = 0
+ connector_types = {
+ 'CCONJ': 1.0, # Coordinantes
+ 'SCONJ': 1.2, # Subordinantes
+ 'ADV': 0.8 # Adverbios conectivos
+ }
+
+ for token in doc:
+ if (token.pos_ in connector_types and
+ token.dep_ in ['cc', 'mark', 'advmod'] and
+ not token.is_stop):
+ connector_count += connector_types[token.pos_]
+
+ # 3. Cálculo de scores normalizados
+ if total_possible_connections > 0:
+ lexical_cohesion = lexical_connections / total_possible_connections
+ else:
+ lexical_cohesion = 0
+
+ if len(sentences) > 1:
+ connector_cohesion = min(1.0, connector_count / (len(sentences) - 1))
+ else:
+ connector_cohesion = 0
+
+ # 4. Score final ponderado
+ weights = {
+ 'lexical': 0.7,
+ 'connectors': 0.3
+ }
+
+ cohesion_score = (
+ weights['lexical'] * lexical_cohesion +
+ weights['connectors'] * connector_cohesion
+ )
+
+ # 5. Logging para diagnóstico
+ logger.info(f"""
+ Análisis de Cohesión:
+ - Conexiones léxicas encontradas: {lexical_connections}
+ - Conexiones posibles: {total_possible_connections}
+ - Lexical cohesion score: {lexical_cohesion}
+ - Conectores encontrados: {connector_count}
+ - Connector cohesion score: {connector_cohesion}
+ - Score final: {cohesion_score}
+ """)
+
+ return cohesion_score
+
+ except Exception as e:
+ logger.error(f"Error en analyze_cohesion: {str(e)}")
+ return 0.0
+
+def analyze_structure(doc):
+ try:
+ if len(doc) == 0:
+ return 0.0
+
+ structure_scores = []
+ for token in doc:
+ if token.dep_ == 'ROOT':
+ result = get_dependency_depths(token)
+ structure_scores.append(result['final_score'])
+
+ if not structure_scores:
+ return 0.0
+
+ return min(1.0, sum(structure_scores) / len(structure_scores))
+
+ except Exception as e:
+ logger.error(f"Error en analyze_structure: {str(e)}")
+ return 0.0
+
+# Funciones auxiliares de análisis
+
+def get_dependency_depths(token, depth=0, analyzed_tokens=None):
+ """
+ Analiza la profundidad y calidad de las relaciones de dependencia.
+
+ Args:
+ token: Token a analizar
+ depth: Profundidad actual en el árbol
+ analyzed_tokens: Set para evitar ciclos en el análisis
+
+ Returns:
+ dict: Información detallada sobre las dependencias
+ - depths: Lista de profundidades
+ - relations: Diccionario con tipos de relaciones encontradas
+ - complexity_score: Puntuación de complejidad
+ """
+ if analyzed_tokens is None:
+ analyzed_tokens = set()
+
+ # Evitar ciclos
+ if token.i in analyzed_tokens:
+ return {
+ 'depths': [],
+ 'relations': {},
+ 'complexity_score': 0
+ }
+
+ analyzed_tokens.add(token.i)
+
+ # Pesos para diferentes tipos de dependencias
+ dependency_weights = {
+ # Dependencias principales
+ 'nsubj': 1.2, # Sujeto nominal
+ 'obj': 1.1, # Objeto directo
+ 'iobj': 1.1, # Objeto indirecto
+ 'ROOT': 1.3, # Raíz
+
+ # Modificadores
+ 'amod': 0.8, # Modificador adjetival
+ 'advmod': 0.8, # Modificador adverbial
+ 'nmod': 0.9, # Modificador nominal
+
+ # Estructuras complejas
+ 'csubj': 1.4, # Cláusula como sujeto
+ 'ccomp': 1.3, # Complemento clausal
+ 'xcomp': 1.2, # Complemento clausal abierto
+ 'advcl': 1.2, # Cláusula adverbial
+
+ # Coordinación y subordinación
+ 'conj': 1.1, # Conjunción
+ 'cc': 0.7, # Coordinación
+ 'mark': 0.8, # Marcador
+
+ # Otros
+ 'det': 0.5, # Determinante
+ 'case': 0.5, # Caso
+ 'punct': 0.1 # Puntuación
+ }
+
+ # Inicializar resultados
+ current_result = {
+ 'depths': [depth],
+ 'relations': {token.dep_: 1},
+ 'complexity_score': dependency_weights.get(token.dep_, 0.5) * (depth + 1)
+ }
+
+ # Analizar hijos recursivamente
+ for child in token.children:
+ child_result = get_dependency_depths(child, depth + 1, analyzed_tokens)
+
+ # Combinar profundidades
+ current_result['depths'].extend(child_result['depths'])
+
+ # Combinar relaciones
+ for rel, count in child_result['relations'].items():
+ current_result['relations'][rel] = current_result['relations'].get(rel, 0) + count
+
+ # Acumular score de complejidad
+ current_result['complexity_score'] += child_result['complexity_score']
+
+ # Calcular métricas adicionales
+ current_result['max_depth'] = max(current_result['depths'])
+ current_result['avg_depth'] = sum(current_result['depths']) / len(current_result['depths'])
+ current_result['relation_diversity'] = len(current_result['relations'])
+
+ # Calcular score ponderado por tipo de estructura
+ structure_bonus = 0
+
+ # Bonus por estructuras complejas
+ if 'csubj' in current_result['relations'] or 'ccomp' in current_result['relations']:
+ structure_bonus += 0.3
+
+ # Bonus por coordinación balanceada
+ if 'conj' in current_result['relations'] and 'cc' in current_result['relations']:
+ structure_bonus += 0.2
+
+ # Bonus por modificación rica
+ if len(set(['amod', 'advmod', 'nmod']) & set(current_result['relations'])) >= 2:
+ structure_bonus += 0.2
+
+ current_result['final_score'] = (
+ current_result['complexity_score'] * (1 + structure_bonus)
+ )
+
+ return current_result
+
+def normalize_score(value, metric_type,
+ min_threshold=0.0, target_threshold=1.0,
+ range_factor=2.0, optimal_length=None,
+ optimal_connections=None, optimal_depth=None):
+ """
+ Normaliza un valor considerando umbrales específicos por tipo de métrica.
+
+ Args:
+ value: Valor a normalizar
+ metric_type: Tipo de métrica ('vocabulary', 'structure', 'cohesion', 'clarity')
+ min_threshold: Valor mínimo aceptable
+ target_threshold: Valor objetivo
+ range_factor: Factor para ajustar el rango
+ optimal_length: Longitud óptima (opcional)
+ optimal_connections: Número óptimo de conexiones (opcional)
+ optimal_depth: Profundidad óptima de estructura (opcional)
+
+ Returns:
+ float: Valor normalizado entre 0 y 1
+ """
+ try:
+ # Definir umbrales por tipo de métrica
+ METRIC_THRESHOLDS = {
+ 'vocabulary': {
+ 'min': 0.60,
+ 'target': 0.75,
+ 'range_factor': 1.5
+ },
+ 'structure': {
+ 'min': 0.65,
+ 'target': 0.80,
+ 'range_factor': 1.8
+ },
+ 'cohesion': {
+ 'min': 0.55,
+ 'target': 0.70,
+ 'range_factor': 1.6
+ },
+ 'clarity': {
+ 'min': 0.60,
+ 'target': 0.75,
+ 'range_factor': 1.7
+ }
+ }
+
+ # Validar valores negativos o cero
+ if value < 0:
+ logger.warning(f"Valor negativo recibido: {value}")
+ return 0.0
+
+ # Manejar caso donde el valor es cero
+ if value == 0:
+ logger.warning("Valor cero recibido")
+ return 0.0
+
+ # Obtener umbrales específicos para el tipo de métrica
+ thresholds = METRIC_THRESHOLDS.get(metric_type, {
+ 'min': min_threshold,
+ 'target': target_threshold,
+ 'range_factor': range_factor
+ })
+
+ # Identificar el valor de referencia a usar
+ if optimal_depth is not None:
+ reference = optimal_depth
+ elif optimal_connections is not None:
+ reference = optimal_connections
+ elif optimal_length is not None:
+ reference = optimal_length
+ else:
+ reference = thresholds['target']
+
+ # Validar valor de referencia
+ if reference <= 0:
+ logger.warning(f"Valor de referencia inválido: {reference}")
+ return 0.0
+
+ # Calcular score basado en umbrales
+ if value < thresholds['min']:
+ # Valor por debajo del mínimo
+ score = (value / thresholds['min']) * 0.5 # Máximo 0.5 para valores bajo el mínimo
+ elif value < thresholds['target']:
+ # Valor entre mínimo y objetivo
+ range_size = thresholds['target'] - thresholds['min']
+ progress = (value - thresholds['min']) / range_size
+ score = 0.5 + (progress * 0.5) # Escala entre 0.5 y 1.0
+ else:
+ # Valor alcanza o supera el objetivo
+ score = 1.0
+
+ # Penalizar valores muy por encima del objetivo
+ if value > (thresholds['target'] * thresholds['range_factor']):
+ excess = (value - thresholds['target']) / (thresholds['target'] * thresholds['range_factor'])
+ score = max(0.7, 1.0 - excess) # No bajar de 0.7 para valores altos
+
+ # Asegurar que el resultado esté entre 0 y 1
+ return max(0.0, min(1.0, score))
+
+ except Exception as e:
+ logger.error(f"Error en normalize_score: {str(e)}")
+ return 0.0
+
+
+# Funciones de generación de gráficos
+def generate_sentence_graphs(doc):
+ """Genera visualizaciones de estructura de oraciones"""
+ fig, ax = plt.subplots(figsize=(10, 6))
+ # Implementar visualización
+ plt.close()
+ return fig
+
+def generate_word_connections(doc):
+ """Genera red de conexiones de palabras"""
+ fig, ax = plt.subplots(figsize=(10, 6))
+ # Implementar visualización
+ plt.close()
+ return fig
+
+def generate_connection_paths(doc):
+ """Genera patrones de conexión"""
+ fig, ax = plt.subplots(figsize=(10, 6))
+ # Implementar visualización
+ plt.close()
+ return fig
+
+def create_vocabulary_network(doc):
+ """
+ Genera el grafo de red de vocabulario.
+ """
+ G = nx.Graph()
+
+ # Crear nodos para palabras significativas
+ words = [token.text.lower() for token in doc if token.is_alpha and not token.is_stop]
+ word_freq = Counter(words)
+
+ # Añadir nodos con tamaño basado en frecuencia
+ for word, freq in word_freq.items():
+ G.add_node(word, size=freq)
+
+ # Crear conexiones basadas en co-ocurrencia
+ window_size = 5
+ for i in range(len(words) - window_size):
+ window = words[i:i+window_size]
+ for w1, w2 in combinations(set(window), 2):
+ if G.has_edge(w1, w2):
+ G[w1][w2]['weight'] += 1
+ else:
+ G.add_edge(w1, w2, weight=1)
+
+ # Crear visualización
+ fig, ax = plt.subplots(figsize=(12, 8))
+ pos = nx.spring_layout(G)
+
+ # Dibujar nodos
+ nx.draw_networkx_nodes(G, pos,
+ node_size=[G.nodes[node]['size']*100 for node in G.nodes],
+ node_color='lightblue',
+ alpha=0.7)
+
+ # Dibujar conexiones
+ nx.draw_networkx_edges(G, pos,
+ width=[G[u][v]['weight']*0.5 for u,v in G.edges],
+ alpha=0.5)
+
+ # Añadir etiquetas
+ nx.draw_networkx_labels(G, pos)
+
+ plt.title("Red de Vocabulario")
+ plt.axis('off')
+ return fig
+
+def create_syntax_complexity_graph(doc):
+ """
+ Genera el diagrama de arco de complejidad sintáctica.
+ Muestra la estructura de dependencias con colores basados en la complejidad.
+ """
+ try:
+ # Preparar datos para la visualización
+ sentences = list(doc.sents)
+ if not sentences:
+ return None
+
+ # Crear figura para el gráfico
+ fig, ax = plt.subplots(figsize=(12, len(sentences) * 2))
+
+ # Colores para diferentes niveles de profundidad
+ depth_colors = plt.cm.viridis(np.linspace(0, 1, 6))
+
+ y_offset = 0
+ max_x = 0
+
+ for sent in sentences:
+ words = [token.text for token in sent]
+ x_positions = range(len(words))
+ max_x = max(max_x, len(words))
+
+ # Dibujar palabras
+ plt.plot(x_positions, [y_offset] * len(words), 'k-', alpha=0.2)
+ plt.scatter(x_positions, [y_offset] * len(words), alpha=0)
+
+ # Añadir texto
+ for i, word in enumerate(words):
+ plt.annotate(word, (i, y_offset), xytext=(0, -10),
+ textcoords='offset points', ha='center')
+
+ # Dibujar arcos de dependencia
+ for token in sent:
+ if token.dep_ != "ROOT":
+ # Calcular profundidad de dependencia
+ depth = 0
+ current = token
+ while current.head != current:
+ depth += 1
+ current = current.head
+
+ # Determinar posiciones para el arco
+ start = token.i - sent[0].i
+ end = token.head.i - sent[0].i
+
+ # Altura del arco basada en la distancia entre palabras
+ height = 0.5 * abs(end - start)
+
+ # Color basado en la profundidad
+ color = depth_colors[min(depth, len(depth_colors)-1)]
+
+ # Crear arco
+ arc = patches.Arc((min(start, end) + abs(end - start)/2, y_offset),
+ width=abs(end - start),
+ height=height,
+ angle=0,
+ theta1=0,
+ theta2=180,
+ color=color,
+ alpha=0.6)
+ ax.add_patch(arc)
+
+ y_offset -= 2
+
+ # Configurar el gráfico
+ plt.xlim(-1, max_x)
+ plt.ylim(y_offset - 1, 1)
+ plt.axis('off')
+ plt.title("Complejidad Sintáctica")
+
+ return fig
+
+ except Exception as e:
+ logger.error(f"Error en create_syntax_complexity_graph: {str(e)}")
+ return None
+
+
+def create_cohesion_heatmap(doc):
+ """Genera un mapa de calor que muestra la cohesión entre párrafos/oraciones."""
+ try:
+ sentences = list(doc.sents)
+ n_sentences = len(sentences)
+
+ if n_sentences < 2:
+ return None
+
+ similarity_matrix = np.zeros((n_sentences, n_sentences))
+
+ for i in range(n_sentences):
+ for j in range(n_sentences):
+ sent1_lemmas = {token.lemma_ for token in sentences[i]
+ if token.is_alpha and not token.is_stop}
+ sent2_lemmas = {token.lemma_ for token in sentences[j]
+ if token.is_alpha and not token.is_stop}
+
+ if sent1_lemmas and sent2_lemmas:
+ intersection = len(sent1_lemmas & sent2_lemmas) # Corregido aquí
+ union = len(sent1_lemmas | sent2_lemmas) # Y aquí
+ similarity_matrix[i, j] = intersection / union if union > 0 else 0
+
+ # Crear visualización
+ fig, ax = plt.subplots(figsize=(10, 8))
+
+ sns.heatmap(similarity_matrix,
+ cmap='YlOrRd',
+ square=True,
+ xticklabels=False,
+ yticklabels=False,
+ cbar_kws={'label': 'Cohesión'},
+ ax=ax)
+
+ plt.title("Mapa de Cohesión Textual")
+ plt.xlabel("Oraciones")
+ plt.ylabel("Oraciones")
+
+ plt.tight_layout()
+ return fig
+
+ except Exception as e:
+ logger.error(f"Error en create_cohesion_heatmap: {str(e)}")
+ return None
diff --git a/modules/studentact/current_situation_interface--FAIL.py b/modules/studentact/current_situation_interface--FAIL.py
index 1542732c53c4e01730d333ef174bd83699641469..cae6e5be1412c8006108b6c8c77719bd5d684e63 100644
--- a/modules/studentact/current_situation_interface--FAIL.py
+++ b/modules/studentact/current_situation_interface--FAIL.py
@@ -1,608 +1,608 @@
-# modules/studentact/current_situation_interface.py
-
-import streamlit as st
-import logging
-from ..utils.widget_utils import generate_unique_key
-import matplotlib.pyplot as plt
-import numpy as np
-
-from ..database.current_situation_mongo_db import store_current_situation_result
-
-from ..database.writing_progress_mongo_db import (
- store_writing_baseline,
- store_writing_progress,
- get_writing_baseline,
- get_writing_progress,
- get_latest_writing_metrics
-)
-
-from .current_situation_analysis import (
- analyze_text_dimensions,
- analyze_clarity,
- analyze_vocabulary_diversity,
- analyze_cohesion,
- analyze_structure,
- get_dependency_depths,
- normalize_score,
- generate_sentence_graphs,
- generate_word_connections,
- generate_connection_paths,
- create_vocabulary_network,
- create_syntax_complexity_graph,
- create_cohesion_heatmap
-)
-
-# Configuración del estilo de matplotlib para el gráfico de radar
-plt.rcParams['font.family'] = 'sans-serif'
-plt.rcParams['axes.grid'] = True
-plt.rcParams['axes.spines.top'] = False
-plt.rcParams['axes.spines.right'] = False
-
-logger = logging.getLogger(__name__)
-####################################
-
-TEXT_TYPES = {
- 'academic_article': {
- 'name': 'Artículo Académico',
- 'thresholds': {
- 'vocabulary': {'min': 0.70, 'target': 0.85},
- 'structure': {'min': 0.75, 'target': 0.90},
- 'cohesion': {'min': 0.65, 'target': 0.80},
- 'clarity': {'min': 0.70, 'target': 0.85}
- }
- },
- 'student_essay': {
- 'name': 'Trabajo Universitario',
- 'thresholds': {
- 'vocabulary': {'min': 0.60, 'target': 0.75},
- 'structure': {'min': 0.65, 'target': 0.80},
- 'cohesion': {'min': 0.55, 'target': 0.70},
- 'clarity': {'min': 0.60, 'target': 0.75}
- }
- },
- 'general_communication': {
- 'name': 'Comunicación General',
- 'thresholds': {
- 'vocabulary': {'min': 0.50, 'target': 0.65},
- 'structure': {'min': 0.55, 'target': 0.70},
- 'cohesion': {'min': 0.45, 'target': 0.60},
- 'clarity': {'min': 0.50, 'target': 0.65}
- }
- }
-}
-####################################
-
-ANALYSIS_DIMENSION_MAPPING = {
- 'morphosyntactic': {
- 'primary': ['vocabulary', 'clarity'],
- 'secondary': ['structure'],
- 'tools': ['arc_diagrams', 'word_repetition']
- },
- 'semantic': {
- 'primary': ['cohesion', 'structure'],
- 'secondary': ['vocabulary'],
- 'tools': ['concept_graphs', 'semantic_networks']
- },
- 'discourse': {
- 'primary': ['cohesion', 'structure'],
- 'secondary': ['clarity'],
- 'tools': ['comparative_analysis']
- }
-}
-
-##############################################################################
-# FUNCIÓN PRINCIPAL
-##############################################################################
-def display_current_situation_interface(lang_code, nlp_models, t):
- """
- TAB:
- - Expander con radio para tipo de texto
- Contenedor-1 con expanders:
- - Expander "Métricas de la línea base"
- - Expander "Métricas de la iteración"
- Contenedor-2 (2 columnas):
- - Col1: Texto base
- - Col2: Texto iteración
- Al final, Recomendaciones en un expander (una sola “fila”).
- """
-
- # --- Inicializar session_state ---
- if 'base_text' not in st.session_state:
- st.session_state.base_text = ""
- if 'iter_text' not in st.session_state:
- st.session_state.iter_text = ""
- if 'base_metrics' not in st.session_state:
- st.session_state.base_metrics = {}
- if 'iter_metrics' not in st.session_state:
- st.session_state.iter_metrics = {}
- if 'show_base' not in st.session_state:
- st.session_state.show_base = False
- if 'show_iter' not in st.session_state:
- st.session_state.show_iter = False
-
- # Creamos un tab
- tabs = st.tabs(["Análisis de Texto"])
- with tabs[0]:
- # [1] Expander con radio para seleccionar tipo de texto
- with st.expander("Selecciona el tipo de texto", expanded=True):
- text_type = st.radio(
- "¿Qué tipo de texto quieres analizar?",
- options=list(TEXT_TYPES.keys()),
- format_func=lambda x: TEXT_TYPES[x]['name'],
- index=0
- )
- st.session_state.current_text_type = text_type
-
- st.markdown("---")
-
- # ---------------------------------------------------------------------
- # CONTENEDOR-1: Expanders para métricas base e iteración
- # ---------------------------------------------------------------------
- with st.container():
- # --- Expander para la línea base ---
- with st.expander("Métricas de la línea base", expanded=False):
- if st.session_state.show_base and st.session_state.base_metrics:
- # Mostramos los valores reales
- display_metrics_in_one_row(st.session_state.base_metrics, text_type)
- else:
- # Mostramos la maqueta vacía
- display_empty_metrics_row()
-
- # --- Expander para la iteración ---
- with st.expander("Métricas de la iteración", expanded=False):
- if st.session_state.show_iter and st.session_state.iter_metrics:
- display_metrics_in_one_row(st.session_state.iter_metrics, text_type)
- else:
- display_empty_metrics_row()
-
- st.markdown("---")
-
- # ---------------------------------------------------------------------
- # CONTENEDOR-2: 2 columnas (texto base | texto iteración)
- # ---------------------------------------------------------------------
- with st.container():
- col_left, col_right = st.columns(2)
-
- # Columna izquierda: Texto base
- with col_left:
- st.markdown("**Texto base**")
- text_base = st.text_area(
- label="",
- value=st.session_state.base_text,
- key="text_base_area",
- placeholder="Pega aquí tu texto base",
- )
- if st.button("Analizar Base"):
- with st.spinner("Analizando texto base..."):
- doc = nlp_models[lang_code](text_base)
- metrics = analyze_text_dimensions(doc)
-
- st.session_state.base_text = text_base
- st.session_state.base_metrics = metrics
- st.session_state.show_base = True
- # Al analizar base, reiniciamos la iteración
- st.session_state.show_iter = False
-
- # Columna derecha: Texto iteración
- with col_right:
- st.markdown("**Texto de iteración**")
- text_iter = st.text_area(
- label="",
- value=st.session_state.iter_text,
- key="text_iter_area",
- placeholder="Edita y mejora tu texto...",
- disabled=not st.session_state.show_base
- )
- if st.button("Analizar Iteración", disabled=not st.session_state.show_base):
- with st.spinner("Analizando iteración..."):
- doc = nlp_models[lang_code](text_iter)
- metrics = analyze_text_dimensions(doc)
-
- st.session_state.iter_text = text_iter
- st.session_state.iter_metrics = metrics
- st.session_state.show_iter = True
-
- # ---------------------------------------------------------------------
- # Recomendaciones al final en un expander (una sola “fila”)
- # ---------------------------------------------------------------------
- if st.session_state.show_iter:
- with st.expander("Recomendaciones", expanded=False):
- reco_list = []
- for dimension, values in st.session_state.iter_metrics.items():
- score = values['normalized_score']
- target = TEXT_TYPES[text_type]['thresholds'][dimension]['target']
- if score < target:
- # Aquí, en lugar de get_dimension_suggestions, unificamos con:
- suggestions = suggest_improvement_tools_list(dimension)
- reco_list.extend(suggestions)
-
- if reco_list:
- # Todas en una sola línea
- st.write(" | ".join(reco_list))
- else:
- st.info("¡No hay recomendaciones! Todas las métricas superan la meta.")
-
-
-
-
-
-
-
-#Funciones de visualización ##################################
-############################################################
-# Funciones de visualización para las métricas
-############################################################
-
-def display_metrics_in_one_row(metrics, text_type):
- """
- Muestra las cuatro dimensiones (Vocabulario, Estructura, Cohesión, Claridad)
- en una sola línea, usando 4 columnas con ancho uniforme.
- """
- thresholds = TEXT_TYPES[text_type]['thresholds']
- dimensions = ["vocabulary", "structure", "cohesion", "clarity"]
-
- col1, col2, col3, col4 = st.columns([1,1,1,1])
- cols = [col1, col2, col3, col4]
-
- for dim, col in zip(dimensions, cols):
- score = metrics[dim]['normalized_score']
- target = thresholds[dim]['target']
- min_val = thresholds[dim]['min']
-
- if score < min_val:
- status = "⚠️ Por mejorar"
- color = "inverse"
- elif score < target:
- status = "📈 Aceptable"
- color = "off"
- else:
- status = "✅ Óptimo"
- color = "normal"
-
- with col:
- col.metric(
- label=dim.capitalize(),
- value=f"{score:.2f}",
- delta=f"{status} (Meta: {target:.2f})",
- delta_color=color,
- border=True
- )
-
-
-# -------------------------------------------------------------------------
-# Función que muestra una fila de 4 columnas “vacías”
-# -------------------------------------------------------------------------
-def display_empty_metrics_row():
- """
- Muestra una fila de 4 columnas vacías (Vocabulario, Estructura, Cohesión, Claridad).
- Cada columna se dibuja con st.metric en blanco (“-”).
- """
- empty_cols = st.columns([1,1,1,1])
- labels = ["Vocabulario", "Estructura", "Cohesión", "Claridad"]
-
- for col, lbl in zip(empty_cols, labels):
- with col:
- col.metric(
- label=lbl,
- value="-",
- delta="",
- border=True
- )
-
-
-
-####################################################################
-
-def display_metrics_analysis(metrics, text_type=None):
- """
- Muestra los resultados del análisis: métricas verticalmente y gráfico radar.
- """
- try:
- # Usar valor por defecto si no se especifica tipo
- text_type = text_type or 'student_essay'
-
- # Obtener umbrales según el tipo de texto
- thresholds = TEXT_TYPES[text_type]['thresholds']
-
- # Crear dos columnas para las métricas y el gráfico
- metrics_col, graph_col = st.columns([1, 1.5])
-
- # Columna de métricas
- with metrics_col:
- metrics_config = [
- {
- 'label': "Vocabulario",
- 'key': 'vocabulary',
- 'value': metrics['vocabulary']['normalized_score'],
- 'help': "Riqueza y variedad del vocabulario",
- 'thresholds': thresholds['vocabulary']
- },
- {
- 'label': "Estructura",
- 'key': 'structure',
- 'value': metrics['structure']['normalized_score'],
- 'help': "Organización y complejidad de oraciones",
- 'thresholds': thresholds['structure']
- },
- {
- 'label': "Cohesión",
- 'key': 'cohesion',
- 'value': metrics['cohesion']['normalized_score'],
- 'help': "Conexión y fluidez entre ideas",
- 'thresholds': thresholds['cohesion']
- },
- {
- 'label': "Claridad",
- 'key': 'clarity',
- 'value': metrics['clarity']['normalized_score'],
- 'help': "Facilidad de comprensión del texto",
- 'thresholds': thresholds['clarity']
- }
- ]
-
- # Mostrar métricas
- for metric in metrics_config:
- value = metric['value']
- if value < metric['thresholds']['min']:
- status = "⚠️ Por mejorar"
- color = "inverse"
- elif value < metric['thresholds']['target']:
- status = "📈 Aceptable"
- color = "off"
- else:
- status = "✅ Óptimo"
- color = "normal"
-
- st.metric(
- metric['label'],
- f"{value:.2f}",
- f"{status} (Meta: {metric['thresholds']['target']:.2f})",
- delta_color=color,
- help=metric['help']
- )
- st.markdown("
", unsafe_allow_html=True)
-
- except Exception as e:
- logger.error(f"Error mostrando resultados: {str(e)}")
- st.error("Error al mostrar los resultados")
-
-def display_comparison_results(baseline_metrics, current_metrics):
- """Muestra comparación entre línea base y métricas actuales"""
-
- # Crear columnas para métricas y gráfico
- metrics_col, graph_col = st.columns([1, 1.5])
-
- with metrics_col:
- for dimension in ['vocabulary', 'structure', 'cohesion', 'clarity']:
- baseline = baseline_metrics[dimension]['normalized_score']
- current = current_metrics[dimension]['normalized_score']
- delta = current - baseline
-
- st.metric(
- dimension.title(),
- f"{current:.2f}",
- f"{delta:+.2f}",
- delta_color="normal" if delta >= 0 else "inverse"
- )
-
- # Sugerir herramientas de mejora
- if delta < 0:
- suggest_improvement_tools(dimension)
-
- with graph_col:
- display_radar_chart_comparison(
- baseline_metrics,
- current_metrics
- )
-
-def display_metrics_and_suggestions(metrics, text_type, title, show_suggestions=False):
- """
- Muestra métricas y opcionalmente sugerencias de mejora.
- Args:
- metrics: Diccionario con las métricas analizadas
- text_type: Tipo de texto seleccionado
- title: Título para las métricas ("Base" o "Iteración")
- show_suggestions: Booleano para mostrar sugerencias
- """
- try:
- thresholds = TEXT_TYPES[text_type]['thresholds']
-
- st.markdown(f"### Métricas {title}")
-
- for dimension, values in metrics.items():
- score = values['normalized_score']
- target = thresholds[dimension]['target']
- min_val = thresholds[dimension]['min']
-
- # Determinar estado y color
- if score < min_val:
- status = "⚠️ Por mejorar"
- color = "inverse"
- elif score < target:
- status = "📈 Aceptable"
- color = "off"
- else:
- status = "✅ Óptimo"
- color = "normal"
-
- # Mostrar métrica
- st.metric(
- dimension.title(),
- f"{score:.2f}",
- f"{status} (Meta: {target:.2f})",
- delta_color=color,
- help=f"Meta: {target:.2f}, Mínimo: {min_val:.2f}"
- )
-
- # Mostrar sugerencias si es necesario
- if show_suggestions and score < target:
- suggest_improvement_tools(dimension)
-
- # Agregar espacio entre métricas
- st.markdown("
", unsafe_allow_html=True)
-
- except Exception as e:
- logger.error(f"Error mostrando métricas: {str(e)}")
- st.error("Error al mostrar métricas")
-
-def display_radar_chart(metrics_config, thresholds, baseline_metrics=None):
- """
- Muestra el gráfico radar con los resultados.
- Args:
- metrics_config: Configuración actual de métricas
- thresholds: Umbrales para las métricas
- baseline_metrics: Métricas de línea base (opcional)
- """
- try:
- # Preparar datos para el gráfico
- categories = [m['label'] for m in metrics_config]
- values_current = [m['value'] for m in metrics_config]
- min_values = [m['thresholds']['min'] for m in metrics_config]
- target_values = [m['thresholds']['target'] for m in metrics_config]
-
- # Crear y configurar gráfico
- fig = plt.figure(figsize=(8, 8))
- ax = fig.add_subplot(111, projection='polar')
-
- # Configurar radar
- angles = [n / float(len(categories)) * 2 * np.pi for n in range(len(categories))]
- angles += angles[:1]
- values_current += values_current[:1]
- min_values += min_values[:1]
- target_values += target_values[:1]
-
- # Configurar ejes
- ax.set_xticks(angles[:-1])
- ax.set_xticklabels(categories, fontsize=10)
- circle_ticks = np.arange(0, 1.1, 0.2)
- ax.set_yticks(circle_ticks)
- ax.set_yticklabels([f'{tick:.1f}' for tick in circle_ticks], fontsize=8)
- ax.set_ylim(0, 1)
-
- # Dibujar áreas de umbrales
- ax.plot(angles, min_values, '#e74c3c', linestyle='--', linewidth=1,
- label='Mínimo', alpha=0.5)
- ax.plot(angles, target_values, '#2ecc71', linestyle='--', linewidth=1,
- label='Meta', alpha=0.5)
- ax.fill_between(angles, target_values, [1]*len(angles),
- color='#2ecc71', alpha=0.1)
- ax.fill_between(angles, [0]*len(angles), min_values,
- color='#e74c3c', alpha=0.1)
-
- # Si hay línea base, dibujarla primero
- if baseline_metrics is not None:
- values_baseline = [baseline_metrics[m['key']]['normalized_score']
- for m in metrics_config]
- values_baseline += values_baseline[:1]
- ax.plot(angles, values_baseline, '#888888', linewidth=2,
- label='Línea base', linestyle='--')
- ax.fill(angles, values_baseline, '#888888', alpha=0.1)
-
- # Dibujar valores actuales
- label = 'Actual' if baseline_metrics else 'Tu escritura'
- color = '#3498db' if baseline_metrics else '#3498db'
-
- ax.plot(angles, values_current, color, linewidth=2, label=label)
- ax.fill(angles, values_current, color, alpha=0.2)
-
- # Ajustar leyenda
- legend_handles = []
- if baseline_metrics:
- legend_handles.extend([
- plt.Line2D([], [], color='#888888', linestyle='--',
- label='Línea base'),
- plt.Line2D([], [], color='#3498db', label='Actual')
- ])
- else:
- legend_handles.extend([
- plt.Line2D([], [], color='#3498db', label='Tu escritura')
- ])
-
- legend_handles.extend([
- plt.Line2D([], [], color='#e74c3c', linestyle='--', label='Mínimo'),
- plt.Line2D([], [], color='#2ecc71', linestyle='--', label='Meta')
- ])
-
- ax.legend(
- handles=legend_handles,
- loc='upper right',
- bbox_to_anchor=(1.3, 1.1),
- fontsize=10,
- frameon=True,
- facecolor='white',
- edgecolor='none',
- shadow=True
- )
-
- plt.tight_layout()
- st.pyplot(fig)
- plt.close()
-
- except Exception as e:
- logger.error(f"Error mostrando gráfico radar: {str(e)}")
- st.error("Error al mostrar el gráfico")
-
-#Funciones auxiliares ##################################
-
-
-############################################################
-# Unificamos la lógica de sugerencias en una función
-############################################################
-def suggest_improvement_tools_list(dimension):
- """
- Retorna en forma de lista las herramientas sugeridas
- basadas en 'ANALYSIS_DIMENSION_MAPPING'.
- """
- suggestions = []
- for analysis, mapping in ANALYSIS_DIMENSION_MAPPING.items():
- # Verificamos si la dimensión está en primary o secondary
- if dimension in mapping['primary'] or dimension in mapping['secondary']:
- suggestions.extend(mapping['tools'])
- # Si no hay nada, al menos retornamos un placeholder
- return suggestions if suggestions else ["Sin sugerencias específicas."]
-
-
-def prepare_metrics_config(metrics, text_type='student_essay'):
- """
- Prepara la configuración de métricas en el mismo formato que display_results.
- Args:
- metrics: Diccionario con las métricas analizadas
- text_type: Tipo de texto para los umbrales
- Returns:
- list: Lista de configuraciones de métricas
- """
- # Obtener umbrales según el tipo de texto
- thresholds = TEXT_TYPES[text_type]['thresholds']
-
- # Usar la misma estructura que en display_results
- return [
- {
- 'label': "Vocabulario",
- 'key': 'vocabulary',
- 'value': metrics['vocabulary']['normalized_score'],
- 'help': "Riqueza y variedad del vocabulario",
- 'thresholds': thresholds['vocabulary']
- },
- {
- 'label': "Estructura",
- 'key': 'structure',
- 'value': metrics['structure']['normalized_score'],
- 'help': "Organización y complejidad de oraciones",
- 'thresholds': thresholds['structure']
- },
- {
- 'label': "Cohesión",
- 'key': 'cohesion',
- 'value': metrics['cohesion']['normalized_score'],
- 'help': "Conexión y fluidez entre ideas",
- 'thresholds': thresholds['cohesion']
- },
- {
- 'label': "Claridad",
- 'key': 'clarity',
- 'value': metrics['clarity']['normalized_score'],
- 'help': "Facilidad de comprensión del texto",
- 'thresholds': thresholds['clarity']
- }
- ]
-
+# modules/studentact/current_situation_interface.py
+
+import streamlit as st
+import logging
+from ..utils.widget_utils import generate_unique_key
+import matplotlib.pyplot as plt
+import numpy as np
+
+from ..database.current_situation_mongo_db import store_current_situation_result
+
+from ..database.writing_progress_mongo_db import (
+ store_writing_baseline,
+ store_writing_progress,
+ get_writing_baseline,
+ get_writing_progress,
+ get_latest_writing_metrics
+)
+
+from .current_situation_analysis import (
+ analyze_text_dimensions,
+ analyze_clarity,
+ analyze_vocabulary_diversity,
+ analyze_cohesion,
+ analyze_structure,
+ get_dependency_depths,
+ normalize_score,
+ generate_sentence_graphs,
+ generate_word_connections,
+ generate_connection_paths,
+ create_vocabulary_network,
+ create_syntax_complexity_graph,
+ create_cohesion_heatmap
+)
+
+# Configuración del estilo de matplotlib para el gráfico de radar
+plt.rcParams['font.family'] = 'sans-serif'
+plt.rcParams['axes.grid'] = True
+plt.rcParams['axes.spines.top'] = False
+plt.rcParams['axes.spines.right'] = False
+
+logger = logging.getLogger(__name__)
+####################################
+
+TEXT_TYPES = {
+ 'academic_article': {
+ 'name': 'Artículo Académico',
+ 'thresholds': {
+ 'vocabulary': {'min': 0.70, 'target': 0.85},
+ 'structure': {'min': 0.75, 'target': 0.90},
+ 'cohesion': {'min': 0.65, 'target': 0.80},
+ 'clarity': {'min': 0.70, 'target': 0.85}
+ }
+ },
+ 'student_essay': {
+ 'name': 'Trabajo Universitario',
+ 'thresholds': {
+ 'vocabulary': {'min': 0.60, 'target': 0.75},
+ 'structure': {'min': 0.65, 'target': 0.80},
+ 'cohesion': {'min': 0.55, 'target': 0.70},
+ 'clarity': {'min': 0.60, 'target': 0.75}
+ }
+ },
+ 'general_communication': {
+ 'name': 'Comunicación General',
+ 'thresholds': {
+ 'vocabulary': {'min': 0.50, 'target': 0.65},
+ 'structure': {'min': 0.55, 'target': 0.70},
+ 'cohesion': {'min': 0.45, 'target': 0.60},
+ 'clarity': {'min': 0.50, 'target': 0.65}
+ }
+ }
+}
+####################################
+
+ANALYSIS_DIMENSION_MAPPING = {
+ 'morphosyntactic': {
+ 'primary': ['vocabulary', 'clarity'],
+ 'secondary': ['structure'],
+ 'tools': ['arc_diagrams', 'word_repetition']
+ },
+ 'semantic': {
+ 'primary': ['cohesion', 'structure'],
+ 'secondary': ['vocabulary'],
+ 'tools': ['concept_graphs', 'semantic_networks']
+ },
+ 'discourse': {
+ 'primary': ['cohesion', 'structure'],
+ 'secondary': ['clarity'],
+ 'tools': ['comparative_analysis']
+ }
+}
+
+##############################################################################
+# FUNCIÓN PRINCIPAL
+##############################################################################
+def display_current_situation_interface(lang_code, nlp_models, t):
+ """
+ TAB:
+ - Expander con radio para tipo de texto
+ Contenedor-1 con expanders:
+ - Expander "Métricas de la línea base"
+ - Expander "Métricas de la iteración"
+ Contenedor-2 (2 columnas):
+ - Col1: Texto base
+ - Col2: Texto iteración
+ Al final, Recomendaciones en un expander (una sola “fila”).
+ """
+
+ # --- Inicializar session_state ---
+ if 'base_text' not in st.session_state:
+ st.session_state.base_text = ""
+ if 'iter_text' not in st.session_state:
+ st.session_state.iter_text = ""
+ if 'base_metrics' not in st.session_state:
+ st.session_state.base_metrics = {}
+ if 'iter_metrics' not in st.session_state:
+ st.session_state.iter_metrics = {}
+ if 'show_base' not in st.session_state:
+ st.session_state.show_base = False
+ if 'show_iter' not in st.session_state:
+ st.session_state.show_iter = False
+
+ # Creamos un tab
+ tabs = st.tabs(["Análisis de Texto"])
+ with tabs[0]:
+ # [1] Expander con radio para seleccionar tipo de texto
+ with st.expander("Selecciona el tipo de texto", expanded=True):
+ text_type = st.radio(
+ "¿Qué tipo de texto quieres analizar?",
+ options=list(TEXT_TYPES.keys()),
+ format_func=lambda x: TEXT_TYPES[x]['name'],
+ index=0
+ )
+ st.session_state.current_text_type = text_type
+
+ st.markdown("---")
+
+ # ---------------------------------------------------------------------
+ # CONTENEDOR-1: Expanders para métricas base e iteración
+ # ---------------------------------------------------------------------
+ with st.container():
+ # --- Expander para la línea base ---
+ with st.expander("Métricas de la línea base", expanded=False):
+ if st.session_state.show_base and st.session_state.base_metrics:
+ # Mostramos los valores reales
+ display_metrics_in_one_row(st.session_state.base_metrics, text_type)
+ else:
+ # Mostramos la maqueta vacía
+ display_empty_metrics_row()
+
+ # --- Expander para la iteración ---
+ with st.expander("Métricas de la iteración", expanded=False):
+ if st.session_state.show_iter and st.session_state.iter_metrics:
+ display_metrics_in_one_row(st.session_state.iter_metrics, text_type)
+ else:
+ display_empty_metrics_row()
+
+ st.markdown("---")
+
+ # ---------------------------------------------------------------------
+ # CONTENEDOR-2: 2 columnas (texto base | texto iteración)
+ # ---------------------------------------------------------------------
+ with st.container():
+ col_left, col_right = st.columns(2)
+
+ # Columna izquierda: Texto base
+ with col_left:
+ st.markdown("**Texto base**")
+ text_base = st.text_area(
+ label="",
+ value=st.session_state.base_text,
+ key="text_base_area",
+ placeholder="Pega aquí tu texto base",
+ )
+ if st.button("Analizar Base"):
+ with st.spinner("Analizando texto base..."):
+ doc = nlp_models[lang_code](text_base)
+ metrics = analyze_text_dimensions(doc)
+
+ st.session_state.base_text = text_base
+ st.session_state.base_metrics = metrics
+ st.session_state.show_base = True
+ # Al analizar base, reiniciamos la iteración
+ st.session_state.show_iter = False
+
+ # Columna derecha: Texto iteración
+ with col_right:
+ st.markdown("**Texto de iteración**")
+ text_iter = st.text_area(
+ label="",
+ value=st.session_state.iter_text,
+ key="text_iter_area",
+ placeholder="Edita y mejora tu texto...",
+ disabled=not st.session_state.show_base
+ )
+ if st.button("Analizar Iteración", disabled=not st.session_state.show_base):
+ with st.spinner("Analizando iteración..."):
+ doc = nlp_models[lang_code](text_iter)
+ metrics = analyze_text_dimensions(doc)
+
+ st.session_state.iter_text = text_iter
+ st.session_state.iter_metrics = metrics
+ st.session_state.show_iter = True
+
+ # ---------------------------------------------------------------------
+ # Recomendaciones al final en un expander (una sola “fila”)
+ # ---------------------------------------------------------------------
+ if st.session_state.show_iter:
+ with st.expander("Recomendaciones", expanded=False):
+ reco_list = []
+ for dimension, values in st.session_state.iter_metrics.items():
+ score = values['normalized_score']
+ target = TEXT_TYPES[text_type]['thresholds'][dimension]['target']
+ if score < target:
+ # Aquí, en lugar de get_dimension_suggestions, unificamos con:
+ suggestions = suggest_improvement_tools_list(dimension)
+ reco_list.extend(suggestions)
+
+ if reco_list:
+ # Todas en una sola línea
+ st.write(" | ".join(reco_list))
+ else:
+ st.info("¡No hay recomendaciones! Todas las métricas superan la meta.")
+
+
+
+
+
+
+
+#Funciones de visualización ##################################
+############################################################
+# Funciones de visualización para las métricas
+############################################################
+
+def display_metrics_in_one_row(metrics, text_type):
+ """
+ Muestra las cuatro dimensiones (Vocabulario, Estructura, Cohesión, Claridad)
+ en una sola línea, usando 4 columnas con ancho uniforme.
+ """
+ thresholds = TEXT_TYPES[text_type]['thresholds']
+ dimensions = ["vocabulary", "structure", "cohesion", "clarity"]
+
+ col1, col2, col3, col4 = st.columns([1,1,1,1])
+ cols = [col1, col2, col3, col4]
+
+ for dim, col in zip(dimensions, cols):
+ score = metrics[dim]['normalized_score']
+ target = thresholds[dim]['target']
+ min_val = thresholds[dim]['min']
+
+ if score < min_val:
+ status = "⚠️ Por mejorar"
+ color = "inverse"
+ elif score < target:
+ status = "📈 Aceptable"
+ color = "off"
+ else:
+ status = "✅ Óptimo"
+ color = "normal"
+
+ with col:
+ col.metric(
+ label=dim.capitalize(),
+ value=f"{score:.2f}",
+ delta=f"{status} (Meta: {target:.2f})",
+ delta_color=color,
+ border=True
+ )
+
+
+# -------------------------------------------------------------------------
+# Función que muestra una fila de 4 columnas “vacías”
+# -------------------------------------------------------------------------
+def display_empty_metrics_row():
+ """
+ Muestra una fila de 4 columnas vacías (Vocabulario, Estructura, Cohesión, Claridad).
+ Cada columna se dibuja con st.metric en blanco (“-”).
+ """
+ empty_cols = st.columns([1,1,1,1])
+ labels = ["Vocabulario", "Estructura", "Cohesión", "Claridad"]
+
+ for col, lbl in zip(empty_cols, labels):
+ with col:
+ col.metric(
+ label=lbl,
+ value="-",
+ delta="",
+ border=True
+ )
+
+
+
+####################################################################
+
+def display_metrics_analysis(metrics, text_type=None):
+ """
+ Muestra los resultados del análisis: métricas verticalmente y gráfico radar.
+ """
+ try:
+ # Usar valor por defecto si no se especifica tipo
+ text_type = text_type or 'student_essay'
+
+ # Obtener umbrales según el tipo de texto
+ thresholds = TEXT_TYPES[text_type]['thresholds']
+
+ # Crear dos columnas para las métricas y el gráfico
+ metrics_col, graph_col = st.columns([1, 1.5])
+
+ # Columna de métricas
+ with metrics_col:
+ metrics_config = [
+ {
+ 'label': "Vocabulario",
+ 'key': 'vocabulary',
+ 'value': metrics['vocabulary']['normalized_score'],
+ 'help': "Riqueza y variedad del vocabulario",
+ 'thresholds': thresholds['vocabulary']
+ },
+ {
+ 'label': "Estructura",
+ 'key': 'structure',
+ 'value': metrics['structure']['normalized_score'],
+ 'help': "Organización y complejidad de oraciones",
+ 'thresholds': thresholds['structure']
+ },
+ {
+ 'label': "Cohesión",
+ 'key': 'cohesion',
+ 'value': metrics['cohesion']['normalized_score'],
+ 'help': "Conexión y fluidez entre ideas",
+ 'thresholds': thresholds['cohesion']
+ },
+ {
+ 'label': "Claridad",
+ 'key': 'clarity',
+ 'value': metrics['clarity']['normalized_score'],
+ 'help': "Facilidad de comprensión del texto",
+ 'thresholds': thresholds['clarity']
+ }
+ ]
+
+ # Mostrar métricas
+ for metric in metrics_config:
+ value = metric['value']
+ if value < metric['thresholds']['min']:
+ status = "⚠️ Por mejorar"
+ color = "inverse"
+ elif value < metric['thresholds']['target']:
+ status = "📈 Aceptable"
+ color = "off"
+ else:
+ status = "✅ Óptimo"
+ color = "normal"
+
+ st.metric(
+ metric['label'],
+ f"{value:.2f}",
+ f"{status} (Meta: {metric['thresholds']['target']:.2f})",
+ delta_color=color,
+ help=metric['help']
+ )
+ st.markdown("
", unsafe_allow_html=True)
+
+ except Exception as e:
+ logger.error(f"Error mostrando resultados: {str(e)}")
+ st.error("Error al mostrar los resultados")
+
+def display_comparison_results(baseline_metrics, current_metrics):
+ """Muestra comparación entre línea base y métricas actuales"""
+
+ # Crear columnas para métricas y gráfico
+ metrics_col, graph_col = st.columns([1, 1.5])
+
+ with metrics_col:
+ for dimension in ['vocabulary', 'structure', 'cohesion', 'clarity']:
+ baseline = baseline_metrics[dimension]['normalized_score']
+ current = current_metrics[dimension]['normalized_score']
+ delta = current - baseline
+
+ st.metric(
+ dimension.title(),
+ f"{current:.2f}",
+ f"{delta:+.2f}",
+ delta_color="normal" if delta >= 0 else "inverse"
+ )
+
+ # Sugerir herramientas de mejora
+ if delta < 0:
+ suggest_improvement_tools(dimension)
+
+ with graph_col:
+ display_radar_chart_comparison(
+ baseline_metrics,
+ current_metrics
+ )
+
+def display_metrics_and_suggestions(metrics, text_type, title, show_suggestions=False):
+ """
+ Muestra métricas y opcionalmente sugerencias de mejora.
+ Args:
+ metrics: Diccionario con las métricas analizadas
+ text_type: Tipo de texto seleccionado
+ title: Título para las métricas ("Base" o "Iteración")
+ show_suggestions: Booleano para mostrar sugerencias
+ """
+ try:
+ thresholds = TEXT_TYPES[text_type]['thresholds']
+
+ st.markdown(f"### Métricas {title}")
+
+ for dimension, values in metrics.items():
+ score = values['normalized_score']
+ target = thresholds[dimension]['target']
+ min_val = thresholds[dimension]['min']
+
+ # Determinar estado y color
+ if score < min_val:
+ status = "⚠️ Por mejorar"
+ color = "inverse"
+ elif score < target:
+ status = "📈 Aceptable"
+ color = "off"
+ else:
+ status = "✅ Óptimo"
+ color = "normal"
+
+ # Mostrar métrica
+ st.metric(
+ dimension.title(),
+ f"{score:.2f}",
+ f"{status} (Meta: {target:.2f})",
+ delta_color=color,
+ help=f"Meta: {target:.2f}, Mínimo: {min_val:.2f}"
+ )
+
+ # Mostrar sugerencias si es necesario
+ if show_suggestions and score < target:
+ suggest_improvement_tools(dimension)
+
+ # Agregar espacio entre métricas
+ st.markdown("
", unsafe_allow_html=True)
+
+ except Exception as e:
+ logger.error(f"Error mostrando métricas: {str(e)}")
+ st.error("Error al mostrar métricas")
+
+def display_radar_chart(metrics_config, thresholds, baseline_metrics=None):
+ """
+ Muestra el gráfico radar con los resultados.
+ Args:
+ metrics_config: Configuración actual de métricas
+ thresholds: Umbrales para las métricas
+ baseline_metrics: Métricas de línea base (opcional)
+ """
+ try:
+ # Preparar datos para el gráfico
+ categories = [m['label'] for m in metrics_config]
+ values_current = [m['value'] for m in metrics_config]
+ min_values = [m['thresholds']['min'] for m in metrics_config]
+ target_values = [m['thresholds']['target'] for m in metrics_config]
+
+ # Crear y configurar gráfico
+ fig = plt.figure(figsize=(8, 8))
+ ax = fig.add_subplot(111, projection='polar')
+
+ # Configurar radar
+ angles = [n / float(len(categories)) * 2 * np.pi for n in range(len(categories))]
+ angles += angles[:1]
+ values_current += values_current[:1]
+ min_values += min_values[:1]
+ target_values += target_values[:1]
+
+ # Configurar ejes
+ ax.set_xticks(angles[:-1])
+ ax.set_xticklabels(categories, fontsize=10)
+ circle_ticks = np.arange(0, 1.1, 0.2)
+ ax.set_yticks(circle_ticks)
+ ax.set_yticklabels([f'{tick:.1f}' for tick in circle_ticks], fontsize=8)
+ ax.set_ylim(0, 1)
+
+ # Dibujar áreas de umbrales
+ ax.plot(angles, min_values, '#e74c3c', linestyle='--', linewidth=1,
+ label='Mínimo', alpha=0.5)
+ ax.plot(angles, target_values, '#2ecc71', linestyle='--', linewidth=1,
+ label='Meta', alpha=0.5)
+ ax.fill_between(angles, target_values, [1]*len(angles),
+ color='#2ecc71', alpha=0.1)
+ ax.fill_between(angles, [0]*len(angles), min_values,
+ color='#e74c3c', alpha=0.1)
+
+ # Si hay línea base, dibujarla primero
+ if baseline_metrics is not None:
+ values_baseline = [baseline_metrics[m['key']]['normalized_score']
+ for m in metrics_config]
+ values_baseline += values_baseline[:1]
+ ax.plot(angles, values_baseline, '#888888', linewidth=2,
+ label='Línea base', linestyle='--')
+ ax.fill(angles, values_baseline, '#888888', alpha=0.1)
+
+ # Dibujar valores actuales
+ label = 'Actual' if baseline_metrics else 'Tu escritura'
+ color = '#3498db' if baseline_metrics else '#3498db'
+
+ ax.plot(angles, values_current, color, linewidth=2, label=label)
+ ax.fill(angles, values_current, color, alpha=0.2)
+
+ # Ajustar leyenda
+ legend_handles = []
+ if baseline_metrics:
+ legend_handles.extend([
+ plt.Line2D([], [], color='#888888', linestyle='--',
+ label='Línea base'),
+ plt.Line2D([], [], color='#3498db', label='Actual')
+ ])
+ else:
+ legend_handles.extend([
+ plt.Line2D([], [], color='#3498db', label='Tu escritura')
+ ])
+
+ legend_handles.extend([
+ plt.Line2D([], [], color='#e74c3c', linestyle='--', label='Mínimo'),
+ plt.Line2D([], [], color='#2ecc71', linestyle='--', label='Meta')
+ ])
+
+ ax.legend(
+ handles=legend_handles,
+ loc='upper right',
+ bbox_to_anchor=(1.3, 1.1),
+ fontsize=10,
+ frameon=True,
+ facecolor='white',
+ edgecolor='none',
+ shadow=True
+ )
+
+ plt.tight_layout()
+ st.pyplot(fig)
+ plt.close()
+
+ except Exception as e:
+ logger.error(f"Error mostrando gráfico radar: {str(e)}")
+ st.error("Error al mostrar el gráfico")
+
+#Funciones auxiliares ##################################
+
+
+############################################################
+# Unificamos la lógica de sugerencias en una función
+############################################################
+def suggest_improvement_tools_list(dimension):
+ """
+ Retorna en forma de lista las herramientas sugeridas
+ basadas en 'ANALYSIS_DIMENSION_MAPPING'.
+ """
+ suggestions = []
+ for analysis, mapping in ANALYSIS_DIMENSION_MAPPING.items():
+ # Verificamos si la dimensión está en primary o secondary
+ if dimension in mapping['primary'] or dimension in mapping['secondary']:
+ suggestions.extend(mapping['tools'])
+ # Si no hay nada, al menos retornamos un placeholder
+ return suggestions if suggestions else ["Sin sugerencias específicas."]
+
+
+def prepare_metrics_config(metrics, text_type='student_essay'):
+ """
+ Prepara la configuración de métricas en el mismo formato que display_results.
+ Args:
+ metrics: Diccionario con las métricas analizadas
+ text_type: Tipo de texto para los umbrales
+ Returns:
+ list: Lista de configuraciones de métricas
+ """
+ # Obtener umbrales según el tipo de texto
+ thresholds = TEXT_TYPES[text_type]['thresholds']
+
+ # Usar la misma estructura que en display_results
+ return [
+ {
+ 'label': "Vocabulario",
+ 'key': 'vocabulary',
+ 'value': metrics['vocabulary']['normalized_score'],
+ 'help': "Riqueza y variedad del vocabulario",
+ 'thresholds': thresholds['vocabulary']
+ },
+ {
+ 'label': "Estructura",
+ 'key': 'structure',
+ 'value': metrics['structure']['normalized_score'],
+ 'help': "Organización y complejidad de oraciones",
+ 'thresholds': thresholds['structure']
+ },
+ {
+ 'label': "Cohesión",
+ 'key': 'cohesion',
+ 'value': metrics['cohesion']['normalized_score'],
+ 'help': "Conexión y fluidez entre ideas",
+ 'thresholds': thresholds['cohesion']
+ },
+ {
+ 'label': "Claridad",
+ 'key': 'clarity',
+ 'value': metrics['clarity']['normalized_score'],
+ 'help': "Facilidad de comprensión del texto",
+ 'thresholds': thresholds['clarity']
+ }
+ ]
+
diff --git a/modules/studentact/current_situation_interface-v1.py b/modules/studentact/current_situation_interface-v1.py
index 9b6672e62369d9375cb85df8f4636f7cc423a38f..6119f6bf146976da43fe9311b5fc54551173341a 100644
--- a/modules/studentact/current_situation_interface-v1.py
+++ b/modules/studentact/current_situation_interface-v1.py
@@ -1,272 +1,272 @@
-# modules/studentact/current_situation_interface.py
-
-import streamlit as st
-import logging
-from ..utils.widget_utils import generate_unique_key
-from .current_situation_analysis import (
- analyze_text_dimensions,
- analyze_clarity,
- analyze_reference_clarity,
- analyze_vocabulary_diversity,
- analyze_cohesion,
- analyze_structure,
- get_dependency_depths,
- normalize_score,
- generate_sentence_graphs,
- generate_word_connections,
- generate_connection_paths,
- create_vocabulary_network,
- create_syntax_complexity_graph,
- create_cohesion_heatmap,
-)
-
-logger = logging.getLogger(__name__)
-####################################
-def display_current_situation_interface(lang_code, nlp_models, t):
- """
- Interfaz simplificada para el análisis inicial, enfocada en recomendaciones directas.
- """
- # Inicializar estados si no existen
- if 'text_input' not in st.session_state:
- st.session_state.text_input = ""
- if 'show_results' not in st.session_state:
- st.session_state.show_results = False
- if 'current_doc' not in st.session_state:
- st.session_state.current_doc = None
- if 'current_metrics' not in st.session_state:
- st.session_state.current_metrics = None
-
- st.markdown("## Análisis Inicial de Escritura")
-
- # Container principal con dos columnas
- with st.container():
- input_col, results_col = st.columns([1,2])
-
- with input_col:
- st.markdown("### Ingresa tu texto")
-
- # Función para manejar cambios en el texto
- def on_text_change():
- st.session_state.text_input = st.session_state.text_area
- st.session_state.show_results = False # Resetear resultados cuando el texto cambia
-
- # Text area con manejo de estado
- text_input = st.text_area(
- t.get('input_prompt', "Escribe o pega tu texto aquí:"),
- height=400,
- key="text_area",
- value=st.session_state.text_input,
- on_change=on_text_change,
- help="Este texto será analizado para darte recomendaciones personalizadas"
- )
-
- # Botón de análisis
- if st.button(
- t.get('analyze_button', "Analizar mi escritura"),
- type="primary",
- disabled=not text_input.strip(),
- use_container_width=True,
- ):
- try:
- with st.spinner(t.get('processing', "Analizando...")):
- # Procesar texto y obtener métricas
- doc = nlp_models[lang_code](text_input)
- metrics = analyze_text_dimensions(doc)
-
- # Actualizar estado con nuevos resultados
- st.session_state.current_doc = doc
- st.session_state.current_metrics = metrics
- st.session_state.show_results = True
-
- # Mantener el texto en el estado
- st.session_state.text_input = text_input
-
- except Exception as e:
- logger.error(f"Error en análisis: {str(e)}")
- st.error(t.get('analysis_error', "Error al analizar el texto"))
-
- # Mostrar resultados en la columna derecha
- with results_col:
- if st.session_state.show_results and st.session_state.current_metrics is not None:
- display_recommendations(st.session_state.current_metrics, t)
-
- # Opción para ver detalles
- with st.expander("🔍 Ver análisis detallado", expanded=False):
- display_current_situation_visual(
- st.session_state.current_doc,
- st.session_state.current_metrics
- )
-
-def display_current_situation_visual(doc, metrics):
- """
- Muestra visualizaciones detalladas del análisis.
- """
- try:
- st.markdown("### 📊 Visualizaciones Detalladas")
-
- # 1. Visualización de vocabulario
- with st.expander("Análisis de Vocabulario", expanded=True):
- vocab_graph = create_vocabulary_network(doc)
- if vocab_graph:
- st.pyplot(vocab_graph)
- plt.close(vocab_graph)
-
- # 2. Visualización de estructura
- with st.expander("Análisis de Estructura", expanded=True):
- syntax_graph = create_syntax_complexity_graph(doc)
- if syntax_graph:
- st.pyplot(syntax_graph)
- plt.close(syntax_graph)
-
- # 3. Visualización de cohesión
- with st.expander("Análisis de Cohesión", expanded=True):
- cohesion_graph = create_cohesion_heatmap(doc)
- if cohesion_graph:
- st.pyplot(cohesion_graph)
- plt.close(cohesion_graph)
-
- except Exception as e:
- logger.error(f"Error en visualización: {str(e)}")
- st.error("Error al generar las visualizaciones")
-
-
-####################################
-def display_recommendations(metrics, t):
- """
- Muestra recomendaciones basadas en las métricas del texto.
- """
- # 1. Resumen Visual con Explicación
- st.markdown("### 📊 Resumen de tu Análisis")
-
- # Explicación del sistema de medición
- st.markdown("""
- **¿Cómo interpretar los resultados?**
-
- Cada métrica se mide en una escala de 0.0 a 1.0, donde:
- - 0.0 - 0.4: Necesita atención prioritaria
- - 0.4 - 0.6: En desarrollo
- - 0.6 - 0.8: Buen nivel
- - 0.8 - 1.0: Nivel avanzado
- """)
-
- # Métricas con explicaciones detalladas
- col1, col2, col3, col4 = st.columns(4)
-
- with col1:
- st.metric(
- "Vocabulario",
- f"{metrics['vocabulary']['normalized_score']:.2f}",
- help="Mide la variedad y riqueza de tu vocabulario. Un valor alto indica un uso diverso de palabras sin repeticiones excesivas."
- )
- with st.expander("ℹ️ Detalles"):
- st.write("""
- **Vocabulario**
- - Evalúa la diversidad léxica
- - Considera palabras únicas vs. totales
- - Detecta repeticiones innecesarias
- - Valor óptimo: > 0.7
- """)
-
- with col2:
- st.metric(
- "Estructura",
- f"{metrics['structure']['normalized_score']:.2f}",
- help="Evalúa la complejidad y variedad de las estructuras sintácticas en tus oraciones."
- )
- with st.expander("ℹ️ Detalles"):
- st.write("""
- **Estructura**
- - Analiza la complejidad sintáctica
- - Mide variación en construcciones
- - Evalúa longitud de oraciones
- - Valor óptimo: > 0.6
- """)
-
- with col3:
- st.metric(
- "Cohesión",
- f"{metrics['cohesion']['normalized_score']:.2f}",
- help="Indica qué tan bien conectadas están tus ideas y párrafos entre sí."
- )
- with st.expander("ℹ️ Detalles"):
- st.write("""
- **Cohesión**
- - Mide conexiones entre ideas
- - Evalúa uso de conectores
- - Analiza progresión temática
- - Valor óptimo: > 0.65
- """)
-
- with col4:
- st.metric(
- "Claridad",
- f"{metrics['clarity']['normalized_score']:.2f}",
- help="Evalúa la facilidad de comprensión general de tu texto."
- )
- with st.expander("ℹ️ Detalles"):
- st.write("""
- **Claridad**
- - Evalúa comprensibilidad
- - Considera estructura lógica
- - Mide precisión expresiva
- - Valor óptimo: > 0.7
- """)
-
- st.markdown("---")
-
- # 2. Recomendaciones basadas en puntuaciones
- st.markdown("### 💡 Recomendaciones Personalizadas")
-
- # Recomendaciones morfosintácticas
- if metrics['structure']['normalized_score'] < 0.6:
- st.warning("""
- #### 📝 Análisis Morfosintáctico Recomendado
-
- **Tu nivel actual sugiere que sería beneficioso:**
- 1. Realizar el análisis morfosintáctico de 3 párrafos diferentes
- 2. Practicar la combinación de oraciones simples en compuestas
- 3. Identificar y clasificar tipos de oraciones en textos académicos
- 4. Ejercitar la variación sintáctica
-
- *Hacer clic en "Comenzar ejercicios" para acceder al módulo morfosintáctico*
- """)
-
- # Recomendaciones semánticas
- if metrics['vocabulary']['normalized_score'] < 0.7:
- st.warning("""
- #### 📚 Análisis Semántico Recomendado
-
- **Para mejorar tu vocabulario y expresión:**
- A. Realiza el análisis semántico de un texto académico
- B. Identifica y agrupa campos semánticos relacionados
- C. Practica la sustitución léxica en tus párrafos
- D. Construye redes de conceptos sobre tu tema
- E. Analiza las relaciones entre ideas principales
-
- *Hacer clic en "Comenzar ejercicios" para acceder al módulo semántico*
- """)
-
- # Recomendaciones de cohesión
- if metrics['cohesion']['normalized_score'] < 0.65:
- st.warning("""
- #### 🔄 Análisis del Discurso Recomendado
-
- **Para mejorar la conexión entre ideas:**
- 1. Realizar el análisis del discurso de un texto modelo
- 2. Practicar el uso de diferentes conectores textuales
- 3. Identificar cadenas de referencia en textos académicos
- 4. Ejercitar la progresión temática en tus escritos
-
- *Hacer clic en "Comenzar ejercicios" para acceder al módulo de análisis del discurso*
- """)
-
- # Botón de acción
- st.markdown("---")
- col1, col2, col3 = st.columns([1,2,1])
- with col2:
- st.button(
- "🎯 Comenzar ejercicios recomendados",
- type="primary",
- use_container_width=True,
- key="start_exercises"
+# modules/studentact/current_situation_interface.py
+
+import streamlit as st
+import logging
+from ..utils.widget_utils import generate_unique_key
+from .current_situation_analysis import (
+ analyze_text_dimensions,
+ analyze_clarity,
+ analyze_reference_clarity,
+ analyze_vocabulary_diversity,
+ analyze_cohesion,
+ analyze_structure,
+ get_dependency_depths,
+ normalize_score,
+ generate_sentence_graphs,
+ generate_word_connections,
+ generate_connection_paths,
+ create_vocabulary_network,
+ create_syntax_complexity_graph,
+ create_cohesion_heatmap,
+)
+
+logger = logging.getLogger(__name__)
+####################################
+def display_current_situation_interface(lang_code, nlp_models, t):
+ """
+ Interfaz simplificada para el análisis inicial, enfocada en recomendaciones directas.
+ """
+ # Inicializar estados si no existen
+ if 'text_input' not in st.session_state:
+ st.session_state.text_input = ""
+ if 'show_results' not in st.session_state:
+ st.session_state.show_results = False
+ if 'current_doc' not in st.session_state:
+ st.session_state.current_doc = None
+ if 'current_metrics' not in st.session_state:
+ st.session_state.current_metrics = None
+
+ st.markdown("## Análisis Inicial de Escritura")
+
+ # Container principal con dos columnas
+ with st.container():
+ input_col, results_col = st.columns([1,2])
+
+ with input_col:
+ st.markdown("### Ingresa tu texto")
+
+ # Función para manejar cambios en el texto
+ def on_text_change():
+ st.session_state.text_input = st.session_state.text_area
+ st.session_state.show_results = False # Resetear resultados cuando el texto cambia
+
+ # Text area con manejo de estado
+ text_input = st.text_area(
+ t.get('input_prompt', "Escribe o pega tu texto aquí:"),
+ height=400,
+ key="text_area",
+ value=st.session_state.text_input,
+ on_change=on_text_change,
+ help="Este texto será analizado para darte recomendaciones personalizadas"
+ )
+
+ # Botón de análisis
+ if st.button(
+ t.get('analyze_button', "Analizar mi escritura"),
+ type="primary",
+ disabled=not text_input.strip(),
+ use_container_width=True,
+ ):
+ try:
+ with st.spinner(t.get('processing', "Analizando...")):
+ # Procesar texto y obtener métricas
+ doc = nlp_models[lang_code](text_input)
+ metrics = analyze_text_dimensions(doc)
+
+ # Actualizar estado con nuevos resultados
+ st.session_state.current_doc = doc
+ st.session_state.current_metrics = metrics
+ st.session_state.show_results = True
+
+ # Mantener el texto en el estado
+ st.session_state.text_input = text_input
+
+ except Exception as e:
+ logger.error(f"Error en análisis: {str(e)}")
+ st.error(t.get('analysis_error', "Error al analizar el texto"))
+
+ # Mostrar resultados en la columna derecha
+ with results_col:
+ if st.session_state.show_results and st.session_state.current_metrics is not None:
+ display_recommendations(st.session_state.current_metrics, t)
+
+ # Opción para ver detalles
+ with st.expander("🔍 Ver análisis detallado", expanded=False):
+ display_current_situation_visual(
+ st.session_state.current_doc,
+ st.session_state.current_metrics
+ )
+
+def display_current_situation_visual(doc, metrics):
+ """
+ Muestra visualizaciones detalladas del análisis.
+ """
+ try:
+ st.markdown("### 📊 Visualizaciones Detalladas")
+
+ # 1. Visualización de vocabulario
+ with st.expander("Análisis de Vocabulario", expanded=True):
+ vocab_graph = create_vocabulary_network(doc)
+ if vocab_graph:
+ st.pyplot(vocab_graph)
+ plt.close(vocab_graph)
+
+ # 2. Visualización de estructura
+ with st.expander("Análisis de Estructura", expanded=True):
+ syntax_graph = create_syntax_complexity_graph(doc)
+ if syntax_graph:
+ st.pyplot(syntax_graph)
+ plt.close(syntax_graph)
+
+ # 3. Visualización de cohesión
+ with st.expander("Análisis de Cohesión", expanded=True):
+ cohesion_graph = create_cohesion_heatmap(doc)
+ if cohesion_graph:
+ st.pyplot(cohesion_graph)
+ plt.close(cohesion_graph)
+
+ except Exception as e:
+ logger.error(f"Error en visualización: {str(e)}")
+ st.error("Error al generar las visualizaciones")
+
+
+####################################
+def display_recommendations(metrics, t):
+ """
+ Muestra recomendaciones basadas en las métricas del texto.
+ """
+ # 1. Resumen Visual con Explicación
+ st.markdown("### 📊 Resumen de tu Análisis")
+
+ # Explicación del sistema de medición
+ st.markdown("""
+ **¿Cómo interpretar los resultados?**
+
+ Cada métrica se mide en una escala de 0.0 a 1.0, donde:
+ - 0.0 - 0.4: Necesita atención prioritaria
+ - 0.4 - 0.6: En desarrollo
+ - 0.6 - 0.8: Buen nivel
+ - 0.8 - 1.0: Nivel avanzado
+ """)
+
+ # Métricas con explicaciones detalladas
+ col1, col2, col3, col4 = st.columns(4)
+
+ with col1:
+ st.metric(
+ "Vocabulario",
+ f"{metrics['vocabulary']['normalized_score']:.2f}",
+ help="Mide la variedad y riqueza de tu vocabulario. Un valor alto indica un uso diverso de palabras sin repeticiones excesivas."
+ )
+ with st.expander("ℹ️ Detalles"):
+ st.write("""
+ **Vocabulario**
+ - Evalúa la diversidad léxica
+ - Considera palabras únicas vs. totales
+ - Detecta repeticiones innecesarias
+ - Valor óptimo: > 0.7
+ """)
+
+ with col2:
+ st.metric(
+ "Estructura",
+ f"{metrics['structure']['normalized_score']:.2f}",
+ help="Evalúa la complejidad y variedad de las estructuras sintácticas en tus oraciones."
+ )
+ with st.expander("ℹ️ Detalles"):
+ st.write("""
+ **Estructura**
+ - Analiza la complejidad sintáctica
+ - Mide variación en construcciones
+ - Evalúa longitud de oraciones
+ - Valor óptimo: > 0.6
+ """)
+
+ with col3:
+ st.metric(
+ "Cohesión",
+ f"{metrics['cohesion']['normalized_score']:.2f}",
+ help="Indica qué tan bien conectadas están tus ideas y párrafos entre sí."
+ )
+ with st.expander("ℹ️ Detalles"):
+ st.write("""
+ **Cohesión**
+ - Mide conexiones entre ideas
+ - Evalúa uso de conectores
+ - Analiza progresión temática
+ - Valor óptimo: > 0.65
+ """)
+
+ with col4:
+ st.metric(
+ "Claridad",
+ f"{metrics['clarity']['normalized_score']:.2f}",
+ help="Evalúa la facilidad de comprensión general de tu texto."
+ )
+ with st.expander("ℹ️ Detalles"):
+ st.write("""
+ **Claridad**
+ - Evalúa comprensibilidad
+ - Considera estructura lógica
+ - Mide precisión expresiva
+ - Valor óptimo: > 0.7
+ """)
+
+ st.markdown("---")
+
+ # 2. Recomendaciones basadas en puntuaciones
+ st.markdown("### 💡 Recomendaciones Personalizadas")
+
+ # Recomendaciones morfosintácticas
+ if metrics['structure']['normalized_score'] < 0.6:
+ st.warning("""
+ #### 📝 Análisis Morfosintáctico Recomendado
+
+ **Tu nivel actual sugiere que sería beneficioso:**
+ 1. Realizar el análisis morfosintáctico de 3 párrafos diferentes
+ 2. Practicar la combinación de oraciones simples en compuestas
+ 3. Identificar y clasificar tipos de oraciones en textos académicos
+ 4. Ejercitar la variación sintáctica
+
+ *Hacer clic en "Comenzar ejercicios" para acceder al módulo morfosintáctico*
+ """)
+
+ # Recomendaciones semánticas
+ if metrics['vocabulary']['normalized_score'] < 0.7:
+ st.warning("""
+ #### 📚 Análisis Semántico Recomendado
+
+ **Para mejorar tu vocabulario y expresión:**
+ A. Realiza el análisis semántico de un texto académico
+ B. Identifica y agrupa campos semánticos relacionados
+ C. Practica la sustitución léxica en tus párrafos
+ D. Construye redes de conceptos sobre tu tema
+ E. Analiza las relaciones entre ideas principales
+
+ *Hacer clic en "Comenzar ejercicios" para acceder al módulo semántico*
+ """)
+
+ # Recomendaciones de cohesión
+ if metrics['cohesion']['normalized_score'] < 0.65:
+ st.warning("""
+ #### 🔄 Análisis del Discurso Recomendado
+
+ **Para mejorar la conexión entre ideas:**
+ 1. Realizar el análisis del discurso de un texto modelo
+ 2. Practicar el uso de diferentes conectores textuales
+ 3. Identificar cadenas de referencia en textos académicos
+ 4. Ejercitar la progresión temática en tus escritos
+
+ *Hacer clic en "Comenzar ejercicios" para acceder al módulo de análisis del discurso*
+ """)
+
+ # Botón de acción
+ st.markdown("---")
+ col1, col2, col3 = st.columns([1,2,1])
+ with col2:
+ st.button(
+ "🎯 Comenzar ejercicios recomendados",
+ type="primary",
+ use_container_width=True,
+ key="start_exercises"
)
\ No newline at end of file
diff --git a/modules/studentact/current_situation_interface-v2.py b/modules/studentact/current_situation_interface-v2.py
index 5e20278e31b6b6b528279cde78cfe78e81d1344e..64316c76a1bc41b01bdfd35d76e7f47117aefa24 100644
--- a/modules/studentact/current_situation_interface-v2.py
+++ b/modules/studentact/current_situation_interface-v2.py
@@ -1,291 +1,291 @@
-# modules/studentact/current_situation_interface.py
-
-import streamlit as st
-import logging
-from ..utils.widget_utils import generate_unique_key
-
-from ..database.current_situation_mongo_db import store_current_situation_result
-
-from .current_situation_analysis import (
- analyze_text_dimensions,
- analyze_clarity,
- analyze_reference_clarity,
- analyze_vocabulary_diversity,
- analyze_cohesion,
- analyze_structure,
- get_dependency_depths,
- normalize_score,
- generate_sentence_graphs,
- generate_word_connections,
- generate_connection_paths,
- create_vocabulary_network,
- create_syntax_complexity_graph,
- create_cohesion_heatmap,
-)
-
-logger = logging.getLogger(__name__)
-####################################
-
-def display_current_situation_interface(lang_code, nlp_models, t):
- """
- Interfaz simplificada para el análisis inicial, enfocada en recomendaciones directas.
- """
- try:
- # Inicializar estados si no existen
- if 'text_input' not in st.session_state:
- st.session_state.text_input = ""
- if 'show_results' not in st.session_state:
- st.session_state.show_results = False
- if 'current_doc' not in st.session_state:
- st.session_state.current_doc = None
- if 'current_metrics' not in st.session_state:
- st.session_state.current_metrics = None
-
- st.markdown("## Análisis Inicial de Escritura")
-
- # Container principal con dos columnas
- with st.container():
- input_col, results_col = st.columns([1,2])
-
- with input_col:
- st.markdown("### Ingresa tu texto")
-
- # Función para manejar cambios en el texto
- def on_text_change():
- st.session_state.text_input = st.session_state.text_area
- st.session_state.show_results = False # Resetear resultados cuando el texto cambia
-
- # Text area con manejo de estado
- text_input = st.text_area(
- t.get('input_prompt', "Escribe o pega tu texto aquí:"),
- height=400,
- key="text_area",
- value=st.session_state.text_input,
- on_change=on_text_change,
- help="Este texto será analizado para darte recomendaciones personalizadas"
- )
-
- if st.button(
- t.get('analyze_button', "Analizar mi escritura"),
- type="primary",
- disabled=not text_input.strip(),
- use_container_width=True,
- ):
- try:
- with st.spinner(t.get('processing', "Analizando...")):
- # Procesar texto y obtener métricas
- doc = nlp_models[lang_code](text_input)
- metrics = analyze_text_dimensions(doc)
-
- # Guardar en MongoDB
- storage_success = store_current_situation_result(
- username=st.session_state.username,
- text=text_input,
- metrics=metrics,
- feedback=None # Por ahora sin feedback
- )
-
- if not storage_success:
- logger.warning("No se pudo guardar el análisis en la base de datos")
-
- # Actualizar estado
- st.session_state.current_doc = doc
- st.session_state.current_metrics = metrics
- st.session_state.show_results = True
- st.session_state.text_input = text_input
-
- except Exception as e:
- logger.error(f"Error en análisis: {str(e)}")
- st.error(t.get('analysis_error', "Error al analizar el texto"))
-
- # Mostrar resultados en la columna derecha
- with results_col:
- if st.session_state.show_results and st.session_state.current_metrics is not None:
- display_recommendations(st.session_state.current_metrics, t)
-
- # Opción para ver detalles
- with st.expander("🔍 Ver análisis detallado", expanded=False):
- display_current_situation_visual(
- st.session_state.current_doc,
- st.session_state.current_metrics
- )
-
- except Exception as e:
- logger.error(f"Error en interfaz: {str(e)}")
- st.error("Ocurrió un error. Por favor, intente de nuevo.")
-
-
-
-def display_current_situation_visual(doc, metrics):
- """
- Muestra visualizaciones detalladas del análisis.
- """
- try:
- st.markdown("### 📊 Visualizaciones Detalladas")
-
- # 1. Visualización de vocabulario
- with st.expander("Análisis de Vocabulario", expanded=True):
- vocab_graph = create_vocabulary_network(doc)
- if vocab_graph:
- st.pyplot(vocab_graph)
- plt.close(vocab_graph)
-
- # 2. Visualización de estructura
- with st.expander("Análisis de Estructura", expanded=True):
- syntax_graph = create_syntax_complexity_graph(doc)
- if syntax_graph:
- st.pyplot(syntax_graph)
- plt.close(syntax_graph)
-
- # 3. Visualización de cohesión
- with st.expander("Análisis de Cohesión", expanded=True):
- cohesion_graph = create_cohesion_heatmap(doc)
- if cohesion_graph:
- st.pyplot(cohesion_graph)
- plt.close(cohesion_graph)
-
- except Exception as e:
- logger.error(f"Error en visualización: {str(e)}")
- st.error("Error al generar las visualizaciones")
-
-
-####################################
-def display_recommendations(metrics, t):
- """
- Muestra recomendaciones basadas en las métricas del texto.
- """
- # 1. Resumen Visual con Explicación
- st.markdown("### 📊 Resumen de tu Análisis")
-
- # Explicación del sistema de medición
- st.markdown("""
- **¿Cómo interpretar los resultados?**
-
- Cada métrica se mide en una escala de 0.0 a 1.0, donde:
- - 0.0 - 0.4: Necesita atención prioritaria
- - 0.4 - 0.6: En desarrollo
- - 0.6 - 0.8: Buen nivel
- - 0.8 - 1.0: Nivel avanzado
- """)
-
- # Métricas con explicaciones detalladas
- col1, col2, col3, col4 = st.columns(4)
-
- with col1:
- st.metric(
- "Vocabulario",
- f"{metrics['vocabulary']['normalized_score']:.2f}",
- help="Mide la variedad y riqueza de tu vocabulario. Un valor alto indica un uso diverso de palabras sin repeticiones excesivas."
- )
- with st.expander("ℹ️ Detalles"):
- st.write("""
- **Vocabulario**
- - Evalúa la diversidad léxica
- - Considera palabras únicas vs. totales
- - Detecta repeticiones innecesarias
- - Valor óptimo: > 0.7
- """)
-
- with col2:
- st.metric(
- "Estructura",
- f"{metrics['structure']['normalized_score']:.2f}",
- help="Evalúa la complejidad y variedad de las estructuras sintácticas en tus oraciones."
- )
- with st.expander("ℹ️ Detalles"):
- st.write("""
- **Estructura**
- - Analiza la complejidad sintáctica
- - Mide variación en construcciones
- - Evalúa longitud de oraciones
- - Valor óptimo: > 0.6
- """)
-
- with col3:
- st.metric(
- "Cohesión",
- f"{metrics['cohesion']['normalized_score']:.2f}",
- help="Indica qué tan bien conectadas están tus ideas y párrafos entre sí."
- )
- with st.expander("ℹ️ Detalles"):
- st.write("""
- **Cohesión**
- - Mide conexiones entre ideas
- - Evalúa uso de conectores
- - Analiza progresión temática
- - Valor óptimo: > 0.65
- """)
-
- with col4:
- st.metric(
- "Claridad",
- f"{metrics['clarity']['normalized_score']:.2f}",
- help="Evalúa la facilidad de comprensión general de tu texto."
- )
- with st.expander("ℹ️ Detalles"):
- st.write("""
- **Claridad**
- - Evalúa comprensibilidad
- - Considera estructura lógica
- - Mide precisión expresiva
- - Valor óptimo: > 0.7
- """)
-
- st.markdown("---")
-
- # 2. Recomendaciones basadas en puntuaciones
- st.markdown("### 💡 Recomendaciones Personalizadas")
-
- # Recomendaciones morfosintácticas
- if metrics['structure']['normalized_score'] < 0.6:
- st.warning("""
- #### 📝 Análisis Morfosintáctico Recomendado
-
- **Tu nivel actual sugiere que sería beneficioso:**
- 1. Realizar el análisis morfosintáctico de 3 párrafos diferentes
- 2. Practicar la combinación de oraciones simples en compuestas
- 3. Identificar y clasificar tipos de oraciones en textos académicos
- 4. Ejercitar la variación sintáctica
-
- *Hacer clic en "Comenzar ejercicios" para acceder al módulo morfosintáctico*
- """)
-
- # Recomendaciones semánticas
- if metrics['vocabulary']['normalized_score'] < 0.7:
- st.warning("""
- #### 📚 Análisis Semántico Recomendado
-
- **Para mejorar tu vocabulario y expresión:**
- A. Realiza el análisis semántico de un texto académico
- B. Identifica y agrupa campos semánticos relacionados
- C. Practica la sustitución léxica en tus párrafos
- D. Construye redes de conceptos sobre tu tema
- E. Analiza las relaciones entre ideas principales
-
- *Hacer clic en "Comenzar ejercicios" para acceder al módulo semántico*
- """)
-
- # Recomendaciones de cohesión
- if metrics['cohesion']['normalized_score'] < 0.65:
- st.warning("""
- #### 🔄 Análisis del Discurso Recomendado
-
- **Para mejorar la conexión entre ideas:**
- 1. Realizar el análisis del discurso de un texto modelo
- 2. Practicar el uso de diferentes conectores textuales
- 3. Identificar cadenas de referencia en textos académicos
- 4. Ejercitar la progresión temática en tus escritos
-
- *Hacer clic en "Comenzar ejercicios" para acceder al módulo de análisis del discurso*
- """)
-
- # Botón de acción
- st.markdown("---")
- col1, col2, col3 = st.columns([1,2,1])
- with col2:
- st.button(
- "🎯 Comenzar ejercicios recomendados",
- type="primary",
- use_container_width=True,
- key="start_exercises"
- )
+# modules/studentact/current_situation_interface.py
+
+import streamlit as st
+import logging
+from ..utils.widget_utils import generate_unique_key
+
+from ..database.current_situation_mongo_db import store_current_situation_result
+
+from .current_situation_analysis import (
+ analyze_text_dimensions,
+ analyze_clarity,
+ analyze_reference_clarity,
+ analyze_vocabulary_diversity,
+ analyze_cohesion,
+ analyze_structure,
+ get_dependency_depths,
+ normalize_score,
+ generate_sentence_graphs,
+ generate_word_connections,
+ generate_connection_paths,
+ create_vocabulary_network,
+ create_syntax_complexity_graph,
+ create_cohesion_heatmap,
+)
+
+logger = logging.getLogger(__name__)
+####################################
+
+def display_current_situation_interface(lang_code, nlp_models, t):
+ """
+ Interfaz simplificada para el análisis inicial, enfocada en recomendaciones directas.
+ """
+ try:
+ # Inicializar estados si no existen
+ if 'text_input' not in st.session_state:
+ st.session_state.text_input = ""
+ if 'show_results' not in st.session_state:
+ st.session_state.show_results = False
+ if 'current_doc' not in st.session_state:
+ st.session_state.current_doc = None
+ if 'current_metrics' not in st.session_state:
+ st.session_state.current_metrics = None
+
+ st.markdown("## Análisis Inicial de Escritura")
+
+ # Container principal con dos columnas
+ with st.container():
+ input_col, results_col = st.columns([1,2])
+
+ with input_col:
+ st.markdown("### Ingresa tu texto")
+
+ # Función para manejar cambios en el texto
+ def on_text_change():
+ st.session_state.text_input = st.session_state.text_area
+ st.session_state.show_results = False # Resetear resultados cuando el texto cambia
+
+ # Text area con manejo de estado
+ text_input = st.text_area(
+ t.get('input_prompt', "Escribe o pega tu texto aquí:"),
+ height=400,
+ key="text_area",
+ value=st.session_state.text_input,
+ on_change=on_text_change,
+ help="Este texto será analizado para darte recomendaciones personalizadas"
+ )
+
+ if st.button(
+ t.get('analyze_button', "Analizar mi escritura"),
+ type="primary",
+ disabled=not text_input.strip(),
+ use_container_width=True,
+ ):
+ try:
+ with st.spinner(t.get('processing', "Analizando...")):
+ # Procesar texto y obtener métricas
+ doc = nlp_models[lang_code](text_input)
+ metrics = analyze_text_dimensions(doc)
+
+ # Guardar en MongoDB
+ storage_success = store_current_situation_result(
+ username=st.session_state.username,
+ text=text_input,
+ metrics=metrics,
+ feedback=None # Por ahora sin feedback
+ )
+
+ if not storage_success:
+ logger.warning("No se pudo guardar el análisis en la base de datos")
+
+ # Actualizar estado
+ st.session_state.current_doc = doc
+ st.session_state.current_metrics = metrics
+ st.session_state.show_results = True
+ st.session_state.text_input = text_input
+
+ except Exception as e:
+ logger.error(f"Error en análisis: {str(e)}")
+ st.error(t.get('analysis_error', "Error al analizar el texto"))
+
+ # Mostrar resultados en la columna derecha
+ with results_col:
+ if st.session_state.show_results and st.session_state.current_metrics is not None:
+ display_recommendations(st.session_state.current_metrics, t)
+
+ # Opción para ver detalles
+ with st.expander("🔍 Ver análisis detallado", expanded=False):
+ display_current_situation_visual(
+ st.session_state.current_doc,
+ st.session_state.current_metrics
+ )
+
+ except Exception as e:
+ logger.error(f"Error en interfaz: {str(e)}")
+ st.error("Ocurrió un error. Por favor, intente de nuevo.")
+
+
+
+def display_current_situation_visual(doc, metrics):
+ """
+ Muestra visualizaciones detalladas del análisis.
+ """
+ try:
+ st.markdown("### 📊 Visualizaciones Detalladas")
+
+ # 1. Visualización de vocabulario
+ with st.expander("Análisis de Vocabulario", expanded=True):
+ vocab_graph = create_vocabulary_network(doc)
+ if vocab_graph:
+ st.pyplot(vocab_graph)
+ plt.close(vocab_graph)
+
+ # 2. Visualización de estructura
+ with st.expander("Análisis de Estructura", expanded=True):
+ syntax_graph = create_syntax_complexity_graph(doc)
+ if syntax_graph:
+ st.pyplot(syntax_graph)
+ plt.close(syntax_graph)
+
+ # 3. Visualización de cohesión
+ with st.expander("Análisis de Cohesión", expanded=True):
+ cohesion_graph = create_cohesion_heatmap(doc)
+ if cohesion_graph:
+ st.pyplot(cohesion_graph)
+ plt.close(cohesion_graph)
+
+ except Exception as e:
+ logger.error(f"Error en visualización: {str(e)}")
+ st.error("Error al generar las visualizaciones")
+
+
+####################################
+def display_recommendations(metrics, t):
+ """
+ Muestra recomendaciones basadas en las métricas del texto.
+ """
+ # 1. Resumen Visual con Explicación
+ st.markdown("### 📊 Resumen de tu Análisis")
+
+ # Explicación del sistema de medición
+ st.markdown("""
+ **¿Cómo interpretar los resultados?**
+
+ Cada métrica se mide en una escala de 0.0 a 1.0, donde:
+ - 0.0 - 0.4: Necesita atención prioritaria
+ - 0.4 - 0.6: En desarrollo
+ - 0.6 - 0.8: Buen nivel
+ - 0.8 - 1.0: Nivel avanzado
+ """)
+
+ # Métricas con explicaciones detalladas
+ col1, col2, col3, col4 = st.columns(4)
+
+ with col1:
+ st.metric(
+ "Vocabulario",
+ f"{metrics['vocabulary']['normalized_score']:.2f}",
+ help="Mide la variedad y riqueza de tu vocabulario. Un valor alto indica un uso diverso de palabras sin repeticiones excesivas."
+ )
+ with st.expander("ℹ️ Detalles"):
+ st.write("""
+ **Vocabulario**
+ - Evalúa la diversidad léxica
+ - Considera palabras únicas vs. totales
+ - Detecta repeticiones innecesarias
+ - Valor óptimo: > 0.7
+ """)
+
+ with col2:
+ st.metric(
+ "Estructura",
+ f"{metrics['structure']['normalized_score']:.2f}",
+ help="Evalúa la complejidad y variedad de las estructuras sintácticas en tus oraciones."
+ )
+ with st.expander("ℹ️ Detalles"):
+ st.write("""
+ **Estructura**
+ - Analiza la complejidad sintáctica
+ - Mide variación en construcciones
+ - Evalúa longitud de oraciones
+ - Valor óptimo: > 0.6
+ """)
+
+ with col3:
+ st.metric(
+ "Cohesión",
+ f"{metrics['cohesion']['normalized_score']:.2f}",
+ help="Indica qué tan bien conectadas están tus ideas y párrafos entre sí."
+ )
+ with st.expander("ℹ️ Detalles"):
+ st.write("""
+ **Cohesión**
+ - Mide conexiones entre ideas
+ - Evalúa uso de conectores
+ - Analiza progresión temática
+ - Valor óptimo: > 0.65
+ """)
+
+ with col4:
+ st.metric(
+ "Claridad",
+ f"{metrics['clarity']['normalized_score']:.2f}",
+ help="Evalúa la facilidad de comprensión general de tu texto."
+ )
+ with st.expander("ℹ️ Detalles"):
+ st.write("""
+ **Claridad**
+ - Evalúa comprensibilidad
+ - Considera estructura lógica
+ - Mide precisión expresiva
+ - Valor óptimo: > 0.7
+ """)
+
+ st.markdown("---")
+
+ # 2. Recomendaciones basadas en puntuaciones
+ st.markdown("### 💡 Recomendaciones Personalizadas")
+
+ # Recomendaciones morfosintácticas
+ if metrics['structure']['normalized_score'] < 0.6:
+ st.warning("""
+ #### 📝 Análisis Morfosintáctico Recomendado
+
+ **Tu nivel actual sugiere que sería beneficioso:**
+ 1. Realizar el análisis morfosintáctico de 3 párrafos diferentes
+ 2. Practicar la combinación de oraciones simples en compuestas
+ 3. Identificar y clasificar tipos de oraciones en textos académicos
+ 4. Ejercitar la variación sintáctica
+
+ *Hacer clic en "Comenzar ejercicios" para acceder al módulo morfosintáctico*
+ """)
+
+ # Recomendaciones semánticas
+ if metrics['vocabulary']['normalized_score'] < 0.7:
+ st.warning("""
+ #### 📚 Análisis Semántico Recomendado
+
+ **Para mejorar tu vocabulario y expresión:**
+ A. Realiza el análisis semántico de un texto académico
+ B. Identifica y agrupa campos semánticos relacionados
+ C. Practica la sustitución léxica en tus párrafos
+ D. Construye redes de conceptos sobre tu tema
+ E. Analiza las relaciones entre ideas principales
+
+ *Hacer clic en "Comenzar ejercicios" para acceder al módulo semántico*
+ """)
+
+ # Recomendaciones de cohesión
+ if metrics['cohesion']['normalized_score'] < 0.65:
+ st.warning("""
+ #### 🔄 Análisis del Discurso Recomendado
+
+ **Para mejorar la conexión entre ideas:**
+ 1. Realizar el análisis del discurso de un texto modelo
+ 2. Practicar el uso de diferentes conectores textuales
+ 3. Identificar cadenas de referencia en textos académicos
+ 4. Ejercitar la progresión temática en tus escritos
+
+ *Hacer clic en "Comenzar ejercicios" para acceder al módulo de análisis del discurso*
+ """)
+
+ # Botón de acción
+ st.markdown("---")
+ col1, col2, col3 = st.columns([1,2,1])
+ with col2:
+ st.button(
+ "🎯 Comenzar ejercicios recomendados",
+ type="primary",
+ use_container_width=True,
+ key="start_exercises"
+ )
diff --git a/modules/studentact/current_situation_interface-v3.py b/modules/studentact/current_situation_interface-v3.py
index 1646ab980c38d96e0916a80b7ac262b07c69836b..599801971ea94f92e107469154a201bf248825cd 100644
--- a/modules/studentact/current_situation_interface-v3.py
+++ b/modules/studentact/current_situation_interface-v3.py
@@ -1,190 +1,190 @@
-# modules/studentact/current_situation_interface.py
-
-import streamlit as st
-import logging
-from ..utils.widget_utils import generate_unique_key
-import matplotlib.pyplot as plt
-import numpy as np
-from ..database.current_situation_mongo_db import store_current_situation_result
-
-from .current_situation_analysis import (
- analyze_text_dimensions,
- analyze_clarity,
- analyze_reference_clarity,
- analyze_vocabulary_diversity,
- analyze_cohesion,
- analyze_structure,
- get_dependency_depths,
- normalize_score,
- generate_sentence_graphs,
- generate_word_connections,
- generate_connection_paths,
- create_vocabulary_network,
- create_syntax_complexity_graph,
- create_cohesion_heatmap,
-)
-
-# Configuración del estilo de matplotlib para el gráfico de radar
-plt.rcParams['font.family'] = 'sans-serif'
-plt.rcParams['axes.grid'] = True
-plt.rcParams['axes.spines.top'] = False
-plt.rcParams['axes.spines.right'] = False
-
-logger = logging.getLogger(__name__)
-####################################
-
-def display_current_situation_interface(lang_code, nlp_models, t):
- """
- Interfaz simplificada con gráfico de radar para visualizar métricas.
- """
- try:
- # Inicializar estados si no existen
- if 'text_input' not in st.session_state:
- st.session_state.text_input = ""
- if 'show_results' not in st.session_state:
- st.session_state.show_results = False
- if 'current_doc' not in st.session_state:
- st.session_state.current_doc = None
- if 'current_metrics' not in st.session_state:
- st.session_state.current_metrics = None
-
- st.markdown("## Análisis Inicial de Escritura")
-
- # Container principal con dos columnas
- with st.container():
- input_col, results_col = st.columns([1,2])
-
- with input_col:
- #st.markdown("### Ingresa tu texto")
-
- # Función para manejar cambios en el texto
- def on_text_change():
- st.session_state.text_input = st.session_state.text_area
- st.session_state.show_results = False
-
- # Text area con manejo de estado
- text_input = st.text_area(
- t.get('input_prompt', "Escribe o pega tu texto aquí:"),
- height=400,
- key="text_area",
- value=st.session_state.text_input,
- on_change=on_text_change,
- help="Este texto será analizado para darte recomendaciones personalizadas"
- )
-
- if st.button(
- t.get('analyze_button', "Analizar mi escritura"),
- type="primary",
- disabled=not text_input.strip(),
- use_container_width=True,
- ):
- try:
- with st.spinner(t.get('processing', "Analizando...")):
- doc = nlp_models[lang_code](text_input)
- metrics = analyze_text_dimensions(doc)
-
- # Guardar en MongoDB
- storage_success = store_current_situation_result(
- username=st.session_state.username,
- text=text_input,
- metrics=metrics,
- feedback=None
- )
-
- if not storage_success:
- logger.warning("No se pudo guardar el análisis en la base de datos")
-
- st.session_state.current_doc = doc
- st.session_state.current_metrics = metrics
- st.session_state.show_results = True
- st.session_state.text_input = text_input
-
- except Exception as e:
- logger.error(f"Error en análisis: {str(e)}")
- st.error(t.get('analysis_error', "Error al analizar el texto"))
-
- # Mostrar resultados en la columna derecha
- with results_col:
- if st.session_state.show_results and st.session_state.current_metrics is not None:
- display_radar_chart(st.session_state.current_metrics)
-
- except Exception as e:
- logger.error(f"Error en interfaz: {str(e)}")
- st.error("Ocurrió un error. Por favor, intente de nuevo.")
-
-def display_radar_chart(metrics):
- """
- Muestra un gráfico de radar con las métricas del usuario y el patrón ideal.
- """
- try:
- # Container con proporción reducida
- with st.container():
- # Métricas en la parte superior
- col1, col2, col3, col4 = st.columns(4)
- with col1:
- st.metric("Vocabulario", f"{metrics['vocabulary']['normalized_score']:.2f}", "1.00")
- with col2:
- st.metric("Estructura", f"{metrics['structure']['normalized_score']:.2f}", "1.00")
- with col3:
- st.metric("Cohesión", f"{metrics['cohesion']['normalized_score']:.2f}", "1.00")
- with col4:
- st.metric("Claridad", f"{metrics['clarity']['normalized_score']:.2f}", "1.00")
-
- # Contenedor para el gráfico con ancho controlado
- _, graph_col, _ = st.columns([1,2,1])
-
- with graph_col:
- # Preparar datos
- categories = ['Vocabulario', 'Estructura', 'Cohesión', 'Claridad']
- values_user = [
- metrics['vocabulary']['normalized_score'],
- metrics['structure']['normalized_score'],
- metrics['cohesion']['normalized_score'],
- metrics['clarity']['normalized_score']
- ]
- values_pattern = [1.0, 1.0, 1.0, 1.0] # Patrón ideal
-
- # Crear figura más compacta
- fig = plt.figure(figsize=(6, 6))
- ax = fig.add_subplot(111, projection='polar')
-
- # Número de variables
- num_vars = len(categories)
-
- # Calcular ángulos
- angles = [n / float(num_vars) * 2 * np.pi for n in range(num_vars)]
- angles += angles[:1]
-
- # Extender valores para cerrar polígonos
- values_user += values_user[:1]
- values_pattern += values_pattern[:1]
-
- # Configurar ejes y etiquetas
- ax.set_xticks(angles[:-1])
- ax.set_xticklabels(categories, fontsize=8)
-
- # Círculos concéntricos y etiquetas
- circle_ticks = np.arange(0, 1.1, 0.2) # Reducido a 5 niveles
- ax.set_yticks(circle_ticks)
- ax.set_yticklabels([f'{tick:.1f}' for tick in circle_ticks], fontsize=8)
- ax.set_ylim(0, 1)
-
- # Dibujar patrón ideal
- ax.plot(angles, values_pattern, 'g--', linewidth=1, label='Patrón', alpha=0.5)
- ax.fill(angles, values_pattern, 'g', alpha=0.1)
-
- # Dibujar valores del usuario
- ax.plot(angles, values_user, 'b-', linewidth=2, label='Tu escritura')
- ax.fill(angles, values_user, 'b', alpha=0.2)
-
- # Leyenda
- ax.legend(loc='upper right', bbox_to_anchor=(0.1, 0.1), fontsize=8)
-
- # Ajustes finales
- plt.tight_layout()
- st.pyplot(fig)
- plt.close()
-
- except Exception as e:
- logger.error(f"Error generando gráfico de radar: {str(e)}")
+# modules/studentact/current_situation_interface.py
+
+import streamlit as st
+import logging
+from ..utils.widget_utils import generate_unique_key
+import matplotlib.pyplot as plt
+import numpy as np
+from ..database.current_situation_mongo_db import store_current_situation_result
+
+from .current_situation_analysis import (
+ analyze_text_dimensions,
+ analyze_clarity,
+ analyze_reference_clarity,
+ analyze_vocabulary_diversity,
+ analyze_cohesion,
+ analyze_structure,
+ get_dependency_depths,
+ normalize_score,
+ generate_sentence_graphs,
+ generate_word_connections,
+ generate_connection_paths,
+ create_vocabulary_network,
+ create_syntax_complexity_graph,
+ create_cohesion_heatmap,
+)
+
+# Configuración del estilo de matplotlib para el gráfico de radar
+plt.rcParams['font.family'] = 'sans-serif'
+plt.rcParams['axes.grid'] = True
+plt.rcParams['axes.spines.top'] = False
+plt.rcParams['axes.spines.right'] = False
+
+logger = logging.getLogger(__name__)
+####################################
+
+def display_current_situation_interface(lang_code, nlp_models, t):
+ """
+ Interfaz simplificada con gráfico de radar para visualizar métricas.
+ """
+ try:
+ # Inicializar estados si no existen
+ if 'text_input' not in st.session_state:
+ st.session_state.text_input = ""
+ if 'show_results' not in st.session_state:
+ st.session_state.show_results = False
+ if 'current_doc' not in st.session_state:
+ st.session_state.current_doc = None
+ if 'current_metrics' not in st.session_state:
+ st.session_state.current_metrics = None
+
+ st.markdown("## Análisis Inicial de Escritura")
+
+ # Container principal con dos columnas
+ with st.container():
+ input_col, results_col = st.columns([1,2])
+
+ with input_col:
+ #st.markdown("### Ingresa tu texto")
+
+ # Función para manejar cambios en el texto
+ def on_text_change():
+ st.session_state.text_input = st.session_state.text_area
+ st.session_state.show_results = False
+
+ # Text area con manejo de estado
+ text_input = st.text_area(
+ t.get('input_prompt', "Escribe o pega tu texto aquí:"),
+ height=400,
+ key="text_area",
+ value=st.session_state.text_input,
+ on_change=on_text_change,
+ help="Este texto será analizado para darte recomendaciones personalizadas"
+ )
+
+ if st.button(
+ t.get('analyze_button', "Analizar mi escritura"),
+ type="primary",
+ disabled=not text_input.strip(),
+ use_container_width=True,
+ ):
+ try:
+ with st.spinner(t.get('processing', "Analizando...")):
+ doc = nlp_models[lang_code](text_input)
+ metrics = analyze_text_dimensions(doc)
+
+ # Guardar en MongoDB
+ storage_success = store_current_situation_result(
+ username=st.session_state.username,
+ text=text_input,
+ metrics=metrics,
+ feedback=None
+ )
+
+ if not storage_success:
+ logger.warning("No se pudo guardar el análisis en la base de datos")
+
+ st.session_state.current_doc = doc
+ st.session_state.current_metrics = metrics
+ st.session_state.show_results = True
+ st.session_state.text_input = text_input
+
+ except Exception as e:
+ logger.error(f"Error en análisis: {str(e)}")
+ st.error(t.get('analysis_error', "Error al analizar el texto"))
+
+ # Mostrar resultados en la columna derecha
+ with results_col:
+ if st.session_state.show_results and st.session_state.current_metrics is not None:
+ display_radar_chart(st.session_state.current_metrics)
+
+ except Exception as e:
+ logger.error(f"Error en interfaz: {str(e)}")
+ st.error("Ocurrió un error. Por favor, intente de nuevo.")
+
+def display_radar_chart(metrics):
+ """
+ Muestra un gráfico de radar con las métricas del usuario y el patrón ideal.
+ """
+ try:
+ # Container con proporción reducida
+ with st.container():
+ # Métricas en la parte superior
+ col1, col2, col3, col4 = st.columns(4)
+ with col1:
+ st.metric("Vocabulario", f"{metrics['vocabulary']['normalized_score']:.2f}", "1.00")
+ with col2:
+ st.metric("Estructura", f"{metrics['structure']['normalized_score']:.2f}", "1.00")
+ with col3:
+ st.metric("Cohesión", f"{metrics['cohesion']['normalized_score']:.2f}", "1.00")
+ with col4:
+ st.metric("Claridad", f"{metrics['clarity']['normalized_score']:.2f}", "1.00")
+
+ # Contenedor para el gráfico con ancho controlado
+ _, graph_col, _ = st.columns([1,2,1])
+
+ with graph_col:
+ # Preparar datos
+ categories = ['Vocabulario', 'Estructura', 'Cohesión', 'Claridad']
+ values_user = [
+ metrics['vocabulary']['normalized_score'],
+ metrics['structure']['normalized_score'],
+ metrics['cohesion']['normalized_score'],
+ metrics['clarity']['normalized_score']
+ ]
+ values_pattern = [1.0, 1.0, 1.0, 1.0] # Patrón ideal
+
+ # Crear figura más compacta
+ fig = plt.figure(figsize=(6, 6))
+ ax = fig.add_subplot(111, projection='polar')
+
+ # Número de variables
+ num_vars = len(categories)
+
+ # Calcular ángulos
+ angles = [n / float(num_vars) * 2 * np.pi for n in range(num_vars)]
+ angles += angles[:1]
+
+ # Extender valores para cerrar polígonos
+ values_user += values_user[:1]
+ values_pattern += values_pattern[:1]
+
+ # Configurar ejes y etiquetas
+ ax.set_xticks(angles[:-1])
+ ax.set_xticklabels(categories, fontsize=8)
+
+ # Círculos concéntricos y etiquetas
+ circle_ticks = np.arange(0, 1.1, 0.2) # Reducido a 5 niveles
+ ax.set_yticks(circle_ticks)
+ ax.set_yticklabels([f'{tick:.1f}' for tick in circle_ticks], fontsize=8)
+ ax.set_ylim(0, 1)
+
+ # Dibujar patrón ideal
+ ax.plot(angles, values_pattern, 'g--', linewidth=1, label='Patrón', alpha=0.5)
+ ax.fill(angles, values_pattern, 'g', alpha=0.1)
+
+ # Dibujar valores del usuario
+ ax.plot(angles, values_user, 'b-', linewidth=2, label='Tu escritura')
+ ax.fill(angles, values_user, 'b', alpha=0.2)
+
+ # Leyenda
+ ax.legend(loc='upper right', bbox_to_anchor=(0.1, 0.1), fontsize=8)
+
+ # Ajustes finales
+ plt.tight_layout()
+ st.pyplot(fig)
+ plt.close()
+
+ except Exception as e:
+ logger.error(f"Error generando gráfico de radar: {str(e)}")
st.error("Error al generar la visualización")
\ No newline at end of file
diff --git a/modules/studentact/current_situation_interface.py b/modules/studentact/current_situation_interface.py
index 5790793b09e44622e9ae116dffc0cd424a11f214..27d522bea7a017121700e993e9155d787f3e3aea 100644
--- a/modules/studentact/current_situation_interface.py
+++ b/modules/studentact/current_situation_interface.py
@@ -1,448 +1,448 @@
-# modules/studentact/current_situation_interface.py
-
-import streamlit as st
-import logging
-from ..utils.widget_utils import generate_unique_key
-import matplotlib.pyplot as plt
-import numpy as np
-from ..database.current_situation_mongo_db import store_current_situation_result
-
-# Importaciones locales
-from translations import get_translations
-
-# Importamos la función de recomendaciones personalizadas si existe
-try:
- from .claude_recommendations import display_personalized_recommendations
-except ImportError:
- # Si no existe el módulo, definimos una función placeholder
- def display_personalized_recommendations(text, metrics, text_type, lang_code, t):
- # Obtener el mensaje de advertencia traducido si está disponible
- warning = t.get('module_not_available', "Módulo de recomendaciones personalizadas no disponible. Por favor, contacte al administrador.")
- st.warning(warning)
-
-from .current_situation_analysis import (
- analyze_text_dimensions,
- analyze_clarity,
- analyze_vocabulary_diversity,
- analyze_cohesion,
- analyze_structure,
- get_dependency_depths,
- normalize_score,
- generate_sentence_graphs,
- generate_word_connections,
- generate_connection_paths,
- create_vocabulary_network,
- create_syntax_complexity_graph,
- create_cohesion_heatmap
-)
-
-# Configuración del estilo de matplotlib para el gráfico de radar
-plt.rcParams['font.family'] = 'sans-serif'
-plt.rcParams['axes.grid'] = True
-plt.rcParams['axes.spines.top'] = False
-plt.rcParams['axes.spines.right'] = False
-
-logger = logging.getLogger(__name__)
-
-# Definición de tipos de texto con umbrales
-TEXT_TYPES = {
- 'academic_article': {
- # Los nombres se obtendrán de las traducciones
- 'thresholds': {
- 'vocabulary': {'min': 0.70, 'target': 0.85},
- 'structure': {'min': 0.75, 'target': 0.90},
- 'cohesion': {'min': 0.65, 'target': 0.80},
- 'clarity': {'min': 0.70, 'target': 0.85}
- }
- },
- 'student_essay': {
- 'thresholds': {
- 'vocabulary': {'min': 0.60, 'target': 0.75},
- 'structure': {'min': 0.65, 'target': 0.80},
- 'cohesion': {'min': 0.55, 'target': 0.70},
- 'clarity': {'min': 0.60, 'target': 0.75}
- }
- },
- 'general_communication': {
- 'thresholds': {
- 'vocabulary': {'min': 0.50, 'target': 0.65},
- 'structure': {'min': 0.55, 'target': 0.70},
- 'cohesion': {'min': 0.45, 'target': 0.60},
- 'clarity': {'min': 0.50, 'target': 0.65}
- }
- }
-}
-
-####################################################
-####################################################
-def display_current_situation_interface(lang_code, nlp_models, t):
- """
- Interfaz simplificada con gráfico de radar para visualizar métricas.
- """
- # Agregar logs para depuración
- logger.info(f"Idioma: {lang_code}")
- logger.info(f"Claves en t: {list(t.keys())}")
-
- # Inicializar estados si no existen
- if 'text_input' not in st.session_state:
- st.session_state.text_input = ""
- if 'text_area' not in st.session_state:
- st.session_state.text_area = ""
- if 'show_results' not in st.session_state:
- st.session_state.show_results = False
- if 'current_doc' not in st.session_state:
- st.session_state.current_doc = None
- if 'current_metrics' not in st.session_state:
- st.session_state.current_metrics = None
- if 'current_recommendations' not in st.session_state:
- st.session_state.current_recommendations = None
-
- try:
- # Container principal con dos columnas
- with st.container():
- input_col, results_col = st.columns([1,2])
-
-###############################################################################################
- # CSS personalizado para que el formulario ocupe todo el alto disponible
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
-###############################################################################################
- with input_col:
- with st.form(key=f"text_input_form_{lang_code}"):
- text_input = st.text_area(
- t.get('input_prompt', "Escribe o pega tu texto aquí:"),
- height=800,
- key=f"text_area_{lang_code}",
- value=st.session_state.text_input,
- help=t.get('help', "Este texto será analizado para darte recomendaciones personalizadas")
- )
-
- submit_button = st.form_submit_button(
- t.get('analyze_button', "Analizar mi escritura"),
- type="primary",
- use_container_width=True
- )
-
- if submit_button:
- if text_input.strip():
- st.session_state.text_input = text_input
-
-#######################################################################
- # Código para análisis...
- try:
- with st.spinner(t.get('processing', "Analizando...")): # Usando t.get directamente
- doc = nlp_models[lang_code](text_input)
- metrics = analyze_text_dimensions(doc)
-
- storage_success = store_current_situation_result(
- username=st.session_state.username,
- text=text_input,
- metrics=metrics,
- feedback=None
- )
-
- if not storage_success:
- logger.warning("No se pudo guardar el análisis en la base de datos")
-
- st.session_state.current_doc = doc
- st.session_state.current_metrics = metrics
- st.session_state.show_results = True
-
- except Exception as e:
- logger.error(f"Error en análisis: {str(e)}")
- st.error(t.get('analysis_error', "Error al analizar el texto")) # Usando t.get directamente
-
- # Mostrar resultados en la columna derecha
- with results_col:
- if st.session_state.show_results and st.session_state.current_metrics is not None:
- # Primero los radio buttons para tipo de texto - usando t.get directamente
- st.markdown(f"### {t.get('text_type_header', 'Tipo de texto')}")
-
- # Preparar opciones de tipos de texto con nombres traducidos
- text_type_options = {}
- for text_type_key in TEXT_TYPES.keys():
- # Fallback a nombres genéricos si no hay traducción
- default_names = {
- 'academic_article': 'Academic Article' if lang_code == 'en' else 'Article Académique' if lang_code == 'fr' else 'Artigo Acadêmico' if lang_code == 'pt' else 'Artículo Académico',
- 'student_essay': 'Student Essay' if lang_code == 'en' else 'Devoir Universitaire' if lang_code == 'fr' else 'Trabalho Universitário' if lang_code == 'pt' else 'Trabajo Universitario',
- 'general_communication': 'General Communication' if lang_code == 'en' else 'Communication Générale' if lang_code == 'fr' else 'Comunicação Geral' if lang_code == 'pt' else 'Comunicación General'
- }
- text_type_options[text_type_key] = default_names.get(text_type_key, text_type_key)
-
- text_type = st.radio(
- label=t.get('text_type_header', "Tipo de texto"), # Usando t.get directamente
- options=list(TEXT_TYPES.keys()),
- format_func=lambda x: text_type_options.get(x, x),
- horizontal=True,
- key="text_type_radio",
- label_visibility="collapsed",
- help=t.get('text_type_help', "Selecciona el tipo de texto para ajustar los criterios de evaluación") # Usando t.get directamente
- )
-
- st.session_state.current_text_type = text_type
-
- # Crear subtabs con nombres traducidos
- diagnosis_tab = "Diagnosis" if lang_code == 'en' else "Diagnostic" if lang_code == 'fr' else "Diagnóstico" if lang_code == 'pt' else "Diagnóstico"
- recommendations_tab = "Recommendations" if lang_code == 'en' else "Recommandations" if lang_code == 'fr' else "Recomendações" if lang_code == 'pt' else "Recomendaciones"
-
- subtab1, subtab2 = st.tabs([diagnosis_tab, recommendations_tab])
-
- # Mostrar resultados en el primer subtab
- with subtab1:
- display_diagnosis(
- metrics=st.session_state.current_metrics,
- text_type=text_type,
- lang_code=lang_code,
- t=t # Pasar t directamente, no current_situation_t
- )
-
- # Mostrar recomendaciones en el segundo subtab
- with subtab2:
- # Llamar directamente a la función de recomendaciones personalizadas
- display_personalized_recommendations(
- text=text_input,
- metrics=st.session_state.current_metrics,
- text_type=text_type,
- lang_code=lang_code,
- t=t
- )
-
- except Exception as e:
- logger.error(f"Error en interfaz principal: {str(e)}")
- st.error(t.get('error_interface', "Ocurrió un error al cargar la interfaz")) # Usando t.get directamente
-
-#################################################################
-#################################################################
-def display_diagnosis(metrics, text_type=None, lang_code='es', t=None):
- """
- Muestra los resultados del análisis: métricas verticalmente y gráfico radar.
- """
- try:
- # Asegurar que tenemos traducciones
- if t is None:
- t = {}
-
- # Traducciones para títulos y etiquetas
- dimension_labels = {
- 'es': {
- 'title': "Tipo de texto",
- 'vocabulary': "Vocabulario",
- 'structure': "Estructura",
- 'cohesion': "Cohesión",
- 'clarity': "Claridad",
- 'improvement': "⚠️ Por mejorar",
- 'acceptable': "📈 Aceptable",
- 'optimal': "✅ Óptimo",
- 'target': "Meta: {:.2f}"
- },
- 'en': {
- 'title': "Text Type",
- 'vocabulary': "Vocabulary",
- 'structure': "Structure",
- 'cohesion': "Cohesion",
- 'clarity': "Clarity",
- 'improvement': "⚠️ Needs improvement",
- 'acceptable': "📈 Acceptable",
- 'optimal': "✅ Optimal",
- 'target': "Target: {:.2f}"
- },
- 'fr': {
- 'title': "Type de texte",
- 'vocabulary': "Vocabulaire",
- 'structure': "Structure",
- 'cohesion': "Cohésion",
- 'clarity': "Clarté",
- 'improvement': "⚠️ À améliorer",
- 'acceptable': "📈 Acceptable",
- 'optimal': "✅ Optimal",
- 'target': "Objectif: {:.2f}"
- },
- 'pt': {
- 'title': "Tipo de texto",
- 'vocabulary': "Vocabulário",
- 'structure': "Estrutura",
- 'cohesion': "Coesão",
- 'clarity': "Clareza",
- 'improvement': "⚠️ Precisa melhorar",
- 'acceptable': "📈 Aceitável",
- 'optimal': "✅ Ótimo",
- 'target': "Meta: {:.2f}"
- }
- }
-
- # Obtener traducciones para el idioma actual, con fallback a español
- labels = dimension_labels.get(lang_code, dimension_labels['es'])
-
- # Usar valor por defecto si no se especifica tipo
- text_type = text_type or 'student_essay'
-
- # Obtener umbrales según el tipo de texto
- thresholds = TEXT_TYPES[text_type]['thresholds']
-
- # Crear dos columnas para las métricas y el gráfico
- metrics_col, graph_col = st.columns([1, 1.5])
-
- # Columna de métricas
- with metrics_col:
- metrics_config = [
- {
- 'label': labels['vocabulary'],
- 'key': 'vocabulary',
- 'value': metrics['vocabulary']['normalized_score'],
- 'help': t.get('vocabulary_help', "Riqueza y variedad del vocabulario"),
- 'thresholds': thresholds['vocabulary']
- },
- {
- 'label': labels['structure'],
- 'key': 'structure',
- 'value': metrics['structure']['normalized_score'],
- 'help': t.get('structure_help', "Organización y complejidad de oraciones"),
- 'thresholds': thresholds['structure']
- },
- {
- 'label': labels['cohesion'],
- 'key': 'cohesion',
- 'value': metrics['cohesion']['normalized_score'],
- 'help': t.get('cohesion_help', "Conexión y fluidez entre ideas"),
- 'thresholds': thresholds['cohesion']
- },
- {
- 'label': labels['clarity'],
- 'key': 'clarity',
- 'value': metrics['clarity']['normalized_score'],
- 'help': t.get('clarity_help', "Facilidad de comprensión del texto"),
- 'thresholds': thresholds['clarity']
- }
- ]
-
- # Mostrar métricas con textos traducidos
- for metric in metrics_config:
- value = metric['value']
- if value < metric['thresholds']['min']:
- status = labels['improvement']
- color = "inverse"
- elif value < metric['thresholds']['target']:
- status = labels['acceptable']
- color = "off"
- else:
- status = labels['optimal']
- color = "normal"
-
- target_text = labels['target'].format(metric['thresholds']['target'])
-
- st.metric(
- metric['label'],
- f"{value:.2f}",
- f"{status} ({target_text})",
- delta_color=color,
- help=metric['help']
- )
- st.markdown("
", unsafe_allow_html=True)
-
- # Gráfico radar en la columna derecha
- with graph_col:
- display_radar_chart(metrics_config, thresholds, lang_code) # Pasar el parámetro lang_code
-
- except Exception as e:
- logger.error(f"Error mostrando resultados: {str(e)}")
- st.error(t.get('error_results', "Error al mostrar los resultados"))
-
-##################################################################
-##################################################################
-def display_radar_chart(metrics_config, thresholds, lang_code='es'):
- """
- Muestra el gráfico radar con los resultados.
- """
- try:
- # Traducción de las etiquetas de leyenda según el idioma
- legend_translations = {
- 'es': {'min': 'Mínimo', 'target': 'Meta', 'user': 'Tu escritura'},
- 'en': {'min': 'Minimum', 'target': 'Target', 'user': 'Your writing'},
- 'fr': {'min': 'Minimum', 'target': 'Objectif', 'user': 'Votre écriture'},
- 'pt': {'min': 'Mínimo', 'target': 'Meta', 'user': 'Sua escrita'}
- }
-
- # Usar español por defecto si el idioma no está soportado
- translations = legend_translations.get(lang_code, legend_translations['es'])
-
- # Preparar datos para el gráfico
- categories = [m['label'] for m in metrics_config]
- values_user = [m['value'] for m in metrics_config]
- min_values = [m['thresholds']['min'] for m in metrics_config]
- target_values = [m['thresholds']['target'] for m in metrics_config]
-
- # Crear y configurar gráfico
- fig = plt.figure(figsize=(8, 8))
- ax = fig.add_subplot(111, projection='polar')
-
- # Configurar radar
- angles = [n / float(len(categories)) * 2 * np.pi for n in range(len(categories))]
- angles += angles[:1]
- values_user += values_user[:1]
- min_values += min_values[:1]
- target_values += target_values[:1]
-
- # Configurar ejes
- ax.set_xticks(angles[:-1])
- ax.set_xticklabels(categories, fontsize=10)
- circle_ticks = np.arange(0, 1.1, 0.2)
- ax.set_yticks(circle_ticks)
- ax.set_yticklabels([f'{tick:.1f}' for tick in circle_ticks], fontsize=8)
- ax.set_ylim(0, 1)
-
- # Dibujar áreas de umbrales con etiquetas traducidas
- ax.plot(angles, min_values, '#e74c3c', linestyle='--', linewidth=1, label=translations['min'], alpha=0.5)
- ax.plot(angles, target_values, '#2ecc71', linestyle='--', linewidth=1, label=translations['target'], alpha=0.5)
- ax.fill_between(angles, target_values, [1]*len(angles), color='#2ecc71', alpha=0.1)
- ax.fill_between(angles, [0]*len(angles), min_values, color='#e74c3c', alpha=0.1)
-
- # Dibujar valores del usuario con etiqueta traducida
- ax.plot(angles, values_user, '#3498db', linewidth=2, label=translations['user'])
- ax.fill(angles, values_user, '#3498db', alpha=0.2)
-
- # Ajustar leyenda
- ax.legend(
- loc='upper right',
- bbox_to_anchor=(1.3, 1.1),
- fontsize=10,
- frameon=True,
- facecolor='white',
- edgecolor='none',
- shadow=True
- )
-
- plt.tight_layout()
- st.pyplot(fig)
- plt.close()
-
- except Exception as e:
- logger.error(f"Error mostrando gráfico radar: {str(e)}")
+# modules/studentact/current_situation_interface.py
+
+import streamlit as st
+import logging
+from ..utils.widget_utils import generate_unique_key
+import matplotlib.pyplot as plt
+import numpy as np
+from ..database.current_situation_mongo_db import store_current_situation_result
+
+# Importaciones locales
+from translations import get_translations
+
+# Importamos la función de recomendaciones personalizadas si existe
+try:
+ from .claude_recommendations import display_personalized_recommendations
+except ImportError:
+ # Si no existe el módulo, definimos una función placeholder
+ def display_personalized_recommendations(text, metrics, text_type, lang_code, t):
+ # Obtener el mensaje de advertencia traducido si está disponible
+ warning = t.get('module_not_available', "Módulo de recomendaciones personalizadas no disponible. Por favor, contacte al administrador.")
+ st.warning(warning)
+
+from .current_situation_analysis import (
+ analyze_text_dimensions,
+ analyze_clarity,
+ analyze_vocabulary_diversity,
+ analyze_cohesion,
+ analyze_structure,
+ get_dependency_depths,
+ normalize_score,
+ generate_sentence_graphs,
+ generate_word_connections,
+ generate_connection_paths,
+ create_vocabulary_network,
+ create_syntax_complexity_graph,
+ create_cohesion_heatmap
+)
+
+# Configuración del estilo de matplotlib para el gráfico de radar
+plt.rcParams['font.family'] = 'sans-serif'
+plt.rcParams['axes.grid'] = True
+plt.rcParams['axes.spines.top'] = False
+plt.rcParams['axes.spines.right'] = False
+
+logger = logging.getLogger(__name__)
+
+# Definición de tipos de texto con umbrales
+TEXT_TYPES = {
+ 'academic_article': {
+ # Los nombres se obtendrán de las traducciones
+ 'thresholds': {
+ 'vocabulary': {'min': 0.70, 'target': 0.85},
+ 'structure': {'min': 0.75, 'target': 0.90},
+ 'cohesion': {'min': 0.65, 'target': 0.80},
+ 'clarity': {'min': 0.70, 'target': 0.85}
+ }
+ },
+ 'student_essay': {
+ 'thresholds': {
+ 'vocabulary': {'min': 0.60, 'target': 0.75},
+ 'structure': {'min': 0.65, 'target': 0.80},
+ 'cohesion': {'min': 0.55, 'target': 0.70},
+ 'clarity': {'min': 0.60, 'target': 0.75}
+ }
+ },
+ 'general_communication': {
+ 'thresholds': {
+ 'vocabulary': {'min': 0.50, 'target': 0.65},
+ 'structure': {'min': 0.55, 'target': 0.70},
+ 'cohesion': {'min': 0.45, 'target': 0.60},
+ 'clarity': {'min': 0.50, 'target': 0.65}
+ }
+ }
+}
+
+####################################################
+####################################################
+def display_current_situation_interface(lang_code, nlp_models, t):
+ """
+ Interfaz simplificada con gráfico de radar para visualizar métricas.
+ """
+ # Agregar logs para depuración
+ logger.info(f"Idioma: {lang_code}")
+ logger.info(f"Claves en t: {list(t.keys())}")
+
+ # Inicializar estados si no existen
+ if 'text_input' not in st.session_state:
+ st.session_state.text_input = ""
+ if 'text_area' not in st.session_state:
+ st.session_state.text_area = ""
+ if 'show_results' not in st.session_state:
+ st.session_state.show_results = False
+ if 'current_doc' not in st.session_state:
+ st.session_state.current_doc = None
+ if 'current_metrics' not in st.session_state:
+ st.session_state.current_metrics = None
+ if 'current_recommendations' not in st.session_state:
+ st.session_state.current_recommendations = None
+
+ try:
+ # Container principal con dos columnas
+ with st.container():
+ input_col, results_col = st.columns([1,2])
+
+###############################################################################################
+ # CSS personalizado para que el formulario ocupe todo el alto disponible
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+###############################################################################################
+ with input_col:
+ with st.form(key=f"text_input_form_{lang_code}"):
+ text_input = st.text_area(
+ t.get('input_prompt', "Escribe o pega tu texto aquí:"),
+ height=800,
+ key=f"text_area_{lang_code}",
+ value=st.session_state.text_input,
+ help=t.get('help', "Este texto será analizado para darte recomendaciones personalizadas")
+ )
+
+ submit_button = st.form_submit_button(
+ t.get('analyze_button', "Analizar mi escritura"),
+ type="primary",
+ use_container_width=True
+ )
+
+ if submit_button:
+ if text_input.strip():
+ st.session_state.text_input = text_input
+
+#######################################################################
+ # Código para análisis...
+ try:
+ with st.spinner(t.get('processing', "Analizando...")): # Usando t.get directamente
+ doc = nlp_models[lang_code](text_input)
+ metrics = analyze_text_dimensions(doc)
+
+ storage_success = store_current_situation_result(
+ username=st.session_state.username,
+ text=text_input,
+ metrics=metrics,
+ feedback=None
+ )
+
+ if not storage_success:
+ logger.warning("No se pudo guardar el análisis en la base de datos")
+
+ st.session_state.current_doc = doc
+ st.session_state.current_metrics = metrics
+ st.session_state.show_results = True
+
+ except Exception as e:
+ logger.error(f"Error en análisis: {str(e)}")
+ st.error(t.get('analysis_error', "Error al analizar el texto")) # Usando t.get directamente
+
+ # Mostrar resultados en la columna derecha
+ with results_col:
+ if st.session_state.show_results and st.session_state.current_metrics is not None:
+ # Primero los radio buttons para tipo de texto - usando t.get directamente
+ st.markdown(f"### {t.get('text_type_header', 'Tipo de texto')}")
+
+ # Preparar opciones de tipos de texto con nombres traducidos
+ text_type_options = {}
+ for text_type_key in TEXT_TYPES.keys():
+ # Fallback a nombres genéricos si no hay traducción
+ default_names = {
+ 'academic_article': 'Academic Article' if lang_code == 'en' else 'Article Académique' if lang_code == 'fr' else 'Artigo Acadêmico' if lang_code == 'pt' else 'Artículo Académico',
+ 'student_essay': 'Student Essay' if lang_code == 'en' else 'Devoir Universitaire' if lang_code == 'fr' else 'Trabalho Universitário' if lang_code == 'pt' else 'Trabajo Universitario',
+ 'general_communication': 'General Communication' if lang_code == 'en' else 'Communication Générale' if lang_code == 'fr' else 'Comunicação Geral' if lang_code == 'pt' else 'Comunicación General'
+ }
+ text_type_options[text_type_key] = default_names.get(text_type_key, text_type_key)
+
+ text_type = st.radio(
+ label=t.get('text_type_header', "Tipo de texto"), # Usando t.get directamente
+ options=list(TEXT_TYPES.keys()),
+ format_func=lambda x: text_type_options.get(x, x),
+ horizontal=True,
+ key="text_type_radio",
+ label_visibility="collapsed",
+ help=t.get('text_type_help', "Selecciona el tipo de texto para ajustar los criterios de evaluación") # Usando t.get directamente
+ )
+
+ st.session_state.current_text_type = text_type
+
+ # Crear subtabs con nombres traducidos
+ diagnosis_tab = "Diagnosis" if lang_code == 'en' else "Diagnostic" if lang_code == 'fr' else "Diagnóstico" if lang_code == 'pt' else "Diagnóstico"
+ recommendations_tab = "Recommendations" if lang_code == 'en' else "Recommandations" if lang_code == 'fr' else "Recomendações" if lang_code == 'pt' else "Recomendaciones"
+
+ subtab1, subtab2 = st.tabs([diagnosis_tab, recommendations_tab])
+
+ # Mostrar resultados en el primer subtab
+ with subtab1:
+ display_diagnosis(
+ metrics=st.session_state.current_metrics,
+ text_type=text_type,
+ lang_code=lang_code,
+ t=t # Pasar t directamente, no current_situation_t
+ )
+
+ # Mostrar recomendaciones en el segundo subtab
+ with subtab2:
+ # Llamar directamente a la función de recomendaciones personalizadas
+ display_personalized_recommendations(
+ text=text_input,
+ metrics=st.session_state.current_metrics,
+ text_type=text_type,
+ lang_code=lang_code,
+ t=t
+ )
+
+ except Exception as e:
+ logger.error(f"Error en interfaz principal: {str(e)}")
+ st.error(t.get('error_interface', "Ocurrió un error al cargar la interfaz")) # Usando t.get directamente
+
+#################################################################
+#################################################################
+def display_diagnosis(metrics, text_type=None, lang_code='es', t=None):
+ """
+ Muestra los resultados del análisis: métricas verticalmente y gráfico radar.
+ """
+ try:
+ # Asegurar que tenemos traducciones
+ if t is None:
+ t = {}
+
+ # Traducciones para títulos y etiquetas
+ dimension_labels = {
+ 'es': {
+ 'title': "Tipo de texto",
+ 'vocabulary': "Vocabulario",
+ 'structure': "Estructura",
+ 'cohesion': "Cohesión",
+ 'clarity': "Claridad",
+ 'improvement': "⚠️ Por mejorar",
+ 'acceptable': "📈 Aceptable",
+ 'optimal': "✅ Óptimo",
+ 'target': "Meta: {:.2f}"
+ },
+ 'en': {
+ 'title': "Text Type",
+ 'vocabulary': "Vocabulary",
+ 'structure': "Structure",
+ 'cohesion': "Cohesion",
+ 'clarity': "Clarity",
+ 'improvement': "⚠️ Needs improvement",
+ 'acceptable': "📈 Acceptable",
+ 'optimal': "✅ Optimal",
+ 'target': "Target: {:.2f}"
+ },
+ 'fr': {
+ 'title': "Type de texte",
+ 'vocabulary': "Vocabulaire",
+ 'structure': "Structure",
+ 'cohesion': "Cohésion",
+ 'clarity': "Clarté",
+ 'improvement': "⚠️ À améliorer",
+ 'acceptable': "📈 Acceptable",
+ 'optimal': "✅ Optimal",
+ 'target': "Objectif: {:.2f}"
+ },
+ 'pt': {
+ 'title': "Tipo de texto",
+ 'vocabulary': "Vocabulário",
+ 'structure': "Estrutura",
+ 'cohesion': "Coesão",
+ 'clarity': "Clareza",
+ 'improvement': "⚠️ Precisa melhorar",
+ 'acceptable': "📈 Aceitável",
+ 'optimal': "✅ Ótimo",
+ 'target': "Meta: {:.2f}"
+ }
+ }
+
+ # Obtener traducciones para el idioma actual, con fallback a español
+ labels = dimension_labels.get(lang_code, dimension_labels['es'])
+
+ # Usar valor por defecto si no se especifica tipo
+ text_type = text_type or 'student_essay'
+
+ # Obtener umbrales según el tipo de texto
+ thresholds = TEXT_TYPES[text_type]['thresholds']
+
+ # Crear dos columnas para las métricas y el gráfico
+ metrics_col, graph_col = st.columns([1, 1.5])
+
+ # Columna de métricas
+ with metrics_col:
+ metrics_config = [
+ {
+ 'label': labels['vocabulary'],
+ 'key': 'vocabulary',
+ 'value': metrics['vocabulary']['normalized_score'],
+ 'help': t.get('vocabulary_help', "Riqueza y variedad del vocabulario"),
+ 'thresholds': thresholds['vocabulary']
+ },
+ {
+ 'label': labels['structure'],
+ 'key': 'structure',
+ 'value': metrics['structure']['normalized_score'],
+ 'help': t.get('structure_help', "Organización y complejidad de oraciones"),
+ 'thresholds': thresholds['structure']
+ },
+ {
+ 'label': labels['cohesion'],
+ 'key': 'cohesion',
+ 'value': metrics['cohesion']['normalized_score'],
+ 'help': t.get('cohesion_help', "Conexión y fluidez entre ideas"),
+ 'thresholds': thresholds['cohesion']
+ },
+ {
+ 'label': labels['clarity'],
+ 'key': 'clarity',
+ 'value': metrics['clarity']['normalized_score'],
+ 'help': t.get('clarity_help', "Facilidad de comprensión del texto"),
+ 'thresholds': thresholds['clarity']
+ }
+ ]
+
+ # Mostrar métricas con textos traducidos
+ for metric in metrics_config:
+ value = metric['value']
+ if value < metric['thresholds']['min']:
+ status = labels['improvement']
+ color = "inverse"
+ elif value < metric['thresholds']['target']:
+ status = labels['acceptable']
+ color = "off"
+ else:
+ status = labels['optimal']
+ color = "normal"
+
+ target_text = labels['target'].format(metric['thresholds']['target'])
+
+ st.metric(
+ metric['label'],
+ f"{value:.2f}",
+ f"{status} ({target_text})",
+ delta_color=color,
+ help=metric['help']
+ )
+ st.markdown("
", unsafe_allow_html=True)
+
+ # Gráfico radar en la columna derecha
+ with graph_col:
+ display_radar_chart(metrics_config, thresholds, lang_code) # Pasar el parámetro lang_code
+
+ except Exception as e:
+ logger.error(f"Error mostrando resultados: {str(e)}")
+ st.error(t.get('error_results', "Error al mostrar los resultados"))
+
+##################################################################
+##################################################################
+def display_radar_chart(metrics_config, thresholds, lang_code='es'):
+ """
+ Muestra el gráfico radar con los resultados.
+ """
+ try:
+ # Traducción de las etiquetas de leyenda según el idioma
+ legend_translations = {
+ 'es': {'min': 'Mínimo', 'target': 'Meta', 'user': 'Tu escritura'},
+ 'en': {'min': 'Minimum', 'target': 'Target', 'user': 'Your writing'},
+ 'fr': {'min': 'Minimum', 'target': 'Objectif', 'user': 'Votre écriture'},
+ 'pt': {'min': 'Mínimo', 'target': 'Meta', 'user': 'Sua escrita'}
+ }
+
+ # Usar español por defecto si el idioma no está soportado
+ translations = legend_translations.get(lang_code, legend_translations['es'])
+
+ # Preparar datos para el gráfico
+ categories = [m['label'] for m in metrics_config]
+ values_user = [m['value'] for m in metrics_config]
+ min_values = [m['thresholds']['min'] for m in metrics_config]
+ target_values = [m['thresholds']['target'] for m in metrics_config]
+
+ # Crear y configurar gráfico
+ fig = plt.figure(figsize=(8, 8))
+ ax = fig.add_subplot(111, projection='polar')
+
+ # Configurar radar
+ angles = [n / float(len(categories)) * 2 * np.pi for n in range(len(categories))]
+ angles += angles[:1]
+ values_user += values_user[:1]
+ min_values += min_values[:1]
+ target_values += target_values[:1]
+
+ # Configurar ejes
+ ax.set_xticks(angles[:-1])
+ ax.set_xticklabels(categories, fontsize=10)
+ circle_ticks = np.arange(0, 1.1, 0.2)
+ ax.set_yticks(circle_ticks)
+ ax.set_yticklabels([f'{tick:.1f}' for tick in circle_ticks], fontsize=8)
+ ax.set_ylim(0, 1)
+
+ # Dibujar áreas de umbrales con etiquetas traducidas
+ ax.plot(angles, min_values, '#e74c3c', linestyle='--', linewidth=1, label=translations['min'], alpha=0.5)
+ ax.plot(angles, target_values, '#2ecc71', linestyle='--', linewidth=1, label=translations['target'], alpha=0.5)
+ ax.fill_between(angles, target_values, [1]*len(angles), color='#2ecc71', alpha=0.1)
+ ax.fill_between(angles, [0]*len(angles), min_values, color='#e74c3c', alpha=0.1)
+
+ # Dibujar valores del usuario con etiqueta traducida
+ ax.plot(angles, values_user, '#3498db', linewidth=2, label=translations['user'])
+ ax.fill(angles, values_user, '#3498db', alpha=0.2)
+
+ # Ajustar leyenda
+ ax.legend(
+ loc='upper right',
+ bbox_to_anchor=(1.3, 1.1),
+ fontsize=10,
+ frameon=True,
+ facecolor='white',
+ edgecolor='none',
+ shadow=True
+ )
+
+ plt.tight_layout()
+ st.pyplot(fig)
+ plt.close()
+
+ except Exception as e:
+ logger.error(f"Error mostrando gráfico radar: {str(e)}")
st.error("Error al mostrar el gráfico")
\ No newline at end of file
diff --git a/modules/studentact/student_activities.py b/modules/studentact/student_activities.py
index 092639ee181ba48f703e6667d25e3c7a3f26e75e..40b8e2a4ed849660561e2e7fb030d269f9080c07 100644
--- a/modules/studentact/student_activities.py
+++ b/modules/studentact/student_activities.py
@@ -1,111 +1,111 @@
-#modules/studentact/student_activities.py
-
-import streamlit as st
-import pandas as pd
-import matplotlib.pyplot as plt
-import seaborn as sns
-import base64
-from io import BytesIO
-from reportlab.pdfgen import canvas
-from reportlab.lib.pagesizes import letter
-from docx import Document
-from odf.opendocument import OpenDocumentText
-from odf.text import P
-from datetime import datetime, timedelta
-import pytz
-import logging
-
-# Configuración de logging
-logging.basicConfig(level=logging.DEBUG)
-logger = logging.getLogger(__name__)
-
-# Importaciones locales
-try:
- from ..database.morphosintax_mongo_db import get_student_morphosyntax_data
- from ..database.semantic_mongo_db import get_student_semantic_data
- from ..database.discourse_mongo_db import get_student_discourse_data
-
- from ..database.chat_mongo_db import get_chat_history
-
- logger.info("Importaciones locales exitosas")
-except ImportError as e:
- logger.error(f"Error en las importaciones locales: {e}")
-
-def display_student_progress(username, lang_code, t):
- logger.debug(f"Iniciando display_student_progress para {username}")
-
- st.title(f"{t.get('progress_of', 'Progreso de')} {username}")
-
- # Obtener los datos del estudiante
- student_data = get_student_morphosyntax_data(username)
-
- if not student_data or len(student_data.get('entries', [])) == 0:
- logger.warning(f"No se encontraron datos para el estudiante {username}")
- st.warning(t.get("no_data_warning", "No se encontraron datos para este estudiante."))
- st.info(t.get("try_analysis", "Intenta realizar algunos análisis de texto primero."))
- return
-
- logger.debug(f"Datos del estudiante obtenidos: {len(student_data['entries'])} entradas")
-
- # Resumen de actividades
- with st.expander(t.get("activities_summary", "Resumen de Actividades"), expanded=True):
- total_entries = len(student_data['entries'])
- st.write(f"{t.get('total_analyses', 'Total de análisis realizados')}: {total_entries}")
-
- # Gráfico de tipos de análisis
- try:
- analysis_types = [entry.get('analysis_type', 'unknown') for entry in student_data['entries']]
- analysis_counts = pd.Series(analysis_types).value_counts()
- fig, ax = plt.subplots()
- sns.barplot(x=analysis_counts.index, y=analysis_counts.values, ax=ax)
- ax.set_title(t.get("analysis_types_chart", "Tipos de análisis realizados"))
- ax.set_xlabel(t.get("analysis_type", "Tipo de análisis"))
- ax.set_ylabel(t.get("count", "Cantidad"))
- st.pyplot(fig)
- except Exception as e:
- logger.error(f"Error al crear el gráfico: {e}")
- st.error("No se pudo crear el gráfico de tipos de análisis.")
-
- # Función para generar el contenido del archivo de actividades de las últimas 48 horas
- def generate_activity_content_48h():
- content = f"Actividades de {username} en las últimas 48 horas\n\n"
-
- two_days_ago = datetime.now(pytz.utc) - timedelta(days=2)
-
- try:
- morphosyntax_analyses = get_student_morphosyntax_data(username)
- recent_morphosyntax = [a for a in morphosyntax_analyses if datetime.fromisoformat(a['timestamp']) > two_days_ago]
-
- content += f"Análisis morfosintácticos: {len(recent_morphosyntax)}\n"
- for analysis in recent_morphosyntax:
- content += f"- Análisis del {analysis['timestamp']}: {analysis['text'][:50]}...\n"
-
- chat_history = get_chat_history(username, None)
- recent_chats = [c for c in chat_history if datetime.fromisoformat(c['timestamp']) > two_days_ago]
-
- content += f"\nConversaciones de chat: {len(recent_chats)}\n"
- for chat in recent_chats:
- content += f"- Chat del {chat['timestamp']}: {len(chat['messages'])} mensajes\n"
- except Exception as e:
- logger.error(f"Error al generar el contenido de actividades: {e}")
- content += "Error al recuperar los datos de actividades.\n"
-
- return content
-
- # Botones para descargar el histórico de actividades de las últimas 48 horas
- st.subheader(t.get("download_history_48h", "Descargar Histórico de Actividades (Últimas 48 horas)"))
- if st.button("Generar reporte de 48 horas"):
- try:
- report_content = generate_activity_content_48h()
- st.text_area("Reporte de 48 horas", report_content, height=300)
- st.download_button(
- label="Descargar TXT (48h)",
- data=report_content,
- file_name="actividades_48h.txt",
- mime="text/plain"
- )
- except Exception as e:
- logger.error(f"Error al generar el reporte: {e}")
- st.error("No se pudo generar el reporte. Por favor, verifica los logs para más detalles.")
-
+#modules/studentact/student_activities.py
+
+import streamlit as st
+import pandas as pd
+import matplotlib.pyplot as plt
+import seaborn as sns
+import base64
+from io import BytesIO
+from reportlab.pdfgen import canvas
+from reportlab.lib.pagesizes import letter
+from docx import Document
+from odf.opendocument import OpenDocumentText
+from odf.text import P
+from datetime import datetime, timedelta
+import pytz
+import logging
+
+# Configuración de logging
+logging.basicConfig(level=logging.DEBUG)
+logger = logging.getLogger(__name__)
+
+# Importaciones locales
+try:
+ from ..database.morphosintax_mongo_db import get_student_morphosyntax_data
+ from ..database.semantic_mongo_db import get_student_semantic_data
+ from ..database.discourse_mongo_db import get_student_discourse_data
+
+ from ..database.chat_mongo_db import get_chat_history
+
+ logger.info("Importaciones locales exitosas")
+except ImportError as e:
+ logger.error(f"Error en las importaciones locales: {e}")
+
+def display_student_progress(username, lang_code, t):
+ logger.debug(f"Iniciando display_student_progress para {username}")
+
+ st.title(f"{t.get('progress_of', 'Progreso de')} {username}")
+
+ # Obtener los datos del estudiante
+ student_data = get_student_morphosyntax_data(username)
+
+ if not student_data or len(student_data.get('entries', [])) == 0:
+ logger.warning(f"No se encontraron datos para el estudiante {username}")
+ st.warning(t.get("no_data_warning", "No se encontraron datos para este estudiante."))
+ st.info(t.get("try_analysis", "Intenta realizar algunos análisis de texto primero."))
+ return
+
+ logger.debug(f"Datos del estudiante obtenidos: {len(student_data['entries'])} entradas")
+
+ # Resumen de actividades
+ with st.expander(t.get("activities_summary", "Resumen de Actividades"), expanded=True):
+ total_entries = len(student_data['entries'])
+ st.write(f"{t.get('total_analyses', 'Total de análisis realizados')}: {total_entries}")
+
+ # Gráfico de tipos de análisis
+ try:
+ analysis_types = [entry.get('analysis_type', 'unknown') for entry in student_data['entries']]
+ analysis_counts = pd.Series(analysis_types).value_counts()
+ fig, ax = plt.subplots()
+ sns.barplot(x=analysis_counts.index, y=analysis_counts.values, ax=ax)
+ ax.set_title(t.get("analysis_types_chart", "Tipos de análisis realizados"))
+ ax.set_xlabel(t.get("analysis_type", "Tipo de análisis"))
+ ax.set_ylabel(t.get("count", "Cantidad"))
+ st.pyplot(fig)
+ except Exception as e:
+ logger.error(f"Error al crear el gráfico: {e}")
+ st.error("No se pudo crear el gráfico de tipos de análisis.")
+
+ # Función para generar el contenido del archivo de actividades de las últimas 48 horas
+ def generate_activity_content_48h():
+ content = f"Actividades de {username} en las últimas 48 horas\n\n"
+
+ two_days_ago = datetime.now(pytz.utc) - timedelta(days=2)
+
+ try:
+ morphosyntax_analyses = get_student_morphosyntax_data(username)
+ recent_morphosyntax = [a for a in morphosyntax_analyses if datetime.fromisoformat(a['timestamp']) > two_days_ago]
+
+ content += f"Análisis morfosintácticos: {len(recent_morphosyntax)}\n"
+ for analysis in recent_morphosyntax:
+ content += f"- Análisis del {analysis['timestamp']}: {analysis['text'][:50]}...\n"
+
+ chat_history = get_chat_history(username, None)
+ recent_chats = [c for c in chat_history if datetime.fromisoformat(c['timestamp']) > two_days_ago]
+
+ content += f"\nConversaciones de chat: {len(recent_chats)}\n"
+ for chat in recent_chats:
+ content += f"- Chat del {chat['timestamp']}: {len(chat['messages'])} mensajes\n"
+ except Exception as e:
+ logger.error(f"Error al generar el contenido de actividades: {e}")
+ content += "Error al recuperar los datos de actividades.\n"
+
+ return content
+
+ # Botones para descargar el histórico de actividades de las últimas 48 horas
+ st.subheader(t.get("download_history_48h", "Descargar Histórico de Actividades (Últimas 48 horas)"))
+ if st.button("Generar reporte de 48 horas"):
+ try:
+ report_content = generate_activity_content_48h()
+ st.text_area("Reporte de 48 horas", report_content, height=300)
+ st.download_button(
+ label="Descargar TXT (48h)",
+ data=report_content,
+ file_name="actividades_48h.txt",
+ mime="text/plain"
+ )
+ except Exception as e:
+ logger.error(f"Error al generar el reporte: {e}")
+ st.error("No se pudo generar el reporte. Por favor, verifica los logs para más detalles.")
+
logger.debug("Finalizando display_student_progress")
\ No newline at end of file
diff --git a/modules/studentact/student_activities_v2-complet.py b/modules/studentact/student_activities_v2-complet.py
index 836416e10ad01171860e2ba25ab61a91e5c9840f..638797e45d311fa223873be6058fb6093a4fbcc8 100644
--- a/modules/studentact/student_activities_v2-complet.py
+++ b/modules/studentact/student_activities_v2-complet.py
@@ -1,794 +1,794 @@
-##############
-###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
-
-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
- tabs = st.tabs([
- 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 Análisis Morfosintáctico
- with tabs[0]:
- display_morphosyntax_activities(username, t)
-
- # Tab de Análisis Semántico
- with tabs[1]:
- display_semantic_activities(username, t)
-
- # Tab de Análisis del Discurso
- with tabs[2]:
- display_discourse_activities(username, t)
-
- # Tab de Conversaciones del Chat
- with tabs[3]:
- 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 mínimos 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")
-
- with st.expander(f"{t.get('analysis_date', 'Fecha')}: {formatted_date}", expanded=False):
- if analysis['concept_graph']:
- logger.debug("Decodificando gráfico de conceptos")
- try:
- image_bytes = base64.b64decode(analysis['concept_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_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'))
-
-#################################################################################
-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'))
-
-
-
-
-
-
-
-
-
-'''
-##########versión 25-9-2024---02:30 ################ OK (username)####################
-
-def display_student_progress(username, lang_code, t, student_data):
- st.title(f"{t.get('progress_of', 'Progreso de')} {username}")
-
- if not student_data or len(student_data.get('entries', [])) == 0:
- st.warning(t.get("no_data_warning", "No se encontraron datos para este estudiante."))
- st.info(t.get("try_analysis", "Intenta realizar algunos análisis de texto primero."))
- return
-
- with st.expander(t.get("activities_summary", "Resumen de Actividades"), expanded=True):
- total_entries = len(student_data['entries'])
- st.write(f"{t.get('total_analyses', 'Total de análisis realizados')}: {total_entries}")
-
- # Gráfico de tipos de análisis
- analysis_types = [entry['analysis_type'] for entry in student_data['entries']]
- analysis_counts = pd.Series(analysis_types).value_counts()
- fig, ax = plt.subplots()
- analysis_counts.plot(kind='bar', ax=ax)
- ax.set_title(t.get("analysis_types_chart", "Tipos de análisis realizados"))
- ax.set_xlabel(t.get("analysis_type", "Tipo de análisis"))
- ax.set_ylabel(t.get("count", "Cantidad"))
- st.pyplot(fig)
-
- # Mostrar los últimos análisis morfosintácticos
- with st.expander(t.get("morphosyntax_history", "Histórico de Análisis Morfosintácticos")):
- morphosyntax_entries = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'morphosyntax']
- for entry in morphosyntax_entries[:5]: # Mostrar los últimos 5
- st.subheader(f"{t.get('analysis_of', 'Análisis del')} {entry['timestamp']}")
- if 'arc_diagrams' in entry and entry['arc_diagrams']:
- st.components.v1.html(entry['arc_diagrams'][0], height=300, scrolling=True)
-
- # Añadir secciones similares para análisis semánticos y discursivos si es necesario
-
- # Mostrar el historial de chat
- with st.expander(t.get("chat_history", "Historial de Chat")):
- if 'chat_history' in student_data:
- for chat in student_data['chat_history'][:5]: # Mostrar las últimas 5 conversaciones
- st.subheader(f"{t.get('chat_from', 'Chat del')} {chat['timestamp']}")
- for message in chat['messages']:
- st.write(f"{message['role'].capitalize()}: {message['content']}")
- st.write("---")
- else:
- st.write(t.get("no_chat_history", "No hay historial de chat disponible."))
-
-
-##########versión 24-9-2024---17:30 ################ OK FROM--V2 de def get_student_data(username)####################
-
-def display_student_progress(username, lang_code, t, student_data):
- if not student_data or len(student_data['entries']) == 0:
- st.warning(t.get("no_data_warning", "No se encontraron datos para este estudiante."))
- st.info(t.get("try_analysis", "Intenta realizar algunos análisis de texto primero."))
- return
-
- st.title(f"{t.get('progress_of', 'Progreso de')} {username}")
-
- with st.expander(t.get("activities_summary", "Resumen de Actividades y Progreso"), expanded=True):
- total_entries = len(student_data['entries'])
- st.write(f"{t.get('total_analyses', 'Total de análisis realizados')}: {total_entries}")
-
- # Gráfico de tipos de análisis
- analysis_types = [entry['analysis_type'] for entry in student_data['entries']]
- analysis_counts = pd.Series(analysis_types).value_counts()
-
- fig, ax = plt.subplots(figsize=(8, 4))
- analysis_counts.plot(kind='bar', ax=ax)
- ax.set_title(t.get("analysis_types_chart", "Tipos de análisis realizados"))
- ax.set_xlabel(t.get("analysis_type", "Tipo de análisis"))
- ax.set_ylabel(t.get("count", "Cantidad"))
- st.pyplot(fig)
-
- # Histórico de Análisis Morfosintácticos
- with st.expander(t.get("morphosyntax_history", "Histórico de Análisis Morfosintácticos")):
- morphosyntax_entries = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'morphosyntax']
- if not morphosyntax_entries:
- st.warning("No se encontraron análisis morfosintácticos.")
- for entry in morphosyntax_entries:
- st.subheader(f"{t.get('analysis_of', 'Análisis del')} {entry['timestamp']}")
- if 'arc_diagrams' in entry and entry['arc_diagrams']:
- try:
- st.write(entry['arc_diagrams'][0], unsafe_allow_html=True)
- except Exception as e:
- logger.error(f"Error al mostrar diagrama de arco: {str(e)}")
- st.error("Error al mostrar el diagrama de arco.")
- else:
- st.write(t.get("no_arc_diagram", "No se encontró diagrama de arco para este análisis."))
-
- # Histórico de Análisis Semánticos
- with st.expander(t.get("semantic_history", "Histórico de Análisis Semánticos")):
- semantic_entries = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'semantic']
- if not semantic_entries:
- st.warning("No se encontraron análisis semánticos.")
- for entry in semantic_entries:
- st.subheader(f"{t.get('analysis_of', 'Análisis del')} {entry['timestamp']}")
- if 'key_concepts' in entry:
- st.write(t.get("key_concepts", "Conceptos clave:"))
- concepts_str = " | ".join([f"{concept} ({frequency:.2f})" for concept, frequency in entry['key_concepts']])
- st.markdown(f"
{concepts_str}
", unsafe_allow_html=True)
- if 'graph' in entry:
- try:
- img_bytes = base64.b64decode(entry['graph'])
- st.image(img_bytes, caption=t.get("conceptual_relations_graph", "Gráfico de relaciones conceptuales"))
- except Exception as e:
- logger.error(f"Error al mostrar gráfico semántico: {str(e)}")
- st.error(t.get("graph_display_error", f"No se pudo mostrar el gráfico: {str(e)}"))
-
- # Histórico de Análisis Discursivos
- with st.expander(t.get("discourse_history", "Histórico de Análisis Discursivos")):
- discourse_entries = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'discourse']
- for entry in discourse_entries:
- st.subheader(f"{t.get('analysis_of', 'Análisis del')} {entry['timestamp']}")
- for i in [1, 2]:
- if f'key_concepts{i}' in entry:
- st.write(f"{t.get('key_concepts', 'Conceptos clave')} {t.get('document', 'documento')} {i}:")
- concepts_str = " | ".join([f"{concept} ({frequency:.2f})" for concept, frequency in entry[f'key_concepts{i}']])
- st.markdown(f"
{concepts_str}
", unsafe_allow_html=True)
- try:
- if 'combined_graph' in entry and entry['combined_graph']:
- img_bytes = base64.b64decode(entry['combined_graph'])
- st.image(img_bytes, caption=t.get("combined_graph", "Gráfico combinado"))
- elif 'graph1' in entry and 'graph2' in entry:
- col1, col2 = st.columns(2)
- with col1:
- if entry['graph1']:
- img_bytes1 = base64.b64decode(entry['graph1'])
- st.image(img_bytes1, caption=t.get("graph_doc1", "Gráfico documento 1"))
- with col2:
- if entry['graph2']:
- img_bytes2 = base64.b64decode(entry['graph2'])
- st.image(img_bytes2, caption=t.get("graph_doc2", "Gráfico documento 2"))
- except Exception as e:
- st.error(t.get("graph_display_error", f"No se pudieron mostrar los gráficos: {str(e)}"))
-
- # Histórico de Conversaciones con el ChatBot
- with st.expander(t.get("chatbot_history", "Histórico de Conversaciones con el ChatBot")):
- if 'chat_history' in student_data and student_data['chat_history']:
- for i, chat in enumerate(student_data['chat_history']):
- st.subheader(f"{t.get('conversation', 'Conversación')} {i+1} - {chat['timestamp']}")
- for message in chat['messages']:
- if message['role'] == 'user':
- st.write(f"{t.get('user', 'Usuario')}: {message['content']}")
- else:
- st.write(f"{t.get('assistant', 'Asistente')}: {message['content']}")
- st.write("---")
- else:
- st.write(t.get("no_chat_history", "No se encontraron conversaciones con el ChatBot."))
-
- # Añadir logs para depuración
- if st.checkbox(t.get("show_debug_data", "Mostrar datos de depuración")):
- st.write(t.get("student_debug_data", "Datos del estudiante (para depuración):"))
- st.json(student_data)
-
- # Mostrar conteo de tipos de análisis
- analysis_types = [entry['analysis_type'] for entry in student_data['entries']]
- type_counts = {t: analysis_types.count(t) for t in set(analysis_types)}
- st.write("Conteo de tipos de análisis:")
- st.write(type_counts)
-
-
-#############################--- Update 16:00 24-9 #########################################
-def display_student_progress(username, lang_code, t, student_data):
- try:
- st.subheader(t.get('student_activities', 'Student Activitie'))
-
- if not student_data or all(len(student_data.get(key, [])) == 0 for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']):
- st.warning(t.get('no_data_warning', 'No analysis data found for this student.'))
- return
-
- # Resumen de actividades
- total_analyses = sum(len(student_data.get(key, [])) for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses'])
- st.write(f"{t.get('total_analyses', 'Total analyses performed')}: {total_analyses}")
-
- # Gráfico de tipos de análisis
- analysis_counts = {
- t.get('morpho_analyses', 'Morphosyntactic Analyses'): len(student_data.get('morphosyntax_analyses', [])),
- t.get('semantic_analyses', 'Semantic Analyses'): len(student_data.get('semantic_analyses', [])),
- t.get('discourse_analyses', 'Discourse Analyses'): len(student_data.get('discourse_analyses', []))
- }
- # Configurar el estilo de seaborn para un aspecto más atractivo
- sns.set_style("whitegrid")
-
- # Crear una figura más pequeña
- fig, ax = plt.subplots(figsize=(6, 4))
-
- # Usar colores más atractivos
- colors = ['#ff9999', '#66b3ff', '#99ff99']
-
- # Crear el gráfico de barras
- bars = ax.bar(analysis_counts.keys(), analysis_counts.values(), color=colors)
-
- # Añadir etiquetas de valor encima de cada barra
- for bar in bars:
- height = bar.get_height()
- ax.text(bar.get_x() + bar.get_width()/2., height,
- f'{height}',
- ha='center', va='bottom')
-
- # Configurar el título y las etiquetas
- ax.set_title(t.get('analysis_types_chart', 'Types of analyses performed'), fontsize=12)
- ax.set_ylabel(t.get('count', 'Count'), fontsize=10)
-
- # Rotar las etiquetas del eje x para mejor legibilidad
- plt.xticks(rotation=45, ha='right')
-
- # Ajustar el diseño para que todo quepa
- plt.tight_layout()
-
- # Mostrar el gráfico en Streamlit
- st.pyplot(fig)
-
- # Mostrar los últimos análisis
- for analysis_type in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']:
- with st.expander(t.get(f'{analysis_type}_expander', f'{analysis_type.capitalize()} History')):
- for analysis in student_data.get(analysis_type, [])[:5]: # Mostrar los últimos 5
- st.subheader(f"{t.get('analysis_from', 'Analysis from')} {analysis.get('timestamp', 'N/A')}")
- if analysis_type == 'morphosyntax_analyses':
- if 'arc_diagrams' in analysis:
- st.write(analysis['arc_diagrams'][0], unsafe_allow_html=True)
- elif analysis_type == 'semantic_analyses':
- if 'key_concepts' in analysis:
- st.write(t.get('key_concepts', 'Key concepts'))
- st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis['key_concepts']]))
- if 'graph' in analysis:
- st.image(base64.b64decode(analysis['graph']))
- elif analysis_type == 'discourse_analyses':
- for i in [1, 2]:
- if f'key_concepts{i}' in analysis:
- st.write(f"{t.get('key_concepts', 'Key concepts')} {t.get('document', 'Document')} {i}")
- st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis[f'key_concepts{i}']]))
- if 'combined_graph' in analysis:
- st.image(base64.b64decode(analysis['combined_graph']))
-
- # Mostrar el historial de chat
- with st.expander(t.get('chat_history_expander', 'Chat History')):
- for chat in student_data.get('chat_history', [])[:5]: # Mostrar las últimas 5 conversaciones
- st.subheader(f"{t.get('chat_from', 'Chat from')} {chat.get('timestamp', 'N/A')}")
- for message in chat.get('messages', []):
- st.write(f"{message.get('role', 'Unknown').capitalize()}: {message.get('content', 'No content')}")
- st.write("---")
-
- except Exception as e:
- logger.error(f"Error in display_student_progress: {str(e)}", exc_info=True)
- st.error(t.get('error_loading_progress', 'Error loading student progress. Please try again later.'))
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-#####################################################################
-def display_student_progress(username, lang_code, t, student_data):
- st.subheader(t['student_progress'])
-
- if not student_data or all(len(student_data[key]) == 0 for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']):
- st.warning(t['no_data_warning'])
- return
-
- # Resumen de actividades
- total_analyses = sum(len(student_data[key]) for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses'])
- st.write(f"{t['total_analyses']}: {total_analyses}")
-
- # Gráfico de tipos de análisis
- analysis_counts = {
- t['morpho_analyses']: len(student_data['morphosyntax_analyses']),
- t['semantic_analyses']: len(student_data['semantic_analyses']),
- t['discourse_analyses']: len(student_data['discourse_analyses'])
- }
- fig, ax = plt.subplots()
- ax.bar(analysis_counts.keys(), analysis_counts.values())
- ax.set_title(t['analysis_types_chart'])
- st.pyplot(fig)
-
- # Mostrar los últimos análisis
- for analysis_type in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']:
- with st.expander(t[f'{analysis_type}_expander']):
- for analysis in student_data[analysis_type][:5]: # Mostrar los últimos 5
- st.subheader(f"{t['analysis_from']} {analysis['timestamp']}")
- if analysis_type == 'morphosyntax_analyses':
- if 'arc_diagrams' in analysis:
- st.write(analysis['arc_diagrams'][0], unsafe_allow_html=True)
- elif analysis_type == 'semantic_analyses':
- if 'key_concepts' in analysis:
- st.write(t['key_concepts'])
- st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis['key_concepts']]))
- if 'graph' in analysis:
- st.image(base64.b64decode(analysis['graph']))
- elif analysis_type == 'discourse_analyses':
- for i in [1, 2]:
- if f'key_concepts{i}' in analysis:
- st.write(f"{t['key_concepts']} {t['document']} {i}")
- st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis[f'key_concepts{i}']]))
- if 'combined_graph' in analysis:
- st.image(base64.b64decode(analysis['combined_graph']))
-
- # Mostrar el historial de chat
- with st.expander(t['chat_history_expander']):
- for chat in student_data['chat_history'][:5]: # Mostrar las últimas 5 conversaciones
- st.subheader(f"{t['chat_from']} {chat['timestamp']}")
- for message in chat['messages']:
- st.write(f"{message['role'].capitalize()}: {message['content']}")
- st.write("---")
-
-
-
-def display_student_progress(username, lang_code, t, student_data):
- st.subheader(t['student_activities'])
-
- if not student_data or all(len(student_data[key]) == 0 for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']):
- st.warning(t['no_data_warning'])
- return
-
- # Resumen de actividades
- total_analyses = sum(len(student_data[key]) for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses'])
- st.write(f"{t['total_analyses']}: {total_analyses}")
-
- # Gráfico de tipos de análisis
- analysis_counts = {
- t['morphological_analysis']: len(student_data['morphosyntax_analyses']),
- t['semantic_analyses']: len(student_data['semantic_analyses']),
- t['discourse_analyses']: len(student_data['discourse_analyses'])
- }
- fig, ax = plt.subplots()
- ax.bar(analysis_counts.keys(), analysis_counts.values())
- ax.set_title(t['analysis_types_chart'])
- st.pyplot(fig)
-
- # Mostrar los últimos análisis
- for analysis_type in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']:
- with st.expander(t[f'{analysis_type}_expander']):
- for analysis in student_data[analysis_type][:5]: # Mostrar los últimos 5
- st.subheader(f"{t['analysis_from']} {analysis['timestamp']}")
- if analysis_type == 'morphosyntax_analyses':
- if 'arc_diagrams' in analysis:
- st.write(analysis['arc_diagrams'][0], unsafe_allow_html=True)
- elif analysis_type == 'semantic_analyses':
- if 'key_concepts' in analysis:
- st.write(t['key_concepts'])
- st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis['key_concepts']]))
- if 'graph' in analysis:
- st.image(base64.b64decode(analysis['graph']))
- elif analysis_type == 'discourse_analyses':
- for i in [1, 2]:
- if f'key_concepts{i}' in analysis:
- st.write(f"{t['key_concepts']} {t['document']} {i}")
- st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis[f'key_concepts{i}']]))
- if 'combined_graph' in analysis:
- st.image(base64.b64decode(analysis['combined_graph']))
-
- # Mostrar el historial de chat
- with st.expander(t['chat_history_expander']):
- for chat in student_data['chat_history'][:5]: # Mostrar las últimas 5 conversaciones
- st.subheader(f"{t['chat_from']} {chat['timestamp']}")
- for message in chat['messages']:
- st.write(f"{message['role'].capitalize()}: {message['content']}")
- st.write("---")
-
-
-
-
-def display_student_progress(username, lang_code, t, student_data):
- st.subheader(t['student_activities'])
-
- if not student_data or all(len(student_data[key]) == 0 for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']):
- st.warning(t['no_data_warning'])
- return
-
- # Resumen de actividades
- total_analyses = sum(len(student_data[key]) for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses'])
- st.write(f"{t['total_analyses']}: {total_analyses}")
-
- # Gráfico de tipos de análisis
- analysis_counts = {
- t['morphological_analysis']: len(student_data['morphosyntax_analyses']),
- t['semantic_analyses']: len(student_data['semantic_analyses']),
- t['discourse_analyses']: len(student_data['discourse_analyses'])
- }
- fig, ax = plt.subplots()
- ax.bar(analysis_counts.keys(), analysis_counts.values())
- ax.set_title(t['analysis_types_chart'])
- st.pyplot(fig)
-
- # Mostrar los últimos análisis
- for analysis_type in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']:
- with st.expander(t[f'{analysis_type}_expander']):
- for analysis in student_data[analysis_type][:5]: # Mostrar los últimos 5
- st.subheader(f"{t['analysis_from']} {analysis['timestamp']}")
- if analysis_type == 'morphosyntax_analyses':
- if 'arc_diagrams' in analysis:
- st.write(analysis['arc_diagrams'][0], unsafe_allow_html=True)
- elif analysis_type == 'semantic_analyses':
- if 'key_concepts' in analysis:
- st.write(t['key_concepts'])
- st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis['key_concepts']]))
- if 'graph' in analysis:
- st.image(base64.b64decode(analysis['graph']))
- elif analysis_type == 'discourse_analyses':
- for i in [1, 2]:
- if f'key_concepts{i}' in analysis:
- st.write(f"{t['key_concepts']} {t['document']} {i}")
- st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis[f'key_concepts{i}']]))
- if 'combined_graph' in analysis:
- st.image(base64.b64decode(analysis['combined_graph']))
-
- # Mostrar el historial de chat
- with st.expander(t['chat_history_expander']):
- for chat in student_data['chat_history'][:5]: # Mostrar las últimas 5 conversaciones
- st.subheader(f"{t['chat_from']} {chat['timestamp']}")
- for message in chat['messages']:
- st.write(f"{message['role'].capitalize()}: {message['content']}")
- st.write("---")
-
-
-
-
-def display_student_progress(username, lang_code, t):
- st.subheader(t['student_activities'])
- st.write(f"{t['activities_message']} {username}")
-
- # Aquí puedes agregar más contenido estático o placeholder
- st.info(t['activities_placeholder'])
-
- # Si necesitas mostrar algún dato, puedes usar datos de ejemplo o placeholders
- col1, col2, col3 = st.columns(3)
- col1.metric(t['morpho_analyses'], "5") # Ejemplo de dato
- col2.metric(t['semantic_analyses'], "3") # Ejemplo de dato
- col3.metric(t['discourse_analyses'], "2") # Ejemplo de dato
-
-
-
-def display_student_progress(username, lang_code, t):
- st.title(f"Actividades de {username}")
-
- # Obtener todos los datos del estudiante
- student_data = get_student_data(username)
-
- if not student_data or len(student_data.get('entries', [])) == 0:
- st.warning("No se encontraron datos de análisis para este estudiante.")
- st.info("Intenta realizar algunos análisis de texto primero.")
- return
-
- # Resumen de actividades
- with st.expander("Resumen de Actividades", expanded=True):
- total_entries = len(student_data['entries'])
- st.write(f"Total de análisis realizados: {total_entries}")
-
- # Gráfico de tipos de análisis
- analysis_types = [entry['analysis_type'] for entry in student_data['entries']]
- analysis_counts = pd.Series(analysis_types).value_counts()
- fig, ax = plt.subplots()
- analysis_counts.plot(kind='bar', ax=ax)
- ax.set_title("Tipos de análisis realizados")
- ax.set_xlabel("Tipo de análisis")
- ax.set_ylabel("Cantidad")
- st.pyplot(fig)
-
- # Histórico de Análisis Morfosintácticos
- with st.expander("Histórico de Análisis Morfosintácticos"):
- morpho_analyses = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'morphosyntax']
- for analysis in morpho_analyses[:5]: # Mostrar los últimos 5
- st.subheader(f"Análisis del {analysis['timestamp']}")
- if 'arc_diagrams' in analysis:
- st.write(analysis['arc_diagrams'][0], unsafe_allow_html=True)
-
- # Histórico de Análisis Semánticos
- with st.expander("Histórico de Análisis Semánticos"):
- semantic_analyses = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'semantic']
- for analysis in semantic_analyses[:5]: # Mostrar los últimos 5
- st.subheader(f"Análisis del {analysis['timestamp']}")
- if 'key_concepts' in analysis:
- concepts_str = " | ".join([f"{concept} ({frequency:.2f})" for concept, frequency in analysis['key_concepts']])
- st.markdown(f"
{concepts_str}
", unsafe_allow_html=True)
- if 'graph' in analysis:
- try:
- img_bytes = base64.b64decode(analysis['graph'])
- st.image(img_bytes, caption="Gráfico de relaciones conceptuales")
- except Exception as e:
- st.error(f"No se pudo mostrar el gráfico: {str(e)}")
-
- # Histórico de Análisis Discursivos
- with st.expander("Histórico de Análisis Discursivos"):
- discourse_analyses = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'discourse']
- for analysis in discourse_analyses[:5]: # Mostrar los últimos 5
- st.subheader(f"Análisis del {analysis['timestamp']}")
- for i in [1, 2]:
- if f'key_concepts{i}' in analysis:
- concepts_str = " | ".join([f"{concept} ({frequency:.2f})" for concept, frequency in analysis[f'key_concepts{i}']])
- st.write(f"Conceptos clave del documento {i}:")
- st.markdown(f"
{concepts_str}
", unsafe_allow_html=True)
- if 'combined_graph' in analysis:
- try:
- img_bytes = base64.b64decode(analysis['combined_graph'])
- st.image(img_bytes)
- except Exception as e:
- st.error(f"No se pudo mostrar el gráfico combinado: {str(e)}")
-
- # Histórico de Conversaciones con el ChatBot
- with st.expander("Histórico de Conversaciones con el ChatBot"):
- if 'chat_history' in student_data:
- for i, chat in enumerate(student_data['chat_history'][:5]): # Mostrar las últimas 5 conversaciones
- st.subheader(f"Conversación {i+1} - {chat['timestamp']}")
- for message in chat['messages']:
- st.write(f"{message['role'].capitalize()}: {message['content']}")
- st.write("---")
- else:
- st.write("No se encontraron conversaciones con el ChatBot.")
-
- # Opción para mostrar datos de depuración
- if st.checkbox("Mostrar datos de depuración"):
- st.write("Datos del estudiante (para depuración):")
- st.json(student_data)
-
+##############
+###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
+
+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
+ tabs = st.tabs([
+ 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 Análisis Morfosintáctico
+ with tabs[0]:
+ display_morphosyntax_activities(username, t)
+
+ # Tab de Análisis Semántico
+ with tabs[1]:
+ display_semantic_activities(username, t)
+
+ # Tab de Análisis del Discurso
+ with tabs[2]:
+ display_discourse_activities(username, t)
+
+ # Tab de Conversaciones del Chat
+ with tabs[3]:
+ 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 mínimos 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")
+
+ with st.expander(f"{t.get('analysis_date', 'Fecha')}: {formatted_date}", expanded=False):
+ if analysis['concept_graph']:
+ logger.debug("Decodificando gráfico de conceptos")
+ try:
+ image_bytes = base64.b64decode(analysis['concept_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_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'))
+
+#################################################################################
+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'))
+
+
+
+
+
+
+
+
+
+'''
+##########versión 25-9-2024---02:30 ################ OK (username)####################
+
+def display_student_progress(username, lang_code, t, student_data):
+ st.title(f"{t.get('progress_of', 'Progreso de')} {username}")
+
+ if not student_data or len(student_data.get('entries', [])) == 0:
+ st.warning(t.get("no_data_warning", "No se encontraron datos para este estudiante."))
+ st.info(t.get("try_analysis", "Intenta realizar algunos análisis de texto primero."))
+ return
+
+ with st.expander(t.get("activities_summary", "Resumen de Actividades"), expanded=True):
+ total_entries = len(student_data['entries'])
+ st.write(f"{t.get('total_analyses', 'Total de análisis realizados')}: {total_entries}")
+
+ # Gráfico de tipos de análisis
+ analysis_types = [entry['analysis_type'] for entry in student_data['entries']]
+ analysis_counts = pd.Series(analysis_types).value_counts()
+ fig, ax = plt.subplots()
+ analysis_counts.plot(kind='bar', ax=ax)
+ ax.set_title(t.get("analysis_types_chart", "Tipos de análisis realizados"))
+ ax.set_xlabel(t.get("analysis_type", "Tipo de análisis"))
+ ax.set_ylabel(t.get("count", "Cantidad"))
+ st.pyplot(fig)
+
+ # Mostrar los últimos análisis morfosintácticos
+ with st.expander(t.get("morphosyntax_history", "Histórico de Análisis Morfosintácticos")):
+ morphosyntax_entries = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'morphosyntax']
+ for entry in morphosyntax_entries[:5]: # Mostrar los últimos 5
+ st.subheader(f"{t.get('analysis_of', 'Análisis del')} {entry['timestamp']}")
+ if 'arc_diagrams' in entry and entry['arc_diagrams']:
+ st.components.v1.html(entry['arc_diagrams'][0], height=300, scrolling=True)
+
+ # Añadir secciones similares para análisis semánticos y discursivos si es necesario
+
+ # Mostrar el historial de chat
+ with st.expander(t.get("chat_history", "Historial de Chat")):
+ if 'chat_history' in student_data:
+ for chat in student_data['chat_history'][:5]: # Mostrar las últimas 5 conversaciones
+ st.subheader(f"{t.get('chat_from', 'Chat del')} {chat['timestamp']}")
+ for message in chat['messages']:
+ st.write(f"{message['role'].capitalize()}: {message['content']}")
+ st.write("---")
+ else:
+ st.write(t.get("no_chat_history", "No hay historial de chat disponible."))
+
+
+##########versión 24-9-2024---17:30 ################ OK FROM--V2 de def get_student_data(username)####################
+
+def display_student_progress(username, lang_code, t, student_data):
+ if not student_data or len(student_data['entries']) == 0:
+ st.warning(t.get("no_data_warning", "No se encontraron datos para este estudiante."))
+ st.info(t.get("try_analysis", "Intenta realizar algunos análisis de texto primero."))
+ return
+
+ st.title(f"{t.get('progress_of', 'Progreso de')} {username}")
+
+ with st.expander(t.get("activities_summary", "Resumen de Actividades y Progreso"), expanded=True):
+ total_entries = len(student_data['entries'])
+ st.write(f"{t.get('total_analyses', 'Total de análisis realizados')}: {total_entries}")
+
+ # Gráfico de tipos de análisis
+ analysis_types = [entry['analysis_type'] for entry in student_data['entries']]
+ analysis_counts = pd.Series(analysis_types).value_counts()
+
+ fig, ax = plt.subplots(figsize=(8, 4))
+ analysis_counts.plot(kind='bar', ax=ax)
+ ax.set_title(t.get("analysis_types_chart", "Tipos de análisis realizados"))
+ ax.set_xlabel(t.get("analysis_type", "Tipo de análisis"))
+ ax.set_ylabel(t.get("count", "Cantidad"))
+ st.pyplot(fig)
+
+ # Histórico de Análisis Morfosintácticos
+ with st.expander(t.get("morphosyntax_history", "Histórico de Análisis Morfosintácticos")):
+ morphosyntax_entries = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'morphosyntax']
+ if not morphosyntax_entries:
+ st.warning("No se encontraron análisis morfosintácticos.")
+ for entry in morphosyntax_entries:
+ st.subheader(f"{t.get('analysis_of', 'Análisis del')} {entry['timestamp']}")
+ if 'arc_diagrams' in entry and entry['arc_diagrams']:
+ try:
+ st.write(entry['arc_diagrams'][0], unsafe_allow_html=True)
+ except Exception as e:
+ logger.error(f"Error al mostrar diagrama de arco: {str(e)}")
+ st.error("Error al mostrar el diagrama de arco.")
+ else:
+ st.write(t.get("no_arc_diagram", "No se encontró diagrama de arco para este análisis."))
+
+ # Histórico de Análisis Semánticos
+ with st.expander(t.get("semantic_history", "Histórico de Análisis Semánticos")):
+ semantic_entries = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'semantic']
+ if not semantic_entries:
+ st.warning("No se encontraron análisis semánticos.")
+ for entry in semantic_entries:
+ st.subheader(f"{t.get('analysis_of', 'Análisis del')} {entry['timestamp']}")
+ if 'key_concepts' in entry:
+ st.write(t.get("key_concepts", "Conceptos clave:"))
+ concepts_str = " | ".join([f"{concept} ({frequency:.2f})" for concept, frequency in entry['key_concepts']])
+ st.markdown(f"
{concepts_str}
", unsafe_allow_html=True)
+ if 'graph' in entry:
+ try:
+ img_bytes = base64.b64decode(entry['graph'])
+ st.image(img_bytes, caption=t.get("conceptual_relations_graph", "Gráfico de relaciones conceptuales"))
+ except Exception as e:
+ logger.error(f"Error al mostrar gráfico semántico: {str(e)}")
+ st.error(t.get("graph_display_error", f"No se pudo mostrar el gráfico: {str(e)}"))
+
+ # Histórico de Análisis Discursivos
+ with st.expander(t.get("discourse_history", "Histórico de Análisis Discursivos")):
+ discourse_entries = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'discourse']
+ for entry in discourse_entries:
+ st.subheader(f"{t.get('analysis_of', 'Análisis del')} {entry['timestamp']}")
+ for i in [1, 2]:
+ if f'key_concepts{i}' in entry:
+ st.write(f"{t.get('key_concepts', 'Conceptos clave')} {t.get('document', 'documento')} {i}:")
+ concepts_str = " | ".join([f"{concept} ({frequency:.2f})" for concept, frequency in entry[f'key_concepts{i}']])
+ st.markdown(f"
{concepts_str}
", unsafe_allow_html=True)
+ try:
+ if 'combined_graph' in entry and entry['combined_graph']:
+ img_bytes = base64.b64decode(entry['combined_graph'])
+ st.image(img_bytes, caption=t.get("combined_graph", "Gráfico combinado"))
+ elif 'graph1' in entry and 'graph2' in entry:
+ col1, col2 = st.columns(2)
+ with col1:
+ if entry['graph1']:
+ img_bytes1 = base64.b64decode(entry['graph1'])
+ st.image(img_bytes1, caption=t.get("graph_doc1", "Gráfico documento 1"))
+ with col2:
+ if entry['graph2']:
+ img_bytes2 = base64.b64decode(entry['graph2'])
+ st.image(img_bytes2, caption=t.get("graph_doc2", "Gráfico documento 2"))
+ except Exception as e:
+ st.error(t.get("graph_display_error", f"No se pudieron mostrar los gráficos: {str(e)}"))
+
+ # Histórico de Conversaciones con el ChatBot
+ with st.expander(t.get("chatbot_history", "Histórico de Conversaciones con el ChatBot")):
+ if 'chat_history' in student_data and student_data['chat_history']:
+ for i, chat in enumerate(student_data['chat_history']):
+ st.subheader(f"{t.get('conversation', 'Conversación')} {i+1} - {chat['timestamp']}")
+ for message in chat['messages']:
+ if message['role'] == 'user':
+ st.write(f"{t.get('user', 'Usuario')}: {message['content']}")
+ else:
+ st.write(f"{t.get('assistant', 'Asistente')}: {message['content']}")
+ st.write("---")
+ else:
+ st.write(t.get("no_chat_history", "No se encontraron conversaciones con el ChatBot."))
+
+ # Añadir logs para depuración
+ if st.checkbox(t.get("show_debug_data", "Mostrar datos de depuración")):
+ st.write(t.get("student_debug_data", "Datos del estudiante (para depuración):"))
+ st.json(student_data)
+
+ # Mostrar conteo de tipos de análisis
+ analysis_types = [entry['analysis_type'] for entry in student_data['entries']]
+ type_counts = {t: analysis_types.count(t) for t in set(analysis_types)}
+ st.write("Conteo de tipos de análisis:")
+ st.write(type_counts)
+
+
+#############################--- Update 16:00 24-9 #########################################
+def display_student_progress(username, lang_code, t, student_data):
+ try:
+ st.subheader(t.get('student_activities', 'Student Activitie'))
+
+ if not student_data or all(len(student_data.get(key, [])) == 0 for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']):
+ st.warning(t.get('no_data_warning', 'No analysis data found for this student.'))
+ return
+
+ # Resumen de actividades
+ total_analyses = sum(len(student_data.get(key, [])) for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses'])
+ st.write(f"{t.get('total_analyses', 'Total analyses performed')}: {total_analyses}")
+
+ # Gráfico de tipos de análisis
+ analysis_counts = {
+ t.get('morpho_analyses', 'Morphosyntactic Analyses'): len(student_data.get('morphosyntax_analyses', [])),
+ t.get('semantic_analyses', 'Semantic Analyses'): len(student_data.get('semantic_analyses', [])),
+ t.get('discourse_analyses', 'Discourse Analyses'): len(student_data.get('discourse_analyses', []))
+ }
+ # Configurar el estilo de seaborn para un aspecto más atractivo
+ sns.set_style("whitegrid")
+
+ # Crear una figura más pequeña
+ fig, ax = plt.subplots(figsize=(6, 4))
+
+ # Usar colores más atractivos
+ colors = ['#ff9999', '#66b3ff', '#99ff99']
+
+ # Crear el gráfico de barras
+ bars = ax.bar(analysis_counts.keys(), analysis_counts.values(), color=colors)
+
+ # Añadir etiquetas de valor encima de cada barra
+ for bar in bars:
+ height = bar.get_height()
+ ax.text(bar.get_x() + bar.get_width()/2., height,
+ f'{height}',
+ ha='center', va='bottom')
+
+ # Configurar el título y las etiquetas
+ ax.set_title(t.get('analysis_types_chart', 'Types of analyses performed'), fontsize=12)
+ ax.set_ylabel(t.get('count', 'Count'), fontsize=10)
+
+ # Rotar las etiquetas del eje x para mejor legibilidad
+ plt.xticks(rotation=45, ha='right')
+
+ # Ajustar el diseño para que todo quepa
+ plt.tight_layout()
+
+ # Mostrar el gráfico en Streamlit
+ st.pyplot(fig)
+
+ # Mostrar los últimos análisis
+ for analysis_type in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']:
+ with st.expander(t.get(f'{analysis_type}_expander', f'{analysis_type.capitalize()} History')):
+ for analysis in student_data.get(analysis_type, [])[:5]: # Mostrar los últimos 5
+ st.subheader(f"{t.get('analysis_from', 'Analysis from')} {analysis.get('timestamp', 'N/A')}")
+ if analysis_type == 'morphosyntax_analyses':
+ if 'arc_diagrams' in analysis:
+ st.write(analysis['arc_diagrams'][0], unsafe_allow_html=True)
+ elif analysis_type == 'semantic_analyses':
+ if 'key_concepts' in analysis:
+ st.write(t.get('key_concepts', 'Key concepts'))
+ st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis['key_concepts']]))
+ if 'graph' in analysis:
+ st.image(base64.b64decode(analysis['graph']))
+ elif analysis_type == 'discourse_analyses':
+ for i in [1, 2]:
+ if f'key_concepts{i}' in analysis:
+ st.write(f"{t.get('key_concepts', 'Key concepts')} {t.get('document', 'Document')} {i}")
+ st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis[f'key_concepts{i}']]))
+ if 'combined_graph' in analysis:
+ st.image(base64.b64decode(analysis['combined_graph']))
+
+ # Mostrar el historial de chat
+ with st.expander(t.get('chat_history_expander', 'Chat History')):
+ for chat in student_data.get('chat_history', [])[:5]: # Mostrar las últimas 5 conversaciones
+ st.subheader(f"{t.get('chat_from', 'Chat from')} {chat.get('timestamp', 'N/A')}")
+ for message in chat.get('messages', []):
+ st.write(f"{message.get('role', 'Unknown').capitalize()}: {message.get('content', 'No content')}")
+ st.write("---")
+
+ except Exception as e:
+ logger.error(f"Error in display_student_progress: {str(e)}", exc_info=True)
+ st.error(t.get('error_loading_progress', 'Error loading student progress. Please try again later.'))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#####################################################################
+def display_student_progress(username, lang_code, t, student_data):
+ st.subheader(t['student_progress'])
+
+ if not student_data or all(len(student_data[key]) == 0 for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']):
+ st.warning(t['no_data_warning'])
+ return
+
+ # Resumen de actividades
+ total_analyses = sum(len(student_data[key]) for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses'])
+ st.write(f"{t['total_analyses']}: {total_analyses}")
+
+ # Gráfico de tipos de análisis
+ analysis_counts = {
+ t['morpho_analyses']: len(student_data['morphosyntax_analyses']),
+ t['semantic_analyses']: len(student_data['semantic_analyses']),
+ t['discourse_analyses']: len(student_data['discourse_analyses'])
+ }
+ fig, ax = plt.subplots()
+ ax.bar(analysis_counts.keys(), analysis_counts.values())
+ ax.set_title(t['analysis_types_chart'])
+ st.pyplot(fig)
+
+ # Mostrar los últimos análisis
+ for analysis_type in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']:
+ with st.expander(t[f'{analysis_type}_expander']):
+ for analysis in student_data[analysis_type][:5]: # Mostrar los últimos 5
+ st.subheader(f"{t['analysis_from']} {analysis['timestamp']}")
+ if analysis_type == 'morphosyntax_analyses':
+ if 'arc_diagrams' in analysis:
+ st.write(analysis['arc_diagrams'][0], unsafe_allow_html=True)
+ elif analysis_type == 'semantic_analyses':
+ if 'key_concepts' in analysis:
+ st.write(t['key_concepts'])
+ st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis['key_concepts']]))
+ if 'graph' in analysis:
+ st.image(base64.b64decode(analysis['graph']))
+ elif analysis_type == 'discourse_analyses':
+ for i in [1, 2]:
+ if f'key_concepts{i}' in analysis:
+ st.write(f"{t['key_concepts']} {t['document']} {i}")
+ st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis[f'key_concepts{i}']]))
+ if 'combined_graph' in analysis:
+ st.image(base64.b64decode(analysis['combined_graph']))
+
+ # Mostrar el historial de chat
+ with st.expander(t['chat_history_expander']):
+ for chat in student_data['chat_history'][:5]: # Mostrar las últimas 5 conversaciones
+ st.subheader(f"{t['chat_from']} {chat['timestamp']}")
+ for message in chat['messages']:
+ st.write(f"{message['role'].capitalize()}: {message['content']}")
+ st.write("---")
+
+
+
+def display_student_progress(username, lang_code, t, student_data):
+ st.subheader(t['student_activities'])
+
+ if not student_data or all(len(student_data[key]) == 0 for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']):
+ st.warning(t['no_data_warning'])
+ return
+
+ # Resumen de actividades
+ total_analyses = sum(len(student_data[key]) for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses'])
+ st.write(f"{t['total_analyses']}: {total_analyses}")
+
+ # Gráfico de tipos de análisis
+ analysis_counts = {
+ t['morphological_analysis']: len(student_data['morphosyntax_analyses']),
+ t['semantic_analyses']: len(student_data['semantic_analyses']),
+ t['discourse_analyses']: len(student_data['discourse_analyses'])
+ }
+ fig, ax = plt.subplots()
+ ax.bar(analysis_counts.keys(), analysis_counts.values())
+ ax.set_title(t['analysis_types_chart'])
+ st.pyplot(fig)
+
+ # Mostrar los últimos análisis
+ for analysis_type in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']:
+ with st.expander(t[f'{analysis_type}_expander']):
+ for analysis in student_data[analysis_type][:5]: # Mostrar los últimos 5
+ st.subheader(f"{t['analysis_from']} {analysis['timestamp']}")
+ if analysis_type == 'morphosyntax_analyses':
+ if 'arc_diagrams' in analysis:
+ st.write(analysis['arc_diagrams'][0], unsafe_allow_html=True)
+ elif analysis_type == 'semantic_analyses':
+ if 'key_concepts' in analysis:
+ st.write(t['key_concepts'])
+ st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis['key_concepts']]))
+ if 'graph' in analysis:
+ st.image(base64.b64decode(analysis['graph']))
+ elif analysis_type == 'discourse_analyses':
+ for i in [1, 2]:
+ if f'key_concepts{i}' in analysis:
+ st.write(f"{t['key_concepts']} {t['document']} {i}")
+ st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis[f'key_concepts{i}']]))
+ if 'combined_graph' in analysis:
+ st.image(base64.b64decode(analysis['combined_graph']))
+
+ # Mostrar el historial de chat
+ with st.expander(t['chat_history_expander']):
+ for chat in student_data['chat_history'][:5]: # Mostrar las últimas 5 conversaciones
+ st.subheader(f"{t['chat_from']} {chat['timestamp']}")
+ for message in chat['messages']:
+ st.write(f"{message['role'].capitalize()}: {message['content']}")
+ st.write("---")
+
+
+
+
+def display_student_progress(username, lang_code, t, student_data):
+ st.subheader(t['student_activities'])
+
+ if not student_data or all(len(student_data[key]) == 0 for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']):
+ st.warning(t['no_data_warning'])
+ return
+
+ # Resumen de actividades
+ total_analyses = sum(len(student_data[key]) for key in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses'])
+ st.write(f"{t['total_analyses']}: {total_analyses}")
+
+ # Gráfico de tipos de análisis
+ analysis_counts = {
+ t['morphological_analysis']: len(student_data['morphosyntax_analyses']),
+ t['semantic_analyses']: len(student_data['semantic_analyses']),
+ t['discourse_analyses']: len(student_data['discourse_analyses'])
+ }
+ fig, ax = plt.subplots()
+ ax.bar(analysis_counts.keys(), analysis_counts.values())
+ ax.set_title(t['analysis_types_chart'])
+ st.pyplot(fig)
+
+ # Mostrar los últimos análisis
+ for analysis_type in ['morphosyntax_analyses', 'semantic_analyses', 'discourse_analyses']:
+ with st.expander(t[f'{analysis_type}_expander']):
+ for analysis in student_data[analysis_type][:5]: # Mostrar los últimos 5
+ st.subheader(f"{t['analysis_from']} {analysis['timestamp']}")
+ if analysis_type == 'morphosyntax_analyses':
+ if 'arc_diagrams' in analysis:
+ st.write(analysis['arc_diagrams'][0], unsafe_allow_html=True)
+ elif analysis_type == 'semantic_analyses':
+ if 'key_concepts' in analysis:
+ st.write(t['key_concepts'])
+ st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis['key_concepts']]))
+ if 'graph' in analysis:
+ st.image(base64.b64decode(analysis['graph']))
+ elif analysis_type == 'discourse_analyses':
+ for i in [1, 2]:
+ if f'key_concepts{i}' in analysis:
+ st.write(f"{t['key_concepts']} {t['document']} {i}")
+ st.write(", ".join([f"{concept} ({freq:.2f})" for concept, freq in analysis[f'key_concepts{i}']]))
+ if 'combined_graph' in analysis:
+ st.image(base64.b64decode(analysis['combined_graph']))
+
+ # Mostrar el historial de chat
+ with st.expander(t['chat_history_expander']):
+ for chat in student_data['chat_history'][:5]: # Mostrar las últimas 5 conversaciones
+ st.subheader(f"{t['chat_from']} {chat['timestamp']}")
+ for message in chat['messages']:
+ st.write(f"{message['role'].capitalize()}: {message['content']}")
+ st.write("---")
+
+
+
+
+def display_student_progress(username, lang_code, t):
+ st.subheader(t['student_activities'])
+ st.write(f"{t['activities_message']} {username}")
+
+ # Aquí puedes agregar más contenido estático o placeholder
+ st.info(t['activities_placeholder'])
+
+ # Si necesitas mostrar algún dato, puedes usar datos de ejemplo o placeholders
+ col1, col2, col3 = st.columns(3)
+ col1.metric(t['morpho_analyses'], "5") # Ejemplo de dato
+ col2.metric(t['semantic_analyses'], "3") # Ejemplo de dato
+ col3.metric(t['discourse_analyses'], "2") # Ejemplo de dato
+
+
+
+def display_student_progress(username, lang_code, t):
+ st.title(f"Actividades de {username}")
+
+ # Obtener todos los datos del estudiante
+ student_data = get_student_data(username)
+
+ if not student_data or len(student_data.get('entries', [])) == 0:
+ st.warning("No se encontraron datos de análisis para este estudiante.")
+ st.info("Intenta realizar algunos análisis de texto primero.")
+ return
+
+ # Resumen de actividades
+ with st.expander("Resumen de Actividades", expanded=True):
+ total_entries = len(student_data['entries'])
+ st.write(f"Total de análisis realizados: {total_entries}")
+
+ # Gráfico de tipos de análisis
+ analysis_types = [entry['analysis_type'] for entry in student_data['entries']]
+ analysis_counts = pd.Series(analysis_types).value_counts()
+ fig, ax = plt.subplots()
+ analysis_counts.plot(kind='bar', ax=ax)
+ ax.set_title("Tipos de análisis realizados")
+ ax.set_xlabel("Tipo de análisis")
+ ax.set_ylabel("Cantidad")
+ st.pyplot(fig)
+
+ # Histórico de Análisis Morfosintácticos
+ with st.expander("Histórico de Análisis Morfosintácticos"):
+ morpho_analyses = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'morphosyntax']
+ for analysis in morpho_analyses[:5]: # Mostrar los últimos 5
+ st.subheader(f"Análisis del {analysis['timestamp']}")
+ if 'arc_diagrams' in analysis:
+ st.write(analysis['arc_diagrams'][0], unsafe_allow_html=True)
+
+ # Histórico de Análisis Semánticos
+ with st.expander("Histórico de Análisis Semánticos"):
+ semantic_analyses = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'semantic']
+ for analysis in semantic_analyses[:5]: # Mostrar los últimos 5
+ st.subheader(f"Análisis del {analysis['timestamp']}")
+ if 'key_concepts' in analysis:
+ concepts_str = " | ".join([f"{concept} ({frequency:.2f})" for concept, frequency in analysis['key_concepts']])
+ st.markdown(f"
{concepts_str}
", unsafe_allow_html=True)
+ if 'graph' in analysis:
+ try:
+ img_bytes = base64.b64decode(analysis['graph'])
+ st.image(img_bytes, caption="Gráfico de relaciones conceptuales")
+ except Exception as e:
+ st.error(f"No se pudo mostrar el gráfico: {str(e)}")
+
+ # Histórico de Análisis Discursivos
+ with st.expander("Histórico de Análisis Discursivos"):
+ discourse_analyses = [entry for entry in student_data['entries'] if entry['analysis_type'] == 'discourse']
+ for analysis in discourse_analyses[:5]: # Mostrar los últimos 5
+ st.subheader(f"Análisis del {analysis['timestamp']}")
+ for i in [1, 2]:
+ if f'key_concepts{i}' in analysis:
+ concepts_str = " | ".join([f"{concept} ({frequency:.2f})" for concept, frequency in analysis[f'key_concepts{i}']])
+ st.write(f"Conceptos clave del documento {i}:")
+ st.markdown(f"
{concepts_str}
", unsafe_allow_html=True)
+ if 'combined_graph' in analysis:
+ try:
+ img_bytes = base64.b64decode(analysis['combined_graph'])
+ st.image(img_bytes)
+ except Exception as e:
+ st.error(f"No se pudo mostrar el gráfico combinado: {str(e)}")
+
+ # Histórico de Conversaciones con el ChatBot
+ with st.expander("Histórico de Conversaciones con el ChatBot"):
+ if 'chat_history' in student_data:
+ for i, chat in enumerate(student_data['chat_history'][:5]): # Mostrar las últimas 5 conversaciones
+ st.subheader(f"Conversación {i+1} - {chat['timestamp']}")
+ for message in chat['messages']:
+ st.write(f"{message['role'].capitalize()}: {message['content']}")
+ st.write("---")
+ else:
+ st.write("No se encontraron conversaciones con el ChatBot.")
+
+ # Opción para mostrar datos de depuración
+ if st.checkbox("Mostrar datos de depuración"):
+ st.write("Datos del estudiante (para depuración):")
+ st.json(student_data)
+
'''
\ No newline at end of file
diff --git a/modules/studentact/student_activities_v2-error.py b/modules/studentact/student_activities_v2-error.py
index 24ee0b125cc9e272fca3beab0dd05666471d0369..864574edcf68c27f3fd935eda8799efa8308d28e 100644
--- a/modules/studentact/student_activities_v2-error.py
+++ b/modules/studentact/student_activities_v2-error.py
@@ -1,251 +1,251 @@
-##############
-###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
-
-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
- tabs = st.tabs([
- 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 Análisis Morfosintáctico
- with tabs[0]:
- display_morphosyntax_activities(username, t)
-
- # Tab de Análisis Semántico
- with tabs[1]:
- display_semantic_activities(username, t)
-
- # Tab de Análisis del Discurso
- with tabs[2]:
- display_discourse_activities(username, t)
-
- # Tab de Conversaciones del Chat
- with tabs[3]:
- 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:
- analyses = get_student_semantic_analysis(username)
- if not analyses:
- st.info(t.get('no_semantic_analyses', 'No hay análisis semánticos registrados'))
- return
-
- for analysis in analyses:
- with st.expander(
- f"{t.get('analysis_date', 'Fecha')}: {analysis['timestamp']}",
- expanded=False
- ):
-
- # Mostrar conceptos clave
- if 'key_concepts' in analysis:
- st.subheader(t.get('key_concepts', 'Conceptos clave'))
- df = pd.DataFrame(
- analysis['key_concepts'],
- columns=['Concepto', 'Frecuencia']
- )
- st.dataframe(df)
-
- # Mostrar gráfico de conceptos
- if 'concept_graph' in analysis and analysis['concept_graph']:
- st.subheader(t.get('concept_graph', 'Grafo de conceptos'))
- image_bytes = base64.b64decode(analysis['concept_graph'])
- st.image(image_bytes)
-
- 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:
- analyses = get_student_discourse_analysis(username)
- if not analyses:
- st.info(t.get('no_discourse_analyses', 'No hay análisis del discurso registrados'))
- return
-
- for analysis in analyses:
- with st.expander(
- f"{t.get('analysis_date', 'Fecha')}: {analysis['timestamp']}",
- expanded=False
- ):
-
- # Mostrar conceptos clave
- if 'key_concepts1' in analysis and 'key_concepts2' in analysis:
- 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'],
- columns=['Concepto', 'Frecuencia']
- )
- st.dataframe(df1)
-
- with col2:
- st.markdown(f"**{t.get('concepts_text_2', 'Conceptos Texto 2')}**")
- df2 = pd.DataFrame(
- analysis['key_concepts2'],
- columns=['Concepto', 'Frecuencia']
- )
- st.dataframe(df2)
-
- # Mostrar gráficos
- if all(key in analysis for key in ['graph1', 'graph2']):
- st.subheader(t.get('visualizations', 'Visualizaciones'))
-
- col1, col2 = st.columns(2)
- with col1:
- st.markdown(f"**{t.get('graph_text_1', 'Grafo Texto 1')}**")
- if analysis['graph1']:
- image_bytes = base64.b64decode(analysis['graph1'])
- st.image(image_bytes)
-
- with col2:
- st.markdown(f"**{t.get('graph_text_2', 'Grafo Texto 2')}**")
- if analysis['graph2']:
- image_bytes = base64.b64decode(analysis['graph2'])
- st.image(image_bytes)
-
- 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'))
-#################################################################################
-
-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)}")
+##############
+###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
+
+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
+ tabs = st.tabs([
+ 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 Análisis Morfosintáctico
+ with tabs[0]:
+ display_morphosyntax_activities(username, t)
+
+ # Tab de Análisis Semántico
+ with tabs[1]:
+ display_semantic_activities(username, t)
+
+ # Tab de Análisis del Discurso
+ with tabs[2]:
+ display_discourse_activities(username, t)
+
+ # Tab de Conversaciones del Chat
+ with tabs[3]:
+ 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:
+ analyses = get_student_semantic_analysis(username)
+ if not analyses:
+ st.info(t.get('no_semantic_analyses', 'No hay análisis semánticos registrados'))
+ return
+
+ for analysis in analyses:
+ with st.expander(
+ f"{t.get('analysis_date', 'Fecha')}: {analysis['timestamp']}",
+ expanded=False
+ ):
+
+ # Mostrar conceptos clave
+ if 'key_concepts' in analysis:
+ st.subheader(t.get('key_concepts', 'Conceptos clave'))
+ df = pd.DataFrame(
+ analysis['key_concepts'],
+ columns=['Concepto', 'Frecuencia']
+ )
+ st.dataframe(df)
+
+ # Mostrar gráfico de conceptos
+ if 'concept_graph' in analysis and analysis['concept_graph']:
+ st.subheader(t.get('concept_graph', 'Grafo de conceptos'))
+ image_bytes = base64.b64decode(analysis['concept_graph'])
+ st.image(image_bytes)
+
+ 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:
+ analyses = get_student_discourse_analysis(username)
+ if not analyses:
+ st.info(t.get('no_discourse_analyses', 'No hay análisis del discurso registrados'))
+ return
+
+ for analysis in analyses:
+ with st.expander(
+ f"{t.get('analysis_date', 'Fecha')}: {analysis['timestamp']}",
+ expanded=False
+ ):
+
+ # Mostrar conceptos clave
+ if 'key_concepts1' in analysis and 'key_concepts2' in analysis:
+ 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'],
+ columns=['Concepto', 'Frecuencia']
+ )
+ st.dataframe(df1)
+
+ with col2:
+ st.markdown(f"**{t.get('concepts_text_2', 'Conceptos Texto 2')}**")
+ df2 = pd.DataFrame(
+ analysis['key_concepts2'],
+ columns=['Concepto', 'Frecuencia']
+ )
+ st.dataframe(df2)
+
+ # Mostrar gráficos
+ if all(key in analysis for key in ['graph1', 'graph2']):
+ st.subheader(t.get('visualizations', 'Visualizaciones'))
+
+ col1, col2 = st.columns(2)
+ with col1:
+ st.markdown(f"**{t.get('graph_text_1', 'Grafo Texto 1')}**")
+ if analysis['graph1']:
+ image_bytes = base64.b64decode(analysis['graph1'])
+ st.image(image_bytes)
+
+ with col2:
+ st.markdown(f"**{t.get('graph_text_2', 'Grafo Texto 2')}**")
+ if analysis['graph2']:
+ image_bytes = base64.b64decode(analysis['graph2'])
+ st.image(image_bytes)
+
+ 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'))
+#################################################################################
+
+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'))
\ No newline at end of file
diff --git a/modules/studentact/student_activities_v2.py b/modules/studentact/student_activities_v2.py
index 3d647d58210ef70d939a238b74201d2fc23f1934..9f8eac7f06cd9d1ee1ba97b59dea5d31f03873c2 100644
--- a/modules/studentact/student_activities_v2.py
+++ b/modules/studentact/student_activities_v2.py
@@ -1,731 +1,780 @@
-##############
-###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, timedelta
-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
-from ..database.claude_recommendations_mongo_db import get_claude_recommendations
-
-# Importar la función generate_unique_key
-from ..utils.widget_utils import generate_unique_key
-
-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:
- # Cambiado de "Mis Actividades" a "Registro de mis actividades"
- #st.header(t.get('activities_title', 'Registro de mis actividades'))
-
- # Tabs para diferentes tipos de análisis
- # Cambiado "Análisis del Discurso" a "Análisis comparado de textos"
- tabs = st.tabs([
- t.get('current_situation_activities', 'Registros de la función: Mi Situación Actual'),
- t.get('morpho_activities', 'Registros de mis análisis morfosintácticos'),
- t.get('semantic_activities', 'Registros de mis análisis semánticos'),
- t.get('discourse_activities', 'Registros de mis análisis comparado de textos'),
- t.get('chat_activities', 'Registros de mis conversaciones con el tutor virtual')
- ])
-
- # Tab de Situación Actual
- 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 (mantiene nombre interno pero UI muestra "Análisis comparado de textos")
- 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_current_situation_activities(username: str, t: dict):
- """
- Muestra análisis de situación actual junto con las recomendaciones de Claude
- unificando la información de ambas colecciones y emparejándolas por cercanía temporal.
- """
- try:
- # Recuperar datos de ambas colecciones
- logger.info(f"Recuperando análisis de situación actual para {username}")
- situation_analyses = get_current_situation_analysis(username, limit=10)
-
- # Verificar si hay datos
- if situation_analyses:
- logger.info(f"Recuperados {len(situation_analyses)} análisis de situación")
- # Depurar para ver la estructura de datos
- for i, analysis in enumerate(situation_analyses):
- logger.info(f"Análisis #{i+1}: Claves disponibles: {list(analysis.keys())}")
- if 'metrics' in analysis:
- logger.info(f"Métricas disponibles: {list(analysis['metrics'].keys())}")
- else:
- logger.warning("No se encontraron análisis de situación actual")
-
- logger.info(f"Recuperando recomendaciones de Claude para {username}")
- claude_recommendations = get_claude_recommendations(username)
-
- if claude_recommendations:
- logger.info(f"Recuperadas {len(claude_recommendations)} recomendaciones de Claude")
- else:
- logger.warning("No se encontraron recomendaciones de Claude")
-
- # Verificar si hay algún tipo de análisis disponible
- if not situation_analyses and not claude_recommendations:
- logger.info("No se encontraron análisis de situación actual ni recomendaciones")
- st.info(t.get('no_current_situation', 'No hay análisis de situación actual registrados'))
- return
-
- # Crear pares combinados emparejando diagnósticos y recomendaciones cercanos en tiempo
- logger.info("Creando emparejamientos temporales de análisis")
-
- # Convertir timestamps a objetos datetime para comparación
- situation_times = []
- for analysis in situation_analyses:
- if 'timestamp' in analysis:
- try:
- timestamp_str = analysis['timestamp']
- dt = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
- situation_times.append((dt, analysis))
- except Exception as e:
- logger.error(f"Error parseando timestamp de situación: {str(e)}")
-
- recommendation_times = []
- for recommendation in claude_recommendations:
- if 'timestamp' in recommendation:
- try:
- timestamp_str = recommendation['timestamp']
- dt = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
- recommendation_times.append((dt, recommendation))
- except Exception as e:
- logger.error(f"Error parseando timestamp de recomendación: {str(e)}")
-
- # Ordenar por tiempo
- situation_times.sort(key=lambda x: x[0], reverse=True)
- recommendation_times.sort(key=lambda x: x[0], reverse=True)
-
- # Crear pares combinados
- combined_items = []
-
- # Primero, procesar todas las situaciones encontrando la recomendación más cercana
- for sit_time, situation in situation_times:
- # Buscar la recomendación más cercana en tiempo
- best_match = None
- min_diff = timedelta(minutes=30) # Máxima diferencia de tiempo aceptable (30 minutos)
- best_rec_time = None
-
- for rec_time, recommendation in recommendation_times:
- time_diff = abs(sit_time - rec_time)
- if time_diff < min_diff:
- min_diff = time_diff
- best_match = recommendation
- best_rec_time = rec_time
-
- # Crear un elemento combinado
- if best_match:
- timestamp_key = sit_time.isoformat()
- combined_items.append((timestamp_key, {
- 'situation': situation,
- 'recommendation': best_match,
- 'time_diff': min_diff.total_seconds()
- }))
- # Eliminar la recomendación usada para no reutilizarla
- recommendation_times = [(t, r) for t, r in recommendation_times if t != best_rec_time]
- logger.info(f"Emparejado: Diagnóstico {sit_time} con Recomendación {best_rec_time} (diferencia: {min_diff})")
- else:
- # Si no hay recomendación cercana, solo incluir la situación
- timestamp_key = sit_time.isoformat()
- combined_items.append((timestamp_key, {
- 'situation': situation
- }))
- logger.info(f"Sin emparejar: Diagnóstico {sit_time} sin recomendación cercana")
-
- # Agregar recomendaciones restantes sin situación
- for rec_time, recommendation in recommendation_times:
- timestamp_key = rec_time.isoformat()
- combined_items.append((timestamp_key, {
- 'recommendation': recommendation
- }))
- logger.info(f"Sin emparejar: Recomendación {rec_time} sin diagnóstico cercano")
-
- # Ordenar por tiempo (más reciente primero)
- combined_items.sort(key=lambda x: x[0], reverse=True)
-
- logger.info(f"Procesando {len(combined_items)} elementos combinados")
-
- # Mostrar cada par combinado
- for i, (timestamp_key, analysis_pair) in enumerate(combined_items):
- try:
- # Obtener datos de situación y recomendación
- situation_data = analysis_pair.get('situation', {})
- recommendation_data = analysis_pair.get('recommendation', {})
- time_diff = analysis_pair.get('time_diff')
-
- # Si no hay ningún dato, continuar al siguiente
- if not situation_data and not recommendation_data:
- continue
-
- # Determinar qué texto mostrar (priorizar el de la situación)
- text_to_show = situation_data.get('text', recommendation_data.get('text', ''))
- text_type = situation_data.get('text_type', recommendation_data.get('text_type', ''))
-
- # Formatear fecha para mostrar
- try:
- # Usar timestamp del key que ya es un formato ISO
- dt = datetime.fromisoformat(timestamp_key)
- formatted_date = dt.strftime("%d/%m/%Y %H:%M:%S")
- except Exception as date_error:
- logger.error(f"Error formateando fecha: {str(date_error)}")
- formatted_date = timestamp_key
-
- # Determinar el título del expander
- title = f"{t.get('analysis_date', 'Fecha')}: {formatted_date}"
- if text_type:
- text_type_display = {
- 'academic_article': t.get('academic_article', 'Artículo académico'),
- 'student_essay': t.get('student_essay', 'Trabajo universitario'),
- 'general_communication': t.get('general_communication', 'Comunicación general')
- }.get(text_type, text_type)
- title += f" - {text_type_display}"
-
- # Añadir indicador de emparejamiento si existe
- if time_diff is not None:
- if time_diff < 60: # menos de un minuto
- title += f" 🔄 (emparejados)"
- else:
- title += f" 🔄 (emparejados, diferencia: {int(time_diff//60)} min)"
-
- # Usar un ID único para cada expander
- expander_id = f"analysis_{i}_{timestamp_key.replace(':', '_')}"
-
- # Mostrar el análisis en un expander
- with st.expander(title, expanded=False):
- # Mostrar texto analizado con key único
- st.subheader(t.get('analyzed_text', 'Texto analizado'))
- st.text_area(
- "Text Content",
- value=text_to_show,
- height=100,
- disabled=True,
- label_visibility="collapsed",
- key=f"text_area_{expander_id}"
- )
-
- # Crear tabs para separar diagnóstico y recomendaciones
- diagnosis_tab, recommendations_tab = st.tabs([
- t.get('diagnosis_tab', 'Diagnóstico'),
- t.get('recommendations_tab', 'Recomendaciones')
- ])
-
- # Tab de diagnóstico
- with diagnosis_tab:
- if situation_data and 'metrics' in situation_data:
- metrics = situation_data['metrics']
-
- # Dividir en dos columnas
- col1, col2 = st.columns(2)
-
- # Principales métricas en formato de tarjetas
- with col1:
- st.subheader(t.get('key_metrics', 'Métricas clave'))
-
- # Mostrar cada métrica principal
- for metric_name, metric_data in metrics.items():
- try:
- # Determinar la puntuación
- score = None
- if isinstance(metric_data, dict):
- # Intentar diferentes nombres de campo
- if 'normalized_score' in metric_data:
- score = metric_data['normalized_score']
- elif 'score' in metric_data:
- score = metric_data['score']
- elif 'value' in metric_data:
- score = metric_data['value']
- elif isinstance(metric_data, (int, float)):
- score = metric_data
-
- if score is not None:
- # Asegurarse de que score es numérico
- if isinstance(score, (int, float)):
- # Determinar color y emoji basado en la puntuación
- if score < 0.5:
- emoji = "🔴"
- color = "#ffcccc" # light red
- elif score < 0.75:
- emoji = "🟡"
- color = "#ffffcc" # light yellow
- else:
- emoji = "🟢"
- color = "#ccffcc" # light green
-
- # Mostrar la métrica con estilo
- st.markdown(f"""
-
- {emoji} {metric_name.capitalize()}: {score:.2f}
-
- """, unsafe_allow_html=True)
- else:
- # Si no es numérico, mostrar como texto
- st.markdown(f"""
-
- ℹ️ {metric_name.capitalize()}: {str(score)}
-
- """, unsafe_allow_html=True)
- except Exception as e:
- logger.error(f"Error procesando métrica {metric_name}: {str(e)}")
-
- # Mostrar detalles adicionales si están disponibles
- with col2:
- st.subheader(t.get('details', 'Detalles'))
-
- # Para cada métrica, mostrar sus detalles si existen
- for metric_name, metric_data in metrics.items():
- try:
- if isinstance(metric_data, dict):
- # Mostrar detalles directamente o buscar en subcampos
- details = None
- if 'details' in metric_data and metric_data['details']:
- details = metric_data['details']
- else:
- # Crear un diccionario con los detalles excluyendo 'normalized_score' y similares
- details = {k: v for k, v in metric_data.items()
- if k not in ['normalized_score', 'score', 'value']}
-
- if details:
- st.write(f"**{metric_name.capitalize()}**")
- st.json(details, expanded=False)
- except Exception as e:
- logger.error(f"Error mostrando detalles de {metric_name}: {str(e)}")
- else:
- st.info(t.get('no_diagnosis', 'No hay datos de diagnóstico disponibles'))
-
- # Tab de recomendaciones
- with recommendations_tab:
- if recommendation_data and 'recommendations' in recommendation_data:
- st.markdown(f"""
-
- {recommendation_data['recommendations']}
-
- """, unsafe_allow_html=True)
- elif recommendation_data and 'feedback' in recommendation_data:
- st.markdown(f"""
-
- {recommendation_data['feedback']}
-
- """, unsafe_allow_html=True)
- else:
- st.info(t.get('no_recommendations', 'No hay recomendaciones disponibles'))
-
- except Exception as e:
- logger.error(f"Error procesando par de análisis: {str(e)}")
- continue
-
- except Exception as e:
- logger.error(f"Error mostrando actividades de situación actual: {str(e)}")
- st.error(t.get('error_current_situation', 'Error al mostrar análisis de situación actual'))
-
-###############################################################################################
-
-def display_morphosyntax_activities(username: str, t: dict):
- """
- Muestra actividades de análisis morfosintáctico, incluyendo base e iteraciones
- desde las nuevas colecciones: student_morphosyntax_analysis_base y student_morphosyntax_iterations
- """
- try:
- # Importación inline para evitar problemas de circularidad
- # Utilizamos la función de la nueva estructura de DB iterativa
- from ..database.morphosyntax_iterative_mongo_db import get_student_morphosyntax_analysis
-
- logger.info(f"Recuperando análisis morfosintáctico para {username}")
-
- # Esta función ahora trae tanto las bases como sus iteraciones
- base_analyses = get_student_morphosyntax_analysis(username)
-
- if not base_analyses:
- logger.info("No se encontraron análisis morfosintácticos")
- st.info(t.get('no_morpho_analyses', 'No hay análisis morfosintácticos registrados'))
- return
-
- logger.info(f"Procesando {len(base_analyses)} análisis morfosintácticos base")
-
- # Procesar cada análisis base con sus iteraciones
- for base_analysis in base_analyses:
- try:
- # Formatear fecha
- timestamp = datetime.fromisoformat(base_analysis['timestamp'].replace('Z', '+00:00'))
- formatted_date = timestamp.strftime("%d/%m/%Y %H:%M:%S")
-
- # Título del expander: incluir información de si tiene iteraciones
- expander_title = f"{t.get('analysis_date', 'Fecha')}: {formatted_date}"
- if base_analysis.get('has_iterations', False):
- expander_title += f" ({t.get('has_iterations', 'Con iteraciones')})"
-
- with st.expander(expander_title, expanded=False):
- # Mostrar texto base
- st.subheader(t.get('base_text', 'Texto original'))
- st.text_area(
- "Base Text Content",
- value=base_analysis.get('text', ''),
- height=100,
- disabled=True,
- label_visibility="collapsed",
- key=f"base_text_{str(base_analysis['_id'])}"
- )
-
- # Mostrar diagrama de arco base si existe
- if 'arc_diagrams' in base_analysis and base_analysis['arc_diagrams']:
- st.subheader(t.get('syntactic_diagrams', 'Diagrama sintáctico (original)'))
- # Mostrar cada diagrama (normalmente solo uno por oración)
- for diagram in base_analysis['arc_diagrams']:
- st.write(diagram, unsafe_allow_html=True)
-
- # Procesar iteraciones si existen
- if 'iterations' in base_analysis and base_analysis['iterations']:
- st.markdown("---") # Línea divisoria
- st.subheader(t.get('iterations', 'Versiones mejoradas'))
-
- # Crear tabs para cada iteración
- iteration_tabs = st.tabs([
- f"{t.get('iteration', 'Versión')} {i+1}"
- for i in range(len(base_analysis['iterations']))
- ])
-
- # Mostrar cada iteración en su propia pestaña
- for i, (tab, iteration) in enumerate(zip(iteration_tabs, base_analysis['iterations'])):
- with tab:
- # Timestamp de la iteración
- iter_timestamp = datetime.fromisoformat(
- iteration['timestamp'].replace('Z', '+00:00'))
- iter_formatted_date = iter_timestamp.strftime("%d/%m/%Y %H:%M:%S")
- st.caption(f"{t.get('iteration_date', 'Fecha de versión')}: {iter_formatted_date}")
-
- # Texto de la iteración
- st.text_area(
- f"Iteration Text {i+1}",
- value=iteration.get('iteration_text', ''),
- height=100,
- disabled=True,
- label_visibility="collapsed",
- key=f"iter_text_{str(iteration['_id'])}"
- )
-
- # Diagrama de arco de la iteración
- if 'arc_diagrams' in iteration and iteration['arc_diagrams']:
- st.subheader(t.get('iteration_diagram', 'Diagrama sintáctico (mejorado)'))
- for diagram in iteration['arc_diagrams']:
- st.write(diagram, unsafe_allow_html=True)
-
- except Exception as e:
- logger.error(f"Error procesando análisis morfosintáctico: {str(e)}")
- st.error(t.get('error_processing_analysis', 'Error procesando este análisis'))
- continue
-
- 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 centradas en las visualizaciones comparativas
- """
- 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 comparados de textos registrados'))
- return
-
- logger.info(f"Procesando {len(analyses)} análisis del discurso")
- for analysis in analyses:
- try:
- # Formatear fecha
- timestamp = datetime.fromisoformat(analysis['timestamp'].replace('Z', '+00:00'))
- formatted_date = timestamp.strftime("%d/%m/%Y %H:%M:%S")
-
- # Título del expander
- expander_title = f"{t.get('analysis_date', 'Fecha')}: {formatted_date}"
-
- with st.expander(expander_title, expanded=False):
- # Mostrar conceptos clave en dos columnas
- if 'key_concepts1' in analysis and 'key_concepts2' in analysis:
- col1, col2 = st.columns(2)
-
- with col1:
- st.subheader(t.get('concepts_text_1', 'Conceptos Texto 1'))
- if analysis['key_concepts1']:
- df1 = pd.DataFrame(analysis['key_concepts1'], columns=['Concepto', 'Relevancia'])
- st.dataframe(df1, use_container_width=True)
-
- with col2:
- st.subheader(t.get('concepts_text_2', 'Conceptos Texto 2'))
- if analysis['key_concepts2']:
- df2 = pd.DataFrame(analysis['key_concepts2'], columns=['Concepto', 'Relevancia'])
- st.dataframe(df2, use_container_width=True)
-
- # Mostrar visualizaciones semánticas
- st.subheader(t.get('semantic_visualization', 'Visualización Semántica'))
-
- # Mostrar gráficos en fila
- graph_cols = st.columns(3)
-
- # Gráfico 1 (si existe)
- with graph_cols[0]:
- if analysis.get('graph1'):
- try:
- st.caption(t.get('graph1_caption', 'Grafo Texto 1'))
- if isinstance(analysis['graph1'], str) and analysis['graph1'].startswith('data:image'):
- import base64
- image_bytes = base64.b64decode(analysis['graph1'].split(',')[1])
- st.image(image_bytes, use_column_width=True)
- elif isinstance(analysis['graph1'], bytes):
- st.image(analysis['graph1'], use_column_width=True)
- elif analysis['graph1'] is not None:
- st.pyplot(analysis['graph1'])
- except Exception as e:
- logger.error(f"Error mostrando gráfico 1: {str(e)}")
- st.error(t.get('error_graph1', 'Error mostrando gráfico 1'))
-
- # Gráfico 2 (si existe)
- with graph_cols[1]:
- if analysis.get('graph2'):
- try:
- st.caption(t.get('graph2_caption', 'Grafo Texto 2'))
- if isinstance(analysis['graph2'], str) and analysis['graph2'].startswith('data:image'):
- import base64
- image_bytes = base64.b64decode(analysis['graph2'].split(',')[1])
- st.image(image_bytes, use_column_width=True)
- elif isinstance(analysis['graph2'], bytes):
- st.image(analysis['graph2'], use_column_width=True)
- elif analysis['graph2'] is not None:
- st.pyplot(analysis['graph2'])
- except Exception as e:
- logger.error(f"Error mostrando gráfico 2: {str(e)}")
- st.error(t.get('error_graph2', 'Error mostrando gráfico 2'))
-
- # Gráfico combinado (si existe) - en la última columna
- with graph_cols[2]:
- if analysis.get('combined_graph'):
- try:
- st.caption(t.get('combined_graph_caption', 'Grafo Comparativo'))
- if isinstance(analysis['combined_graph'], str):
- try:
- import base64
- # Intentar diferentes formatos de base64
- if analysis['combined_graph'].startswith('data:image'):
- image_bytes = base64.b64decode(analysis['combined_graph'].split(',')[1])
- else:
- image_bytes = base64.b64decode(analysis['combined_graph'])
- st.image(image_bytes, use_column_width=True)
- except:
- logger.error("Error decodificando imagen combinada")
- elif isinstance(analysis['combined_graph'], bytes):
- st.image(analysis['combined_graph'], use_column_width=True)
- elif analysis['combined_graph'] is not None:
- st.pyplot(analysis['combined_graph'])
- except Exception as e:
- logger.error(f"Error mostrando gráfico combinado: {str(e)}")
- st.error(t.get('error_combined_graph', 'Error mostrando gráfico combinado'))
-
- # Si no hay ningún gráfico disponible
- if all(analysis.get(k) is None for k in ['graph1', 'graph2', 'combined_graph']):
- st.info(t.get('no_visualizations', 'No hay visualizaciones disponibles para este análisis'))
-
- 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 comparado de textos'))
-
-
-
-
-
-
-
-
-
-
-
-
-
-#################################################################################
-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'))
-
-#################################################################################
-def display_discourse_comparison(analysis: dict, t: dict):
- """Muestra la comparación de análisis del discurso"""
- # Cambiado para usar "textos comparados" en la UI
- 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)
\ No newline at end of file
+##############
+###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, timedelta
+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
+from ..database.claude_recommendations_mongo_db import get_claude_recommendations
+
+# Importar la función generate_unique_key
+from ..utils.widget_utils import generate_unique_key
+
+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:
+ # Cambiado de "Mis Actividades" a "Registro de mis actividades"
+ #st.header(t.get('activities_title', 'Registro de mis actividades'))
+
+ # Tabs para diferentes tipos de análisis
+ # Cambiado "Análisis del Discurso" a "Análisis comparado de textos"
+ tabs = st.tabs([
+ t.get('current_situation_activities', 'Registros de la función: Mi Situación Actual'),
+ t.get('morpho_activities', 'Registros de mis análisis morfosintácticos'),
+ t.get('semantic_activities', 'Registros de mis análisis semánticos'),
+ t.get('discourse_activities', 'Registros de mis análisis comparado de textos'),
+ t.get('chat_activities', 'Registros de mis conversaciones con el tutor virtual')
+ ])
+
+ # Tab de Situación Actual
+ 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 (mantiene nombre interno pero UI muestra "Análisis comparado de textos")
+ 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_current_situation_activities(username: str, t: dict):
+ """
+ Muestra análisis de situación actual junto con las recomendaciones de Claude
+ unificando la información de ambas colecciones y emparejándolas por cercanía temporal.
+ """
+ try:
+ # Recuperar datos de ambas colecciones
+ logger.info(f"Recuperando análisis de situación actual para {username}")
+ situation_analyses = get_current_situation_analysis(username, limit=10)
+
+ # Verificar si hay datos
+ if situation_analyses:
+ logger.info(f"Recuperados {len(situation_analyses)} análisis de situación")
+ # Depurar para ver la estructura de datos
+ for i, analysis in enumerate(situation_analyses):
+ logger.info(f"Análisis #{i+1}: Claves disponibles: {list(analysis.keys())}")
+ if 'metrics' in analysis:
+ logger.info(f"Métricas disponibles: {list(analysis['metrics'].keys())}")
+ else:
+ logger.warning("No se encontraron análisis de situación actual")
+
+ logger.info(f"Recuperando recomendaciones de Claude para {username}")
+ claude_recommendations = get_claude_recommendations(username)
+
+ if claude_recommendations:
+ logger.info(f"Recuperadas {len(claude_recommendations)} recomendaciones de Claude")
+ else:
+ logger.warning("No se encontraron recomendaciones de Claude")
+
+ # Verificar si hay algún tipo de análisis disponible
+ if not situation_analyses and not claude_recommendations:
+ logger.info("No se encontraron análisis de situación actual ni recomendaciones")
+ st.info(t.get('no_current_situation', 'No hay análisis de situación actual registrados'))
+ return
+
+ # Crear pares combinados emparejando diagnósticos y recomendaciones cercanos en tiempo
+ logger.info("Creando emparejamientos temporales de análisis")
+
+ # Convertir timestamps a objetos datetime para comparación
+ situation_times = []
+ for analysis in situation_analyses:
+ if 'timestamp' in analysis:
+ try:
+ timestamp_str = analysis['timestamp']
+ dt = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
+ situation_times.append((dt, analysis))
+ except Exception as e:
+ logger.error(f"Error parseando timestamp de situación: {str(e)}")
+
+ recommendation_times = []
+ for recommendation in claude_recommendations:
+ if 'timestamp' in recommendation:
+ try:
+ timestamp_str = recommendation['timestamp']
+ dt = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
+ recommendation_times.append((dt, recommendation))
+ except Exception as e:
+ logger.error(f"Error parseando timestamp de recomendación: {str(e)}")
+
+ # Ordenar por tiempo
+ situation_times.sort(key=lambda x: x[0], reverse=True)
+ recommendation_times.sort(key=lambda x: x[0], reverse=True)
+
+ # Crear pares combinados
+ combined_items = []
+
+ # Primero, procesar todas las situaciones encontrando la recomendación más cercana
+ for sit_time, situation in situation_times:
+ # Buscar la recomendación más cercana en tiempo
+ best_match = None
+ min_diff = timedelta(minutes=30) # Máxima diferencia de tiempo aceptable (30 minutos)
+ best_rec_time = None
+
+ for rec_time, recommendation in recommendation_times:
+ time_diff = abs(sit_time - rec_time)
+ if time_diff < min_diff:
+ min_diff = time_diff
+ best_match = recommendation
+ best_rec_time = rec_time
+
+ # Crear un elemento combinado
+ if best_match:
+ timestamp_key = sit_time.isoformat()
+ combined_items.append((timestamp_key, {
+ 'situation': situation,
+ 'recommendation': best_match,
+ 'time_diff': min_diff.total_seconds()
+ }))
+ # Eliminar la recomendación usada para no reutilizarla
+ recommendation_times = [(t, r) for t, r in recommendation_times if t != best_rec_time]
+ logger.info(f"Emparejado: Diagnóstico {sit_time} con Recomendación {best_rec_time} (diferencia: {min_diff})")
+ else:
+ # Si no hay recomendación cercana, solo incluir la situación
+ timestamp_key = sit_time.isoformat()
+ combined_items.append((timestamp_key, {
+ 'situation': situation
+ }))
+ logger.info(f"Sin emparejar: Diagnóstico {sit_time} sin recomendación cercana")
+
+ # Agregar recomendaciones restantes sin situación
+ for rec_time, recommendation in recommendation_times:
+ timestamp_key = rec_time.isoformat()
+ combined_items.append((timestamp_key, {
+ 'recommendation': recommendation
+ }))
+ logger.info(f"Sin emparejar: Recomendación {rec_time} sin diagnóstico cercano")
+
+ # Ordenar por tiempo (más reciente primero)
+ combined_items.sort(key=lambda x: x[0], reverse=True)
+
+ logger.info(f"Procesando {len(combined_items)} elementos combinados")
+
+ # Mostrar cada par combinado
+ for i, (timestamp_key, analysis_pair) in enumerate(combined_items):
+ try:
+ # Obtener datos de situación y recomendación
+ situation_data = analysis_pair.get('situation', {})
+ recommendation_data = analysis_pair.get('recommendation', {})
+ time_diff = analysis_pair.get('time_diff')
+
+ # Si no hay ningún dato, continuar al siguiente
+ if not situation_data and not recommendation_data:
+ continue
+
+ # Determinar qué texto mostrar (priorizar el de la situación)
+ text_to_show = situation_data.get('text', recommendation_data.get('text', ''))
+ text_type = situation_data.get('text_type', recommendation_data.get('text_type', ''))
+
+ # Formatear fecha para mostrar
+ try:
+ # Usar timestamp del key que ya es un formato ISO
+ dt = datetime.fromisoformat(timestamp_key)
+ formatted_date = dt.strftime("%d/%m/%Y %H:%M:%S")
+ except Exception as date_error:
+ logger.error(f"Error formateando fecha: {str(date_error)}")
+ formatted_date = timestamp_key
+
+ # Determinar el título del expander
+ title = f"{t.get('analysis_date', 'Fecha')}: {formatted_date}"
+ if text_type:
+ text_type_display = {
+ 'academic_article': t.get('academic_article', 'Artículo académico'),
+ 'student_essay': t.get('student_essay', 'Trabajo universitario'),
+ 'general_communication': t.get('general_communication', 'Comunicación general')
+ }.get(text_type, text_type)
+ title += f" - {text_type_display}"
+
+ # Añadir indicador de emparejamiento si existe
+ if time_diff is not None:
+ if time_diff < 60: # menos de un minuto
+ title += f" 🔄 (emparejados)"
+ else:
+ title += f" 🔄 (emparejados, diferencia: {int(time_diff//60)} min)"
+
+ # Usar un ID único para cada expander
+ expander_id = f"analysis_{i}_{timestamp_key.replace(':', '_')}"
+
+ # Mostrar el análisis en un expander
+ with st.expander(title, expanded=False):
+ # Mostrar texto analizado con key único
+ st.subheader(t.get('analyzed_text', 'Texto analizado'))
+ st.text_area(
+ "Text Content",
+ value=text_to_show,
+ height=100,
+ disabled=True,
+ label_visibility="collapsed",
+ key=f"text_area_{expander_id}"
+ )
+
+ # Crear tabs para separar diagnóstico y recomendaciones
+ diagnosis_tab, recommendations_tab = st.tabs([
+ t.get('diagnosis_tab', 'Diagnóstico'),
+ t.get('recommendations_tab', 'Recomendaciones')
+ ])
+
+ # Tab de diagnóstico
+ with diagnosis_tab:
+ if situation_data and 'metrics' in situation_data:
+ metrics = situation_data['metrics']
+
+ # Dividir en dos columnas
+ col1, col2 = st.columns(2)
+
+ # Principales métricas en formato de tarjetas
+ with col1:
+ st.subheader(t.get('key_metrics', 'Métricas clave'))
+
+ # Mostrar cada métrica principal
+ for metric_name, metric_data in metrics.items():
+ try:
+ # Determinar la puntuación
+ score = None
+ if isinstance(metric_data, dict):
+ # Intentar diferentes nombres de campo
+ if 'normalized_score' in metric_data:
+ score = metric_data['normalized_score']
+ elif 'score' in metric_data:
+ score = metric_data['score']
+ elif 'value' in metric_data:
+ score = metric_data['value']
+ elif isinstance(metric_data, (int, float)):
+ score = metric_data
+
+ if score is not None:
+ # Asegurarse de que score es numérico
+ if isinstance(score, (int, float)):
+ # Determinar color y emoji basado en la puntuación
+ if score < 0.5:
+ emoji = "🔴"
+ color = "#ffcccc" # light red
+ elif score < 0.75:
+ emoji = "🟡"
+ color = "#ffffcc" # light yellow
+ else:
+ emoji = "🟢"
+ color = "#ccffcc" # light green
+
+ # Mostrar la métrica con estilo
+ st.markdown(f"""
+
+ {emoji} {metric_name.capitalize()}: {score:.2f}
+
+ """, unsafe_allow_html=True)
+ else:
+ # Si no es numérico, mostrar como texto
+ st.markdown(f"""
+
+ ℹ️ {metric_name.capitalize()}: {str(score)}
+
+ """, unsafe_allow_html=True)
+ except Exception as e:
+ logger.error(f"Error procesando métrica {metric_name}: {str(e)}")
+
+ # Mostrar detalles adicionales si están disponibles
+ with col2:
+ st.subheader(t.get('details', 'Detalles'))
+
+ # Para cada métrica, mostrar sus detalles si existen
+ for metric_name, metric_data in metrics.items():
+ try:
+ if isinstance(metric_data, dict):
+ # Mostrar detalles directamente o buscar en subcampos
+ details = None
+ if 'details' in metric_data and metric_data['details']:
+ details = metric_data['details']
+ else:
+ # Crear un diccionario con los detalles excluyendo 'normalized_score' y similares
+ details = {k: v for k, v in metric_data.items()
+ if k not in ['normalized_score', 'score', 'value']}
+
+ if details:
+ st.write(f"**{metric_name.capitalize()}**")
+ st.json(details, expanded=False)
+ except Exception as e:
+ logger.error(f"Error mostrando detalles de {metric_name}: {str(e)}")
+ else:
+ st.info(t.get('no_diagnosis', 'No hay datos de diagnóstico disponibles'))
+
+ # Tab de recomendaciones
+ with recommendations_tab:
+ if recommendation_data and 'recommendations' in recommendation_data:
+ st.markdown(f"""
+
+ {recommendation_data['recommendations']}
+
+ """, unsafe_allow_html=True)
+ elif recommendation_data and 'feedback' in recommendation_data:
+ st.markdown(f"""
+
+ {recommendation_data['feedback']}
+
+ """, unsafe_allow_html=True)
+ else:
+ st.info(t.get('no_recommendations', 'No hay recomendaciones disponibles'))
+
+ except Exception as e:
+ logger.error(f"Error procesando par de análisis: {str(e)}")
+ continue
+
+ except Exception as e:
+ logger.error(f"Error mostrando actividades de situación actual: {str(e)}")
+ st.error(t.get('error_current_situation', 'Error al mostrar análisis de situación actual'))
+
+###############################################################################################
+
+def display_morphosyntax_activities(username: str, t: dict):
+ """
+ Muestra actividades de análisis morfosintáctico, incluyendo base e iteraciones
+ desde las nuevas colecciones: student_morphosyntax_analysis_base y student_morphosyntax_iterations
+ """
+ try:
+ # Importación inline para evitar problemas de circularidad
+ # Utilizamos la función de la nueva estructura de DB iterativa
+ from ..database.morphosyntax_iterative_mongo_db import get_student_morphosyntax_analysis
+
+ logger.info(f"Recuperando análisis morfosintáctico para {username}")
+
+ # Esta función ahora trae tanto las bases como sus iteraciones
+ base_analyses = get_student_morphosyntax_analysis(username)
+
+ if not base_analyses:
+ logger.info("No se encontraron análisis morfosintácticos")
+ st.info(t.get('no_morpho_analyses', 'No hay análisis morfosintácticos registrados'))
+ return
+
+ logger.info(f"Procesando {len(base_analyses)} análisis morfosintácticos base")
+
+ # Procesar cada análisis base con sus iteraciones
+ for base_analysis in base_analyses:
+ try:
+ # Formatear fecha
+ timestamp = datetime.fromisoformat(base_analysis['timestamp'].replace('Z', '+00:00'))
+ formatted_date = timestamp.strftime("%d/%m/%Y %H:%M:%S")
+
+ # Título del expander: incluir información de si tiene iteraciones
+ expander_title = f"{t.get('analysis_date', 'Fecha')}: {formatted_date}"
+ if base_analysis.get('has_iterations', False):
+ expander_title += f" ({t.get('has_iterations', 'Con iteraciones')})"
+
+ with st.expander(expander_title, expanded=False):
+ # Mostrar texto base
+ st.subheader(t.get('base_text', 'Texto original'))
+ st.text_area(
+ "Base Text Content",
+ value=base_analysis.get('text', ''),
+ height=100,
+ disabled=True,
+ label_visibility="collapsed",
+ key=f"base_text_{str(base_analysis['_id'])}"
+ )
+
+ # Mostrar diagrama de arco base si existe
+ if 'arc_diagrams' in base_analysis and base_analysis['arc_diagrams']:
+ st.subheader(t.get('syntactic_diagrams', 'Diagrama sintáctico (original)'))
+ # Mostrar cada diagrama (normalmente solo uno por oración)
+ for diagram in base_analysis['arc_diagrams']:
+ st.write(diagram, unsafe_allow_html=True)
+
+ # Procesar iteraciones si existen
+ if 'iterations' in base_analysis and base_analysis['iterations']:
+ st.markdown("---") # Línea divisoria
+ st.subheader(t.get('iterations', 'Versiones mejoradas'))
+
+ # Crear tabs para cada iteración
+ iteration_tabs = st.tabs([
+ f"{t.get('iteration', 'Versión')} {i+1}"
+ for i in range(len(base_analysis['iterations']))
+ ])
+
+ # Mostrar cada iteración en su propia pestaña
+ for i, (tab, iteration) in enumerate(zip(iteration_tabs, base_analysis['iterations'])):
+ with tab:
+ # Timestamp de la iteración
+ iter_timestamp = datetime.fromisoformat(
+ iteration['timestamp'].replace('Z', '+00:00'))
+ iter_formatted_date = iter_timestamp.strftime("%d/%m/%Y %H:%M:%S")
+ st.caption(f"{t.get('iteration_date', 'Fecha de versión')}: {iter_formatted_date}")
+
+ # Texto de la iteración
+ st.text_area(
+ f"Iteration Text {i+1}",
+ value=iteration.get('iteration_text', ''),
+ height=100,
+ disabled=True,
+ label_visibility="collapsed",
+ key=f"iter_text_{str(iteration['_id'])}"
+ )
+
+ # Diagrama de arco de la iteración
+ if 'arc_diagrams' in iteration and iteration['arc_diagrams']:
+ st.subheader(t.get('iteration_diagram', 'Diagrama sintáctico (mejorado)'))
+ for diagram in iteration['arc_diagrams']:
+ st.write(diagram, unsafe_allow_html=True)
+
+ except Exception as e:
+ logger.error(f"Error procesando análisis morfosintáctico: {str(e)}")
+ st.error(t.get('error_processing_analysis', 'Error procesando este análisis'))
+ continue
+
+ 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_container_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 (mostrado como 'Análisis comparado de textos' en la UI)"""
+ 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")
+ # Usamos el término "análisis comparado de textos" en la UI
+ st.info(t.get('no_discourse_analyses', 'No hay análisis comparados de textos 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']):
+ 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):
+ # Crear dos columnas para mostrar los documentos lado a lado
+ col1, col2 = st.columns(2)
+
+ # Documento 1 - Columna izquierda
+ with col1:
+ st.subheader(t.get('doc1_title', 'Documento 1'))
+ st.markdown(t.get('key_concepts', 'Conceptos Clave'))
+
+ # Mostrar conceptos clave en formato de etiquetas
+ if 'key_concepts1' in analysis and analysis['key_concepts1']:
+ concepts_html = f"""
+
+ {''.join([
+ f'
'
+ f'{concept}'
+ f'({freq:.2f})
'
+ for concept, freq in analysis['key_concepts1']
+ ])}
+
+ """
+ st.markdown(concepts_html, unsafe_allow_html=True)
+ else:
+ st.info(t.get('no_concepts', 'No hay conceptos disponibles'))
+
+ # Mostrar grafo 1
+ if 'graph1' in analysis:
+ try:
+ if isinstance(analysis['graph1'], bytes):
+ st.image(
+ analysis['graph1'],
+ use_container_width=True
+ )
+ else:
+ logger.warning(f"graph1 no es bytes: {type(analysis['graph1'])}")
+ st.warning(t.get('graph_not_available', 'Gráfico no disponible'))
+ except Exception as e:
+ logger.error(f"Error mostrando graph1: {str(e)}")
+ st.error(t.get('error_loading_graph', 'Error al cargar el gráfico'))
+ else:
+ st.info(t.get('no_visualization', 'No hay visualización disponible'))
+
+ # Interpretación del grafo
+ st.markdown("**📊 Interpretación del grafo:**")
+ st.markdown("""
+ - 🔀 Las flechas indican la dirección de la relación entre conceptos
+ - 🎨 Los colores más intensos indican conceptos más centrales en el texto
+ - ⭕ El tamaño de los nodos representa la frecuencia del concepto
+ - ↔️ El grosor de las líneas indica la fuerza de la conexión
+ """)
+
+ # Documento 2 - Columna derecha
+ with col2:
+ st.subheader(t.get('doc2_title', 'Documento 2'))
+ st.markdown(t.get('key_concepts', 'Conceptos Clave'))
+
+ # Mostrar conceptos clave en formato de etiquetas
+ if 'key_concepts2' in analysis and analysis['key_concepts2']:
+ concepts_html = f"""
+
+ {''.join([
+ f'
'
+ f'{concept}'
+ f'({freq:.2f})
'
+ for concept, freq in analysis['key_concepts2']
+ ])}
+
+ """
+ st.markdown(concepts_html, unsafe_allow_html=True)
+ else:
+ st.info(t.get('no_concepts', 'No hay conceptos disponibles'))
+
+ # Mostrar grafo 2
+ if 'graph2' in analysis:
+ try:
+ if isinstance(analysis['graph2'], bytes):
+ st.image(
+ analysis['graph2'],
+ use_container_width=True
+ )
+ else:
+ logger.warning(f"graph2 no es bytes: {type(analysis['graph2'])}")
+ st.warning(t.get('graph_not_available', 'Gráfico no disponible'))
+ except Exception as e:
+ logger.error(f"Error mostrando graph2: {str(e)}")
+ st.error(t.get('error_loading_graph', 'Error al cargar el gráfico'))
+ else:
+ st.info(t.get('no_visualization', 'No hay visualización disponible'))
+
+ # Interpretación del grafo
+ st.markdown("**📊 Interpretación del grafo:**")
+ st.markdown("""
+ - 🔀 Las flechas indican la dirección de la relación entre conceptos
+ - 🎨 Los colores más intensos indican conceptos más centrales en el texto
+ - ⭕ El tamaño de los nodos representa la frecuencia del concepto
+ - ↔️ El grosor de las líneas indica la fuerza de la conexión
+ """)
+
+ 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)}")
+ # Usamos el término "análisis comparado de textos" en la UI
+ st.error(t.get('error_discourse', 'Error al mostrar análisis comparado de textos'))
+
+
+
+#################################################################################
+
+def display_discourse_comparison(analysis: dict, t: dict):
+ """
+ Muestra la comparación de conceptos clave en análisis del discurso.
+ Formato horizontal simplificado.
+ """
+ st.subheader(t.get('comparison_results', 'Resultados de la comparación'))
+
+ # Verificar si tenemos los conceptos necesarios
+ if not ('key_concepts1' in analysis and analysis['key_concepts1']):
+ st.info(t.get('no_concepts', 'No hay conceptos disponibles para comparar'))
+ return
+
+ # Conceptos del Texto 1 - Formato horizontal
+ st.markdown(f"**{t.get('concepts_text_1', 'Conceptos Texto 1')}:**")
+ try:
+ # Comprobar formato y mostrar horizontalmente
+ if isinstance(analysis['key_concepts1'], list) and len(analysis['key_concepts1']) > 0:
+ if isinstance(analysis['key_concepts1'][0], list) and len(analysis['key_concepts1'][0]) == 2:
+ # Formatear como "concepto (valor), concepto2 (valor2), ..."
+ concepts_text = ", ".join([f"{c[0]} ({c[1]})" for c in analysis['key_concepts1'][:10]])
+ st.markdown(f"*{concepts_text}*")
+ else:
+ # Si no tiene el formato esperado, mostrar como lista simple
+ st.markdown(", ".join(str(c) for c in analysis['key_concepts1'][:10]))
+ else:
+ st.write(str(analysis['key_concepts1']))
+ except Exception as e:
+ logger.error(f"Error mostrando key_concepts1: {str(e)}")
+ st.error(t.get('error_concepts1', 'Error mostrando conceptos del Texto 1'))
+
+ # Conceptos del Texto 2 - Formato horizontal
+ st.markdown(f"**{t.get('concepts_text_2', 'Conceptos Texto 2')}:**")
+ if 'key_concepts2' in analysis and analysis['key_concepts2']:
+ try:
+ # Comprobar formato y mostrar horizontalmente
+ if isinstance(analysis['key_concepts2'], list) and len(analysis['key_concepts2']) > 0:
+ if isinstance(analysis['key_concepts2'][0], list) and len(analysis['key_concepts2'][0]) == 2:
+ # Formatear como "concepto (valor), concepto2 (valor2), ..."
+ concepts_text = ", ".join([f"{c[0]} ({c[1]})" for c in analysis['key_concepts2'][:10]])
+ st.markdown(f"*{concepts_text}*")
+ else:
+ # Si no tiene el formato esperado, mostrar como lista simple
+ st.markdown(", ".join(str(c) for c in analysis['key_concepts2'][:10]))
+ else:
+ st.write(str(analysis['key_concepts2']))
+ except Exception as e:
+ logger.error(f"Error mostrando key_concepts2: {str(e)}")
+ st.error(t.get('error_concepts2', 'Error mostrando conceptos del Texto 2'))
+ else:
+ st.info(t.get('no_concepts2', 'No hay conceptos disponibles para el Texto 2'))
+
+
+#################################################################################
+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'))
+
+#################################################################################
diff --git a/modules/studentact/temp_current_situation_interface.py b/modules/studentact/temp_current_situation_interface.py
index 682484db7ddc4e3b8893d96fc6db10c317f3f71c..c5c62f53c8f66bfd658f68862f06b5624f34b0bf 100644
--- a/modules/studentact/temp_current_situation_interface.py
+++ b/modules/studentact/temp_current_situation_interface.py
@@ -1,311 +1,311 @@
-# modules/studentact/current_situation_interface.py
-
-import streamlit as st
-import logging
-from ..utils.widget_utils import generate_unique_key
-from .current_situation_analysis import (
- analyze_text_dimensions,
- create_vocabulary_network,
- create_syntax_complexity_graph,
- create_cohesion_heatmap
-)
-
-logger = logging.getLogger(__name__)
-
-def display_current_situation_interface(lang_code, nlp_models, t):
- """
- Interfaz modular para el análisis de la situación actual del estudiante.
- Esta función maneja la presentación y la interacción con el usuario.
-
- Args:
- lang_code: Código del idioma actual
- nlp_models: Diccionario de modelos de spaCy cargados
- t: Diccionario de traducciones
- """
- st.markdown("## Mi Situación Actual de Escritura")
-
- # Container principal para mejor organización visual
- with st.container():
- # Columnas para entrada y visualización
- text_col, visual_col = st.columns([1,2])
-
- with text_col:
- # Área de entrada de texto
- text_input = st.text_area(
- t.get('current_situation_input', "Ingresa tu texto para analizar:"),
- height=400,
- key=generate_unique_key("current_situation", "input")
- )
-
- # Botón de análisis
- if st.button(
- t.get('analyze_button', "Explorar mi escritura"),
- type="primary",
- disabled=not text_input,
- key=generate_unique_key("current_situation", "analyze")
- ):
- try:
- with st.spinner(t.get('processing', "Analizando texto...")):
- # 1. Procesar el texto
- doc = nlp_models[lang_code](text_input)
- metrics = analyze_text_dimensions(doc)
-
- # 2. Mostrar visualizaciones en la columna derecha
- with visual_col:
- display_current_situation_visual(doc, metrics)
-
- # 3. Obtener retroalimentación de Claude
- feedback = get_claude_feedback(metrics, text_input)
-
- # 4. Guardar los resultados
- from ..database.current_situation_mongo_db import store_current_situation_result
-
- if st.button(t.get('analyze_button', "Explorar mi escritura")):
- with st.spinner(t.get('processing', "Analizando texto...")):
- # Procesar y analizar
- doc = nlp_models[lang_code](text_input)
-
- # Obtener métricas con manejo de errores
- try:
- metrics = analyze_text_dimensions(doc)
- except Exception as e:
- logger.error(f"Error en análisis: {str(e)}")
- st.error("Error en el análisis de dimensiones")
- return
-
- # Obtener feedback
- try:
- feedback = get_claude_feedback(metrics, text_input)
- except Exception as e:
- logger.error(f"Error obteniendo feedback: {str(e)}")
- st.error("Error obteniendo retroalimentación")
- return
-
- # Guardar resultados con verificación
- if store_current_situation_result(
- st.session_state.username,
- text_input,
- metrics,
- feedback
- ):
- st.success(t.get('save_success', "Análisis guardado"))
-
- # Mostrar visualizaciones y recomendaciones
- display_current_situation_visual(doc, metrics)
- show_recommendations(feedback, t)
- else:
- st.error("Error al guardar el análisis")
-
- except Exception as e:
- logger.error(f"Error en interfaz: {str(e)}")
- st.error("Error general en la interfaz")
-
-################################################################
-def display_current_situation_visual(doc, metrics):
- """Visualización mejorada de resultados con interpretaciones"""
- try:
- with st.container():
- # Estilos CSS mejorados para los contenedores
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- # 1. Riqueza de Vocabulario
- with st.expander("📚 Riqueza de Vocabulario", expanded=True):
- st.markdown('
', unsafe_allow_html=True)
- vocabulary_graph = create_vocabulary_network(doc)
- if vocabulary_graph:
- # Mostrar gráfico
- st.pyplot(vocabulary_graph)
- plt.close(vocabulary_graph)
-
- # Interpretación
- st.markdown('
', unsafe_allow_html=True)
- st.markdown("**¿Qué significa este gráfico?**")
- st.markdown("""
- - 🔵 Los nodos azules representan palabras clave en tu texto
- - 📏 El tamaño de cada nodo indica su frecuencia de uso
- - 🔗 Las líneas conectan palabras que aparecen juntas frecuentemente
- - 🎨 Los colores más intensos indican palabras más centrales
- """)
- st.markdown("
", unsafe_allow_html=True)
- st.markdown("
", unsafe_allow_html=True)
-
- # 2. Estructura de Oraciones
- with st.expander("🏗️ Complejidad Estructural", expanded=True):
- st.markdown('
', unsafe_allow_html=True)
- syntax_graph = create_syntax_complexity_graph(doc)
- if syntax_graph:
- st.pyplot(syntax_graph)
- plt.close(syntax_graph)
-
- st.markdown('
', unsafe_allow_html=True)
- st.markdown("**Análisis de la estructura:**")
- st.markdown("""
- - 📊 Las barras muestran la complejidad de cada oración
- - 📈 Mayor altura indica estructuras más elaboradas
- - 🎯 La línea punteada indica el nivel óptimo de complejidad
- - 🔄 Variación en las alturas sugiere dinamismo en la escritura
- """)
- st.markdown("
", unsafe_allow_html=True)
- st.markdown("
", unsafe_allow_html=True)
-
- # 3. Cohesión Textual
- with st.expander("🔄 Cohesión del Texto", expanded=True):
- st.markdown('
', unsafe_allow_html=True)
- cohesion_map = create_cohesion_heatmap(doc)
- if cohesion_map:
- st.pyplot(cohesion_map)
- plt.close(cohesion_map)
-
- st.markdown('
', unsafe_allow_html=True)
- st.markdown("**¿Cómo leer el mapa de calor?**")
- st.markdown("""
- - 🌈 Colores más intensos indican mayor conexión entre oraciones
- - 📝 La diagonal muestra la coherencia interna de cada oración
- - 🔗 Las zonas claras sugieren oportunidades de mejorar conexiones
- - 🎯 Un buen texto muestra patrones de color consistentes
- """)
- st.markdown("
", unsafe_allow_html=True)
- st.markdown("
", unsafe_allow_html=True)
-
- # 4. Métricas Generales
- with st.expander("📊 Resumen de Métricas", expanded=True):
- col1, col2, col3 = st.columns(3)
-
- with col1:
- st.metric(
- "Diversidad Léxica",
- f"{metrics['vocabulary_richness']:.2f}/1.0",
- help="Mide la variedad de palabras diferentes utilizadas"
- )
-
- with col2:
- st.metric(
- "Complejidad Estructural",
- f"{metrics['structural_complexity']:.2f}/1.0",
- help="Indica qué tan elaboradas son las estructuras de las oraciones"
- )
-
- with col3:
- st.metric(
- "Cohesión Textual",
- f"{metrics['cohesion_score']:.2f}/1.0",
- help="Evalúa qué tan bien conectadas están las ideas entre sí"
- )
-
- except Exception as e:
- logger.error(f"Error en visualización: {str(e)}")
- st.error("Error al generar las visualizaciones")
-
-################################################################
-def show_recommendations(feedback, t):
- """
- Muestra las recomendaciones y ejercicios personalizados para el estudiante,
- permitiendo el seguimiento de su progreso.
-
- Args:
- feedback: Diccionario con retroalimentación y ejercicios recomendados
- t: Diccionario de traducciones
- """
- st.markdown("### " + t.get('recommendations_title', "Recomendaciones para mejorar"))
-
- for area, exercises in feedback['recommendations'].items():
- with st.expander(f"💡 {area}"):
- try:
- # Descripción del área de mejora
- st.markdown(exercises['description'])
-
- # Obtener el historial de ejercicios del estudiante
- from ..database.current_situation_mongo_db import get_student_exercises_history
- exercises_history = get_student_exercises_history(st.session_state.username)
-
- # Separar ejercicios en completados y pendientes
- completed = exercises_history.get(area, [])
-
- # Mostrar estado actual
- progress_col1, progress_col2 = st.columns([3,1])
- with progress_col1:
- st.markdown("**Ejercicio sugerido:**")
- st.markdown(exercises['activity'])
-
- with progress_col2:
- # Verificar si el ejercicio ya está completado
- exercise_key = f"{area}_{exercises['activity']}"
- is_completed = exercise_key in completed
-
- if is_completed:
- st.success("✅ Completado")
- else:
- # Botón para marcar ejercicio como completado
- if st.button(
- t.get('mark_complete', "Marcar como completado"),
- key=generate_unique_key("exercise", area),
- type="primary"
- ):
- try:
- from ..database.current_situation_mongo_db import update_exercise_status
-
- # Actualizar estado del ejercicio
- success = update_exercise_status(
- username=st.session_state.username,
- area=area,
- exercise=exercises['activity'],
- completed=True
- )
-
- if success:
- st.success(t.get(
- 'exercise_completed',
- "¡Ejercicio marcado como completado!"
- ))
- st.rerun()
- else:
- st.error(t.get(
- 'exercise_error',
- "Error al actualizar el estado del ejercicio"
- ))
- except Exception as e:
- logger.error(f"Error actualizando estado del ejercicio: {str(e)}")
- st.error(t.get('update_error', "Error al actualizar el ejercicio"))
-
- # Mostrar recursos adicionales si existen
- if 'resources' in exercises:
- st.markdown("**Recursos adicionales:**")
- for resource in exercises['resources']:
- st.markdown(f"- {resource}")
-
- # Mostrar fecha de finalización si está completado
- if is_completed:
- completion_date = exercises_history[exercise_key].get('completion_date')
- if completion_date:
- st.caption(
- t.get('completed_on', "Completado el") +
- f": {completion_date.strftime('%d/%m/%Y %H:%M')}"
- )
-
- except Exception as e:
- logger.error(f"Error mostrando recomendaciones para {area}: {str(e)}")
- st.error(t.get(
- 'recommendations_error',
- f"Error al mostrar las recomendaciones para {area}"
+# modules/studentact/current_situation_interface.py
+
+import streamlit as st
+import logging
+from ..utils.widget_utils import generate_unique_key
+from .current_situation_analysis import (
+ analyze_text_dimensions,
+ create_vocabulary_network,
+ create_syntax_complexity_graph,
+ create_cohesion_heatmap
+)
+
+logger = logging.getLogger(__name__)
+
+def display_current_situation_interface(lang_code, nlp_models, t):
+ """
+ Interfaz modular para el análisis de la situación actual del estudiante.
+ Esta función maneja la presentación y la interacción con el usuario.
+
+ Args:
+ lang_code: Código del idioma actual
+ nlp_models: Diccionario de modelos de spaCy cargados
+ t: Diccionario de traducciones
+ """
+ st.markdown("## Mi Situación Actual de Escritura")
+
+ # Container principal para mejor organización visual
+ with st.container():
+ # Columnas para entrada y visualización
+ text_col, visual_col = st.columns([1,2])
+
+ with text_col:
+ # Área de entrada de texto
+ text_input = st.text_area(
+ t.get('current_situation_input', "Ingresa tu texto para analizar:"),
+ height=400,
+ key=generate_unique_key("current_situation", "input")
+ )
+
+ # Botón de análisis
+ if st.button(
+ t.get('analyze_button', "Explorar mi escritura"),
+ type="primary",
+ disabled=not text_input,
+ key=generate_unique_key("current_situation", "analyze")
+ ):
+ try:
+ with st.spinner(t.get('processing', "Analizando texto...")):
+ # 1. Procesar el texto
+ doc = nlp_models[lang_code](text_input)
+ metrics = analyze_text_dimensions(doc)
+
+ # 2. Mostrar visualizaciones en la columna derecha
+ with visual_col:
+ display_current_situation_visual(doc, metrics)
+
+ # 3. Obtener retroalimentación de Claude
+ feedback = get_claude_feedback(metrics, text_input)
+
+ # 4. Guardar los resultados
+ from ..database.current_situation_mongo_db import store_current_situation_result
+
+ if st.button(t.get('analyze_button', "Explorar mi escritura")):
+ with st.spinner(t.get('processing', "Analizando texto...")):
+ # Procesar y analizar
+ doc = nlp_models[lang_code](text_input)
+
+ # Obtener métricas con manejo de errores
+ try:
+ metrics = analyze_text_dimensions(doc)
+ except Exception as e:
+ logger.error(f"Error en análisis: {str(e)}")
+ st.error("Error en el análisis de dimensiones")
+ return
+
+ # Obtener feedback
+ try:
+ feedback = get_claude_feedback(metrics, text_input)
+ except Exception as e:
+ logger.error(f"Error obteniendo feedback: {str(e)}")
+ st.error("Error obteniendo retroalimentación")
+ return
+
+ # Guardar resultados con verificación
+ if store_current_situation_result(
+ st.session_state.username,
+ text_input,
+ metrics,
+ feedback
+ ):
+ st.success(t.get('save_success', "Análisis guardado"))
+
+ # Mostrar visualizaciones y recomendaciones
+ display_current_situation_visual(doc, metrics)
+ show_recommendations(feedback, t)
+ else:
+ st.error("Error al guardar el análisis")
+
+ except Exception as e:
+ logger.error(f"Error en interfaz: {str(e)}")
+ st.error("Error general en la interfaz")
+
+################################################################
+def display_current_situation_visual(doc, metrics):
+ """Visualización mejorada de resultados con interpretaciones"""
+ try:
+ with st.container():
+ # Estilos CSS mejorados para los contenedores
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # 1. Riqueza de Vocabulario
+ with st.expander("📚 Riqueza de Vocabulario", expanded=True):
+ st.markdown('
', unsafe_allow_html=True)
+ vocabulary_graph = create_vocabulary_network(doc)
+ if vocabulary_graph:
+ # Mostrar gráfico
+ st.pyplot(vocabulary_graph)
+ plt.close(vocabulary_graph)
+
+ # Interpretación
+ st.markdown('
', unsafe_allow_html=True)
+ st.markdown("**¿Qué significa este gráfico?**")
+ st.markdown("""
+ - 🔵 Los nodos azules representan palabras clave en tu texto
+ - 📏 El tamaño de cada nodo indica su frecuencia de uso
+ - 🔗 Las líneas conectan palabras que aparecen juntas frecuentemente
+ - 🎨 Los colores más intensos indican palabras más centrales
+ """)
+ st.markdown("
", unsafe_allow_html=True)
+ st.markdown("
", unsafe_allow_html=True)
+
+ # 2. Estructura de Oraciones
+ with st.expander("🏗️ Complejidad Estructural", expanded=True):
+ st.markdown('
', unsafe_allow_html=True)
+ syntax_graph = create_syntax_complexity_graph(doc)
+ if syntax_graph:
+ st.pyplot(syntax_graph)
+ plt.close(syntax_graph)
+
+ st.markdown('
', unsafe_allow_html=True)
+ st.markdown("**Análisis de la estructura:**")
+ st.markdown("""
+ - 📊 Las barras muestran la complejidad de cada oración
+ - 📈 Mayor altura indica estructuras más elaboradas
+ - 🎯 La línea punteada indica el nivel óptimo de complejidad
+ - 🔄 Variación en las alturas sugiere dinamismo en la escritura
+ """)
+ st.markdown("
", unsafe_allow_html=True)
+ st.markdown("
", unsafe_allow_html=True)
+
+ # 3. Cohesión Textual
+ with st.expander("🔄 Cohesión del Texto", expanded=True):
+ st.markdown('
', unsafe_allow_html=True)
+ cohesion_map = create_cohesion_heatmap(doc)
+ if cohesion_map:
+ st.pyplot(cohesion_map)
+ plt.close(cohesion_map)
+
+ st.markdown('
', unsafe_allow_html=True)
+ st.markdown("**¿Cómo leer el mapa de calor?**")
+ st.markdown("""
+ - 🌈 Colores más intensos indican mayor conexión entre oraciones
+ - 📝 La diagonal muestra la coherencia interna de cada oración
+ - 🔗 Las zonas claras sugieren oportunidades de mejorar conexiones
+ - 🎯 Un buen texto muestra patrones de color consistentes
+ """)
+ st.markdown("
", unsafe_allow_html=True)
+ st.markdown("
", unsafe_allow_html=True)
+
+ # 4. Métricas Generales
+ with st.expander("📊 Resumen de Métricas", expanded=True):
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric(
+ "Diversidad Léxica",
+ f"{metrics['vocabulary_richness']:.2f}/1.0",
+ help="Mide la variedad de palabras diferentes utilizadas"
+ )
+
+ with col2:
+ st.metric(
+ "Complejidad Estructural",
+ f"{metrics['structural_complexity']:.2f}/1.0",
+ help="Indica qué tan elaboradas son las estructuras de las oraciones"
+ )
+
+ with col3:
+ st.metric(
+ "Cohesión Textual",
+ f"{metrics['cohesion_score']:.2f}/1.0",
+ help="Evalúa qué tan bien conectadas están las ideas entre sí"
+ )
+
+ except Exception as e:
+ logger.error(f"Error en visualización: {str(e)}")
+ st.error("Error al generar las visualizaciones")
+
+################################################################
+def show_recommendations(feedback, t):
+ """
+ Muestra las recomendaciones y ejercicios personalizados para el estudiante,
+ permitiendo el seguimiento de su progreso.
+
+ Args:
+ feedback: Diccionario con retroalimentación y ejercicios recomendados
+ t: Diccionario de traducciones
+ """
+ st.markdown("### " + t.get('recommendations_title', "Recomendaciones para mejorar"))
+
+ for area, exercises in feedback['recommendations'].items():
+ with st.expander(f"💡 {area}"):
+ try:
+ # Descripción del área de mejora
+ st.markdown(exercises['description'])
+
+ # Obtener el historial de ejercicios del estudiante
+ from ..database.current_situation_mongo_db import get_student_exercises_history
+ exercises_history = get_student_exercises_history(st.session_state.username)
+
+ # Separar ejercicios en completados y pendientes
+ completed = exercises_history.get(area, [])
+
+ # Mostrar estado actual
+ progress_col1, progress_col2 = st.columns([3,1])
+ with progress_col1:
+ st.markdown("**Ejercicio sugerido:**")
+ st.markdown(exercises['activity'])
+
+ with progress_col2:
+ # Verificar si el ejercicio ya está completado
+ exercise_key = f"{area}_{exercises['activity']}"
+ is_completed = exercise_key in completed
+
+ if is_completed:
+ st.success("✅ Completado")
+ else:
+ # Botón para marcar ejercicio como completado
+ if st.button(
+ t.get('mark_complete', "Marcar como completado"),
+ key=generate_unique_key("exercise", area),
+ type="primary"
+ ):
+ try:
+ from ..database.current_situation_mongo_db import update_exercise_status
+
+ # Actualizar estado del ejercicio
+ success = update_exercise_status(
+ username=st.session_state.username,
+ area=area,
+ exercise=exercises['activity'],
+ completed=True
+ )
+
+ if success:
+ st.success(t.get(
+ 'exercise_completed',
+ "¡Ejercicio marcado como completado!"
+ ))
+ st.rerun()
+ else:
+ st.error(t.get(
+ 'exercise_error',
+ "Error al actualizar el estado del ejercicio"
+ ))
+ except Exception as e:
+ logger.error(f"Error actualizando estado del ejercicio: {str(e)}")
+ st.error(t.get('update_error', "Error al actualizar el ejercicio"))
+
+ # Mostrar recursos adicionales si existen
+ if 'resources' in exercises:
+ st.markdown("**Recursos adicionales:**")
+ for resource in exercises['resources']:
+ st.markdown(f"- {resource}")
+
+ # Mostrar fecha de finalización si está completado
+ if is_completed:
+ completion_date = exercises_history[exercise_key].get('completion_date')
+ if completion_date:
+ st.caption(
+ t.get('completed_on', "Completado el") +
+ f": {completion_date.strftime('%d/%m/%Y %H:%M')}"
+ )
+
+ except Exception as e:
+ logger.error(f"Error mostrando recomendaciones para {area}: {str(e)}")
+ st.error(t.get(
+ 'recommendations_error',
+ f"Error al mostrar las recomendaciones para {area}"
))
\ No newline at end of file
diff --git a/modules/text_analysis/__init__.py b/modules/text_analysis/__init__.py
index ab1a133268ab5bc7e2766a4d05d27c78182dc951..a8781ff2d1a28dde08cad6688e7efb909aeabaa9 100644
--- a/modules/text_analysis/__init__.py
+++ b/modules/text_analysis/__init__.py
@@ -1,29 +1,29 @@
-# modules/text_analysis/__init__.py
-import logging
-
-logging.basicConfig(
- level=logging.INFO,
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
-)
-
-# Importaciones de morpho_analysis
-from .morpho_analysis import (
- perform_advanced_morphosyntactic_analysis,
- get_repeated_words_colors,
- highlight_repeated_words,
- generate_arc_diagram,
- get_detailed_pos_analysis,
- get_morphological_analysis,
- get_sentence_structure_analysis,
- POS_COLORS,
- POS_TRANSLATIONS
-)
-
-# Importaciones de semantic_analysis
-from .semantic_analysis import (
- create_concept_graph,
- visualize_concept_graph,
- identify_key_concepts
-)
-
-
+# modules/text_analysis/__init__.py
+import logging
+
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+)
+
+# Importaciones de morpho_analysis
+from .morpho_analysis import (
+ perform_advanced_morphosyntactic_analysis,
+ get_repeated_words_colors,
+ highlight_repeated_words,
+ generate_arc_diagram,
+ get_detailed_pos_analysis,
+ get_morphological_analysis,
+ get_sentence_structure_analysis,
+ POS_COLORS,
+ POS_TRANSLATIONS
+)
+
+# Importaciones de semantic_analysis
+from .semantic_analysis import (
+ create_concept_graph,
+ visualize_concept_graph,
+ identify_key_concepts
+)
+
+
diff --git a/modules/text_analysis/discourse_analysis.py b/modules/text_analysis/discourse_analysis.py
index 56d71efa756fca28ba827ef4a9988210d070caf9..0b2451b5692bd1145556144ef19f44bc418ba917 100644
--- a/modules/text_analysis/discourse_analysis.py
+++ b/modules/text_analysis/discourse_analysis.py
@@ -1,271 +1,267 @@
-# modules/text_analysis/discourse_analysis.py
-# Configuración de matplotlib
-
-import streamlit as st
-import spacy
-import networkx as nx
-import matplotlib.pyplot as plt
-import pandas as pd
-import numpy as np
-import logging
-
-logger = logging.getLogger(__name__)
-
-from .semantic_analysis import (
- create_concept_graph,
- visualize_concept_graph,
- identify_key_concepts
-)
-
-from .stopwords import (
- get_custom_stopwords,
- process_text,
- get_stopwords_for_spacy
-)
-
-#####################
-# Define colors for grammatical categories
-POS_COLORS = {
- 'ADJ': '#FFA07A', 'ADP': '#98FB98', 'ADV': '#87CEFA', 'AUX': '#DDA0DD',
- 'CCONJ': '#F0E68C', 'DET': '#FFB6C1', 'INTJ': '#FF6347', 'NOUN': '#90EE90',
- 'NUM': '#FAFAD2', 'PART': '#D3D3D3', 'PRON': '#FFA500', 'PROPN': '#20B2AA',
- 'SCONJ': '#DEB887', 'SYM': '#7B68EE', 'VERB': '#FF69B4', 'X': '#A9A9A9',
-}
-
-POS_TRANSLATIONS = {
- 'es': {
- 'ADJ': 'Adjetivo', 'ADP': 'Preposición', 'ADV': 'Adverbio', 'AUX': 'Auxiliar',
- 'CCONJ': 'Conjunción Coordinante', 'DET': 'Determinante', 'INTJ': 'Interjección',
- 'NOUN': 'Sustantivo', 'NUM': 'Número', 'PART': 'Partícula', 'PRON': 'Pronombre',
- 'PROPN': 'Nombre Propio', 'SCONJ': 'Conjunción Subordinante', 'SYM': 'Símbolo',
- 'VERB': 'Verbo', 'X': 'Otro',
- },
- 'en': {
- 'ADJ': 'Adjective', 'ADP': 'Preposition', 'ADV': 'Adverb', 'AUX': 'Auxiliary',
- 'CCONJ': 'Coordinating Conjunction', 'DET': 'Determiner', 'INTJ': 'Interjection',
- 'NOUN': 'Noun', 'NUM': 'Number', 'PART': 'Particle', 'PRON': 'Pronoun',
- 'PROPN': 'Proper Noun', 'SCONJ': 'Subordinating Conjunction', 'SYM': 'Symbol',
- 'VERB': 'Verb', 'X': 'Other',
- },
- 'fr': {
- 'ADJ': 'Adjectif', 'ADP': 'Préposition', 'ADV': 'Adverbe', 'AUX': 'Auxiliaire',
- 'CCONJ': 'Conjonction de Coordination', 'DET': 'Déterminant', 'INTJ': 'Interjection',
- 'NOUN': 'Nom', 'NUM': 'Nombre', 'PART': 'Particule', 'PRON': 'Pronom',
- 'PROPN': 'Nom Propre', 'SCONJ': 'Conjonction de Subordination', 'SYM': 'Symbole',
- 'VERB': 'Verbe', 'X': 'Autre',
- }
-}
-
-ENTITY_LABELS = {
- 'es': {
- "Personas": "lightblue",
- "Lugares": "lightcoral",
- "Inventos": "lightgreen",
- "Fechas": "lightyellow",
- "Conceptos": "lightpink"
- },
- 'en': {
- "People": "lightblue",
- "Places": "lightcoral",
- "Inventions": "lightgreen",
- "Dates": "lightyellow",
- "Concepts": "lightpink"
- },
- 'fr': {
- "Personnes": "lightblue",
- "Lieux": "lightcoral",
- "Inventions": "lightgreen",
- "Dates": "lightyellow",
- "Concepts": "lightpink"
- }
-}
-
-
-#################
-def compare_semantic_analysis(text1, text2, nlp, lang):
- """
- Realiza el análisis semántico comparativo entre dos textos
- """
- try:
- logger.info(f"Iniciando análisis comparativo para idioma: {lang}")
-
- # Obtener stopwords
- stopwords = get_custom_stopwords(lang)
- logger.info(f"Obtenidas {len(stopwords)} stopwords para el idioma {lang}")
-
- # Procesar los textos
- doc1 = nlp(text1)
- doc2 = nlp(text2)
-
- # Identificar conceptos clave
- logger.info("Identificando conceptos clave del primer texto...")
- key_concepts1 = identify_key_concepts(doc1, stopwords=stopwords, min_freq=2, min_length=3)
-
- logger.info("Identificando conceptos clave del segundo texto...")
- key_concepts2 = identify_key_concepts(doc2, stopwords=stopwords, min_freq=2, min_length=3)
-
- if not key_concepts1 or not key_concepts2:
- raise ValueError("No se pudieron identificar conceptos clave en uno o ambos textos")
-
- # Crear grafos
- logger.info("Creando grafos de conceptos...")
- G1 = create_concept_graph(doc1, key_concepts1)
- G2 = create_concept_graph(doc2, key_concepts2)
-
- # Visualizar grafos
- logger.info("Visualizando grafos...")
-
- # Primer grafo
- plt.figure(figsize=(12, 8))
- fig1 = visualize_concept_graph(G1, lang)
- plt.title("Análisis del primer texto", pad=20)
- plt.tight_layout()
-
- # Segundo grafo
- plt.figure(figsize=(12, 8))
- fig2 = visualize_concept_graph(G2, lang)
- plt.title("Análisis del segundo texto", pad=20)
- plt.tight_layout()
-
- logger.info("Análisis comparativo completado exitosamente")
- return fig1, fig2, key_concepts1, key_concepts2
-
- except Exception as e:
- logger.error(f"Error en compare_semantic_analysis: {str(e)}")
- plt.close('all') # Limpiar recursos en caso de error
- raise
- finally:
- plt.close('all') # Asegurar limpieza en todos los casos
-
-
-############################################
-def create_concept_table(key_concepts):
- """
- Crea una tabla de conceptos clave con sus frecuencias
- Args:
- key_concepts: Lista de tuplas (concepto, frecuencia)
- Returns:
- pandas.DataFrame: Tabla formateada de conceptos
- """
- try:
- if not key_concepts:
- logger.warning("Lista de conceptos vacía")
- return pd.DataFrame(columns=['Concepto', 'Frecuencia'])
-
- df = pd.DataFrame(key_concepts, columns=['Concepto', 'Frecuencia'])
- df['Frecuencia'] = df['Frecuencia'].round(2)
- return df
- except Exception as e:
- logger.error(f"Error en create_concept_table: {str(e)}")
- return pd.DataFrame(columns=['Concepto', 'Frecuencia'])
-
-
-##########################################################
-def perform_discourse_analysis(text1, text2, nlp, lang):
- """
- Realiza el análisis completo del discurso
- """
- try:
- logger.info("Iniciando análisis del discurso...")
-
- # Verificar inputs
- if not text1 or not text2:
- raise ValueError("Los textos de entrada no pueden estar vacíos")
-
- if not nlp:
- raise ValueError("Modelo de lenguaje no inicializado")
-
- # Realizar análisis comparativo
- try:
- fig1, fig2, key_concepts1, key_concepts2 = compare_semantic_analysis(
- text1, text2, nlp, lang
- )
- except Exception as e:
- logger.error(f"Error en el análisis comparativo: {str(e)}")
- raise
-
- # Crear tablas de resultados
- try:
- table1 = create_concept_table(key_concepts1)
- table2 = create_concept_table(key_concepts2)
- except Exception as e:
- logger.error(f"Error creando tablas de conceptos: {str(e)}")
- raise
-
- result = {
- 'graph1': fig1,
- 'graph2': fig2,
- 'key_concepts1': key_concepts1,
- 'key_concepts2': key_concepts2,
- 'table1': table1,
- 'table2': table2,
- 'success': True
- }
-
- logger.info("Análisis del discurso completado exitosamente")
- return result
-
- except Exception as e:
- logger.error(f"Error en perform_discourse_analysis: {str(e)}")
- return {
- 'success': False,
- 'error': str(e)
- }
- finally:
- plt.close('all') # Asegurar limpieza en todos los casos
-
-#################################################################
-def create_concept_table(key_concepts):
- """
- Crea una tabla de conceptos clave con sus frecuencias
- Args:
- key_concepts: Lista de tuplas (concepto, frecuencia)
- Returns:
- pandas.DataFrame: Tabla formateada de conceptos
- """
- try:
- df = pd.DataFrame(key_concepts, columns=['Concepto', 'Frecuencia'])
- df['Frecuencia'] = df['Frecuencia'].round(2)
- return df
- except Exception as e:
- logger.error(f"Error en create_concept_table: {str(e)}")
- raise
-
-#################
-def perform_discourse_analysis(text1, text2, nlp, lang):
- """
- Realiza el análisis completo del discurso
- Args:
- text1: Primer texto a analizar
- text2: Segundo texto a analizar
- nlp: Modelo de spaCy cargado
- lang: Código de idioma
- Returns:
- dict: Resultados del análisis
- """
- try:
- # Realizar análisis comparativo
- fig1, fig2, key_concepts1, key_concepts2 = compare_semantic_analysis(
- text1, text2, nlp, lang
- )
-
- # Crear tablas de resultados
- table1 = create_concept_table(key_concepts1)
- table2 = create_concept_table(key_concepts2)
-
- return {
- 'graph1': fig1,
- 'graph2': fig2,
- 'key_concepts1': key_concepts1,
- 'key_concepts2': key_concepts2,
- 'table1': table1,
- 'table2': table2,
- 'success': True
- }
-
- except Exception as e:
- logger.error(f"Error en perform_discourse_analysis: {str(e)}")
- return {
- 'success': False,
- 'error': str(e)
- }
\ No newline at end of file
+# modules/text_analysis/discourse_analysis.py
+# Configuración de matplotlib
+
+import streamlit as st
+import spacy
+import networkx as nx
+import matplotlib.pyplot as plt
+import pandas as pd
+import numpy as np
+import logging
+import io
+import base64
+from collections import Counter, defaultdict
+import logging
+
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+from .semantic_analysis import (
+ create_concept_graph,
+ visualize_concept_graph,
+ identify_key_concepts
+)
+
+
+from .stopwords import (
+ get_custom_stopwords,
+ process_text,
+ get_stopwords_for_spacy
+)
+
+
+#####################
+POS_TRANSLATIONS = {
+ 'es': {
+ 'ADJ': 'Adjetivo', 'ADP': 'Preposición', 'ADV': 'Adverbio', 'AUX': 'Auxiliar',
+ 'CCONJ': 'Conjunción Coordinante', 'DET': 'Determinante', 'INTJ': 'Interjección',
+ 'NOUN': 'Sustantivo', 'NUM': 'Número', 'PART': 'Partícula', 'PRON': 'Pronombre',
+ 'PROPN': 'Nombre Propio', 'SCONJ': 'Conjunción Subordinante', 'SYM': 'Símbolo',
+ 'VERB': 'Verbo', 'X': 'Otro',
+ },
+ 'en': {
+ 'ADJ': 'Adjective', 'ADP': 'Preposition', 'ADV': 'Adverb', 'AUX': 'Auxiliary',
+ 'CCONJ': 'Coordinating Conjunction', 'DET': 'Determiner', 'INTJ': 'Interjection',
+ 'NOUN': 'Noun', 'NUM': 'Number', 'PART': 'Particle', 'PRON': 'Pronoun',
+ 'PROPN': 'Proper Noun', 'SCONJ': 'Subordinating Conjunction', 'SYM': 'Symbol',
+ 'VERB': 'Verb', 'X': 'Other',
+ },
+ 'fr': {
+ 'ADJ': 'Adjectif', 'ADP': 'Préposition', 'ADV': 'Adverbe', 'AUX': 'Auxiliaire',
+ 'CCONJ': 'Conjonction de Coordination', 'DET': 'Déterminant', 'INTJ': 'Interjection',
+ 'NOUN': 'Nom', 'NUM': 'Nombre', 'PART': 'Particule', 'PRON': 'Pronom',
+ 'PROPN': 'Nom Propre', 'SCONJ': 'Conjonction de Subordination', 'SYM': 'Symbole',
+ 'VERB': 'Verbe', 'X': 'Autre',
+ },
+ 'pt': {
+ 'ADJ': 'Adjetivo', 'ADP': 'Preposição', 'ADV': 'Advérbio', 'AUX': 'Auxiliar',
+ 'CCONJ': 'Conjunção Coordenativa', 'DET': 'Determinante', 'INTJ': 'Interjeição',
+ 'NOUN': 'Substantivo', 'NUM': 'Número', 'PART': 'Partícula', 'PRON': 'Pronome',
+ 'PROPN': 'Nome Próprio', 'SCONJ': 'Conjunção Subordinativa', 'SYM': 'Símbolo',
+ 'VERB': 'Verbo', 'X': 'Outro',
+ }
+}
+
+ENTITY_LABELS = {
+ 'es': {
+ "Personas": "lightblue",
+ "Lugares": "lightcoral",
+ "Inventos": "lightgreen",
+ "Fechas": "lightyellow",
+ "Conceptos": "lightpink"
+ },
+ 'en': {
+ "People": "lightblue",
+ "Places": "lightcoral",
+ "Inventions": "lightgreen",
+ "Dates": "lightyellow",
+ "Concepts": "lightpink"
+ },
+ 'fr': {
+ "Personnes": "lightblue",
+ "Lieux": "lightcoral",
+ "Inventions": "lightgreen",
+ "Dates": "lightyellow",
+ "Concepts": "lightpink"
+ },
+ 'pt': {
+ "Pessoas": "lightblue",
+ "Lugares": "lightcoral",
+ "Invenções": "lightgreen",
+ "Datas": "lightyellow",
+ "Conceitos": "lightpink"
+ }
+}
+
+#################
+
+def fig_to_bytes(fig, dpi=100):
+ """Convierte una figura de matplotlib a bytes."""
+ try:
+ buf = io.BytesIO()
+ fig.savefig(buf, format='png', dpi=dpi, bbox_inches='tight') # Sin compression
+ buf.seek(0)
+ return buf.getvalue()
+ except Exception as e:
+ logger.error(f"Error en fig_to_bytes: {str(e)}")
+ return None
+
+#################
+def compare_semantic_analysis(text1, text2, nlp, lang):
+ """
+ Realiza el análisis semántico comparativo entre dos textos
+ """
+ try:
+ logger.info(f"Iniciando análisis comparativo para idioma: {lang}")
+
+ # Obtener stopwords
+ stopwords = get_custom_stopwords(lang)
+ logger.info(f"Obtenidas {len(stopwords)} stopwords para el idioma {lang}")
+
+ # Procesar los textos
+ doc1 = nlp(text1)
+ doc2 = nlp(text2)
+
+ # Identificar conceptos clave
+ logger.info("Identificando conceptos clave del primer texto...")
+ key_concepts1 = identify_key_concepts(doc1, stopwords=stopwords, min_freq=2, min_length=3)
+
+ logger.info("Identificando conceptos clave del segundo texto...")
+ key_concepts2 = identify_key_concepts(doc2, stopwords=stopwords, min_freq=2, min_length=3)
+
+ if not key_concepts1 or not key_concepts2:
+ raise ValueError("No se pudieron identificar conceptos clave en uno o ambos textos")
+
+ # Crear grafos
+ logger.info("Creando grafos de conceptos...")
+ G1 = create_concept_graph(doc1, key_concepts1)
+ G2 = create_concept_graph(doc2, key_concepts2)
+
+ # Visualizar grafos
+ logger.info("Visualizando grafos...")
+
+ # Primer grafo
+ plt.figure(figsize=(12, 8))
+ fig1 = visualize_concept_graph(G1, lang)
+ plt.title("Análisis del primer texto", pad=20)
+ plt.tight_layout()
+
+ # Segundo grafo
+ plt.figure(figsize=(12, 8))
+ fig2 = visualize_concept_graph(G2, lang)
+ plt.title("Análisis del segundo texto", pad=20)
+ plt.tight_layout()
+
+ logger.info("Análisis comparativo completado exitosamente")
+ return fig1, fig2, key_concepts1, key_concepts2
+
+ except Exception as e:
+ logger.error(f"Error en compare_semantic_analysis: {str(e)}")
+ plt.close('all') # Limpiar recursos en caso de error
+ raise
+ finally:
+ plt.close('all') # Asegurar limpieza en todos los casos
+
+
+############################################
+def create_concept_table(key_concepts):
+ """
+ Crea una tabla de conceptos clave con sus frecuencias
+ Args:
+ key_concepts: Lista de tuplas (concepto, frecuencia)
+ Returns:
+ pandas.DataFrame: Tabla formateada de conceptos
+ """
+ try:
+ if not key_concepts:
+ logger.warning("Lista de conceptos vacía")
+ return pd.DataFrame(columns=['Concepto', 'Frecuencia'])
+
+ df = pd.DataFrame(key_concepts, columns=['Concepto', 'Frecuencia'])
+ df['Frecuencia'] = df['Frecuencia'].round(2)
+ return df
+ except Exception as e:
+ logger.error(f"Error en create_concept_table: {str(e)}")
+ return pd.DataFrame(columns=['Concepto', 'Frecuencia'])
+
+
+##########################################################
+
+def perform_discourse_analysis(text1, text2, nlp, lang):
+ """
+ Realiza el análisis completo del discurso
+ Args:
+ text1: Primer texto a analizar
+ text2: Segundo texto a analizar
+ nlp: Modelo de spaCy cargado
+ lang: Código de idioma
+ Returns:
+ dict: Resultados del análisis con gráficos convertidos a bytes
+ """
+ try:
+ logger.info("Iniciando análisis del discurso...")
+
+ # Verificar inputs
+ if not text1 or not text2:
+ raise ValueError("Los textos de entrada no pueden estar vacíos")
+
+ if not nlp:
+ raise ValueError("Modelo de lenguaje no inicializado")
+
+ # Realizar análisis comparativo
+ fig1, fig2, key_concepts1, key_concepts2 = compare_semantic_analysis(
+ text1, text2, nlp, lang
+ )
+
+ logger.info("Análisis comparativo completado, convirtiendo figuras a bytes...")
+
+ # Convertir figuras a bytes para almacenamiento
+ graph1_bytes = fig_to_bytes(fig1)
+ graph2_bytes = fig_to_bytes(fig2)
+
+ logger.info(f"Figura 1 convertida a {len(graph1_bytes) if graph1_bytes else 0} bytes")
+ logger.info(f"Figura 2 convertida a {len(graph2_bytes) if graph2_bytes else 0} bytes")
+
+ # Verificar que las conversiones fueron exitosas antes de continuar
+ if not graph1_bytes or not graph2_bytes:
+ logger.error("Error al convertir figuras a bytes - obteniendo 0 bytes")
+ # Opción 1: Devolver error
+ raise ValueError("No se pudieron convertir las figuras a bytes")
+
+ # Crear tablas de resultados
+ table1 = create_concept_table(key_concepts1)
+ table2 = create_concept_table(key_concepts2)
+
+ # Cerrar figuras para liberar memoria
+ plt.close(fig1)
+ plt.close(fig2)
+
+ result = {
+ 'graph1': graph1_bytes, # Bytes en lugar de figura
+ 'graph2': graph2_bytes, # Bytes en lugar de figura
+ 'combined_graph': None, # No hay gráfico combinado por ahora
+ 'key_concepts1': key_concepts1,
+ 'key_concepts2': key_concepts2,
+ 'table1': table1,
+ 'table2': table2,
+ 'success': True
+ }
+
+ logger.info("Análisis del discurso completado y listo para almacenamiento")
+ return result
+
+ except Exception as e:
+ logger.error(f"Error en perform_discourse_analysis: {str(e)}")
+ # Asegurar limpieza de recursos
+ plt.close('all')
+ return {
+ 'success': False,
+ 'error': str(e)
+ }
+ finally:
+ # Asegurar limpieza en todos los casos
+ plt.close('all')
+
+#################################################################
\ No newline at end of file
diff --git a/modules/text_analysis/morpho_analysis.py b/modules/text_analysis/morpho_analysis.py
index 369dc106004363ac55a10952bd32f858bbc287b0..c4196ac172e0f571f25997ae1df6e51f73e351af 100644
--- a/modules/text_analysis/morpho_analysis.py
+++ b/modules/text_analysis/morpho_analysis.py
@@ -1,256 +1,230 @@
-##modules/text_analysis/morpho_analysis.py
-
-import spacy
-from collections import Counter
-from spacy import displacy
-import re
-from streamlit.components.v1 import html
-import base64
-
-from collections import Counter
-import re
-from ..utils.widget_utils import generate_unique_key
-
-import logging
-logger = logging.getLogger(__name__)
-
-
-# Define colors for grammatical categories
-POS_COLORS = {
- 'ADJ': '#FFA07A', # Light Salmon
- 'ADP': '#98FB98', # Pale Green
- 'ADV': '#87CEFA', # Light Sky Blue
- 'AUX': '#DDA0DD', # Plum
- 'CCONJ': '#F0E68C', # Khaki
- 'DET': '#FFB6C1', # Light Pink
- 'INTJ': '#FF6347', # Tomato
- 'NOUN': '#90EE90', # Light Green
- 'NUM': '#FAFAD2', # Light Goldenrod Yellow
- 'PART': '#D3D3D3', # Light Gray
- 'PRON': '#FFA500', # Orange
- 'PROPN': '#20B2AA', # Light Sea Green
- 'SCONJ': '#DEB887', # Burlywood
- 'SYM': '#7B68EE', # Medium Slate Blue
- 'VERB': '#FF69B4', # Hot Pink
- 'X': '#A9A9A9', # Dark Gray
-}
-
-POS_TRANSLATIONS = {
- 'es': {
- 'ADJ': 'Adjetivo',
- 'ADP': 'Preposición',
- 'ADV': 'Adverbio',
- 'AUX': 'Auxiliar',
- 'CCONJ': 'Conjunción Coordinante',
- 'DET': 'Determinante',
- 'INTJ': 'Interjección',
- 'NOUN': 'Sustantivo',
- 'NUM': 'Número',
- 'PART': 'Partícula',
- 'PRON': 'Pronombre',
- 'PROPN': 'Nombre Propio',
- 'SCONJ': 'Conjunción Subordinante',
- 'SYM': 'Símbolo',
- 'VERB': 'Verbo',
- 'X': 'Otro',
- },
- 'en': {
- 'ADJ': 'Adjective',
- 'ADP': 'Preposition',
- 'ADV': 'Adverb',
- 'AUX': 'Auxiliary',
- 'CCONJ': 'Coordinating Conjunction',
- 'DET': 'Determiner',
- 'INTJ': 'Interjection',
- 'NOUN': 'Noun',
- 'NUM': 'Number',
- 'PART': 'Particle',
- 'PRON': 'Pronoun',
- 'PROPN': 'Proper Noun',
- 'SCONJ': 'Subordinating Conjunction',
- 'SYM': 'Symbol',
- 'VERB': 'Verb',
- 'X': 'Other',
- },
- 'fr': {
- 'ADJ': 'Adjectif',
- 'ADP': 'Préposition',
- 'ADV': 'Adverbe',
- 'AUX': 'Auxiliaire',
- 'CCONJ': 'Conjonction de Coordination',
- 'DET': 'Déterminant',
- 'INTJ': 'Interjection',
- 'NOUN': 'Nom',
- 'NUM': 'Nombre',
- 'PART': 'Particule',
- 'PRON': 'Pronom',
- 'PROPN': 'Nom Propre',
- 'SCONJ': 'Conjonction de Subordination',
- 'SYM': 'Symbole',
- 'VERB': 'Verbe',
- 'X': 'Autre',
- }
-}
-
-#############################################################################################
-def get_repeated_words_colors(doc):
- word_counts = Counter(token.text.lower() for token in doc if token.pos_ != 'PUNCT')
- repeated_words = {word: count for word, count in word_counts.items() if count > 1}
-
- word_colors = {}
- for token in doc:
- if token.text.lower() in repeated_words:
- word_colors[token.text.lower()] = POS_COLORS.get(token.pos_, '#FFFFFF')
-
- return word_colors
-
-######################################################################################################
-def highlight_repeated_words(doc, word_colors):
- highlighted_text = []
- for token in doc:
- if token.text.lower() in word_colors:
- color = word_colors[token.text.lower()]
- highlighted_text.append(f'
{token.text}')
- else:
- highlighted_text.append(token.text)
- return ' '.join(highlighted_text)
-
-#################################################################################################
-
-def generate_arc_diagram(doc):
- """
- Genera diagramas de arco para cada oración en el documento usando spacy-streamlit.
-
- Args:
- doc: Documento procesado por spaCy
- Returns:
- list: Lista de diagramas en formato HTML
- """
- arc_diagrams = []
- try:
- options = {
- "compact": False,
- "color": "#ffffff",
- "bg": "#0d6efd",
- "font": "Arial",
- "offset_x": 50,
- "distance": 100,
- "arrow_spacing": 12,
- "arrow_width": 2,
- "arrow_stroke": 2,
- "word_spacing": 25,
- "maxZoom": 2
- }
-
- for sent in doc.sents:
- try:
- # Usar el método render de displacy directamente con las opciones
- html = displacy.render(sent, style="dep", options=options)
- arc_diagrams.append(html)
- except Exception as e:
- logger.error(f"Error al renderizar oración: {str(e)}")
- continue
-
- return arc_diagrams
- except Exception as e:
- logger.error(f"Error general en generate_arc_diagram: {str(e)}")
- return None
-
-
-#################################################################################################
-def get_detailed_pos_analysis(doc):
- """
- Realiza un análisis detallado de las categorías gramaticales (POS) en el texto.
- """
- pos_counts = Counter(token.pos_ for token in doc)
- total_tokens = len(doc)
- pos_analysis = []
- for pos, count in pos_counts.items():
- percentage = (count / total_tokens) * 100
- pos_analysis.append({
- 'pos': pos,
- 'count': count,
- 'percentage': round(percentage, 2),
- 'examples': [token.text for token in doc if token.pos_ == pos][:5] # Primeros 5 ejemplos
- })
- return sorted(pos_analysis, key=lambda x: x['count'], reverse=True)
-
-#################################################################################################
-def get_morphological_analysis(doc):
- """
- Realiza un análisis morfológico detallado de las palabras en el texto.
- """
- morphology_analysis = []
- for token in doc:
- if token.pos_ in ['NOUN', 'VERB', 'ADJ', 'ADV']: # Enfocarse en categorías principales
- morphology_analysis.append({
- 'text': token.text,
- 'lemma': token.lemma_,
- 'pos': token.pos_,
- 'tag': token.tag_,
- 'dep': token.dep_,
- 'shape': token.shape_,
- 'is_alpha': token.is_alpha,
- 'is_stop': token.is_stop,
- 'morph': str(token.morph)
- })
- return morphology_analysis
-
-#################################################################################################
-def get_sentence_structure_analysis(doc):
- """
- Analiza la estructura de las oraciones en el texto.
- """
- sentence_analysis = []
- for sent in doc.sents:
- sentence_analysis.append({
- 'text': sent.text,
- 'root': sent.root.text,
- 'root_pos': sent.root.pos_,
- 'num_tokens': len(sent),
- 'num_words': len([token for token in sent if token.is_alpha]),
- 'subjects': [token.text for token in sent if "subj" in token.dep_],
- 'objects': [token.text for token in sent if "obj" in token.dep_],
- 'verbs': [token.text for token in sent if token.pos_ == "VERB"]
- })
- return sentence_analysis
-
-#################################################################################################
-def perform_advanced_morphosyntactic_analysis(text, nlp):
- """
- Realiza un análisis morfosintáctico avanzado del texto.
- """
- try:
- # Verificar el idioma del modelo
- model_lang = nlp.lang
- logger.info(f"Realizando análisis con modelo de idioma: {model_lang}")
-
- # Procesar el texto con el modelo específico del idioma
- doc = nlp(text)
-
- # Realizar análisis específico según el idioma
- return {
- 'doc': doc,
- 'pos_analysis': get_detailed_pos_analysis(doc),
- 'morphological_analysis': get_morphological_analysis(doc),
- 'sentence_structure': get_sentence_structure_analysis(doc),
- 'arc_diagrams': generate_arc_diagram(doc), # Quitamos nlp.lang
- 'repeated_words': get_repeated_words_colors(doc),
- 'highlighted_text': highlight_repeated_words(doc, get_repeated_words_colors(doc))
- }
- except Exception as e:
- logger.error(f"Error en análisis morfosintáctico: {str(e)}")
- return None
-
-# Al final del archivo morph_analysis.py
-__all__ = [
- 'perform_advanced_morphosyntactic_analysis',
- 'get_repeated_words_colors',
- 'highlight_repeated_words',
- 'generate_arc_diagram',
- 'get_detailed_pos_analysis',
- 'get_morphological_analysis',
- 'get_sentence_structure_analysis',
- 'POS_COLORS',
- 'POS_TRANSLATIONS'
-]
+##modules/text_analysis/morpho_analysis.py
+
+import spacy
+from collections import Counter
+from spacy import displacy
+import re
+from streamlit.components.v1 import html
+import base64
+
+from collections import Counter
+import re
+from ..utils.widget_utils import generate_unique_key
+
+import logging
+logger = logging.getLogger(__name__)
+
+
+# Define colors for grammatical categories
+POS_COLORS = {
+ 'ADJ': '#FFA07A', # Light Salmon
+ 'ADP': '#98FB98', # Pale Green
+ 'ADV': '#87CEFA', # Light Sky Blue
+ 'AUX': '#DDA0DD', # Plum
+ 'CCONJ': '#F0E68C', # Khaki
+ 'DET': '#FFB6C1', # Light Pink
+ 'INTJ': '#FF6347', # Tomato
+ 'NOUN': '#90EE90', # Light Green
+ 'NUM': '#FAFAD2', # Light Goldenrod Yellow
+ 'PART': '#D3D3D3', # Light Gray
+ 'PRON': '#FFA500', # Orange
+ 'PROPN': '#20B2AA', # Light Sea Green
+ 'SCONJ': '#DEB887', # Burlywood
+ 'SYM': '#7B68EE', # Medium Slate Blue
+ 'VERB': '#FF69B4', # Hot Pink
+ 'X': '#A9A9A9', # Dark Gray
+}
+
+POS_TRANSLATIONS = {
+ 'es': {
+ 'ADJ': 'Adjetivo', 'ADP': 'Preposición', 'ADV': 'Adverbio', 'AUX': 'Auxiliar',
+ 'CCONJ': 'Conjunción Coordinante', 'DET': 'Determinante', 'INTJ': 'Interjección',
+ 'NOUN': 'Sustantivo', 'NUM': 'Número', 'PART': 'Partícula', 'PRON': 'Pronombre',
+ 'PROPN': 'Nombre Propio', 'SCONJ': 'Conjunción Subordinante', 'SYM': 'Símbolo',
+ 'VERB': 'Verbo', 'X': 'Otro',
+ },
+ 'en': {
+ 'ADJ': 'Adjective', 'ADP': 'Preposition', 'ADV': 'Adverb', 'AUX': 'Auxiliary',
+ 'CCONJ': 'Coordinating Conjunction', 'DET': 'Determiner', 'INTJ': 'Interjection',
+ 'NOUN': 'Noun', 'NUM': 'Number', 'PART': 'Particle', 'PRON': 'Pronoun',
+ 'PROPN': 'Proper Noun', 'SCONJ': 'Subordinating Conjunction', 'SYM': 'Symbol',
+ 'VERB': 'Verb', 'X': 'Other',
+ },
+ 'fr': {
+ 'ADJ': 'Adjectif', 'ADP': 'Préposition', 'ADV': 'Adverbe', 'AUX': 'Auxiliaire',
+ 'CCONJ': 'Conjonction de Coordination', 'DET': 'Déterminant', 'INTJ': 'Interjection',
+ 'NOUN': 'Nom', 'NUM': 'Nombre', 'PART': 'Particule', 'PRON': 'Pronom',
+ 'PROPN': 'Nom Propre', 'SCONJ': 'Conjonction de Subordination', 'SYM': 'Symbole',
+ 'VERB': 'Verbe', 'X': 'Autre',
+ },
+ 'pt': {
+ 'ADJ': 'Adjetivo', 'ADP': 'Preposição', 'ADV': 'Advérbio', 'AUX': 'Auxiliar',
+ 'CCONJ': 'Conjunção Coordenativa', 'DET': 'Determinante', 'INTJ': 'Interjeição',
+ 'NOUN': 'Substantivo', 'NUM': 'Número', 'PART': 'Partícula', 'PRON': 'Pronome',
+ 'PROPN': 'Nome Próprio', 'SCONJ': 'Conjunção Subordinativa', 'SYM': 'Símbolo',
+ 'VERB': 'Verbo', 'X': 'Outro',
+ }
+}
+
+#############################################################################################
+def get_repeated_words_colors(doc):
+ word_counts = Counter(token.text.lower() for token in doc if token.pos_ != 'PUNCT')
+ repeated_words = {word: count for word, count in word_counts.items() if count > 1}
+
+ word_colors = {}
+ for token in doc:
+ if token.text.lower() in repeated_words:
+ word_colors[token.text.lower()] = POS_COLORS.get(token.pos_, '#FFFFFF')
+
+ return word_colors
+
+######################################################################################################
+def highlight_repeated_words(doc, word_colors):
+ highlighted_text = []
+ for token in doc:
+ if token.text.lower() in word_colors:
+ color = word_colors[token.text.lower()]
+ highlighted_text.append(f'
{token.text}')
+ else:
+ highlighted_text.append(token.text)
+ return ' '.join(highlighted_text)
+
+#################################################################################################
+
+def generate_arc_diagram(doc):
+ """
+ Genera diagramas de arco para cada oración en el documento usando spacy-streamlit.
+
+ Args:
+ doc: Documento procesado por spaCy
+ Returns:
+ list: Lista de diagramas en formato HTML
+ """
+ arc_diagrams = []
+ try:
+ options = {
+ "compact": False,
+ "color": "#ffffff",
+ "bg": "#0d6efd",
+ "font": "Arial",
+ "offset_x": 50,
+ "distance": 100,
+ "arrow_spacing": 12,
+ "arrow_width": 2,
+ "arrow_stroke": 2,
+ "word_spacing": 25,
+ "maxZoom": 2
+ }
+
+ for sent in doc.sents:
+ try:
+ # Usar el método render de displacy directamente con las opciones
+ html = displacy.render(sent, style="dep", options=options)
+ arc_diagrams.append(html)
+ except Exception as e:
+ logger.error(f"Error al renderizar oración: {str(e)}")
+ continue
+
+ return arc_diagrams
+ except Exception as e:
+ logger.error(f"Error general en generate_arc_diagram: {str(e)}")
+ return None
+
+
+#################################################################################################
+def get_detailed_pos_analysis(doc):
+ """
+ Realiza un análisis detallado de las categorías gramaticales (POS) en el texto.
+ """
+ pos_counts = Counter(token.pos_ for token in doc)
+ total_tokens = len(doc)
+ pos_analysis = []
+ for pos, count in pos_counts.items():
+ percentage = (count / total_tokens) * 100
+ pos_analysis.append({
+ 'pos': pos,
+ 'count': count,
+ 'percentage': round(percentage, 2),
+ 'examples': [token.text for token in doc if token.pos_ == pos][:5] # Primeros 5 ejemplos
+ })
+ return sorted(pos_analysis, key=lambda x: x['count'], reverse=True)
+
+#################################################################################################
+def get_morphological_analysis(doc):
+ """
+ Realiza un análisis morfológico detallado de las palabras en el texto.
+ """
+ morphology_analysis = []
+ for token in doc:
+ if token.pos_ in ['NOUN', 'VERB', 'ADJ', 'ADV']: # Enfocarse en categorías principales
+ morphology_analysis.append({
+ 'text': token.text,
+ 'lemma': token.lemma_,
+ 'pos': token.pos_,
+ 'tag': token.tag_,
+ 'dep': token.dep_,
+ 'shape': token.shape_,
+ 'is_alpha': token.is_alpha,
+ 'is_stop': token.is_stop,
+ 'morph': str(token.morph)
+ })
+ return morphology_analysis
+
+#################################################################################################
+def get_sentence_structure_analysis(doc):
+ """
+ Analiza la estructura de las oraciones en el texto.
+ """
+ sentence_analysis = []
+ for sent in doc.sents:
+ sentence_analysis.append({
+ 'text': sent.text,
+ 'root': sent.root.text,
+ 'root_pos': sent.root.pos_,
+ 'num_tokens': len(sent),
+ 'num_words': len([token for token in sent if token.is_alpha]),
+ 'subjects': [token.text for token in sent if "subj" in token.dep_],
+ 'objects': [token.text for token in sent if "obj" in token.dep_],
+ 'verbs': [token.text for token in sent if token.pos_ == "VERB"]
+ })
+ return sentence_analysis
+
+#################################################################################################
+def perform_advanced_morphosyntactic_analysis(text, nlp):
+ """
+ Realiza un análisis morfosintáctico avanzado del texto.
+ """
+ try:
+ # Verificar el idioma del modelo
+ model_lang = nlp.lang
+ logger.info(f"Realizando análisis con modelo de idioma: {model_lang}")
+
+ # Procesar el texto con el modelo específico del idioma
+ doc = nlp(text)
+
+ # Realizar análisis específico según el idioma
+ return {
+ 'doc': doc,
+ 'pos_analysis': get_detailed_pos_analysis(doc),
+ 'morphological_analysis': get_morphological_analysis(doc),
+ 'sentence_structure': get_sentence_structure_analysis(doc),
+ 'arc_diagrams': generate_arc_diagram(doc), # Quitamos nlp.lang
+ 'repeated_words': get_repeated_words_colors(doc),
+ 'highlighted_text': highlight_repeated_words(doc, get_repeated_words_colors(doc))
+ }
+ except Exception as e:
+ logger.error(f"Error en análisis morfosintáctico: {str(e)}")
+ return None
+
+# Al final del archivo morph_analysis.py
+__all__ = [
+ 'perform_advanced_morphosyntactic_analysis',
+ 'get_repeated_words_colors',
+ 'highlight_repeated_words',
+ 'generate_arc_diagram',
+ 'get_detailed_pos_analysis',
+ 'get_morphological_analysis',
+ 'get_sentence_structure_analysis',
+ 'POS_COLORS',
+ 'POS_TRANSLATIONS'
+]
diff --git a/modules/text_analysis/semantic_analysis-16-10-2024.py b/modules/text_analysis/semantic_analysis-16-10-2024.py
index a6880f6152f90900b31afef1a501a93704120ded..e8684fb9e71fc128772b372e944bd38ec02c131d 100644
--- a/modules/text_analysis/semantic_analysis-16-10-2024.py
+++ b/modules/text_analysis/semantic_analysis-16-10-2024.py
@@ -1,446 +1,446 @@
-# modules/text_analysis/semantic_analysis.py
-# [Mantener todas las importaciones y constantes existentes...]
-
-import streamlit as st
-import spacy
-import networkx as nx
-import matplotlib.pyplot as plt
-import io
-import base64
-from collections import Counter, defaultdict
-from sklearn.feature_extraction.text import TfidfVectorizer
-from sklearn.metrics.pairwise import cosine_similarity
-import logging
-
-logger = logging.getLogger(__name__)
-
-
-# Define colors for grammatical categories
-POS_COLORS = {
- 'ADJ': '#FFA07A', 'ADP': '#98FB98', 'ADV': '#87CEFA', 'AUX': '#DDA0DD',
- 'CCONJ': '#F0E68C', 'DET': '#FFB6C1', 'INTJ': '#FF6347', 'NOUN': '#90EE90',
- 'NUM': '#FAFAD2', 'PART': '#D3D3D3', 'PRON': '#FFA500', 'PROPN': '#20B2AA',
- 'SCONJ': '#DEB887', 'SYM': '#7B68EE', 'VERB': '#FF69B4', 'X': '#A9A9A9',
-}
-
-POS_TRANSLATIONS = {
- 'es': {
- 'ADJ': 'Adjetivo', 'ADP': 'Preposición', 'ADV': 'Adverbio', 'AUX': 'Auxiliar',
- 'CCONJ': 'Conjunción Coordinante', 'DET': 'Determinante', 'INTJ': 'Interjección',
- 'NOUN': 'Sustantivo', 'NUM': 'Número', 'PART': 'Partícula', 'PRON': 'Pronombre',
- 'PROPN': 'Nombre Propio', 'SCONJ': 'Conjunción Subordinante', 'SYM': 'Símbolo',
- 'VERB': 'Verbo', 'X': 'Otro',
- },
- 'en': {
- 'ADJ': 'Adjective', 'ADP': 'Preposition', 'ADV': 'Adverb', 'AUX': 'Auxiliary',
- 'CCONJ': 'Coordinating Conjunction', 'DET': 'Determiner', 'INTJ': 'Interjection',
- 'NOUN': 'Noun', 'NUM': 'Number', 'PART': 'Particle', 'PRON': 'Pronoun',
- 'PROPN': 'Proper Noun', 'SCONJ': 'Subordinating Conjunction', 'SYM': 'Symbol',
- 'VERB': 'Verb', 'X': 'Other',
- },
- 'fr': {
- 'ADJ': 'Adjectif', 'ADP': 'Préposition', 'ADV': 'Adverbe', 'AUX': 'Auxiliaire',
- 'CCONJ': 'Conjonction de Coordination', 'DET': 'Déterminant', 'INTJ': 'Interjection',
- 'NOUN': 'Nom', 'NUM': 'Nombre', 'PART': 'Particule', 'PRON': 'Pronom',
- 'PROPN': 'Nom Propre', 'SCONJ': 'Conjonction de Subordination', 'SYM': 'Symbole',
- 'VERB': 'Verbe', 'X': 'Autre',
- }
-}
-
-ENTITY_LABELS = {
- 'es': {
- "Personas": "lightblue",
- "Lugares": "lightcoral",
- "Inventos": "lightgreen",
- "Fechas": "lightyellow",
- "Conceptos": "lightpink"
- },
- 'en': {
- "People": "lightblue",
- "Places": "lightcoral",
- "Inventions": "lightgreen",
- "Dates": "lightyellow",
- "Concepts": "lightpink"
- },
- 'fr': {
- "Personnes": "lightblue",
- "Lieux": "lightcoral",
- "Inventions": "lightgreen",
- "Dates": "lightyellow",
- "Concepts": "lightpink"
- }
-}
-
-CUSTOM_STOPWORDS = {
- 'es': {
- # Artículos
- 'el', 'la', 'los', 'las', 'un', 'una', 'unos', 'unas',
- # Preposiciones comunes
- 'a', 'ante', 'bajo', 'con', 'contra', 'de', 'desde', 'en',
- 'entre', 'hacia', 'hasta', 'para', 'por', 'según', 'sin',
- 'sobre', 'tras', 'durante', 'mediante',
- # Conjunciones
- 'y', 'e', 'ni', 'o', 'u', 'pero', 'sino', 'porque',
- # Pronombres
- 'yo', 'tú', 'él', 'ella', 'nosotros', 'vosotros', 'ellos',
- 'ellas', 'este', 'esta', 'ese', 'esa', 'aquel', 'aquella',
- # Verbos auxiliares comunes
- 'ser', 'estar', 'haber', 'tener',
- # Palabras comunes en textos académicos
- 'además', 'también', 'asimismo', 'sin embargo', 'no obstante',
- 'por lo tanto', 'entonces', 'así', 'luego', 'pues',
- # Números escritos
- 'uno', 'dos', 'tres', 'primer', 'primera', 'segundo', 'segunda',
- # Otras palabras comunes
- 'cada', 'todo', 'toda', 'todos', 'todas', 'otro', 'otra',
- 'donde', 'cuando', 'como', 'que', 'cual', 'quien',
- 'cuyo', 'cuya', 'hay', 'solo', 'ver', 'si', 'no',
- # Símbolos y caracteres especiales
- '#', '@', '/', '*', '+', '-', '=', '$', '%'
- },
- 'en': {
- # Articles
- 'the', 'a', 'an',
- # Common prepositions
- 'in', 'on', 'at', 'by', 'for', 'with', 'about', 'against',
- 'between', 'into', 'through', 'during', 'before', 'after',
- 'above', 'below', 'to', 'from', 'up', 'down', 'of',
- # Conjunctions
- 'and', 'or', 'but', 'nor', 'so', 'for', 'yet',
- # Pronouns
- 'i', 'you', 'he', 'she', 'it', 'we', 'they', 'this',
- 'that', 'these', 'those', 'my', 'your', 'his', 'her',
- # Auxiliary verbs
- 'be', 'am', 'is', 'are', 'was', 'were', 'been', 'have',
- 'has', 'had', 'do', 'does', 'did',
- # Common academic words
- 'therefore', 'however', 'thus', 'hence', 'moreover',
- 'furthermore', 'nevertheless',
- # Numbers written
- 'one', 'two', 'three', 'first', 'second', 'third',
- # Other common words
- 'where', 'when', 'how', 'what', 'which', 'who',
- 'whom', 'whose', 'there', 'here', 'just', 'only',
- # Symbols and special characters
- '#', '@', '/', '*', '+', '-', '=', '$', '%'
- },
- 'fr': {
- # Articles
- 'le', 'la', 'les', 'un', 'une', 'des',
- # Prepositions
- 'à', 'de', 'dans', 'sur', 'en', 'par', 'pour', 'avec',
- 'sans', 'sous', 'entre', 'derrière', 'chez', 'avant',
- # Conjunctions
- 'et', 'ou', 'mais', 'donc', 'car', 'ni', 'or',
- # Pronouns
- 'je', 'tu', 'il', 'elle', 'nous', 'vous', 'ils',
- 'elles', 'ce', 'cette', 'ces', 'celui', 'celle',
- # Auxiliary verbs
- 'être', 'avoir', 'faire',
- # Academic words
- 'donc', 'cependant', 'néanmoins', 'ainsi', 'toutefois',
- 'pourtant', 'alors',
- # Numbers
- 'un', 'deux', 'trois', 'premier', 'première', 'second',
- # Other common words
- 'où', 'quand', 'comment', 'que', 'qui', 'quoi',
- 'quel', 'quelle', 'plus', 'moins',
- # Symbols
- '#', '@', '/', '*', '+', '-', '=', '$', '%'
- }
-}
-
-##############################################################################################################
-def get_stopwords(lang_code):
- """
- Obtiene el conjunto de stopwords para un idioma específico.
- Combina las stopwords de spaCy con las personalizadas.
- """
- try:
- nlp = spacy.load(f'{lang_code}_core_news_sm')
- spacy_stopwords = nlp.Defaults.stop_words
- custom_stopwords = CUSTOM_STOPWORDS.get(lang_code, set())
- return spacy_stopwords.union(custom_stopwords)
- except:
- return CUSTOM_STOPWORDS.get(lang_code, set())
-
-
-def perform_semantic_analysis(text, nlp, lang_code):
- """
- Realiza el análisis semántico completo del texto.
- Args:
- text: Texto a analizar
- nlp: Modelo de spaCy
- lang_code: Código del idioma
- Returns:
- dict: Resultados del análisis
- """
-
- logger.info(f"Starting semantic analysis for language: {lang_code}")
- try:
- doc = nlp(text)
- key_concepts = identify_key_concepts(doc)
- concept_graph = create_concept_graph(doc, key_concepts)
- concept_graph_fig = visualize_concept_graph(concept_graph, lang_code)
- entities = extract_entities(doc, lang_code)
- entity_graph = create_entity_graph(entities)
- entity_graph_fig = visualize_entity_graph(entity_graph, lang_code)
-
- # Convertir figuras a bytes
- concept_graph_bytes = fig_to_bytes(concept_graph_fig)
- entity_graph_bytes = fig_to_bytes(entity_graph_fig)
-
- logger.info("Semantic analysis completed successfully")
- return {
- 'key_concepts': key_concepts,
- 'concept_graph': concept_graph_bytes,
- 'entities': entities,
- 'entity_graph': entity_graph_bytes
- }
- except Exception as e:
- logger.error(f"Error in perform_semantic_analysis: {str(e)}")
- raise
-
-
-def fig_to_bytes(fig):
- buf = io.BytesIO()
- fig.savefig(buf, format='png')
- buf.seek(0)
- return buf.getvalue()
-
-
-def fig_to_html(fig):
- buf = io.BytesIO()
- fig.savefig(buf, format='png')
- buf.seek(0)
- img_str = base64.b64encode(buf.getvalue()).decode()
- return f'

'
-
-
-
-def identify_key_concepts(doc, min_freq=2, min_length=3):
- """
- Identifica conceptos clave en el texto.
- Args:
- doc: Documento procesado por spaCy
- min_freq: Frecuencia mínima para considerar un concepto
- min_length: Longitud mínima de palabra para considerar
- Returns:
- list: Lista de tuplas (concepto, frecuencia)
- """
- try:
- # Obtener stopwords para el idioma
- stopwords = get_stopwords(doc.lang_)
-
- # Contar frecuencias de palabras
- word_freq = Counter()
-
- for token in doc:
- if (token.lemma_.lower() not in stopwords and
- len(token.lemma_) >= min_length and
- token.is_alpha and
- not token.is_punct and
- not token.like_num):
-
- word_freq[token.lemma_.lower()] += 1
-
- # Filtrar por frecuencia mínima
- concepts = [(word, freq) for word, freq in word_freq.items()
- if freq >= min_freq]
-
- # Ordenar por frecuencia
- concepts.sort(key=lambda x: x[1], reverse=True)
-
- return concepts[:10] # Retornar los 10 conceptos más frecuentes
-
- except Exception as e:
- logger.error(f"Error en identify_key_concepts: {str(e)}")
- return [] # Retornar lista vacía en caso de error
-
-
-def create_concept_graph(doc, key_concepts):
- """
- Crea un grafo de relaciones entre conceptos.
- Args:
- doc: Documento procesado por spaCy
- key_concepts: Lista de tuplas (concepto, frecuencia)
- Returns:
- nx.Graph: Grafo de conceptos
- """
- try:
- G = nx.Graph()
-
- # Crear un conjunto de conceptos clave para búsqueda rápida
- concept_words = {concept[0].lower() for concept in key_concepts}
-
- # Añadir nodos al grafo
- for concept, freq in key_concepts:
- G.add_node(concept.lower(), weight=freq)
-
- # Analizar cada oración
- for sent in doc.sents:
- # Obtener conceptos en la oración actual
- current_concepts = []
- for token in sent:
- if token.lemma_.lower() in concept_words:
- current_concepts.append(token.lemma_.lower())
-
- # Crear conexiones entre conceptos en la misma oración
- for i, concept1 in enumerate(current_concepts):
- for concept2 in current_concepts[i+1:]:
- if concept1 != concept2:
- # Si ya existe la arista, incrementar el peso
- if G.has_edge(concept1, concept2):
- G[concept1][concept2]['weight'] += 1
- # Si no existe, crear nueva arista con peso 1
- else:
- G.add_edge(concept1, concept2, weight=1)
-
- return G
-
- except Exception as e:
- logger.error(f"Error en create_concept_graph: {str(e)}")
- # Retornar un grafo vacío en caso de error
- return nx.Graph()
-
-def visualize_concept_graph(G, lang_code):
- """
- Visualiza el grafo de conceptos.
- Args:
- G: Grafo de networkx
- lang_code: Código del idioma
- Returns:
- matplotlib.figure.Figure: Figura con el grafo visualizado
- """
- try:
- plt.figure(figsize=(12, 8))
-
- # Calcular el layout del grafo
- pos = nx.spring_layout(G)
-
- # Obtener pesos de nodos y aristas
- node_weights = [G.nodes[node].get('weight', 1) * 500 for node in G.nodes()]
- edge_weights = [G[u][v].get('weight', 1) for u, v in G.edges()]
-
- # Dibujar el grafo
- nx.draw_networkx_nodes(G, pos,
- node_size=node_weights,
- node_color='lightblue',
- alpha=0.6)
-
- nx.draw_networkx_edges(G, pos,
- width=edge_weights,
- alpha=0.5,
- edge_color='gray')
-
- nx.draw_networkx_labels(G, pos,
- font_size=10,
- font_weight='bold')
-
- plt.title("Red de conceptos relacionados")
- plt.axis('off')
-
- return plt.gcf()
-
- except Exception as e:
- logger.error(f"Error en visualize_concept_graph: {str(e)}")
- # Retornar una figura vacía en caso de error
- return plt.figure()
-
-def create_entity_graph(entities):
- G = nx.Graph()
- for entity_type, entity_list in entities.items():
- for entity in entity_list:
- G.add_node(entity, type=entity_type)
- for i, entity1 in enumerate(entity_list):
- for entity2 in entity_list[i+1:]:
- G.add_edge(entity1, entity2)
- return G
-
-def visualize_entity_graph(G, lang_code):
- fig, ax = plt.subplots(figsize=(12, 8))
- pos = nx.spring_layout(G)
- for entity_type, color in ENTITY_LABELS[lang_code].items():
- node_list = [node for node, data in G.nodes(data=True) if data['type'] == entity_type]
- nx.draw_networkx_nodes(G, pos, nodelist=node_list, node_color=color, node_size=500, alpha=0.8, ax=ax)
- nx.draw_networkx_edges(G, pos, width=1, alpha=0.5, ax=ax)
- nx.draw_networkx_labels(G, pos, font_size=8, font_weight="bold", ax=ax)
- ax.set_title(f"Relaciones entre Entidades ({lang_code})", fontsize=16)
- ax.axis('off')
- plt.tight_layout()
- return fig
-
-
-#################################################################################
-def create_topic_graph(topics, doc):
- G = nx.Graph()
- for topic in topics:
- G.add_node(topic, weight=doc.text.count(topic))
- for i, topic1 in enumerate(topics):
- for topic2 in topics[i+1:]:
- weight = sum(1 for sent in doc.sents if topic1 in sent.text and topic2 in sent.text)
- if weight > 0:
- G.add_edge(topic1, topic2, weight=weight)
- return G
-
-def visualize_topic_graph(G, lang_code):
- fig, ax = plt.subplots(figsize=(12, 8))
- pos = nx.spring_layout(G)
- node_sizes = [G.nodes[node]['weight'] * 100 for node in G.nodes()]
- nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='lightgreen', alpha=0.8, ax=ax)
- nx.draw_networkx_labels(G, pos, font_size=10, font_weight="bold", ax=ax)
- edge_weights = [G[u][v]['weight'] for u, v in G.edges()]
- nx.draw_networkx_edges(G, pos, width=edge_weights, alpha=0.5, ax=ax)
- ax.set_title(f"Relaciones entre Temas ({lang_code})", fontsize=16)
- ax.axis('off')
- plt.tight_layout()
- return fig
-
-###########################################################################################
-def generate_summary(doc, lang_code):
- sentences = list(doc.sents)
- summary = sentences[:3] # Toma las primeras 3 oraciones como resumen
- return " ".join([sent.text for sent in summary])
-
-def extract_entities(doc, lang_code):
- entities = defaultdict(list)
- for ent in doc.ents:
- if ent.label_ in ENTITY_LABELS[lang_code]:
- entities[ent.label_].append(ent.text)
- return dict(entities)
-
-def analyze_sentiment(doc, lang_code):
- positive_words = sum(1 for token in doc if token.sentiment > 0)
- negative_words = sum(1 for token in doc if token.sentiment < 0)
- total_words = len(doc)
- if positive_words > negative_words:
- return "Positivo"
- elif negative_words > positive_words:
- return "Negativo"
- else:
- return "Neutral"
-
-def extract_topics(doc, lang_code):
- vectorizer = TfidfVectorizer(stop_words='english', max_features=5)
- tfidf_matrix = vectorizer.fit_transform([doc.text])
- feature_names = vectorizer.get_feature_names_out()
- return list(feature_names)
-
-# Asegúrate de que todas las funciones necesarias estén exportadas
-__all__ = [
- 'perform_semantic_analysis',
- 'identify_key_concepts',
- 'create_concept_graph',
- 'visualize_concept_graph',
- 'create_entity_graph',
- 'visualize_entity_graph',
- 'generate_summary',
- 'extract_entities',
- 'analyze_sentiment',
- 'create_topic_graph',
- 'visualize_topic_graph',
- 'extract_topics',
- 'ENTITY_LABELS',
- 'POS_COLORS',
- 'POS_TRANSLATIONS'
+# modules/text_analysis/semantic_analysis.py
+# [Mantener todas las importaciones y constantes existentes...]
+
+import streamlit as st
+import spacy
+import networkx as nx
+import matplotlib.pyplot as plt
+import io
+import base64
+from collections import Counter, defaultdict
+from sklearn.feature_extraction.text import TfidfVectorizer
+from sklearn.metrics.pairwise import cosine_similarity
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+# Define colors for grammatical categories
+POS_COLORS = {
+ 'ADJ': '#FFA07A', 'ADP': '#98FB98', 'ADV': '#87CEFA', 'AUX': '#DDA0DD',
+ 'CCONJ': '#F0E68C', 'DET': '#FFB6C1', 'INTJ': '#FF6347', 'NOUN': '#90EE90',
+ 'NUM': '#FAFAD2', 'PART': '#D3D3D3', 'PRON': '#FFA500', 'PROPN': '#20B2AA',
+ 'SCONJ': '#DEB887', 'SYM': '#7B68EE', 'VERB': '#FF69B4', 'X': '#A9A9A9',
+}
+
+POS_TRANSLATIONS = {
+ 'es': {
+ 'ADJ': 'Adjetivo', 'ADP': 'Preposición', 'ADV': 'Adverbio', 'AUX': 'Auxiliar',
+ 'CCONJ': 'Conjunción Coordinante', 'DET': 'Determinante', 'INTJ': 'Interjección',
+ 'NOUN': 'Sustantivo', 'NUM': 'Número', 'PART': 'Partícula', 'PRON': 'Pronombre',
+ 'PROPN': 'Nombre Propio', 'SCONJ': 'Conjunción Subordinante', 'SYM': 'Símbolo',
+ 'VERB': 'Verbo', 'X': 'Otro',
+ },
+ 'en': {
+ 'ADJ': 'Adjective', 'ADP': 'Preposition', 'ADV': 'Adverb', 'AUX': 'Auxiliary',
+ 'CCONJ': 'Coordinating Conjunction', 'DET': 'Determiner', 'INTJ': 'Interjection',
+ 'NOUN': 'Noun', 'NUM': 'Number', 'PART': 'Particle', 'PRON': 'Pronoun',
+ 'PROPN': 'Proper Noun', 'SCONJ': 'Subordinating Conjunction', 'SYM': 'Symbol',
+ 'VERB': 'Verb', 'X': 'Other',
+ },
+ 'fr': {
+ 'ADJ': 'Adjectif', 'ADP': 'Préposition', 'ADV': 'Adverbe', 'AUX': 'Auxiliaire',
+ 'CCONJ': 'Conjonction de Coordination', 'DET': 'Déterminant', 'INTJ': 'Interjection',
+ 'NOUN': 'Nom', 'NUM': 'Nombre', 'PART': 'Particule', 'PRON': 'Pronom',
+ 'PROPN': 'Nom Propre', 'SCONJ': 'Conjonction de Subordination', 'SYM': 'Symbole',
+ 'VERB': 'Verbe', 'X': 'Autre',
+ }
+}
+
+ENTITY_LABELS = {
+ 'es': {
+ "Personas": "lightblue",
+ "Lugares": "lightcoral",
+ "Inventos": "lightgreen",
+ "Fechas": "lightyellow",
+ "Conceptos": "lightpink"
+ },
+ 'en': {
+ "People": "lightblue",
+ "Places": "lightcoral",
+ "Inventions": "lightgreen",
+ "Dates": "lightyellow",
+ "Concepts": "lightpink"
+ },
+ 'fr': {
+ "Personnes": "lightblue",
+ "Lieux": "lightcoral",
+ "Inventions": "lightgreen",
+ "Dates": "lightyellow",
+ "Concepts": "lightpink"
+ }
+}
+
+CUSTOM_STOPWORDS = {
+ 'es': {
+ # Artículos
+ 'el', 'la', 'los', 'las', 'un', 'una', 'unos', 'unas',
+ # Preposiciones comunes
+ 'a', 'ante', 'bajo', 'con', 'contra', 'de', 'desde', 'en',
+ 'entre', 'hacia', 'hasta', 'para', 'por', 'según', 'sin',
+ 'sobre', 'tras', 'durante', 'mediante',
+ # Conjunciones
+ 'y', 'e', 'ni', 'o', 'u', 'pero', 'sino', 'porque',
+ # Pronombres
+ 'yo', 'tú', 'él', 'ella', 'nosotros', 'vosotros', 'ellos',
+ 'ellas', 'este', 'esta', 'ese', 'esa', 'aquel', 'aquella',
+ # Verbos auxiliares comunes
+ 'ser', 'estar', 'haber', 'tener',
+ # Palabras comunes en textos académicos
+ 'además', 'también', 'asimismo', 'sin embargo', 'no obstante',
+ 'por lo tanto', 'entonces', 'así', 'luego', 'pues',
+ # Números escritos
+ 'uno', 'dos', 'tres', 'primer', 'primera', 'segundo', 'segunda',
+ # Otras palabras comunes
+ 'cada', 'todo', 'toda', 'todos', 'todas', 'otro', 'otra',
+ 'donde', 'cuando', 'como', 'que', 'cual', 'quien',
+ 'cuyo', 'cuya', 'hay', 'solo', 'ver', 'si', 'no',
+ # Símbolos y caracteres especiales
+ '#', '@', '/', '*', '+', '-', '=', '$', '%'
+ },
+ 'en': {
+ # Articles
+ 'the', 'a', 'an',
+ # Common prepositions
+ 'in', 'on', 'at', 'by', 'for', 'with', 'about', 'against',
+ 'between', 'into', 'through', 'during', 'before', 'after',
+ 'above', 'below', 'to', 'from', 'up', 'down', 'of',
+ # Conjunctions
+ 'and', 'or', 'but', 'nor', 'so', 'for', 'yet',
+ # Pronouns
+ 'i', 'you', 'he', 'she', 'it', 'we', 'they', 'this',
+ 'that', 'these', 'those', 'my', 'your', 'his', 'her',
+ # Auxiliary verbs
+ 'be', 'am', 'is', 'are', 'was', 'were', 'been', 'have',
+ 'has', 'had', 'do', 'does', 'did',
+ # Common academic words
+ 'therefore', 'however', 'thus', 'hence', 'moreover',
+ 'furthermore', 'nevertheless',
+ # Numbers written
+ 'one', 'two', 'three', 'first', 'second', 'third',
+ # Other common words
+ 'where', 'when', 'how', 'what', 'which', 'who',
+ 'whom', 'whose', 'there', 'here', 'just', 'only',
+ # Symbols and special characters
+ '#', '@', '/', '*', '+', '-', '=', '$', '%'
+ },
+ 'fr': {
+ # Articles
+ 'le', 'la', 'les', 'un', 'une', 'des',
+ # Prepositions
+ 'à', 'de', 'dans', 'sur', 'en', 'par', 'pour', 'avec',
+ 'sans', 'sous', 'entre', 'derrière', 'chez', 'avant',
+ # Conjunctions
+ 'et', 'ou', 'mais', 'donc', 'car', 'ni', 'or',
+ # Pronouns
+ 'je', 'tu', 'il', 'elle', 'nous', 'vous', 'ils',
+ 'elles', 'ce', 'cette', 'ces', 'celui', 'celle',
+ # Auxiliary verbs
+ 'être', 'avoir', 'faire',
+ # Academic words
+ 'donc', 'cependant', 'néanmoins', 'ainsi', 'toutefois',
+ 'pourtant', 'alors',
+ # Numbers
+ 'un', 'deux', 'trois', 'premier', 'première', 'second',
+ # Other common words
+ 'où', 'quand', 'comment', 'que', 'qui', 'quoi',
+ 'quel', 'quelle', 'plus', 'moins',
+ # Symbols
+ '#', '@', '/', '*', '+', '-', '=', '$', '%'
+ }
+}
+
+##############################################################################################################
+def get_stopwords(lang_code):
+ """
+ Obtiene el conjunto de stopwords para un idioma específico.
+ Combina las stopwords de spaCy con las personalizadas.
+ """
+ try:
+ nlp = spacy.load(f'{lang_code}_core_news_sm')
+ spacy_stopwords = nlp.Defaults.stop_words
+ custom_stopwords = CUSTOM_STOPWORDS.get(lang_code, set())
+ return spacy_stopwords.union(custom_stopwords)
+ except:
+ return CUSTOM_STOPWORDS.get(lang_code, set())
+
+
+def perform_semantic_analysis(text, nlp, lang_code):
+ """
+ Realiza el análisis semántico completo del texto.
+ Args:
+ text: Texto a analizar
+ nlp: Modelo de spaCy
+ lang_code: Código del idioma
+ Returns:
+ dict: Resultados del análisis
+ """
+
+ logger.info(f"Starting semantic analysis for language: {lang_code}")
+ try:
+ doc = nlp(text)
+ key_concepts = identify_key_concepts(doc)
+ concept_graph = create_concept_graph(doc, key_concepts)
+ concept_graph_fig = visualize_concept_graph(concept_graph, lang_code)
+ entities = extract_entities(doc, lang_code)
+ entity_graph = create_entity_graph(entities)
+ entity_graph_fig = visualize_entity_graph(entity_graph, lang_code)
+
+ # Convertir figuras a bytes
+ concept_graph_bytes = fig_to_bytes(concept_graph_fig)
+ entity_graph_bytes = fig_to_bytes(entity_graph_fig)
+
+ logger.info("Semantic analysis completed successfully")
+ return {
+ 'key_concepts': key_concepts,
+ 'concept_graph': concept_graph_bytes,
+ 'entities': entities,
+ 'entity_graph': entity_graph_bytes
+ }
+ except Exception as e:
+ logger.error(f"Error in perform_semantic_analysis: {str(e)}")
+ raise
+
+
+def fig_to_bytes(fig):
+ buf = io.BytesIO()
+ fig.savefig(buf, format='png')
+ buf.seek(0)
+ return buf.getvalue()
+
+
+def fig_to_html(fig):
+ buf = io.BytesIO()
+ fig.savefig(buf, format='png')
+ buf.seek(0)
+ img_str = base64.b64encode(buf.getvalue()).decode()
+ return f'

'
+
+
+
+def identify_key_concepts(doc, min_freq=2, min_length=3):
+ """
+ Identifica conceptos clave en el texto.
+ Args:
+ doc: Documento procesado por spaCy
+ min_freq: Frecuencia mínima para considerar un concepto
+ min_length: Longitud mínima de palabra para considerar
+ Returns:
+ list: Lista de tuplas (concepto, frecuencia)
+ """
+ try:
+ # Obtener stopwords para el idioma
+ stopwords = get_stopwords(doc.lang_)
+
+ # Contar frecuencias de palabras
+ word_freq = Counter()
+
+ for token in doc:
+ if (token.lemma_.lower() not in stopwords and
+ len(token.lemma_) >= min_length and
+ token.is_alpha and
+ not token.is_punct and
+ not token.like_num):
+
+ word_freq[token.lemma_.lower()] += 1
+
+ # Filtrar por frecuencia mínima
+ concepts = [(word, freq) for word, freq in word_freq.items()
+ if freq >= min_freq]
+
+ # Ordenar por frecuencia
+ concepts.sort(key=lambda x: x[1], reverse=True)
+
+ return concepts[:10] # Retornar los 10 conceptos más frecuentes
+
+ except Exception as e:
+ logger.error(f"Error en identify_key_concepts: {str(e)}")
+ return [] # Retornar lista vacía en caso de error
+
+
+def create_concept_graph(doc, key_concepts):
+ """
+ Crea un grafo de relaciones entre conceptos.
+ Args:
+ doc: Documento procesado por spaCy
+ key_concepts: Lista de tuplas (concepto, frecuencia)
+ Returns:
+ nx.Graph: Grafo de conceptos
+ """
+ try:
+ G = nx.Graph()
+
+ # Crear un conjunto de conceptos clave para búsqueda rápida
+ concept_words = {concept[0].lower() for concept in key_concepts}
+
+ # Añadir nodos al grafo
+ for concept, freq in key_concepts:
+ G.add_node(concept.lower(), weight=freq)
+
+ # Analizar cada oración
+ for sent in doc.sents:
+ # Obtener conceptos en la oración actual
+ current_concepts = []
+ for token in sent:
+ if token.lemma_.lower() in concept_words:
+ current_concepts.append(token.lemma_.lower())
+
+ # Crear conexiones entre conceptos en la misma oración
+ for i, concept1 in enumerate(current_concepts):
+ for concept2 in current_concepts[i+1:]:
+ if concept1 != concept2:
+ # Si ya existe la arista, incrementar el peso
+ if G.has_edge(concept1, concept2):
+ G[concept1][concept2]['weight'] += 1
+ # Si no existe, crear nueva arista con peso 1
+ else:
+ G.add_edge(concept1, concept2, weight=1)
+
+ return G
+
+ except Exception as e:
+ logger.error(f"Error en create_concept_graph: {str(e)}")
+ # Retornar un grafo vacío en caso de error
+ return nx.Graph()
+
+def visualize_concept_graph(G, lang_code):
+ """
+ Visualiza el grafo de conceptos.
+ Args:
+ G: Grafo de networkx
+ lang_code: Código del idioma
+ Returns:
+ matplotlib.figure.Figure: Figura con el grafo visualizado
+ """
+ try:
+ plt.figure(figsize=(12, 8))
+
+ # Calcular el layout del grafo
+ pos = nx.spring_layout(G)
+
+ # Obtener pesos de nodos y aristas
+ node_weights = [G.nodes[node].get('weight', 1) * 500 for node in G.nodes()]
+ edge_weights = [G[u][v].get('weight', 1) for u, v in G.edges()]
+
+ # Dibujar el grafo
+ nx.draw_networkx_nodes(G, pos,
+ node_size=node_weights,
+ node_color='lightblue',
+ alpha=0.6)
+
+ nx.draw_networkx_edges(G, pos,
+ width=edge_weights,
+ alpha=0.5,
+ edge_color='gray')
+
+ nx.draw_networkx_labels(G, pos,
+ font_size=10,
+ font_weight='bold')
+
+ plt.title("Red de conceptos relacionados")
+ plt.axis('off')
+
+ return plt.gcf()
+
+ except Exception as e:
+ logger.error(f"Error en visualize_concept_graph: {str(e)}")
+ # Retornar una figura vacía en caso de error
+ return plt.figure()
+
+def create_entity_graph(entities):
+ G = nx.Graph()
+ for entity_type, entity_list in entities.items():
+ for entity in entity_list:
+ G.add_node(entity, type=entity_type)
+ for i, entity1 in enumerate(entity_list):
+ for entity2 in entity_list[i+1:]:
+ G.add_edge(entity1, entity2)
+ return G
+
+def visualize_entity_graph(G, lang_code):
+ fig, ax = plt.subplots(figsize=(12, 8))
+ pos = nx.spring_layout(G)
+ for entity_type, color in ENTITY_LABELS[lang_code].items():
+ node_list = [node for node, data in G.nodes(data=True) if data['type'] == entity_type]
+ nx.draw_networkx_nodes(G, pos, nodelist=node_list, node_color=color, node_size=500, alpha=0.8, ax=ax)
+ nx.draw_networkx_edges(G, pos, width=1, alpha=0.5, ax=ax)
+ nx.draw_networkx_labels(G, pos, font_size=8, font_weight="bold", ax=ax)
+ ax.set_title(f"Relaciones entre Entidades ({lang_code})", fontsize=16)
+ ax.axis('off')
+ plt.tight_layout()
+ return fig
+
+
+#################################################################################
+def create_topic_graph(topics, doc):
+ G = nx.Graph()
+ for topic in topics:
+ G.add_node(topic, weight=doc.text.count(topic))
+ for i, topic1 in enumerate(topics):
+ for topic2 in topics[i+1:]:
+ weight = sum(1 for sent in doc.sents if topic1 in sent.text and topic2 in sent.text)
+ if weight > 0:
+ G.add_edge(topic1, topic2, weight=weight)
+ return G
+
+def visualize_topic_graph(G, lang_code):
+ fig, ax = plt.subplots(figsize=(12, 8))
+ pos = nx.spring_layout(G)
+ node_sizes = [G.nodes[node]['weight'] * 100 for node in G.nodes()]
+ nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='lightgreen', alpha=0.8, ax=ax)
+ nx.draw_networkx_labels(G, pos, font_size=10, font_weight="bold", ax=ax)
+ edge_weights = [G[u][v]['weight'] for u, v in G.edges()]
+ nx.draw_networkx_edges(G, pos, width=edge_weights, alpha=0.5, ax=ax)
+ ax.set_title(f"Relaciones entre Temas ({lang_code})", fontsize=16)
+ ax.axis('off')
+ plt.tight_layout()
+ return fig
+
+###########################################################################################
+def generate_summary(doc, lang_code):
+ sentences = list(doc.sents)
+ summary = sentences[:3] # Toma las primeras 3 oraciones como resumen
+ return " ".join([sent.text for sent in summary])
+
+def extract_entities(doc, lang_code):
+ entities = defaultdict(list)
+ for ent in doc.ents:
+ if ent.label_ in ENTITY_LABELS[lang_code]:
+ entities[ent.label_].append(ent.text)
+ return dict(entities)
+
+def analyze_sentiment(doc, lang_code):
+ positive_words = sum(1 for token in doc if token.sentiment > 0)
+ negative_words = sum(1 for token in doc if token.sentiment < 0)
+ total_words = len(doc)
+ if positive_words > negative_words:
+ return "Positivo"
+ elif negative_words > positive_words:
+ return "Negativo"
+ else:
+ return "Neutral"
+
+def extract_topics(doc, lang_code):
+ vectorizer = TfidfVectorizer(stop_words='english', max_features=5)
+ tfidf_matrix = vectorizer.fit_transform([doc.text])
+ feature_names = vectorizer.get_feature_names_out()
+ return list(feature_names)
+
+# Asegúrate de que todas las funciones necesarias estén exportadas
+__all__ = [
+ 'perform_semantic_analysis',
+ 'identify_key_concepts',
+ 'create_concept_graph',
+ 'visualize_concept_graph',
+ 'create_entity_graph',
+ 'visualize_entity_graph',
+ 'generate_summary',
+ 'extract_entities',
+ 'analyze_sentiment',
+ 'create_topic_graph',
+ 'visualize_topic_graph',
+ 'extract_topics',
+ 'ENTITY_LABELS',
+ 'POS_COLORS',
+ 'POS_TRANSLATIONS'
]
\ No newline at end of file
diff --git a/modules/text_analysis/semantic_analysis.py b/modules/text_analysis/semantic_analysis.py
index fbca4411824e5428bf3b03bcc993276ac62e6c36..931dd45f436f28be989264e5be860fa42913137e 100644
--- a/modules/text_analysis/semantic_analysis.py
+++ b/modules/text_analysis/semantic_analysis.py
@@ -1,484 +1,484 @@
-# modules/text_analysis/semantic_analysis.py
-
-# 1. Importaciones estándar del sistema
-import logging
-import io
-import base64
-from collections import Counter, defaultdict
-
-# 2. Importaciones de terceros
-import streamlit as st
-import spacy
-import networkx as nx
-import matplotlib.pyplot as plt
-from sklearn.feature_extraction.text import TfidfVectorizer
-from sklearn.metrics.pairwise import cosine_similarity
-
-# Solo configurar si no hay handlers ya configurados
-logger = logging.getLogger(__name__)
-
-# 4. Importaciones locales
-from .stopwords import (
- process_text,
- clean_text,
- get_custom_stopwords,
- get_stopwords_for_spacy
-)
-
-
-# Define colors for grammatical categories
-POS_COLORS = {
- 'ADJ': '#FFA07A', 'ADP': '#98FB98', 'ADV': '#87CEFA', 'AUX': '#DDA0DD',
- 'CCONJ': '#F0E68C', 'DET': '#FFB6C1', 'INTJ': '#FF6347', 'NOUN': '#90EE90',
- 'NUM': '#FAFAD2', 'PART': '#D3D3D3', 'PRON': '#FFA500', 'PROPN': '#20B2AA',
- 'SCONJ': '#DEB887', 'SYM': '#7B68EE', 'VERB': '#FF69B4', 'X': '#A9A9A9',
-}
-
-POS_TRANSLATIONS = {
- 'es': {
- 'ADJ': 'Adjetivo', 'ADP': 'Preposición', 'ADV': 'Adverbio', 'AUX': 'Auxiliar',
- 'CCONJ': 'Conjunción Coordinante', 'DET': 'Determinante', 'INTJ': 'Interjección',
- 'NOUN': 'Sustantivo', 'NUM': 'Número', 'PART': 'Partícula', 'PRON': 'Pronombre',
- 'PROPN': 'Nombre Propio', 'SCONJ': 'Conjunción Subordinante', 'SYM': 'Símbolo',
- 'VERB': 'Verbo', 'X': 'Otro',
- },
- 'en': {
- 'ADJ': 'Adjective', 'ADP': 'Preposition', 'ADV': 'Adverb', 'AUX': 'Auxiliary',
- 'CCONJ': 'Coordinating Conjunction', 'DET': 'Determiner', 'INTJ': 'Interjection',
- 'NOUN': 'Noun', 'NUM': 'Number', 'PART': 'Particle', 'PRON': 'Pronoun',
- 'PROPN': 'Proper Noun', 'SCONJ': 'Subordinating Conjunction', 'SYM': 'Symbol',
- 'VERB': 'Verb', 'X': 'Other',
- },
- 'fr': {
- 'ADJ': 'Adjectif', 'ADP': 'Préposition', 'ADV': 'Adverbe', 'AUX': 'Auxiliaire',
- 'CCONJ': 'Conjonction de Coordination', 'DET': 'Déterminant', 'INTJ': 'Interjection',
- 'NOUN': 'Nom', 'NUM': 'Nombre', 'PART': 'Particule', 'PRON': 'Pronom',
- 'PROPN': 'Nom Propre', 'SCONJ': 'Conjonction de Subordination', 'SYM': 'Symbole',
- 'VERB': 'Verbe', 'X': 'Autre',
- }
-}
-
-ENTITY_LABELS = {
- 'es': {
- "Personas": "lightblue",
- "Lugares": "lightcoral",
- "Inventos": "lightgreen",
- "Fechas": "lightyellow",
- "Conceptos": "lightpink"
- },
- 'en': {
- "People": "lightblue",
- "Places": "lightcoral",
- "Inventions": "lightgreen",
- "Dates": "lightyellow",
- "Concepts": "lightpink"
- },
- 'fr': {
- "Personnes": "lightblue",
- "Lieux": "lightcoral",
- "Inventions": "lightgreen",
- "Dates": "lightyellow",
- "Concepts": "lightpink"
- }
-}
-
-def fig_to_bytes(fig):
- """Convierte una figura de matplotlib a bytes."""
- try:
- buf = io.BytesIO()
- fig.savefig(buf, format='png', dpi=300, bbox_inches='tight')
- buf.seek(0)
- return buf.getvalue()
- except Exception as e:
- logger.error(f"Error en fig_to_bytes: {str(e)}")
- return None
-
-###########################################################
-def perform_semantic_analysis(text, nlp, lang_code):
- """
- Realiza el análisis semántico completo del texto.
- """
- if not text or not nlp or not lang_code:
- logger.error("Parámetros inválidos para el análisis semántico")
- return {
- 'success': False,
- 'error': 'Parámetros inválidos'
- }
-
- try:
- logger.info(f"Starting semantic analysis for language: {lang_code}")
-
- # Procesar texto y remover stopwords
- doc = nlp(text)
- if not doc:
- logger.error("Error al procesar el texto con spaCy")
- return {
- 'success': False,
- 'error': 'Error al procesar el texto'
- }
-
- # Identificar conceptos clave
- logger.info("Identificando conceptos clave...")
- stopwords = get_custom_stopwords(lang_code)
- key_concepts = identify_key_concepts(doc, stopwords=stopwords)
-
- if not key_concepts:
- logger.warning("No se identificaron conceptos clave")
- return {
- 'success': False,
- 'error': 'No se pudieron identificar conceptos clave'
- }
-
- # Crear grafo de conceptos
- logger.info(f"Creando grafo de conceptos con {len(key_concepts)} conceptos...")
- concept_graph = create_concept_graph(doc, key_concepts)
-
- if not concept_graph.nodes():
- logger.warning("Se creó un grafo vacío")
- return {
- 'success': False,
- 'error': 'No se pudo crear el grafo de conceptos'
- }
-
- # Visualizar grafo
- logger.info("Visualizando grafo...")
- plt.clf() # Limpiar figura actual
- concept_graph_fig = visualize_concept_graph(concept_graph, lang_code)
-
- # Convertir a bytes
- logger.info("Convirtiendo grafo a bytes...")
- graph_bytes = fig_to_bytes(concept_graph_fig)
-
- if not graph_bytes:
- logger.error("Error al convertir grafo a bytes")
- return {
- 'success': False,
- 'error': 'Error al generar visualización'
- }
-
- # Limpiar recursos
- plt.close(concept_graph_fig)
- plt.close('all')
-
- result = {
- 'success': True,
- 'key_concepts': key_concepts,
- 'concept_graph': graph_bytes
- }
-
- logger.info("Análisis semántico completado exitosamente")
- return result
-
- except Exception as e:
- logger.error(f"Error in perform_semantic_analysis: {str(e)}")
- plt.close('all') # Asegurarse de limpiar recursos
- return {
- 'success': False,
- 'error': str(e)
- }
- finally:
- plt.close('all') # Asegurar limpieza incluso si hay error
-
-############################################################
-
-def identify_key_concepts(doc, stopwords, min_freq=2, min_length=3):
- """
- Identifica conceptos clave en el texto, excluyendo entidades nombradas.
- Args:
- doc: Documento procesado por spaCy
- stopwords: Lista de stopwords
- min_freq: Frecuencia mínima para considerar un concepto
- min_length: Longitud mínima del concepto
- Returns:
- List[Tuple[str, int]]: Lista de tuplas (concepto, frecuencia)
- """
- try:
- word_freq = Counter()
-
- # Crear conjunto de tokens que son parte de entidades
- entity_tokens = set()
- for ent in doc.ents:
- entity_tokens.update(token.i for token in ent)
-
- # Procesar tokens
- for token in doc:
- # Verificar si el token no es parte de una entidad nombrada
- if (token.i not in entity_tokens and # No es parte de una entidad
- token.lemma_.lower() not in stopwords and # No es stopword
- len(token.lemma_) >= min_length and # Longitud mínima
- token.is_alpha and # Es alfabético
- not token.is_punct and # No es puntuación
- not token.like_num and # No es número
- not token.is_space and # No es espacio
- not token.is_stop and # No es stopword de spaCy
- not token.pos_ == 'PROPN' and # No es nombre propio
- not token.pos_ == 'SYM' and # No es símbolo
- not token.pos_ == 'NUM' and # No es número
- not token.pos_ == 'X'): # No es otro
-
- # Convertir a minúsculas y añadir al contador
- word_freq[token.lemma_.lower()] += 1
-
- # Filtrar conceptos por frecuencia mínima y ordenar por frecuencia
- concepts = [(word, freq) for word, freq in word_freq.items()
- if freq >= min_freq]
- concepts.sort(key=lambda x: x[1], reverse=True)
-
- logger.info(f"Identified {len(concepts)} key concepts after excluding entities")
- return concepts[:10]
-
- except Exception as e:
- logger.error(f"Error en identify_key_concepts: {str(e)}")
- return []
-
-########################################################################
-
-def create_concept_graph(doc, key_concepts):
- """
- Crea un grafo de relaciones entre conceptos, ignorando entidades.
- Args:
- doc: Documento procesado por spaCy
- key_concepts: Lista de tuplas (concepto, frecuencia)
- Returns:
- nx.Graph: Grafo de conceptos
- """
- try:
- G = nx.Graph()
-
- # Crear un conjunto de conceptos clave para búsqueda rápida
- concept_words = {concept[0].lower() for concept in key_concepts}
-
- # Crear conjunto de tokens que son parte de entidades
- entity_tokens = set()
- for ent in doc.ents:
- entity_tokens.update(token.i for token in ent)
-
- # Añadir nodos al grafo
- for concept, freq in key_concepts:
- G.add_node(concept.lower(), weight=freq)
-
- # Analizar cada oración
- for sent in doc.sents:
- # Obtener conceptos en la oración actual, excluyendo entidades
- current_concepts = []
- for token in sent:
- if (token.i not in entity_tokens and
- token.lemma_.lower() in concept_words):
- current_concepts.append(token.lemma_.lower())
-
- # Crear conexiones entre conceptos en la misma oración
- for i, concept1 in enumerate(current_concepts):
- for concept2 in current_concepts[i+1:]:
- if concept1 != concept2:
- if G.has_edge(concept1, concept2):
- G[concept1][concept2]['weight'] += 1
- else:
- G.add_edge(concept1, concept2, weight=1)
-
- return G
-
- except Exception as e:
- logger.error(f"Error en create_concept_graph: {str(e)}")
- return nx.Graph()
-
-###############################################################################
-
-def visualize_concept_graph(G, lang_code):
- """
- Visualiza el grafo de conceptos con layout consistente.
- Args:
- G: networkx.Graph - Grafo de conceptos
- lang_code: str - Código del idioma
- Returns:
- matplotlib.figure.Figure - Figura del grafo
- """
- try:
- # Crear nueva figura con mayor tamaño y definir los ejes explícitamente
- fig, ax = plt.subplots(figsize=(15, 10))
-
- if not G.nodes():
- logger.warning("Grafo vacío, retornando figura vacía")
- return fig
-
- # Convertir grafo no dirigido a dirigido para mostrar flechas
- DG = nx.DiGraph(G)
-
- # Calcular centralidad de los nodos para el color
- centrality = nx.degree_centrality(G)
-
- # Establecer semilla para reproducibilidad
- seed = 42
-
- # Calcular layout con parámetros fijos
- pos = nx.spring_layout(
- DG,
- k=2, # Distancia ideal entre nodos
- iterations=50, # Número de iteraciones
- seed=seed # Semilla fija para reproducibilidad
- )
-
- # Calcular factor de escala basado en número de nodos
- num_nodes = len(DG.nodes())
- scale_factor = 1000 if num_nodes < 10 else 500 if num_nodes < 20 else 200
-
- # Obtener pesos ajustados
- node_weights = [DG.nodes[node].get('weight', 1) * scale_factor for node in DG.nodes()]
- edge_weights = [DG[u][v].get('weight', 1) for u, v in DG.edges()]
-
- # Crear mapa de colores basado en centralidad
- node_colors = [plt.cm.viridis(centrality[node]) for node in DG.nodes()]
-
- # Dibujar nodos
- nodes = nx.draw_networkx_nodes(
- DG,
- pos,
- node_size=node_weights,
- node_color=node_colors,
- alpha=0.7,
- ax=ax
- )
-
- # Dibujar aristas con flechas
- edges = nx.draw_networkx_edges(
- DG,
- pos,
- width=edge_weights,
- alpha=0.6,
- edge_color='gray',
- arrows=True,
- arrowsize=20,
- arrowstyle='->',
- connectionstyle='arc3,rad=0.2',
- ax=ax
- )
-
- # Ajustar tamaño de fuente según número de nodos
- font_size = 12 if num_nodes < 10 else 10 if num_nodes < 20 else 8
-
- # Dibujar etiquetas con fondo blanco para mejor legibilidad
- labels = nx.draw_networkx_labels(
- DG,
- pos,
- font_size=font_size,
- font_weight='bold',
- bbox=dict(
- facecolor='white',
- edgecolor='none',
- alpha=0.7
- ),
- ax=ax
- )
-
- # Añadir leyenda de centralidad
- sm = plt.cm.ScalarMappable(
- cmap=plt.cm.viridis,
- norm=plt.Normalize(vmin=0, vmax=1)
- )
- sm.set_array([])
- plt.colorbar(sm, ax=ax, label='Centralidad del concepto')
-
- plt.title("Red de conceptos relacionados", pad=20, fontsize=14)
- ax.set_axis_off()
-
- # Ajustar el layout para que la barra de color no se superponga
- plt.tight_layout()
-
- return fig
-
- except Exception as e:
- logger.error(f"Error en visualize_concept_graph: {str(e)}")
- return plt.figure() # Retornar figura vacía en caso de error
-
-########################################################################
-def create_entity_graph(entities):
- G = nx.Graph()
- for entity_type, entity_list in entities.items():
- for entity in entity_list:
- G.add_node(entity, type=entity_type)
- for i, entity1 in enumerate(entity_list):
- for entity2 in entity_list[i+1:]:
- G.add_edge(entity1, entity2)
- return G
-
-
-#############################################################
-def visualize_entity_graph(G, lang_code):
- fig, ax = plt.subplots(figsize=(12, 8))
- pos = nx.spring_layout(G)
- for entity_type, color in ENTITY_LABELS[lang_code].items():
- node_list = [node for node, data in G.nodes(data=True) if data['type'] == entity_type]
- nx.draw_networkx_nodes(G, pos, nodelist=node_list, node_color=color, node_size=500, alpha=0.8, ax=ax)
- nx.draw_networkx_edges(G, pos, width=1, alpha=0.5, ax=ax)
- nx.draw_networkx_labels(G, pos, font_size=8, font_weight="bold", ax=ax)
- ax.set_title(f"Relaciones entre Entidades ({lang_code})", fontsize=16)
- ax.axis('off')
- plt.tight_layout()
- return fig
-
-
-#################################################################################
-def create_topic_graph(topics, doc):
- G = nx.Graph()
- for topic in topics:
- G.add_node(topic, weight=doc.text.count(topic))
- for i, topic1 in enumerate(topics):
- for topic2 in topics[i+1:]:
- weight = sum(1 for sent in doc.sents if topic1 in sent.text and topic2 in sent.text)
- if weight > 0:
- G.add_edge(topic1, topic2, weight=weight)
- return G
-
-def visualize_topic_graph(G, lang_code):
- fig, ax = plt.subplots(figsize=(12, 8))
- pos = nx.spring_layout(G)
- node_sizes = [G.nodes[node]['weight'] * 100 for node in G.nodes()]
- nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='lightgreen', alpha=0.8, ax=ax)
- nx.draw_networkx_labels(G, pos, font_size=10, font_weight="bold", ax=ax)
- edge_weights = [G[u][v]['weight'] for u, v in G.edges()]
- nx.draw_networkx_edges(G, pos, width=edge_weights, alpha=0.5, ax=ax)
- ax.set_title(f"Relaciones entre Temas ({lang_code})", fontsize=16)
- ax.axis('off')
- plt.tight_layout()
- return fig
-
-###########################################################################################
-def generate_summary(doc, lang_code):
- sentences = list(doc.sents)
- summary = sentences[:3] # Toma las primeras 3 oraciones como resumen
- return " ".join([sent.text for sent in summary])
-
-def extract_entities(doc, lang_code):
- entities = defaultdict(list)
- for ent in doc.ents:
- if ent.label_ in ENTITY_LABELS[lang_code]:
- entities[ent.label_].append(ent.text)
- return dict(entities)
-
-def analyze_sentiment(doc, lang_code):
- positive_words = sum(1 for token in doc if token.sentiment > 0)
- negative_words = sum(1 for token in doc if token.sentiment < 0)
- total_words = len(doc)
- if positive_words > negative_words:
- return "Positivo"
- elif negative_words > positive_words:
- return "Negativo"
- else:
- return "Neutral"
-
-def extract_topics(doc, lang_code):
- vectorizer = TfidfVectorizer(stop_words='english', max_features=5)
- tfidf_matrix = vectorizer.fit_transform([doc.text])
- feature_names = vectorizer.get_feature_names_out()
- return list(feature_names)
-
-# Asegúrate de que todas las funciones necesarias estén exportadas
-__all__ = [
- 'perform_semantic_analysis',
- 'identify_key_concepts',
- 'create_concept_graph',
- 'visualize_concept_graph',
- 'fig_to_bytes', # Faltaba esta coma
- 'ENTITY_LABELS',
- 'POS_COLORS',
- 'POS_TRANSLATIONS'
+# modules/text_analysis/semantic_analysis.py
+
+# 1. Importaciones estándar del sistema
+import logging
+import io
+import base64
+from collections import Counter, defaultdict
+
+# 2. Importaciones de terceros
+import streamlit as st
+import spacy
+import networkx as nx
+import matplotlib.pyplot as plt
+from sklearn.feature_extraction.text import TfidfVectorizer
+from sklearn.metrics.pairwise import cosine_similarity
+
+# Solo configurar si no hay handlers ya configurados
+logger = logging.getLogger(__name__)
+
+# 4. Importaciones locales
+from .stopwords import (
+ process_text,
+ clean_text,
+ get_custom_stopwords,
+ get_stopwords_for_spacy
+)
+
+
+# Define colors for grammatical categories
+POS_COLORS = {
+ 'ADJ': '#FFA07A', 'ADP': '#98FB98', 'ADV': '#87CEFA', 'AUX': '#DDA0DD',
+ 'CCONJ': '#F0E68C', 'DET': '#FFB6C1', 'INTJ': '#FF6347', 'NOUN': '#90EE90',
+ 'NUM': '#FAFAD2', 'PART': '#D3D3D3', 'PRON': '#FFA500', 'PROPN': '#20B2AA',
+ 'SCONJ': '#DEB887', 'SYM': '#7B68EE', 'VERB': '#FF69B4', 'X': '#A9A9A9',
+}
+
+POS_TRANSLATIONS = {
+ 'es': {
+ 'ADJ': 'Adjetivo', 'ADP': 'Preposición', 'ADV': 'Adverbio', 'AUX': 'Auxiliar',
+ 'CCONJ': 'Conjunción Coordinante', 'DET': 'Determinante', 'INTJ': 'Interjección',
+ 'NOUN': 'Sustantivo', 'NUM': 'Número', 'PART': 'Partícula', 'PRON': 'Pronombre',
+ 'PROPN': 'Nombre Propio', 'SCONJ': 'Conjunción Subordinante', 'SYM': 'Símbolo',
+ 'VERB': 'Verbo', 'X': 'Otro',
+ },
+ 'en': {
+ 'ADJ': 'Adjective', 'ADP': 'Preposition', 'ADV': 'Adverb', 'AUX': 'Auxiliary',
+ 'CCONJ': 'Coordinating Conjunction', 'DET': 'Determiner', 'INTJ': 'Interjection',
+ 'NOUN': 'Noun', 'NUM': 'Number', 'PART': 'Particle', 'PRON': 'Pronoun',
+ 'PROPN': 'Proper Noun', 'SCONJ': 'Subordinating Conjunction', 'SYM': 'Symbol',
+ 'VERB': 'Verb', 'X': 'Other',
+ },
+ 'fr': {
+ 'ADJ': 'Adjectif', 'ADP': 'Préposition', 'ADV': 'Adverbe', 'AUX': 'Auxiliaire',
+ 'CCONJ': 'Conjonction de Coordination', 'DET': 'Déterminant', 'INTJ': 'Interjection',
+ 'NOUN': 'Nom', 'NUM': 'Nombre', 'PART': 'Particule', 'PRON': 'Pronom',
+ 'PROPN': 'Nom Propre', 'SCONJ': 'Conjonction de Subordination', 'SYM': 'Symbole',
+ 'VERB': 'Verbe', 'X': 'Autre',
+ }
+}
+
+ENTITY_LABELS = {
+ 'es': {
+ "Personas": "lightblue",
+ "Lugares": "lightcoral",
+ "Inventos": "lightgreen",
+ "Fechas": "lightyellow",
+ "Conceptos": "lightpink"
+ },
+ 'en': {
+ "People": "lightblue",
+ "Places": "lightcoral",
+ "Inventions": "lightgreen",
+ "Dates": "lightyellow",
+ "Concepts": "lightpink"
+ },
+ 'fr': {
+ "Personnes": "lightblue",
+ "Lieux": "lightcoral",
+ "Inventions": "lightgreen",
+ "Dates": "lightyellow",
+ "Concepts": "lightpink"
+ }
+}
+
+def fig_to_bytes(fig):
+ """Convierte una figura de matplotlib a bytes."""
+ try:
+ buf = io.BytesIO()
+ fig.savefig(buf, format='png', dpi=300, bbox_inches='tight')
+ buf.seek(0)
+ return buf.getvalue()
+ except Exception as e:
+ logger.error(f"Error en fig_to_bytes: {str(e)}")
+ return None
+
+###########################################################
+def perform_semantic_analysis(text, nlp, lang_code):
+ """
+ Realiza el análisis semántico completo del texto.
+ """
+ if not text or not nlp or not lang_code:
+ logger.error("Parámetros inválidos para el análisis semántico")
+ return {
+ 'success': False,
+ 'error': 'Parámetros inválidos'
+ }
+
+ try:
+ logger.info(f"Starting semantic analysis for language: {lang_code}")
+
+ # Procesar texto y remover stopwords
+ doc = nlp(text)
+ if not doc:
+ logger.error("Error al procesar el texto con spaCy")
+ return {
+ 'success': False,
+ 'error': 'Error al procesar el texto'
+ }
+
+ # Identificar conceptos clave
+ logger.info("Identificando conceptos clave...")
+ stopwords = get_custom_stopwords(lang_code)
+ key_concepts = identify_key_concepts(doc, stopwords=stopwords)
+
+ if not key_concepts:
+ logger.warning("No se identificaron conceptos clave")
+ return {
+ 'success': False,
+ 'error': 'No se pudieron identificar conceptos clave'
+ }
+
+ # Crear grafo de conceptos
+ logger.info(f"Creando grafo de conceptos con {len(key_concepts)} conceptos...")
+ concept_graph = create_concept_graph(doc, key_concepts)
+
+ if not concept_graph.nodes():
+ logger.warning("Se creó un grafo vacío")
+ return {
+ 'success': False,
+ 'error': 'No se pudo crear el grafo de conceptos'
+ }
+
+ # Visualizar grafo
+ logger.info("Visualizando grafo...")
+ plt.clf() # Limpiar figura actual
+ concept_graph_fig = visualize_concept_graph(concept_graph, lang_code)
+
+ # Convertir a bytes
+ logger.info("Convirtiendo grafo a bytes...")
+ graph_bytes = fig_to_bytes(concept_graph_fig)
+
+ if not graph_bytes:
+ logger.error("Error al convertir grafo a bytes")
+ return {
+ 'success': False,
+ 'error': 'Error al generar visualización'
+ }
+
+ # Limpiar recursos
+ plt.close(concept_graph_fig)
+ plt.close('all')
+
+ result = {
+ 'success': True,
+ 'key_concepts': key_concepts,
+ 'concept_graph': graph_bytes
+ }
+
+ logger.info("Análisis semántico completado exitosamente")
+ return result
+
+ except Exception as e:
+ logger.error(f"Error in perform_semantic_analysis: {str(e)}")
+ plt.close('all') # Asegurarse de limpiar recursos
+ return {
+ 'success': False,
+ 'error': str(e)
+ }
+ finally:
+ plt.close('all') # Asegurar limpieza incluso si hay error
+
+############################################################
+
+def identify_key_concepts(doc, stopwords, min_freq=2, min_length=3):
+ """
+ Identifica conceptos clave en el texto, excluyendo entidades nombradas.
+ Args:
+ doc: Documento procesado por spaCy
+ stopwords: Lista de stopwords
+ min_freq: Frecuencia mínima para considerar un concepto
+ min_length: Longitud mínima del concepto
+ Returns:
+ List[Tuple[str, int]]: Lista de tuplas (concepto, frecuencia)
+ """
+ try:
+ word_freq = Counter()
+
+ # Crear conjunto de tokens que son parte de entidades
+ entity_tokens = set()
+ for ent in doc.ents:
+ entity_tokens.update(token.i for token in ent)
+
+ # Procesar tokens
+ for token in doc:
+ # Verificar si el token no es parte de una entidad nombrada
+ if (token.i not in entity_tokens and # No es parte de una entidad
+ token.lemma_.lower() not in stopwords and # No es stopword
+ len(token.lemma_) >= min_length and # Longitud mínima
+ token.is_alpha and # Es alfabético
+ not token.is_punct and # No es puntuación
+ not token.like_num and # No es número
+ not token.is_space and # No es espacio
+ not token.is_stop and # No es stopword de spaCy
+ not token.pos_ == 'PROPN' and # No es nombre propio
+ not token.pos_ == 'SYM' and # No es símbolo
+ not token.pos_ == 'NUM' and # No es número
+ not token.pos_ == 'X'): # No es otro
+
+ # Convertir a minúsculas y añadir al contador
+ word_freq[token.lemma_.lower()] += 1
+
+ # Filtrar conceptos por frecuencia mínima y ordenar por frecuencia
+ concepts = [(word, freq) for word, freq in word_freq.items()
+ if freq >= min_freq]
+ concepts.sort(key=lambda x: x[1], reverse=True)
+
+ logger.info(f"Identified {len(concepts)} key concepts after excluding entities")
+ return concepts[:10]
+
+ except Exception as e:
+ logger.error(f"Error en identify_key_concepts: {str(e)}")
+ return []
+
+########################################################################
+
+def create_concept_graph(doc, key_concepts):
+ """
+ Crea un grafo de relaciones entre conceptos, ignorando entidades.
+ Args:
+ doc: Documento procesado por spaCy
+ key_concepts: Lista de tuplas (concepto, frecuencia)
+ Returns:
+ nx.Graph: Grafo de conceptos
+ """
+ try:
+ G = nx.Graph()
+
+ # Crear un conjunto de conceptos clave para búsqueda rápida
+ concept_words = {concept[0].lower() for concept in key_concepts}
+
+ # Crear conjunto de tokens que son parte de entidades
+ entity_tokens = set()
+ for ent in doc.ents:
+ entity_tokens.update(token.i for token in ent)
+
+ # Añadir nodos al grafo
+ for concept, freq in key_concepts:
+ G.add_node(concept.lower(), weight=freq)
+
+ # Analizar cada oración
+ for sent in doc.sents:
+ # Obtener conceptos en la oración actual, excluyendo entidades
+ current_concepts = []
+ for token in sent:
+ if (token.i not in entity_tokens and
+ token.lemma_.lower() in concept_words):
+ current_concepts.append(token.lemma_.lower())
+
+ # Crear conexiones entre conceptos en la misma oración
+ for i, concept1 in enumerate(current_concepts):
+ for concept2 in current_concepts[i+1:]:
+ if concept1 != concept2:
+ if G.has_edge(concept1, concept2):
+ G[concept1][concept2]['weight'] += 1
+ else:
+ G.add_edge(concept1, concept2, weight=1)
+
+ return G
+
+ except Exception as e:
+ logger.error(f"Error en create_concept_graph: {str(e)}")
+ return nx.Graph()
+
+###############################################################################
+
+def visualize_concept_graph(G, lang_code):
+ """
+ Visualiza el grafo de conceptos con layout consistente.
+ Args:
+ G: networkx.Graph - Grafo de conceptos
+ lang_code: str - Código del idioma
+ Returns:
+ matplotlib.figure.Figure - Figura del grafo
+ """
+ try:
+ # Crear nueva figura con mayor tamaño y definir los ejes explícitamente
+ fig, ax = plt.subplots(figsize=(15, 10))
+
+ if not G.nodes():
+ logger.warning("Grafo vacío, retornando figura vacía")
+ return fig
+
+ # Convertir grafo no dirigido a dirigido para mostrar flechas
+ DG = nx.DiGraph(G)
+
+ # Calcular centralidad de los nodos para el color
+ centrality = nx.degree_centrality(G)
+
+ # Establecer semilla para reproducibilidad
+ seed = 42
+
+ # Calcular layout con parámetros fijos
+ pos = nx.spring_layout(
+ DG,
+ k=2, # Distancia ideal entre nodos
+ iterations=50, # Número de iteraciones
+ seed=seed # Semilla fija para reproducibilidad
+ )
+
+ # Calcular factor de escala basado en número de nodos
+ num_nodes = len(DG.nodes())
+ scale_factor = 1000 if num_nodes < 10 else 500 if num_nodes < 20 else 200
+
+ # Obtener pesos ajustados
+ node_weights = [DG.nodes[node].get('weight', 1) * scale_factor for node in DG.nodes()]
+ edge_weights = [DG[u][v].get('weight', 1) for u, v in DG.edges()]
+
+ # Crear mapa de colores basado en centralidad
+ node_colors = [plt.cm.viridis(centrality[node]) for node in DG.nodes()]
+
+ # Dibujar nodos
+ nodes = nx.draw_networkx_nodes(
+ DG,
+ pos,
+ node_size=node_weights,
+ node_color=node_colors,
+ alpha=0.7,
+ ax=ax
+ )
+
+ # Dibujar aristas con flechas
+ edges = nx.draw_networkx_edges(
+ DG,
+ pos,
+ width=edge_weights,
+ alpha=0.6,
+ edge_color='gray',
+ arrows=True,
+ arrowsize=20,
+ arrowstyle='->',
+ connectionstyle='arc3,rad=0.2',
+ ax=ax
+ )
+
+ # Ajustar tamaño de fuente según número de nodos
+ font_size = 12 if num_nodes < 10 else 10 if num_nodes < 20 else 8
+
+ # Dibujar etiquetas con fondo blanco para mejor legibilidad
+ labels = nx.draw_networkx_labels(
+ DG,
+ pos,
+ font_size=font_size,
+ font_weight='bold',
+ bbox=dict(
+ facecolor='white',
+ edgecolor='none',
+ alpha=0.7
+ ),
+ ax=ax
+ )
+
+ # Añadir leyenda de centralidad
+ sm = plt.cm.ScalarMappable(
+ cmap=plt.cm.viridis,
+ norm=plt.Normalize(vmin=0, vmax=1)
+ )
+ sm.set_array([])
+ plt.colorbar(sm, ax=ax, label='Centralidad del concepto')
+
+ plt.title("Red de conceptos relacionados", pad=20, fontsize=14)
+ ax.set_axis_off()
+
+ # Ajustar el layout para que la barra de color no se superponga
+ plt.tight_layout()
+
+ return fig
+
+ except Exception as e:
+ logger.error(f"Error en visualize_concept_graph: {str(e)}")
+ return plt.figure() # Retornar figura vacía en caso de error
+
+########################################################################
+def create_entity_graph(entities):
+ G = nx.Graph()
+ for entity_type, entity_list in entities.items():
+ for entity in entity_list:
+ G.add_node(entity, type=entity_type)
+ for i, entity1 in enumerate(entity_list):
+ for entity2 in entity_list[i+1:]:
+ G.add_edge(entity1, entity2)
+ return G
+
+
+#############################################################
+def visualize_entity_graph(G, lang_code):
+ fig, ax = plt.subplots(figsize=(12, 8))
+ pos = nx.spring_layout(G)
+ for entity_type, color in ENTITY_LABELS[lang_code].items():
+ node_list = [node for node, data in G.nodes(data=True) if data['type'] == entity_type]
+ nx.draw_networkx_nodes(G, pos, nodelist=node_list, node_color=color, node_size=500, alpha=0.8, ax=ax)
+ nx.draw_networkx_edges(G, pos, width=1, alpha=0.5, ax=ax)
+ nx.draw_networkx_labels(G, pos, font_size=8, font_weight="bold", ax=ax)
+ ax.set_title(f"Relaciones entre Entidades ({lang_code})", fontsize=16)
+ ax.axis('off')
+ plt.tight_layout()
+ return fig
+
+
+#################################################################################
+def create_topic_graph(topics, doc):
+ G = nx.Graph()
+ for topic in topics:
+ G.add_node(topic, weight=doc.text.count(topic))
+ for i, topic1 in enumerate(topics):
+ for topic2 in topics[i+1:]:
+ weight = sum(1 for sent in doc.sents if topic1 in sent.text and topic2 in sent.text)
+ if weight > 0:
+ G.add_edge(topic1, topic2, weight=weight)
+ return G
+
+def visualize_topic_graph(G, lang_code):
+ fig, ax = plt.subplots(figsize=(12, 8))
+ pos = nx.spring_layout(G)
+ node_sizes = [G.nodes[node]['weight'] * 100 for node in G.nodes()]
+ nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='lightgreen', alpha=0.8, ax=ax)
+ nx.draw_networkx_labels(G, pos, font_size=10, font_weight="bold", ax=ax)
+ edge_weights = [G[u][v]['weight'] for u, v in G.edges()]
+ nx.draw_networkx_edges(G, pos, width=edge_weights, alpha=0.5, ax=ax)
+ ax.set_title(f"Relaciones entre Temas ({lang_code})", fontsize=16)
+ ax.axis('off')
+ plt.tight_layout()
+ return fig
+
+###########################################################################################
+def generate_summary(doc, lang_code):
+ sentences = list(doc.sents)
+ summary = sentences[:3] # Toma las primeras 3 oraciones como resumen
+ return " ".join([sent.text for sent in summary])
+
+def extract_entities(doc, lang_code):
+ entities = defaultdict(list)
+ for ent in doc.ents:
+ if ent.label_ in ENTITY_LABELS[lang_code]:
+ entities[ent.label_].append(ent.text)
+ return dict(entities)
+
+def analyze_sentiment(doc, lang_code):
+ positive_words = sum(1 for token in doc if token.sentiment > 0)
+ negative_words = sum(1 for token in doc if token.sentiment < 0)
+ total_words = len(doc)
+ if positive_words > negative_words:
+ return "Positivo"
+ elif negative_words > positive_words:
+ return "Negativo"
+ else:
+ return "Neutral"
+
+def extract_topics(doc, lang_code):
+ vectorizer = TfidfVectorizer(stop_words='english', max_features=5)
+ tfidf_matrix = vectorizer.fit_transform([doc.text])
+ feature_names = vectorizer.get_feature_names_out()
+ return list(feature_names)
+
+# Asegúrate de que todas las funciones necesarias estén exportadas
+__all__ = [
+ 'perform_semantic_analysis',
+ 'identify_key_concepts',
+ 'create_concept_graph',
+ 'visualize_concept_graph',
+ 'fig_to_bytes', # Faltaba esta coma
+ 'ENTITY_LABELS',
+ 'POS_COLORS',
+ 'POS_TRANSLATIONS'
]
\ No newline at end of file
diff --git a/modules/text_analysis/stopwords.py b/modules/text_analysis/stopwords.py
index 38feeeb41ba539c0b226c8e4e86462408a2fec4c..844d14f3a10108599907f31e65e63d0189e744d0 100644
--- a/modules/text_analysis/stopwords.py
+++ b/modules/text_analysis/stopwords.py
@@ -1,188 +1,188 @@
-# modules/text_analysis/stopwords.py
-import spacy
-from typing import Set, List
-
-def get_custom_stopwords(lang_code: str) -> Set[str]:
- """
- Retorna un conjunto de stopwords personalizadas según el idioma.
- """
- # Stopwords base en español
- # Símbolos, números y caracteres especiales
-
- SYMBOLS_AND_NUMBERS = {
- # Números
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
-
- # Signos de puntuación básicos
- '.', ',', ';', ':', '!', '¡', '?', '¿', '"', "'",
-
- # Símbolos matemáticos
- '+', '-', '*', '/', '=', '<', '>', '%',
-
- # Paréntesis y otros delimitadores
- '(', ')', '[', ']', '{', '}',
-
- # Otros símbolos comunes
- '@', '#', '$', '€', '£', '¥', '&', '_', '|', '\\', '/',
-
- # Caracteres especiales
- '•', '·', '…', '—', '–', '°', '´', '`', '^', '¨',
-
- # Símbolos de ordenamiento
- '§', '†', '‡', '¶',
-
- # Símbolos de copyright y marcas registradas
- '©', '®', '™',
-
- # Fracciones comunes
- '½', '¼', '¾', '⅓', '⅔',
-
- # Otros caracteres especiales
- '±', '×', '÷', '∞', '≠', '≤', '≥', '≈', '∑', '∏', '√',
-
- # Espacios y caracteres de control
- ' ', '\t', '\n', '\r', '\f', '\v'
-}
- spanish_stopwords = {
- 'el', 'la', 'los', 'las', 'un', 'una', 'unos', 'unas', 'y', 'o', 'pero', 'si',
- 'de', 'del', 'al', 'a', 'ante', 'bajo', 'cabe', 'con', 'contra', 'de', 'desde',
- 'en', 'entre', 'hacia', 'hasta', 'para', 'por', 'según', 'sin', 'sobre', 'tras',
- 'que', 'más', 'este', 'esta', 'estos', 'estas', 'ese', 'esa', 'esos', 'esas',
- 'muy', 'mucho', 'muchos', 'muchas', 'ser', 'estar', 'tener', 'hacer', 'como',
- 'cuando', 'donde', 'quien', 'cual', 'mientras', 'sino', 'pues', 'porque',
- 'cada', 'cual', 'cuales', 'cuanta', 'cuantas', 'cuanto', 'cuantos', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve', 'diez',
- 'once', 'doce', 'trece', 'catorce', 'quince', 'dieciséis', 'diecisiete', 'dieciocho', 'diecinueve', 'veinte',
- 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa', 'cien', 'mil', 'millón',
- 'primero', 'segundo', 'tercero', 'cuarto', 'quinto', 'sexto', 'séptimo', 'octavo', 'noveno', 'décimo'
- }
-
- # Stopwords base en inglés
- english_stopwords = {
- 'the', 'be', 'to', 'of', 'and', 'a', 'in', 'that', 'have', 'i', 'it', 'for',
- 'not', 'on', 'with', 'he', 'as', 'you', 'do', 'at', 'this', 'but', 'his',
- 'by', 'from', 'they', 'we', 'say', 'her', 'she', 'or', 'an', 'will', 'my',
- 'one', 'all', 'would', 'there', 'their', 'what', 'so', 'up', 'out', 'if',
- 'about', 'who', 'get', 'which', 'go', 'me', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten',
- 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty',
- 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety', 'hundred', 'thousand', 'million',
- 'first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth'
- }
-
- french_stopwords = {
- 'le', 'la', 'les', 'un', 'une', 'des', 'du', 'de', 'et', 'ou', 'mais', 'si',
- 'à', 'dans', 'sur', 'pour', 'en', 'vers', 'par', 'avec', 'sans', 'sous', 'sur',
- 'que', 'qui', 'quoi', 'dont', 'où', 'quand', 'comment', 'pourquoi',
- 'ce', 'cet', 'cette', 'ces', 'mon', 'ton', 'son', 'ma', 'ta', 'sa',
- 'mes', 'tes', 'ses', 'notre', 'votre', 'leur', 'nos', 'vos', 'leurs',
- 'je', 'tu', 'il', 'elle', 'nous', 'vous', 'ils', 'elles',
- 'me', 'te', 'se', 'lui', 'leur', 'y', 'en', 'plus', 'moins',
- 'très', 'trop', 'peu', 'beaucoup', 'assez', 'tout', 'toute', 'tous', 'toutes',
- 'autre', 'autres', 'même', 'mêmes', 'tel', 'telle', 'tels', 'telles',
- 'quel', 'quelle', 'quels', 'quelles', 'quelque', 'quelques',
- 'aucun', 'aucune', 'aucuns', 'aucunes', 'plusieurs', 'chaque',
- 'être', 'avoir', 'faire', 'dire', 'aller', 'venir', 'voir', 'savoir',
- 'pouvoir', 'vouloir', 'falloir', 'devoir', 'croire', 'sembler',
- 'alors', 'ainsi', 'car', 'donc', 'or', 'ni', 'ne', 'pas', 'plus',
- 'jamais', 'toujours', 'parfois', 'souvent', 'maintenant', 'après',
- 'avant', 'pendant', 'depuis', 'déjà', 'encore', 'ici', 'là',
- 'oui', 'non', 'peut-être', 'bien', 'mal', 'aussi', 'surtout',
- 'c\'est', 'j\'ai', 'n\'est', 'd\'un', 'd\'une', 'qu\'il', 'qu\'elle',
- 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix',
- 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf', 'vingt',
- 'trente', 'quarante', 'cinquante', 'soixante', 'soixante-dix', 'quatre-vingts', 'quatre-vingt-dix', 'cent', 'mille', 'million',
- 'premier', 'deuxième', 'troisième', 'quatrième', 'cinquième', 'sixième', 'septième', 'huitième', 'neuvième', 'dixième'
-}
-
- stopwords_dict = {
- 'es': spanish_stopwords,
- 'en': english_stopwords,
- 'fr': french_stopwords
- }
-
- # Obtener stopwords del idioma especificado o devolver conjunto vacío si no existe
- return stopwords_dict.get(lang_code, set())
-
-def process_text(text: str, lang_code: str, nlp) -> List[str]:
- """
- Procesa un texto completo, removiendo stopwords, símbolos y números.
-
- Args:
- text (str): Texto a procesar
- lang_code (str): Código del idioma ('es', 'en', 'fr')
- nlp: Modelo de spaCy cargado
-
- Returns:
- List[str]: Lista de tokens procesados
- """
- try:
- # Obtener stopwords personalizadas
- custom_stopwords = get_custom_stopwords(lang_code)
-
- # Procesar el texto con spaCy
- doc = nlp(text)
-
- # Filtrar y procesar tokens
- processed_tokens = []
- for token in doc:
- # Convertir a minúsculas y obtener el lema
- lemma = token.lemma_.lower()
-
- # Aplicar filtros
- if (len(lemma) >= 2 and # Longitud mínima
- lemma not in custom_stopwords and # No es stopword
- not token.is_punct and # No es puntuación
- not token.is_space and # No es espacio
- lemma not in SYMBOLS_AND_NUMBERS and # No es símbolo o número
- not any(char in string.punctuation for char in lemma) and # No contiene puntuación
- not any(char.isdigit() for char in lemma)): # No contiene números
-
- processed_tokens.append(lemma)
-
- return processed_tokens
-
- except Exception as e:
- logger.error(f"Error en process_text: {str(e)}")
- return []
-
-def clean_text(text: str) -> str:
- """
- Limpia un texto removiendo caracteres especiales y normalizando espacios.
-
- Args:
- text (str): Texto a limpiar
-
- Returns:
- str: Texto limpio
- """
- # Remover caracteres especiales y números
- cleaned = ''.join(char for char in text if char not in SYMBOLS_AND_NUMBERS)
-
- # Normalizar espacios
- cleaned = ' '.join(cleaned.split())
-
- return cleaned.strip()
-
-def get_stopwords_for_spacy(lang_code: str, nlp) -> Set[str]:
- """
- Combina stopwords personalizadas con las de spaCy.
-
- Args:
- lang_code (str): Código del idioma
- nlp: Modelo de spaCy
-
- Returns:
- Set[str]: Conjunto combinado de stopwords
- """
- custom_stops = get_custom_stopwords(lang_code)
- spacy_stops = nlp.Defaults.stop_words if hasattr(nlp.Defaults, 'stop_words') else set()
-
- return custom_stops.union(spacy_stops)
-
-# Asegúrate de exportar todas las funciones necesarias
-__all__ = [
- 'get_custom_stopwords',
- 'process_text',
- 'clean_text',
- 'get_stopwords_for_spacy',
- 'SYMBOLS_AND_NUMBERS'
+# modules/text_analysis/stopwords.py
+import spacy
+from typing import Set, List
+
+def get_custom_stopwords(lang_code: str) -> Set[str]:
+ """
+ Retorna un conjunto de stopwords personalizadas según el idioma.
+ """
+ # Stopwords base en español
+ # Símbolos, números y caracteres especiales
+
+ SYMBOLS_AND_NUMBERS = {
+ # Números
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+
+ # Signos de puntuación básicos
+ '.', ',', ';', ':', '!', '¡', '?', '¿', '"', "'",
+
+ # Símbolos matemáticos
+ '+', '-', '*', '/', '=', '<', '>', '%',
+
+ # Paréntesis y otros delimitadores
+ '(', ')', '[', ']', '{', '}',
+
+ # Otros símbolos comunes
+ '@', '#', '$', '€', '£', '¥', '&', '_', '|', '\\', '/',
+
+ # Caracteres especiales
+ '•', '·', '…', '—', '–', '°', '´', '`', '^', '¨',
+
+ # Símbolos de ordenamiento
+ '§', '†', '‡', '¶',
+
+ # Símbolos de copyright y marcas registradas
+ '©', '®', '™',
+
+ # Fracciones comunes
+ '½', '¼', '¾', '⅓', '⅔',
+
+ # Otros caracteres especiales
+ '±', '×', '÷', '∞', '≠', '≤', '≥', '≈', '∑', '∏', '√',
+
+ # Espacios y caracteres de control
+ ' ', '\t', '\n', '\r', '\f', '\v'
+}
+ spanish_stopwords = {
+ 'el', 'la', 'los', 'las', 'un', 'una', 'unos', 'unas', 'y', 'o', 'pero', 'si',
+ 'de', 'del', 'al', 'a', 'ante', 'bajo', 'cabe', 'con', 'contra', 'de', 'desde',
+ 'en', 'entre', 'hacia', 'hasta', 'para', 'por', 'según', 'sin', 'sobre', 'tras',
+ 'que', 'más', 'este', 'esta', 'estos', 'estas', 'ese', 'esa', 'esos', 'esas',
+ 'muy', 'mucho', 'muchos', 'muchas', 'ser', 'estar', 'tener', 'hacer', 'como',
+ 'cuando', 'donde', 'quien', 'cual', 'mientras', 'sino', 'pues', 'porque',
+ 'cada', 'cual', 'cuales', 'cuanta', 'cuantas', 'cuanto', 'cuantos', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve', 'diez',
+ 'once', 'doce', 'trece', 'catorce', 'quince', 'dieciséis', 'diecisiete', 'dieciocho', 'diecinueve', 'veinte',
+ 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa', 'cien', 'mil', 'millón',
+ 'primero', 'segundo', 'tercero', 'cuarto', 'quinto', 'sexto', 'séptimo', 'octavo', 'noveno', 'décimo'
+ }
+
+ # Stopwords base en inglés
+ english_stopwords = {
+ 'the', 'be', 'to', 'of', 'and', 'a', 'in', 'that', 'have', 'i', 'it', 'for',
+ 'not', 'on', 'with', 'he', 'as', 'you', 'do', 'at', 'this', 'but', 'his',
+ 'by', 'from', 'they', 'we', 'say', 'her', 'she', 'or', 'an', 'will', 'my',
+ 'one', 'all', 'would', 'there', 'their', 'what', 'so', 'up', 'out', 'if',
+ 'about', 'who', 'get', 'which', 'go', 'me', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten',
+ 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty',
+ 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety', 'hundred', 'thousand', 'million',
+ 'first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth'
+ }
+
+ french_stopwords = {
+ 'le', 'la', 'les', 'un', 'une', 'des', 'du', 'de', 'et', 'ou', 'mais', 'si',
+ 'à', 'dans', 'sur', 'pour', 'en', 'vers', 'par', 'avec', 'sans', 'sous', 'sur',
+ 'que', 'qui', 'quoi', 'dont', 'où', 'quand', 'comment', 'pourquoi',
+ 'ce', 'cet', 'cette', 'ces', 'mon', 'ton', 'son', 'ma', 'ta', 'sa',
+ 'mes', 'tes', 'ses', 'notre', 'votre', 'leur', 'nos', 'vos', 'leurs',
+ 'je', 'tu', 'il', 'elle', 'nous', 'vous', 'ils', 'elles',
+ 'me', 'te', 'se', 'lui', 'leur', 'y', 'en', 'plus', 'moins',
+ 'très', 'trop', 'peu', 'beaucoup', 'assez', 'tout', 'toute', 'tous', 'toutes',
+ 'autre', 'autres', 'même', 'mêmes', 'tel', 'telle', 'tels', 'telles',
+ 'quel', 'quelle', 'quels', 'quelles', 'quelque', 'quelques',
+ 'aucun', 'aucune', 'aucuns', 'aucunes', 'plusieurs', 'chaque',
+ 'être', 'avoir', 'faire', 'dire', 'aller', 'venir', 'voir', 'savoir',
+ 'pouvoir', 'vouloir', 'falloir', 'devoir', 'croire', 'sembler',
+ 'alors', 'ainsi', 'car', 'donc', 'or', 'ni', 'ne', 'pas', 'plus',
+ 'jamais', 'toujours', 'parfois', 'souvent', 'maintenant', 'après',
+ 'avant', 'pendant', 'depuis', 'déjà', 'encore', 'ici', 'là',
+ 'oui', 'non', 'peut-être', 'bien', 'mal', 'aussi', 'surtout',
+ 'c\'est', 'j\'ai', 'n\'est', 'd\'un', 'd\'une', 'qu\'il', 'qu\'elle',
+ 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix',
+ 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf', 'vingt',
+ 'trente', 'quarante', 'cinquante', 'soixante', 'soixante-dix', 'quatre-vingts', 'quatre-vingt-dix', 'cent', 'mille', 'million',
+ 'premier', 'deuxième', 'troisième', 'quatrième', 'cinquième', 'sixième', 'septième', 'huitième', 'neuvième', 'dixième'
+}
+
+ stopwords_dict = {
+ 'es': spanish_stopwords,
+ 'en': english_stopwords,
+ 'fr': french_stopwords
+ }
+
+ # Obtener stopwords del idioma especificado o devolver conjunto vacío si no existe
+ return stopwords_dict.get(lang_code, set())
+
+def process_text(text: str, lang_code: str, nlp) -> List[str]:
+ """
+ Procesa un texto completo, removiendo stopwords, símbolos y números.
+
+ Args:
+ text (str): Texto a procesar
+ lang_code (str): Código del idioma ('es', 'en', 'fr')
+ nlp: Modelo de spaCy cargado
+
+ Returns:
+ List[str]: Lista de tokens procesados
+ """
+ try:
+ # Obtener stopwords personalizadas
+ custom_stopwords = get_custom_stopwords(lang_code)
+
+ # Procesar el texto con spaCy
+ doc = nlp(text)
+
+ # Filtrar y procesar tokens
+ processed_tokens = []
+ for token in doc:
+ # Convertir a minúsculas y obtener el lema
+ lemma = token.lemma_.lower()
+
+ # Aplicar filtros
+ if (len(lemma) >= 2 and # Longitud mínima
+ lemma not in custom_stopwords and # No es stopword
+ not token.is_punct and # No es puntuación
+ not token.is_space and # No es espacio
+ lemma not in SYMBOLS_AND_NUMBERS and # No es símbolo o número
+ not any(char in string.punctuation for char in lemma) and # No contiene puntuación
+ not any(char.isdigit() for char in lemma)): # No contiene números
+
+ processed_tokens.append(lemma)
+
+ return processed_tokens
+
+ except Exception as e:
+ logger.error(f"Error en process_text: {str(e)}")
+ return []
+
+def clean_text(text: str) -> str:
+ """
+ Limpia un texto removiendo caracteres especiales y normalizando espacios.
+
+ Args:
+ text (str): Texto a limpiar
+
+ Returns:
+ str: Texto limpio
+ """
+ # Remover caracteres especiales y números
+ cleaned = ''.join(char for char in text if char not in SYMBOLS_AND_NUMBERS)
+
+ # Normalizar espacios
+ cleaned = ' '.join(cleaned.split())
+
+ return cleaned.strip()
+
+def get_stopwords_for_spacy(lang_code: str, nlp) -> Set[str]:
+ """
+ Combina stopwords personalizadas con las de spaCy.
+
+ Args:
+ lang_code (str): Código del idioma
+ nlp: Modelo de spaCy
+
+ Returns:
+ Set[str]: Conjunto combinado de stopwords
+ """
+ custom_stops = get_custom_stopwords(lang_code)
+ spacy_stops = nlp.Defaults.stop_words if hasattr(nlp.Defaults, 'stop_words') else set()
+
+ return custom_stops.union(spacy_stops)
+
+# Asegúrate de exportar todas las funciones necesarias
+__all__ = [
+ 'get_custom_stopwords',
+ 'process_text',
+ 'clean_text',
+ 'get_stopwords_for_spacy',
+ 'SYMBOLS_AND_NUMBERS'
]
\ No newline at end of file
diff --git a/modules/ui/user_page.py b/modules/ui/user_page.py
index ff44223fd5a21bf4a42e90fa3c51984ca14a9a48..db72111a1908b9aecef03bbcea96bd8b71df8b65 100644
--- a/modules/ui/user_page.py
+++ b/modules/ui/user_page.py
@@ -1,349 +1,349 @@
-import streamlit as st
-import logging
-from datetime import datetime, timezone
-from dateutil.parser import parse
-
-# Configuración del logger
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
-#Importaciones locales.
-
-from ..utils.widget_utils import generate_unique_key
-from session_state import initialize_session_state, logout
-
-from translations import get_translations
-
-from ..auth.auth import authenticate_user, authenticate_student, authenticate_admin
-
-from ..admin.admin_ui import admin_page
-
-from ..chatbot import display_sidebar_chat
-
-# Students activities
-from ..studentact.student_activities_v2 import display_student_activities
-from ..studentact.current_situation_interface import display_current_situation_interface
-from ..studentact.current_situation_analysis import analyze_text_dimensions
-
-
-##Importaciones desde la configuración de bases datos #######
-
-from ..database.sql_db import (
- get_user,
- get_admin_user,
- get_student_user,
- get_teacher_user,
- create_user,
- create_student_user,
- create_teacher_user,
- create_admin_user,
- update_student_user, # Agregada
- delete_student_user, # Agregada
- record_login,
- record_logout,
- get_recent_sessions,
- get_user_total_time,
- store_application_request,
- store_student_feedback
-)
-
-from ..database.mongo_db import (
- get_collection,
- insert_document,
- find_documents,
- update_document,
- delete_document
-)
-
-from ..database.morphosintax_mongo_db import (
- store_student_morphosyntax_result,
- get_student_morphosyntax_analysis,
- update_student_morphosyntax_analysis,
- delete_student_morphosyntax_analysis,
- get_student_morphosyntax_data
-)
-
-from ..database.chat_mongo_db import store_chat_history, get_chat_history
-
-##Importaciones desde los análisis #######
-from ..morphosyntax.morphosyntax_interface import (
- display_morphosyntax_interface,
- display_arc_diagram
-)
-
-from ..semantic.semantic_interface import (
- display_semantic_interface,
- display_semantic_results
-)
-
-from ..semantic.semantic_live_interface import display_semantic_live_interface
-
-from ..discourse.discourse_live_interface import display_discourse_live_interface
-
-from ..discourse.discourse_interface import ( # Agregar esta importación
- display_discourse_interface,
- display_discourse_results
-)
-
-
-
-####################################################################################
-def user_page(lang_code, t):
- logger.info(f"Entrando en user_page para el estudiante: {st.session_state.username}")
-
- # Inicializar el tab seleccionado si no existe
- if 'selected_tab' not in st.session_state:
- st.session_state.selected_tab = 0
-
- # Inicializar el estado del análisis en vivo
- if 'semantic_live_active' not in st.session_state:
- st.session_state.semantic_live_active = False
-
- # Manejar la carga inicial de datos del usuario
- if 'user_data' not in st.session_state:
- with st.spinner(t.get('loading_data', "Cargando tus datos...")):
- try:
- st.session_state.user_data = get_student_morphosyntax_data(st.session_state.username)
- st.session_state.last_data_fetch = datetime.now(timezone.utc).isoformat()
- except Exception as e:
- logger.error(f"Error al obtener datos del usuario: {str(e)}")
- st.error(t.get('data_load_error', "Hubo un problema al cargar tus datos. Por favor, intenta recargar la página."))
- return
-
- logger.info(f"Idioma actual: {st.session_state.lang_code}")
- logger.info(f"Modelos NLP cargados: {'nlp_models' in st.session_state}")
-
- # Configuración de idiomas disponibles
- languages = {'Español': 'es', 'English': 'en', 'Français': 'fr', 'Português': 'pt'}
-
- # Estilos CSS personalizados
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- # Barra superior con información del usuario y controles
- with st.container():
- col1, col2, col3 = st.columns([2, 2, 1])
- with col1:
- st.markdown(f"
{t['welcome']}, {st.session_state.username}
",
- unsafe_allow_html=True)
- with col2:
- selected_lang = st.selectbox(
- t['select_language'],
- list(languages.keys()),
- index=list(languages.values()).index(st.session_state.lang_code),
- key=f"language_selector_{st.session_state.username}_{st.session_state.lang_code}"
- )
- new_lang_code = languages[selected_lang]
- if st.session_state.lang_code != new_lang_code:
- st.session_state.lang_code = new_lang_code
- st.rerun()
- with col3:
- if st.button(t['logout'],
- key=f"logout_button_{st.session_state.username}_{st.session_state.lang_code}"):
- st.session_state.clear()
- st.rerun()
-
- st.markdown("---")
-
- # Asegurarse de que tenemos las traducciones del chatbot
- chatbot_t = t.get('CHATBOT_TRANSLATIONS', {}).get(lang_code, {})
-
- # Mostrar chatbot en sidebar
- display_sidebar_chat(lang_code, chatbot_t)
-
- # Inicializar estados para todos los tabs
- if 'tab_states' not in st.session_state:
- st.session_state.tab_states = {
- 'current_situation_active': False,
- 'morpho_active': False,
- #'semantic_live_active': False,
- 'semantic_active': False,
- #'discourse_live_active': False,
- 'discourse_active': False,
- 'activities_active': False,
- 'feedback_active': False
- }
-
- # Sistema de tabs
- tab_names = [
- t.get('current_situation_tab', "Mi Situación Actual"),
- t.get('morpho_tab', 'Análisis Morfosintáctico'),
- #t.get('semantic_live_tab', 'Análisis Semántico Vivo'),
- t.get('semantic_tab', 'Análisis Semántico'),
- #t.get('discourse_live_tab', 'Análisis de Discurso Vivo'),
- t.get('discourse_tab', 'Análisis comparado de textos'),
- t.get('activities_tab', 'Registro de mis actividades'),
- t.get('feedback_tab', 'Formulario de Comentarios')
- ]
-
- tabs = st.tabs(tab_names)
-
- # Manejar el contenido de cada tab
- for index, tab in enumerate(tabs):
- with tab:
- try:
- # Actualizar el tab seleccionado solo si no hay un análisis activo
- if tab.selected and st.session_state.selected_tab != index:
- can_switch = True
- for state_key in st.session_state.tab_states.keys():
- if st.session_state.tab_states[state_key] and index != get_tab_index(state_key):
- can_switch = False
- break
- if can_switch:
- st.session_state.selected_tab = index
-
- if index == 0: # Situación actual
- st.session_state.tab_states['current_situation_active'] = True
- display_current_situation_interface(
- st.session_state.lang_code,
- st.session_state.nlp_models,
- t # Pasamos todo el diccionario de traducciones
- )
-
- elif index == 1: # Morfosintáctico
- st.session_state.tab_states['morpho_active'] = True
- display_morphosyntax_interface(
- st.session_state.lang_code,
- st.session_state.nlp_models,
- t # Pasamos todo el diccionario de traducciones
- )
-
- #elif index == 2: # Semántico Vivo
- # st.session_state.tab_states['semantic_live_active'] = True
- # display_semantic_live_interface(
- # st.session_state.lang_code,
- # st.session_state.nlp_models,
- # t # Pasamos todo el diccionario de traducciones
- # )
-
- elif index == 2: # Semántico
- st.session_state.tab_states['semantic_active'] = True
- display_semantic_interface(
- st.session_state.lang_code,
- st.session_state.nlp_models,
- t # Pasamos todo el diccionario de traducciones
- )
-
- #elif index == 4: # Discurso Vivo
- # st.session_state.tab_states['discourse_live_active'] = True
- # display_discourse_live_interface(
- # st.session_state.lang_code,
- # st.session_state.nlp_models,
- # t # Pasamos todo el diccionario de traducciones
- # )
-
- elif index == 3: # Discurso
- st.session_state.tab_states['discourse_active'] = True
- display_discourse_interface(
- st.session_state.lang_code,
- st.session_state.nlp_models,
- t # Pasamos todo el diccionario de traducciones
- )
-
- elif index == 4: # Actividades
- st.session_state.tab_states['activities_active'] = True
- display_student_activities(
- username=st.session_state.username,
- lang_code=st.session_state.lang_code,
- t=t # Pasamos todo el diccionario de traducciones
- )
-
- elif index == 5: # Feedback
- st.session_state.tab_states['feedback_active'] = True
- display_feedback_form(
- st.session_state.lang_code,
- t # Ya estaba recibiendo el diccionario completo
- )
-
- except Exception as e:
- # Desactivar el estado en caso de error
- state_key = get_state_key_for_index(index)
- if state_key:
- st.session_state.tab_states[state_key] = False
- logger.error(f"Error en tab {index}: {str(e)}")
- st.error(t.get('tab_error', 'Error al cargar esta sección'))
-
- # Panel de depuración (solo visible en desarrollo)
- if st.session_state.get('debug_mode', False):
- with st.expander("Debug Info"):
- st.write(f"Página actual: {st.session_state.page}")
- st.write(f"Usuario: {st.session_state.get('username', 'No logueado')}")
- st.write(f"Rol: {st.session_state.get('role', 'No definido')}")
- st.write(f"Idioma: {st.session_state.lang_code}")
- st.write(f"Tab seleccionado: {st.session_state.selected_tab}")
- st.write(f"Última actualización de datos: {st.session_state.get('last_data_fetch', 'Nunca')}")
- st.write(f"Traducciones disponibles: {list(t.keys())}")
-
-
-def get_tab_index(state_key):
- """Obtiene el índice del tab basado en la clave de estado"""
- index_map = {
- 'current_situation_active': 0,
- 'morpho_active': 1,
- #'semantic_live_active': 2,
- 'semantic_active': 2,
- #'discourse_live_active': 4,
- 'discourse_active': 3,
- 'activities_active': 4,
- 'feedback_active': 5
- }
- return index_map.get(state_key, -1)
-
-def get_state_key_for_index(index):
- """Obtiene la clave de estado basada en el índice del tab"""
- state_map = {
- 0: 'current_situation_active',
- 1: 'morpho_active',
- #2: 'semantic_live_active',
- 2: 'semantic_active',
- #4: 'discourse_live_active',
- 3: 'discourse_active',
- 4: 'activities_active',
- 5: 'feedback_active'
- }
- return state_map.get(index)
-
-def display_feedback_form(lang_code, t):
- """
- Muestra el formulario de retroalimentación
- Args:
- lang_code: Código de idioma
- t: Diccionario de traducciones
- """
- logging.info(f"display_feedback_form called with lang_code: {lang_code}")
-
- # Obtener traducciones específicas para el formulario de feedback
- feedback_t = t.get('FEEDBACK', {})
-
- # Si no hay traducciones específicas, usar el diccionario general
- if not feedback_t:
- feedback_t = t
-
- # st.header(feedback_t.get('feedback_title', 'Formulario de Opinión'))
-
- name = st.text_input(feedback_t.get('name', 'Nombre'))
- email = st.text_input(feedback_t.get('email', 'Correo electrónico'))
- feedback = st.text_area(feedback_t.get('feedback', 'Retroalimentación'))
-
- if st.button(feedback_t.get('submit', 'Enviar')):
- if name and email and feedback:
- if store_student_feedback(st.session_state.username, name, email, feedback):
- st.success(feedback_t.get('feedback_success', 'Gracias por tu respuesta'))
- else:
- st.error(feedback_t.get('feedback_error', 'Hubo un problema al enviar el formulario. Por favor, intenta de nuevo.'))
- else:
+import streamlit as st
+import logging
+from datetime import datetime, timezone
+from dateutil.parser import parse
+
+# Configuración del logger
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+#Importaciones locales.
+
+from ..utils.widget_utils import generate_unique_key
+from session_state import initialize_session_state, logout
+
+from translations import get_translations
+
+from ..auth.auth import authenticate_user, authenticate_student, authenticate_admin
+
+from ..admin.admin_ui import admin_page
+
+from ..chatbot import display_sidebar_chat
+
+# Students activities
+from ..studentact.student_activities_v2 import display_student_activities
+from ..studentact.current_situation_interface import display_current_situation_interface
+from ..studentact.current_situation_analysis import analyze_text_dimensions
+
+
+##Importaciones desde la configuración de bases datos #######
+
+from ..database.sql_db import (
+ get_user,
+ get_admin_user,
+ get_student_user,
+ get_teacher_user,
+ create_user,
+ create_student_user,
+ create_teacher_user,
+ create_admin_user,
+ update_student_user, # Agregada
+ delete_student_user, # Agregada
+ record_login,
+ record_logout,
+ get_recent_sessions,
+ get_user_total_time,
+ store_application_request,
+ store_student_feedback
+)
+
+from ..database.mongo_db import (
+ get_collection,
+ insert_document,
+ find_documents,
+ update_document,
+ delete_document
+)
+
+from ..database.morphosintax_mongo_db import (
+ store_student_morphosyntax_result,
+ get_student_morphosyntax_analysis,
+ update_student_morphosyntax_analysis,
+ delete_student_morphosyntax_analysis,
+ get_student_morphosyntax_data
+)
+
+from ..database.chat_mongo_db import store_chat_history, get_chat_history
+
+##Importaciones desde los análisis #######
+from ..morphosyntax.morphosyntax_interface import (
+ display_morphosyntax_interface,
+ display_arc_diagram
+)
+
+from ..semantic.semantic_interface import (
+ display_semantic_interface,
+ display_semantic_results
+)
+
+from ..semantic.semantic_live_interface import display_semantic_live_interface
+
+from ..discourse.discourse_live_interface import display_discourse_live_interface
+
+from ..discourse.discourse_interface import ( # Agregar esta importación
+ display_discourse_interface,
+ display_discourse_results
+)
+
+
+
+####################################################################################
+def user_page(lang_code, t):
+ logger.info(f"Entrando en user_page para el estudiante: {st.session_state.username}")
+
+ # Inicializar el tab seleccionado si no existe
+ if 'selected_tab' not in st.session_state:
+ st.session_state.selected_tab = 0
+
+ # Inicializar el estado del análisis en vivo
+ if 'semantic_live_active' not in st.session_state:
+ st.session_state.semantic_live_active = False
+
+ # Manejar la carga inicial de datos del usuario
+ if 'user_data' not in st.session_state:
+ with st.spinner(t.get('loading_data', "Cargando tus datos...")):
+ try:
+ st.session_state.user_data = get_student_morphosyntax_data(st.session_state.username)
+ st.session_state.last_data_fetch = datetime.now(timezone.utc).isoformat()
+ except Exception as e:
+ logger.error(f"Error al obtener datos del usuario: {str(e)}")
+ st.error(t.get('data_load_error', "Hubo un problema al cargar tus datos. Por favor, intenta recargar la página."))
+ return
+
+ logger.info(f"Idioma actual: {st.session_state.lang_code}")
+ logger.info(f"Modelos NLP cargados: {'nlp_models' in st.session_state}")
+
+ # Configuración de idiomas disponibles
+ languages = {'Español': 'es', 'English': 'en', 'Français': 'fr', 'Português': 'pt'}
+
+ # Estilos CSS personalizados
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # Barra superior con información del usuario y controles
+ with st.container():
+ col1, col2, col3 = st.columns([2, 2, 1])
+ with col1:
+ st.markdown(f"
{t['welcome']}, {st.session_state.username}
",
+ unsafe_allow_html=True)
+ with col2:
+ selected_lang = st.selectbox(
+ t['select_language'],
+ list(languages.keys()),
+ index=list(languages.values()).index(st.session_state.lang_code),
+ key=f"language_selector_{st.session_state.username}_{st.session_state.lang_code}"
+ )
+ new_lang_code = languages[selected_lang]
+ if st.session_state.lang_code != new_lang_code:
+ st.session_state.lang_code = new_lang_code
+ st.rerun()
+ with col3:
+ if st.button(t['logout'],
+ key=f"logout_button_{st.session_state.username}_{st.session_state.lang_code}"):
+ st.session_state.clear()
+ st.rerun()
+
+ st.markdown("---")
+
+ # Asegurarse de que tenemos las traducciones del chatbot
+ chatbot_t = t.get('CHATBOT_TRANSLATIONS', {}).get(lang_code, {})
+
+ # Mostrar chatbot en sidebar
+ display_sidebar_chat(lang_code, chatbot_t)
+
+ # Inicializar estados para todos los tabs
+ if 'tab_states' not in st.session_state:
+ st.session_state.tab_states = {
+ 'current_situation_active': False,
+ 'morpho_active': False,
+ #'semantic_live_active': False,
+ 'semantic_active': False,
+ #'discourse_live_active': False,
+ 'discourse_active': False,
+ 'activities_active': False,
+ 'feedback_active': False
+ }
+
+ # Sistema de tabs
+ tab_names = [
+ t.get('current_situation_tab', "Mi Situación Actual"),
+ t.get('morpho_tab', 'Análisis Morfosintáctico'),
+ #t.get('semantic_live_tab', 'Análisis Semántico Vivo'),
+ t.get('semantic_tab', 'Análisis Semántico'),
+ #t.get('discourse_live_tab', 'Análisis de Discurso Vivo'),
+ t.get('discourse_tab', 'Análisis comparado de textos'),
+ t.get('activities_tab', 'Registro de mis actividades'),
+ t.get('feedback_tab', 'Formulario de Comentarios')
+ ]
+
+ tabs = st.tabs(tab_names)
+
+ # Manejar el contenido de cada tab
+ for index, tab in enumerate(tabs):
+ with tab:
+ try:
+ # Actualizar el tab seleccionado solo si no hay un análisis activo
+ if tab.selected and st.session_state.selected_tab != index:
+ can_switch = True
+ for state_key in st.session_state.tab_states.keys():
+ if st.session_state.tab_states[state_key] and index != get_tab_index(state_key):
+ can_switch = False
+ break
+ if can_switch:
+ st.session_state.selected_tab = index
+
+ if index == 0: # Situación actual
+ st.session_state.tab_states['current_situation_active'] = True
+ display_current_situation_interface(
+ st.session_state.lang_code,
+ st.session_state.nlp_models,
+ t # Pasamos todo el diccionario de traducciones
+ )
+
+ elif index == 1: # Morfosintáctico
+ st.session_state.tab_states['morpho_active'] = True
+ display_morphosyntax_interface(
+ st.session_state.lang_code,
+ st.session_state.nlp_models,
+ t # Pasamos todo el diccionario de traducciones
+ )
+
+ #elif index == 2: # Semántico Vivo
+ # st.session_state.tab_states['semantic_live_active'] = True
+ # display_semantic_live_interface(
+ # st.session_state.lang_code,
+ # st.session_state.nlp_models,
+ # t # Pasamos todo el diccionario de traducciones
+ # )
+
+ elif index == 2: # Semántico
+ st.session_state.tab_states['semantic_active'] = True
+ display_semantic_interface(
+ st.session_state.lang_code,
+ st.session_state.nlp_models,
+ t # Pasamos todo el diccionario de traducciones
+ )
+
+ #elif index == 4: # Discurso Vivo
+ # st.session_state.tab_states['discourse_live_active'] = True
+ # display_discourse_live_interface(
+ # st.session_state.lang_code,
+ # st.session_state.nlp_models,
+ # t # Pasamos todo el diccionario de traducciones
+ # )
+
+ elif index == 3: # Discurso
+ st.session_state.tab_states['discourse_active'] = True
+ display_discourse_interface(
+ st.session_state.lang_code,
+ st.session_state.nlp_models,
+ t # Pasamos todo el diccionario de traducciones
+ )
+
+ elif index == 4: # Actividades
+ st.session_state.tab_states['activities_active'] = True
+ display_student_activities(
+ username=st.session_state.username,
+ lang_code=st.session_state.lang_code,
+ t=t # Pasamos todo el diccionario de traducciones
+ )
+
+ elif index == 5: # Feedback
+ st.session_state.tab_states['feedback_active'] = True
+ display_feedback_form(
+ st.session_state.lang_code,
+ t # Ya estaba recibiendo el diccionario completo
+ )
+
+ except Exception as e:
+ # Desactivar el estado en caso de error
+ state_key = get_state_key_for_index(index)
+ if state_key:
+ st.session_state.tab_states[state_key] = False
+ logger.error(f"Error en tab {index}: {str(e)}")
+ st.error(t.get('tab_error', 'Error al cargar esta sección'))
+
+ # Panel de depuración (solo visible en desarrollo)
+ if st.session_state.get('debug_mode', False):
+ with st.expander("Debug Info"):
+ st.write(f"Página actual: {st.session_state.page}")
+ st.write(f"Usuario: {st.session_state.get('username', 'No logueado')}")
+ st.write(f"Rol: {st.session_state.get('role', 'No definido')}")
+ st.write(f"Idioma: {st.session_state.lang_code}")
+ st.write(f"Tab seleccionado: {st.session_state.selected_tab}")
+ st.write(f"Última actualización de datos: {st.session_state.get('last_data_fetch', 'Nunca')}")
+ st.write(f"Traducciones disponibles: {list(t.keys())}")
+
+
+def get_tab_index(state_key):
+ """Obtiene el índice del tab basado en la clave de estado"""
+ index_map = {
+ 'current_situation_active': 0,
+ 'morpho_active': 1,
+ #'semantic_live_active': 2,
+ 'semantic_active': 2,
+ #'discourse_live_active': 4,
+ 'discourse_active': 3,
+ 'activities_active': 4,
+ 'feedback_active': 5
+ }
+ return index_map.get(state_key, -1)
+
+def get_state_key_for_index(index):
+ """Obtiene la clave de estado basada en el índice del tab"""
+ state_map = {
+ 0: 'current_situation_active',
+ 1: 'morpho_active',
+ #2: 'semantic_live_active',
+ 2: 'semantic_active',
+ #4: 'discourse_live_active',
+ 3: 'discourse_active',
+ 4: 'activities_active',
+ 5: 'feedback_active'
+ }
+ return state_map.get(index)
+
+def display_feedback_form(lang_code, t):
+ """
+ Muestra el formulario de retroalimentación
+ Args:
+ lang_code: Código de idioma
+ t: Diccionario de traducciones
+ """
+ logging.info(f"display_feedback_form called with lang_code: {lang_code}")
+
+ # Obtener traducciones específicas para el formulario de feedback
+ feedback_t = t.get('FEEDBACK', {})
+
+ # Si no hay traducciones específicas, usar el diccionario general
+ if not feedback_t:
+ feedback_t = t
+
+ #st.header(feedback_t.get('feedback_title', 'Formulario de Opinión'))
+
+ name = st.text_input(feedback_t.get('name', 'Nombre'))
+ email = st.text_input(feedback_t.get('email', 'Correo electrónico'))
+ feedback = st.text_area(feedback_t.get('feedback', 'Retroalimentación'))
+
+ if st.button(feedback_t.get('submit', 'Enviar')):
+ if name and email and feedback:
+ if store_student_feedback(st.session_state.username, name, email, feedback):
+ st.success(feedback_t.get('feedback_success', 'Gracias por tu respuesta'))
+ else:
+ st.error(feedback_t.get('feedback_error', 'Hubo un problema al enviar el formulario. Por favor, intenta de nuevo.'))
+ else:
st.warning(feedback_t.get('complete_all_fields', 'Por favor, completa todos los campos'))
\ No newline at end of file
diff --git a/modules/utils/spacy_utils.py b/modules/utils/spacy_utils.py
index b4e11f03da74cba8dfb5be725650c17880b22704..c1a52ebea7e62eadbbc48c53ed3a6f20021cfa9d 100644
--- a/modules/utils/spacy_utils.py
+++ b/modules/utils/spacy_utils.py
@@ -1,10 +1,9 @@
-# modules/spacy_utils.py
-import spacy
-
-def load_spacy_models():
- return {
- 'es': spacy.load("es_core_news_lg"),
- 'en': spacy.load("en_core_web_lg"),
- 'fr': spacy.load("fr_core_news_lg"),
- 'pt': spacy.load("pt_core_news_lg")
+# modules/spacy_utils.py
+import spacy
+
+def load_spacy_models():
+ return {
+ 'es': spacy.load("es_core_news_lg"),
+ 'en': spacy.load("en_core_web_lg"),
+ 'fr': spacy.load("fr_core_news_lg")
}
\ No newline at end of file
diff --git a/modules/utils/widget_utils.py b/modules/utils/widget_utils.py
index c96b9f0c3a91f9f5edcca1781221d78adddbe464..03ba3c86b6eb596b7bdfb6c7e32a61319a21df96 100644
--- a/modules/utils/widget_utils.py
+++ b/modules/utils/widget_utils.py
@@ -1,7 +1,7 @@
-# modules/utils/widget_utils.py
-import streamlit as st
-
-def generate_unique_key(module_name, element_type="input", username=None):
- # Si el nombre de usuario no se pasa explícitamente, lo toma de session_state
- username = username or st.session_state.username
+# modules/utils/widget_utils.py
+import streamlit as st
+
+def generate_unique_key(module_name, element_type="input", username=None):
+ # Si el nombre de usuario no se pasa explícitamente, lo toma de session_state
+ username = username or st.session_state.username
return f"{module_name}_{element_type}_{username}"
\ No newline at end of file
diff --git a/pre-requirements.txt b/pre-requirements.txt
index 7a3d347cbca9e2cb11d8096a531564a6ad9ac685..4fee332e72e67d105c2e955f46ef9c081049d087 100644
--- a/pre-requirements.txt
+++ b/pre-requirements.txt
@@ -1,3 +1,3 @@
-#https://huggingface.co/spacy/es_core_news_lg/resolve/main/es_core_news_lg-any-py3-none-any.whl
-#https://huggingface.co/spacy/en_core_web_lg/resolve/main/en_core_web_lg-any-py3-none-any.whl
+#https://huggingface.co/spacy/es_core_news_lg/resolve/main/es_core_news_lg-any-py3-none-any.whl
+#https://huggingface.co/spacy/en_core_web_lg/resolve/main/en_core_web_lg-any-py3-none-any.whl
#https://huggingface.co/spacy/fr_core_news_lg/resolve/main/fr_core_news_lg-any-py3-none-any.whl
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 35de0645e98df9b29f36701133e4e8da3c301b58..099fd0bb5b874dc8f66f3fa9ff1df1842d96c884 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,59 +1,59 @@
-anthropic
-azure-identity
-azure-cosmos
-antiword
-bcrypt
-certifi
-cairosvg
-python-dotenv
-drawSvg
-docx2txt
-
-# Modelos de spaCy desde Hugging Face
-# https://huggingface.co/spacy/es_core_news_lg/resolve/main/es_core_news_lg-any-py3-none-any.whl
-# https://huggingface.co/spacy/en_core_web_lg/resolve/main/en_core_web_lg-any-py3-none-any.whl
-# https://huggingface.co/spacy/fr_core_news_lg/resolve/main/fr_core_news_lg-any-py3-none-any.whl
-
-# Enlaces alternativos desde GitHub (comentados)
-es-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/es_core_news_lg-3.8.0/es_core_news_lg-3.8.0-py3-none-any.whl
-en-core-web-lg @ https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl
-fr-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/fr_core_news_lg-3.8.0/fr_core_news_lg-3.8.0-py3-none-any.whl
-pt-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/pt_core_news_lg-3.8.0/pt_core_news_lg-3.8.0-py3-none-any.whl
-
-numpy
-networkx
-matplotlib
-odfpy
-plotly
-pydantic
-python-dateutil
-pandas
-python-docx
-#pywin32
-pymssql
-python-dotenv
-pymongo
-PyPDF2
-rlPyCairo
-requests
-reportlab
-#spacy>=3.7.0,<3.8.0
-#spacy=3.8.0
-#spacy
-spacy-streamlit
-seaborn
-squarify
-streamlit
-streamlit-float
-streamlit-player
-streamlit-chat
-streamlit-antd-components
-streamlit-option-menu
-scipy
-sentencepiece
-scikit-learn
-svglib
-transformers
-torch
-tqdm
+anthropic
+azure-identity
+azure-cosmos
+antiword
+bcrypt
+certifi
+cairosvg
+python-dotenv
+drawSvg
+docx2txt
+
+# Modelos de spaCy desde Hugging Face
+# https://huggingface.co/spacy/es_core_news_lg/resolve/main/es_core_news_lg-any-py3-none-any.whl
+# https://huggingface.co/spacy/en_core_web_lg/resolve/main/en_core_web_lg-any-py3-none-any.whl
+# https://huggingface.co/spacy/fr_core_news_lg/resolve/main/fr_core_news_lg-any-py3-none-any.whl
+
+# Enlaces alternativos desde GitHub (comentados)
+es-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/es_core_news_lg-3.8.0/es_core_news_lg-3.8.0-py3-none-any.whl
+en-core-web-lg @ https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl
+fr-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/fr_core_news_lg-3.8.0/fr_core_news_lg-3.8.0-py3-none-any.whl
+pt-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/pt_core_news_lg-3.8.0/pt_core_news_lg-3.8.0-py3-none-any.whl
+
+numpy
+networkx
+matplotlib
+odfpy
+plotly
+pydantic
+python-dateutil
+pandas
+python-docx
+#pywin32
+pymssql
+python-dotenv
+pymongo
+PyPDF2
+rlPyCairo
+requests
+reportlab
+#spacy>=3.7.0,<3.8.0
+#spacy=3.8.0
+#spacy
+spacy-streamlit
+seaborn
+squarify
+streamlit
+streamlit-float
+streamlit-player
+streamlit-chat
+streamlit-antd-components
+streamlit-option-menu
+scipy
+sentencepiece
+scikit-learn
+svglib
+transformers
+torch
+tqdm
thinc
\ No newline at end of file
diff --git a/translations/__init__.py b/translations/__init__.py
index 0662e0f6ee2136aa96ac707f0638ae274518c563..0d918fc82c934a0636a699164c59539c86cacc47 100644
--- a/translations/__init__.py
+++ b/translations/__init__.py
@@ -1,64 +1,64 @@
-# translations/__init__.py
-import logging
-from importlib import import_module
-
-logger = logging.getLogger(__name__)
-
-def get_translations(lang_code):
- # Asegurarse de que lang_code sea válido
- if lang_code not in ['es', 'en', 'fr', 'pt']:
- print(f"Invalid lang_code: {lang_code}. Defaulting to 'es'")
- lang_code = 'es'
-
- try:
- # Importar dinámicamente el módulo de traducción
- translation_module = import_module(f'.{lang_code}', package='translations')
- translations = getattr(translation_module, 'TRANSLATIONS', {})
- except ImportError:
- logger.warning(f"Translation module for {lang_code} not found. Falling back to English.")
- # Importar el módulo de inglés como fallback
- translation_module = import_module('.en', package='translations')
- translations = getattr(translation_module, 'TRANSLATIONS', {})
-
- def get_text(key, section='COMMON', default=''):
- return translations.get(section, {}).get(key, default)
-
- return {
- 'get_text': get_text,
- **translations.get('COMMON', {}),
- **translations.get('TABS', {}),
- **translations.get('MORPHOSYNTACTIC', {}),
- **translations.get('SEMANTIC', {}),
- **translations.get('DISCOURSE', {}),
- **translations.get('ACTIVITIES', {}),
- **translations.get('FEEDBACK', {}),
- **translations.get('TEXT_TYPES', {}),
- **translations.get('CURRENT_SITUATION', {}) # Añadir esta línea
- }
-
-# Nueva función para obtener traducciones específicas del landing page
-def get_landing_translations(lang_code):
- # Asegurarse de que lang_code sea válido
- if lang_code not in ['es', 'en', 'fr', 'pt']:
- print(f"Invalid lang_code: {lang_code}. Defaulting to 'es'")
- lang_code = 'es'
-
- try:
- # Importar dinámicamente el módulo de traducción del landing page
- from .landing_translations import LANDING_TRANSLATIONS
-
- # Asegurarse de que el idioma esté disponible, si no usar español como fallback
- if lang_code not in LANDING_TRANSLATIONS:
- logger.warning(f"Landing translations for {lang_code} not found. Falling back to Spanish.")
- lang_code = 'es'
-
- return LANDING_TRANSLATIONS[lang_code]
- except ImportError:
- logger.warning("Landing translations module not found. Using default translations.")
- # Crear un conjunto mínimo de traducciones por defecto
- return {
- 'select_language': 'Select language' if lang_code == 'en' else 'Selecciona tu idioma',
- 'login': 'Login' if lang_code == 'en' else 'Iniciar Sesión',
- 'register': 'Sign Up' if lang_code == 'en' else 'Registrarse',
- # Añadir más traducciones por defecto si es necesario
- }
\ No newline at end of file
+# translations/__init__.py
+import logging
+from importlib import import_module
+
+logger = logging.getLogger(__name__)
+
+def get_translations(lang_code):
+ # Asegurarse de que lang_code sea válido
+ if lang_code not in ['es', 'en', 'fr', 'pt']:
+ print(f"Invalid lang_code: {lang_code}. Defaulting to 'es'")
+ lang_code = 'es'
+
+ try:
+ # Importar dinámicamente el módulo de traducción
+ translation_module = import_module(f'.{lang_code}', package='translations')
+ translations = getattr(translation_module, 'TRANSLATIONS', {})
+ except ImportError:
+ logger.warning(f"Translation module for {lang_code} not found. Falling back to English.")
+ # Importar el módulo de inglés como fallback
+ translation_module = import_module('.en', package='translations')
+ translations = getattr(translation_module, 'TRANSLATIONS', {})
+
+ def get_text(key, section='COMMON', default=''):
+ return translations.get(section, {}).get(key, default)
+
+ return {
+ 'get_text': get_text,
+ **translations.get('COMMON', {}),
+ **translations.get('TABS', {}),
+ **translations.get('MORPHOSYNTACTIC', {}),
+ **translations.get('SEMANTIC', {}),
+ **translations.get('DISCOURSE', {}),
+ **translations.get('ACTIVITIES', {}),
+ **translations.get('FEEDBACK', {}),
+ **translations.get('TEXT_TYPES', {}),
+ **translations.get('CURRENT_SITUATION', {}) # Añadir esta línea
+ }
+
+# Nueva función para obtener traducciones específicas del landing page
+def get_landing_translations(lang_code):
+ # Asegurarse de que lang_code sea válido
+ if lang_code not in ['es', 'en', 'fr', 'pt']:
+ print(f"Invalid lang_code: {lang_code}. Defaulting to 'es'")
+ lang_code = 'es'
+
+ try:
+ # Importar dinámicamente el módulo de traducción del landing page
+ from .landing_translations import LANDING_TRANSLATIONS
+
+ # Asegurarse de que el idioma esté disponible, si no usar español como fallback
+ if lang_code not in LANDING_TRANSLATIONS:
+ logger.warning(f"Landing translations for {lang_code} not found. Falling back to Spanish.")
+ lang_code = 'es'
+
+ return LANDING_TRANSLATIONS[lang_code]
+ except ImportError:
+ logger.warning("Landing translations module not found. Using default translations.")
+ # Crear un conjunto mínimo de traducciones por defecto
+ return {
+ 'select_language': 'Select language' if lang_code == 'en' else 'Selecciona tu idioma',
+ 'login': 'Login' if lang_code == 'en' else 'Iniciar Sesión',
+ 'register': 'Sign Up' if lang_code == 'en' else 'Registrarse',
+ # Añadir más traducciones por defecto si es necesario
+ }
diff --git a/translations/en.py b/translations/en.py
index 40bddcf4b5d10a168f21859c49d2dbedc004797b..e5f152d6bc439411aeb97a4d5bf37578eac857f6 100644
--- a/translations/en.py
+++ b/translations/en.py
@@ -1,357 +1,357 @@
-# translations/en.py
-
-COMMON = {
- # A
- 'initial_instruction': "To start a new semantic analysis, upload a new text file (.txt)",
- 'analysis_complete': "Analysis complete and saved. To perform a new analysis, upload another file.",
- 'current_analysis_message': "Showing analysis of file: {}. To perform a new analysis, please upload another file.",
- 'upload_prompt': "Attach a file to start the analysis",
- 'analysis_completed': "Analysis completed",
- 'analysis_section': "Semantic Analysis",
- 'analyze_document': 'Analyze document',
- 'analysis_saved_success': 'Analysis saved successfully',
- 'analysis_save_error': 'Error saving the analysis',
- 'analyze_button': "Analyze text",
- 'analyzing_doc': "Analyzing document",
- 'activities_message':"Activities messages",
- 'activities_placeholder':"Activities placeholder",
- 'analysis_placeholder':"Analysis placeholder",
- 'analyze_button' : "Analyze",
- 'analysis_types_chart' : "Analyze type chart",
- 'analysis_from': "Analysis carried out on",
- # C
- 'chat_title': "Analysis Chat",
- 'export_button': "Export Current Analysis",
- 'export_success': "Analysis and chat exported successfully.",
- 'export_error': "There was a problem exporting the analysis and chat.",
- 'get_text': "Get text.",
- 'hello': "Hello",
- # L
- 'logout': "End session.",
- 'loading_data': "Loading data",
- 'load_selected_file': 'Load selected file',
- # N
- 'no_analysis': "No analysis available. Use the chat to perform an analysis.",
- 'nothing_to_export': "No analysis or chat to export.",
- 'results_title': "Analysis Results",
- 'select_language': "Language select",
- 'student_activities':"Student activities",
- # T
- 'total_analyses': "Total analyses",
- # W
- 'welcome': "Welcome to AIdeaText"
-}
-
-TABS = {
- 'current_situation_tab': "Current situation",
- 'morpho_tab': "Morphosyntactic analysis",
- 'semantic_live_tab': "Semantic live",
- 'semantic_tab': "Semantic analysis",
- 'discourse_live_tab': "Discourse live",
- 'discourse_tab': "Discourse analysis",
- 'activities_tab': "My activities",
- 'feedback_tab': "Feedback form"
-}
-
-CURRENT_SITUATION = {
- 'title': "My Current Situation",
- 'input_prompt': "Write or paste your text here:",
- 'first_analyze_button': "Analyze my writing",
- 'processing': "Analyzing...",
- 'analysis_error': "Error analyzing text",
- 'help': "We will analyze your text to determine its current status", # <-- Added this line
- # Radio buttons for text type
- 'text_type_header': "Text type",
- 'text_type_help': "Select the text type to adjust evaluation criteria",
- # Metrics
- 'vocabulary_label': "Vocabulary",
- 'vocabulary_help': "Richness and variety of vocabulary",
- 'structure_label': "Structure",
- 'structure_help': "Organization and complexity of sentences",
- 'cohesion_label': "Cohesion",
- 'cohesion_help': "Connection and fluidity between ideas",
- 'clarity_label': "Clarity",
- 'clarity_help': "Ease of text comprehension",
- # Metric states
- 'metric_improvement': "⚠️ Needs improvement",
- 'metric_acceptable': "📈 Acceptable",
- 'metric_optimal': "✅ Optimal",
- 'metric_target': "Goal: {:.2f}",
- # Errors
- 'error_interface': "An error occurred while loading the interface",
- 'error_results': "Error displaying results",
- 'error_chart': "Error displaying chart"
-}
-
-MORPHOSYNTACTIC = {
- #A
- 'arc_diagram': "Syntactic analysis: Arc diagram",
- #B
- 'tab_text_baseline': "Produce first text",
- 'tab_iterations': "Produce new versions of the first text",
-
- # Pestaña 1 texto base
- 'btn_new_morpho_analysis': "New morphosyntactic analysis",
- 'btn_analyze_baseline': "Analyze the entered text",
- 'input_baseline_text': "Enter the first text to analyze",
- 'warn_enter_text': "Please enter a text to analyze",
- 'error_processing_baseline': "Error processing the initial text",
- 'arc_diagram_baseline_label': "Arc diagram of the initial text",
- 'baseline_diagram_not_available': "Arc diagram of the initial text not available",
-
- # Pestaña 2 Iteración del texto
- 'info_first_analyze_base': "Check if the initial text exists",
- 'iteration_text_subheader': "New version of the initial text",
- 'input_iteration_text': "Enter a new version of the initial text and compare both texts' arcs",
- 'btn_analyze_iteration': "Analyze changes",
- 'warn_enter_iteration_text': "Enter a new version of the initial text and compare both texts' arcs",
- 'iteration_saved': "Changes saved successfully",
- 'error_iteration': "Error processing the new changes"
-}
-
-SEMANTIC = {
- # A
-
- # C
- 'chat_title': "Semantic Analysis Chat",
- 'chat_placeholder': "Ask a question or use a command (/summary, /entities, /sentiment, /topics, /concept_graph, /entity_graph, /topic_graph)",
- 'clear_chat': "Clear chat",
- 'conceptual_relations': "Conceptual Relations",
- # D
- 'delete_file': "Delete file",
- # E
- 'error_message': "There was a problem saving the semantic analysis. Please try again.",
- # F
- 'file_uploader': "Or upload a text file",
- 'file_upload_success': "File uploaded and saved successfully",
- 'file_upload_error': 'Error uploading file',
- 'file_section': "Files",
- 'file_loaded_success': "File loaded successfully",
- 'file_load_error': "Error loading file",
- 'file_upload_error': "Error uploading and saving file",
- 'file_deleted_success': 'File deleted successfully',
- 'file_delete_error': 'Error deleting file',
- # G
- 'graph_title': "Semantic Analysis Visualization",
- # I
- 'identified_entities': "Identified Entities",
- # K
- 'key_concepts': "Key Concepts",
- # N
- 'no_analysis': "No analysis available. Please upload or select a file.",
- 'no_results': "No results available. Please perform an analysis first.",
- 'no_file': "Please upload a file to start the analysis.",
- 'no_file_selected': "Please select an archive to start the analysis.",
- # S
- 'semantic_title': "Semantic Analysis",
- 'semantic_initial_message': "This is a general-purpose chatbot, but it has a specific function for visual text analysis: it generates a graph with the main entities of the text. To produce it, enter a text file in txt, pdf, doc, docx or odt format and press the 'analyze file' button. After generating the graph, you can interact with the chat based on the document.",
- 'send_button': "Send",
- 'select_saved_file': "Select saved file",
- 'success_message': "Semantic analysis saved successfully.",
- 'semantic_analyze_button': 'Semantic Analysis',
- 'semantic_export_button': 'Export Semantic Analysis',
- 'semantic_new_button': 'New Semantic Analysis',
- 'semantic_file_uploader': 'Upload a text file for semantic analysis',
- # T
- 'text_input_label': "Enter a text to analyze (max. 5,000 words):",
- 'text_input_placeholder': "The purpose of this application is to improve your writing skills...",
- 'title': "AIdeaText - Semantic Analysis",
- # U
- 'upload_file': "Upload file",
- # W
- 'warning_message': "Please enter a text or upload a file to analyze."
-}
-
-DISCOURSE = {
- 'file1_label': "Pattern Document",
- 'file2_label': "Compared Document",
- 'discourse_title': "AIdeaText - Discourse Analysis",
- 'file_uploader1': "Upload text file 1 (Pattern)",
- 'file_uploader2': "Upload text file 2 (Comparison)",
- 'discourse_analyze_button': "Compare texts",
- 'discourse_initial_message': "This is a general purpose chatbot, but it has a specific function for visual text analysis: it generates two graphs with the main entities of each file to make a comparison between both texts. To produce it, enter one file first and then another in txt, pdf, doc, docx or odt format and press the 'analyze file' button. After the graph is generated, you can interact with the chat based on the document.",
- 'analyze_button': "Analyze texts",
- 'comparison': "Comparison of Semantic Relations",
- 'success_message': "Discourse analysis saved successfully.",
- 'error_message': "There was a problem saving the discourse analysis. Please try again.",
- 'warning_message': "Please upload both files to analyze.",
- 'no_results': "No results available. Please perform an analysis first.",
- 'key_concepts': "Key Concepts",
- 'graph_not_available': "The graph is not available.",
- 'concepts_not_available': "Key concepts are not available.",
- 'comparison_not_available': "The comparison is not available.",
- 'warning_message': "Please enter a text or upload a file to analyze.",
- 'morphosyntax_history': "Morphosyntax history",
- 'analysis_of': "Analysis of"
-}
-
-ACTIVITIES = {
- # Nuevas etiquetas actualizadas
- 'current_situation_activities': "Records of function: My Current Situation",
- 'morpho_activities': "Records of my morphosyntactic analyses",
- 'semantic_activities': "Records of my semantic analyses",
- 'discourse_activities': "Records of my text comparison analyses",
- 'chat_activities': "Records of my conversations with the virtual tutor",
-
- # Mantener otras claves existentes
- 'current_situation_tab': "Current situation",
- 'morpho_tab': "Morphosyntactic analysis",
- 'semantic_tab': "Semantic analysis",
- 'discourse_tab': "Text comparison analysis",
- 'activities_tab': "My activities record",
- 'feedback_tab': "Feedback form",
-
- # Resto de las claves que estén en el diccionario ACTIVITIES
- 'analysis_types_chart_title': "Types of analyses performed",
- 'analysis_types_chart_x': "Analysis type",
- 'analysis_types_chart_y': "Count",
- 'analysis_from': "Analysis from",
- 'assistant': "Assistant",
- 'activities_summary': "Activities and Progress Summary",
- 'chat_history_expander': "Chat History",
- 'chat_from': "Chat from",
- 'combined_graph': "Combined Graph",
- 'conceptual_relations_graph': "Conceptual Relations Graph",
- 'conversation': "Conversation",
- 'discourse_analyses_expander': "Text Comparison Analyses History", # Actualizado
- 'discourse_analyses': "Text Comparison Analyses", # Actualizado
- 'discourse_history': "Text Comparison Analysis History", # Actualizado
- 'document': "Document",
- 'data_load_error': "Error loading student data",
- 'graph_display_error': "Could not display the graph",
- 'graph_doc1': "Graph document 1",
- 'graph_doc2': "Graph document 2",
- 'key_concepts': "Key concepts",
- 'loading_data': "Loading student data...",
- 'morphological_analysis': "Morphological Analysis",
- 'morphosyntax_analyses_expander': "Morphosyntactic Analyses History",
- 'morphosyntax_history': "Morphosyntactic Analysis History",
- 'no_arc_diagram': "No arc diagram found for this analysis.",
- 'no_chat_history': "No conversations with the Virtual Tutor were found.", # Actualizado
- 'no_data_warning': "No analysis data found for this student.",
- 'progress_of': "Progress of",
- 'semantic_analyses': "Semantic Analyses",
- 'semantic_analyses_expander': "Semantic Analyses History",
- 'semantic_history': "Semantic Analysis History",
- 'show_debug_data': "Show debug data",
- 'student_debug_data': "Student data (for debugging):",
- 'summary_title': "Activities Summary",
- 'title': "My Activities Record", # Actualizado
- 'timestamp': "Timestamp",
- 'total_analyses': "Total analyses performed:",
- 'try_analysis': "Try performing some text analyses first.",
- 'user': "User",
-
- # Nuevas traducciones específicas para la sección de actividades
- 'diagnosis_tab': "Diagnosis",
- 'recommendations_tab': "Recommendations",
- 'key_metrics': "Key metrics",
- 'details': "Details",
- 'analyzed_text': "Analyzed text",
- 'analysis_date': "Date",
- 'academic_article': "Academic article",
- 'student_essay': "Student essay",
- 'general_communication': "General communication",
- 'no_diagnosis': "No diagnosis data available",
- 'no_recommendations': "No recommendations available",
- 'error_current_situation': "Error displaying current situation analysis",
- 'no_current_situation': "No current situation analyses recorded",
- 'no_morpho_analyses': "No morphosyntactic analyses recorded",
- 'error_morpho': "Error displaying morphosyntactic analysis",
- 'no_semantic_analyses': "No semantic analyses recorded",
- 'error_semantic': "Error displaying semantic analysis",
- 'no_discourse_analyses': "No text comparison analyses recorded", # Actualizado
- 'error_discourse': "Error displaying text comparison analysis", # Actualizado
- 'no_chat_history': "No conversation records with the virtual tutor", # Actualizado
- 'error_chat': "Error displaying conversation records", # Actualizado
- 'error_loading_activities': "Error loading activities",
- 'chat_date': "Conversation date",
- 'invalid_chat_format': "Invalid chat format",
- 'comparison_results': "Comparison results",
- 'concepts_text_1': "Concepts Text 1",
- 'concepts_text_2': "Concepts Text 2",
- 'no_visualization': "No comparative visualization available",
- 'no_graph': "No visualization available",
- 'error_loading_graph': "Error loading graph",
- 'syntactic_diagrams': "Syntactic diagrams"
-}
-
-FEEDBACK = {
- 'email': "Email",
- 'feedback': "Feedback",
- 'feedback_title': "Feedback form",
- 'feedback_error': "There was a problem submitting the form. Please try again.",
- 'feedback_success': "Thank for your feedback",
- 'complete_all_fields': "Please, complete all fields",
- 'name': "Name",
- 'submit': "Submit"
-}
-
-
-CHATBOT_TRANSLATIONS = {
- 'chat_title': "AIdeaText Assistant",
- 'input_placeholder': "Any questions?",
- 'initial_message': "Hi! I'm your assistant. How can I help?",
- 'expand_chat': "Open assistant",
- 'clear_chat': "Clear chat",
- 'processing': "Processing...",
- 'error_message': "Sorry, an error occurred"
-}
-
-TEXT_TYPES = {
- 'descriptive': [
- 'What are you describing?',
- 'What are its main characteristics?',
- 'How does it look, sound, smell, or feel?',
- 'What makes it unique or special?'
- ],
- 'narrative': [
- 'Who is the protagonist?',
- 'Where and when does the story take place?',
- 'What event starts the action?',
- 'What happens next?',
- 'How does the story end?'
- ],
- 'expository': [
- 'What is the main topic?',
- 'What important aspects do you want to explain?',
- 'Can you provide examples or data to support your explanation?',
- 'How does this topic relate to other concepts?'
- ],
- 'argumentative': [
- 'What is your main thesis or argument?',
- 'What are your supporting arguments?',
- 'What evidence do you have to back up your arguments?',
- 'What are the counterarguments and how do you refute them?',
- 'What is your conclusion?'
- ],
- 'instructive': [
- 'What task or process are you explaining?',
- 'What materials or tools are needed?',
- 'What are the steps to follow?',
- 'Are there any important precautions or tips to mention?'
- ],
- 'pitch': [
- 'What?',
- 'What for?',
- 'For whom?',
- 'How?'
- ]
- }
-
-# Configuration of the language model for English
-NLP_MODEL = 'en_core_web_lg'
-
-# Esta línea es crucial:
-TRANSLATIONS = {
- 'COMMON': COMMON,
- 'TABS': TABS,
- 'MORPHOSYNTACTIC': MORPHOSYNTACTIC,
- 'SEMANTIC': SEMANTIC,
- 'DISCOURSE': DISCOURSE,
- 'ACTIVITIES': ACTIVITIES,
- 'FEEDBACK': FEEDBACK,
- 'TEXT_TYPES': TEXT_TYPES,
- 'CURRENT_SITUATION': CURRENT_SITUATION, # Añadir esta línea
- 'NLP_MODEL': NLP_MODEL
+# translations/en.py
+
+COMMON = {
+ # A
+ 'initial_instruction': "To start a new semantic analysis, upload a new text file (.txt)",
+ 'analysis_complete': "Analysis complete and saved. To perform a new analysis, upload another file.",
+ 'current_analysis_message': "Showing analysis of file: {}. To perform a new analysis, please upload another file.",
+ 'upload_prompt': "Attach a file to start the analysis",
+ 'analysis_completed': "Analysis completed",
+ 'analysis_section': "Semantic Analysis",
+ 'analyze_document': 'Analyze document',
+ 'analysis_saved_success': 'Analysis saved successfully',
+ 'analysis_save_error': 'Error saving the analysis',
+ 'analyze_button': "Analyze text",
+ 'analyzing_doc': "Analyzing document",
+ 'activities_message':"Activities messages",
+ 'activities_placeholder':"Activities placeholder",
+ 'analysis_placeholder':"Analysis placeholder",
+ 'analyze_button' : "Analyze",
+ 'analysis_types_chart' : "Analyze type chart",
+ 'analysis_from': "Analysis carried out on",
+ # C
+ 'chat_title': "Analysis Chat",
+ 'export_button': "Export Current Analysis",
+ 'export_success': "Analysis and chat exported successfully.",
+ 'export_error': "There was a problem exporting the analysis and chat.",
+ 'get_text': "Get text.",
+ 'hello': "Hello",
+ # L
+ 'logout': "End session.",
+ 'loading_data': "Loading data",
+ 'load_selected_file': 'Load selected file',
+ # N
+ 'no_analysis': "No analysis available. Use the chat to perform an analysis.",
+ 'nothing_to_export': "No analysis or chat to export.",
+ 'results_title': "Analysis Results",
+ 'select_language': "Language select",
+ 'student_activities':"Student activities",
+ # T
+ 'total_analyses': "Total analyses",
+ # W
+ 'welcome': "Welcome to AIdeaText"
+}
+
+TABS = {
+ 'current_situation_tab': "Current situation",
+ 'morpho_tab': "Morphosyntactic analysis",
+ 'semantic_live_tab': "Semantic live",
+ 'semantic_tab': "Semantic analysis",
+ 'discourse_live_tab': "Discourse live",
+ 'discourse_tab': "Discourse analysis",
+ 'activities_tab': "My activities",
+ 'feedback_tab': "Feedback form"
+}
+
+CURRENT_SITUATION = {
+ 'title': "My Current Situation",
+ 'input_prompt': "Write or paste your text here:",
+ 'first_analyze_button': "Analyze my writing",
+ 'processing': "Analyzing...",
+ 'analysis_error': "Error analyzing text",
+ 'help': "We will analyze your text to determine its current status", # <-- Added this line
+ # Radio buttons for text type
+ 'text_type_header': "Text type",
+ 'text_type_help': "Select the text type to adjust evaluation criteria",
+ # Metrics
+ 'vocabulary_label': "Vocabulary",
+ 'vocabulary_help': "Richness and variety of vocabulary",
+ 'structure_label': "Structure",
+ 'structure_help': "Organization and complexity of sentences",
+ 'cohesion_label': "Cohesion",
+ 'cohesion_help': "Connection and fluidity between ideas",
+ 'clarity_label': "Clarity",
+ 'clarity_help': "Ease of text comprehension",
+ # Metric states
+ 'metric_improvement': "⚠️ Needs improvement",
+ 'metric_acceptable': "📈 Acceptable",
+ 'metric_optimal': "✅ Optimal",
+ 'metric_target': "Goal: {:.2f}",
+ # Errors
+ 'error_interface': "An error occurred while loading the interface",
+ 'error_results': "Error displaying results",
+ 'error_chart': "Error displaying chart"
+}
+
+MORPHOSYNTACTIC = {
+ #A
+ 'arc_diagram': "Syntactic analysis: Arc diagram",
+ #B
+ 'tab_text_baseline': "Produce first text",
+ 'tab_iterations': "Produce new versions of the first text",
+
+ # Pestaña 1 texto base
+ 'btn_new_morpho_analysis': "New morphosyntactic analysis",
+ 'btn_analyze_baseline': "Analyze the entered text",
+ 'input_baseline_text': "Enter the first text to analyze",
+ 'warn_enter_text': "Please enter a text to analyze",
+ 'error_processing_baseline': "Error processing the initial text",
+ 'arc_diagram_baseline_label': "Arc diagram of the initial text",
+ 'baseline_diagram_not_available': "Arc diagram of the initial text not available",
+
+ # Pestaña 2 Iteración del texto
+ 'info_first_analyze_base': "Check if the initial text exists",
+ 'iteration_text_subheader': "New version of the initial text",
+ 'input_iteration_text': "Enter a new version of the initial text and compare both texts' arcs",
+ 'btn_analyze_iteration': "Analyze changes",
+ 'warn_enter_iteration_text': "Enter a new version of the initial text and compare both texts' arcs",
+ 'iteration_saved': "Changes saved successfully",
+ 'error_iteration': "Error processing the new changes"
+}
+
+SEMANTIC = {
+ # A
+
+ # C
+ 'chat_title': "Semantic Analysis Chat",
+ 'chat_placeholder': "Ask a question or use a command (/summary, /entities, /sentiment, /topics, /concept_graph, /entity_graph, /topic_graph)",
+ 'clear_chat': "Clear chat",
+ 'conceptual_relations': "Conceptual Relations",
+ # D
+ 'delete_file': "Delete file",
+ # E
+ 'error_message': "There was a problem saving the semantic analysis. Please try again.",
+ # F
+ 'file_uploader': "Or upload a text file",
+ 'file_upload_success': "File uploaded and saved successfully",
+ 'file_upload_error': 'Error uploading file',
+ 'file_section': "Files",
+ 'file_loaded_success': "File loaded successfully",
+ 'file_load_error': "Error loading file",
+ 'file_upload_error': "Error uploading and saving file",
+ 'file_deleted_success': 'File deleted successfully',
+ 'file_delete_error': 'Error deleting file',
+ # G
+ 'graph_title': "Semantic Analysis Visualization",
+ # I
+ 'identified_entities': "Identified Entities",
+ # K
+ 'key_concepts': "Key Concepts",
+ # N
+ 'no_analysis': "No analysis available. Please upload or select a file.",
+ 'no_results': "No results available. Please perform an analysis first.",
+ 'no_file': "Please upload a file to start the analysis.",
+ 'no_file_selected': "Please select an archive to start the analysis.",
+ # S
+ 'semantic_title': "Semantic Analysis",
+ 'semantic_initial_message': "This is a general-purpose chatbot, but it has a specific function for visual text analysis: it generates a graph with the main entities of the text. To produce it, enter a text file in txt, pdf, doc, docx or odt format and press the 'analyze file' button. After generating the graph, you can interact with the chat based on the document.",
+ 'send_button': "Send",
+ 'select_saved_file': "Select saved file",
+ 'success_message': "Semantic analysis saved successfully.",
+ 'semantic_analyze_button': 'Semantic Analysis',
+ 'semantic_export_button': 'Export Semantic Analysis',
+ 'semantic_new_button': 'New Semantic Analysis',
+ 'semantic_file_uploader': 'Upload a text file for semantic analysis',
+ # T
+ 'text_input_label': "Enter a text to analyze (max. 5,000 words):",
+ 'text_input_placeholder': "The purpose of this application is to improve your writing skills...",
+ 'title': "AIdeaText - Semantic Analysis",
+ # U
+ 'upload_file': "Upload file",
+ # W
+ 'warning_message': "Please enter a text or upload a file to analyze."
+}
+
+DISCOURSE = {
+ 'file1_label': "Pattern Document",
+ 'file2_label': "Compared Document",
+ 'discourse_title': "AIdeaText - Discourse Analysis",
+ 'file_uploader1': "Upload text file 1 (Pattern)",
+ 'file_uploader2': "Upload text file 2 (Comparison)",
+ 'discourse_analyze_button': "Compare texts",
+ 'discourse_initial_message': "This is a general purpose chatbot, but it has a specific function for visual text analysis: it generates two graphs with the main entities of each file to make a comparison between both texts. To produce it, enter one file first and then another in txt, pdf, doc, docx or odt format and press the 'analyze file' button. After the graph is generated, you can interact with the chat based on the document.",
+ 'analyze_button': "Analyze texts",
+ 'comparison': "Comparison of Semantic Relations",
+ 'success_message': "Discourse analysis saved successfully.",
+ 'error_message': "There was a problem saving the discourse analysis. Please try again.",
+ 'warning_message': "Please upload both files to analyze.",
+ 'no_results': "No results available. Please perform an analysis first.",
+ 'key_concepts': "Key Concepts",
+ 'graph_not_available': "The graph is not available.",
+ 'concepts_not_available': "Key concepts are not available.",
+ 'comparison_not_available': "The comparison is not available.",
+ 'warning_message': "Please enter a text or upload a file to analyze.",
+ 'morphosyntax_history': "Morphosyntax history",
+ 'analysis_of': "Analysis of"
+}
+
+ACTIVITIES = {
+ # Nuevas etiquetas actualizadas
+ 'current_situation_activities': "Records of function: My Current Situation",
+ 'morpho_activities': "Records of my morphosyntactic analyses",
+ 'semantic_activities': "Records of my semantic analyses",
+ 'discourse_activities': "Records of my text comparison analyses",
+ 'chat_activities': "Records of my conversations with the virtual tutor",
+
+ # Mantener otras claves existentes
+ 'current_situation_tab': "Current situation",
+ 'morpho_tab': "Morphosyntactic analysis",
+ 'semantic_tab': "Semantic analysis",
+ 'discourse_tab': "Text comparison analysis",
+ 'activities_tab': "My activities record",
+ 'feedback_tab': "Feedback form",
+
+ # Resto de las claves que estén en el diccionario ACTIVITIES
+ 'analysis_types_chart_title': "Types of analyses performed",
+ 'analysis_types_chart_x': "Analysis type",
+ 'analysis_types_chart_y': "Count",
+ 'analysis_from': "Analysis from",
+ 'assistant': "Assistant",
+ 'activities_summary': "Activities and Progress Summary",
+ 'chat_history_expander': "Chat History",
+ 'chat_from': "Chat from",
+ 'combined_graph': "Combined Graph",
+ 'conceptual_relations_graph': "Conceptual Relations Graph",
+ 'conversation': "Conversation",
+ 'discourse_analyses_expander': "Text Comparison Analyses History", # Actualizado
+ 'discourse_analyses': "Text Comparison Analyses", # Actualizado
+ 'discourse_history': "Text Comparison Analysis History", # Actualizado
+ 'document': "Document",
+ 'data_load_error': "Error loading student data",
+ 'graph_display_error': "Could not display the graph",
+ 'graph_doc1': "Graph document 1",
+ 'graph_doc2': "Graph document 2",
+ 'key_concepts': "Key concepts",
+ 'loading_data': "Loading student data...",
+ 'morphological_analysis': "Morphological Analysis",
+ 'morphosyntax_analyses_expander': "Morphosyntactic Analyses History",
+ 'morphosyntax_history': "Morphosyntactic Analysis History",
+ 'no_arc_diagram': "No arc diagram found for this analysis.",
+ 'no_chat_history': "No conversations with the Virtual Tutor were found.", # Actualizado
+ 'no_data_warning': "No analysis data found for this student.",
+ 'progress_of': "Progress of",
+ 'semantic_analyses': "Semantic Analyses",
+ 'semantic_analyses_expander': "Semantic Analyses History",
+ 'semantic_history': "Semantic Analysis History",
+ 'show_debug_data': "Show debug data",
+ 'student_debug_data': "Student data (for debugging):",
+ 'summary_title': "Activities Summary",
+ 'title': "My Activities Record", # Actualizado
+ 'timestamp': "Timestamp",
+ 'total_analyses': "Total analyses performed:",
+ 'try_analysis': "Try performing some text analyses first.",
+ 'user': "User",
+
+ # Nuevas traducciones específicas para la sección de actividades
+ 'diagnosis_tab': "Diagnosis",
+ 'recommendations_tab': "Recommendations",
+ 'key_metrics': "Key metrics",
+ 'details': "Details",
+ 'analyzed_text': "Analyzed text",
+ 'analysis_date': "Date",
+ 'academic_article': "Academic article",
+ 'student_essay': "Student essay",
+ 'general_communication': "General communication",
+ 'no_diagnosis': "No diagnosis data available",
+ 'no_recommendations': "No recommendations available",
+ 'error_current_situation': "Error displaying current situation analysis",
+ 'no_current_situation': "No current situation analyses recorded",
+ 'no_morpho_analyses': "No morphosyntactic analyses recorded",
+ 'error_morpho': "Error displaying morphosyntactic analysis",
+ 'no_semantic_analyses': "No semantic analyses recorded",
+ 'error_semantic': "Error displaying semantic analysis",
+ 'no_discourse_analyses': "No text comparison analyses recorded", # Actualizado
+ 'error_discourse': "Error displaying text comparison analysis", # Actualizado
+ 'no_chat_history': "No conversation records with the virtual tutor", # Actualizado
+ 'error_chat': "Error displaying conversation records", # Actualizado
+ 'error_loading_activities': "Error loading activities",
+ 'chat_date': "Conversation date",
+ 'invalid_chat_format': "Invalid chat format",
+ 'comparison_results': "Comparison results",
+ 'concepts_text_1': "Concepts Text 1",
+ 'concepts_text_2': "Concepts Text 2",
+ 'no_visualization': "No comparative visualization available",
+ 'no_graph': "No visualization available",
+ 'error_loading_graph': "Error loading graph",
+ 'syntactic_diagrams': "Syntactic diagrams"
+}
+
+FEEDBACK = {
+ 'email': "Email",
+ 'feedback': "Feedback",
+ 'feedback_title': "Feedback form",
+ 'feedback_error': "There was a problem submitting the form. Please try again.",
+ 'feedback_success': "Thank for your feedback",
+ 'complete_all_fields': "Please, complete all fields",
+ 'name': "Name",
+ 'submit': "Submit"
+}
+
+
+CHATBOT_TRANSLATIONS = {
+ 'chat_title': "AIdeaText Assistant",
+ 'input_placeholder': "Any questions?",
+ 'initial_message': "Hi! I'm your assistant. How can I help?",
+ 'expand_chat': "Open assistant",
+ 'clear_chat': "Clear chat",
+ 'processing': "Processing...",
+ 'error_message': "Sorry, an error occurred"
+}
+
+TEXT_TYPES = {
+ 'descriptive': [
+ 'What are you describing?',
+ 'What are its main characteristics?',
+ 'How does it look, sound, smell, or feel?',
+ 'What makes it unique or special?'
+ ],
+ 'narrative': [
+ 'Who is the protagonist?',
+ 'Where and when does the story take place?',
+ 'What event starts the action?',
+ 'What happens next?',
+ 'How does the story end?'
+ ],
+ 'expository': [
+ 'What is the main topic?',
+ 'What important aspects do you want to explain?',
+ 'Can you provide examples or data to support your explanation?',
+ 'How does this topic relate to other concepts?'
+ ],
+ 'argumentative': [
+ 'What is your main thesis or argument?',
+ 'What are your supporting arguments?',
+ 'What evidence do you have to back up your arguments?',
+ 'What are the counterarguments and how do you refute them?',
+ 'What is your conclusion?'
+ ],
+ 'instructive': [
+ 'What task or process are you explaining?',
+ 'What materials or tools are needed?',
+ 'What are the steps to follow?',
+ 'Are there any important precautions or tips to mention?'
+ ],
+ 'pitch': [
+ 'What?',
+ 'What for?',
+ 'For whom?',
+ 'How?'
+ ]
+ }
+
+# Configuration of the language model for English
+NLP_MODEL = 'en_core_web_lg'
+
+# Esta línea es crucial:
+TRANSLATIONS = {
+ 'COMMON': COMMON,
+ 'TABS': TABS,
+ 'MORPHOSYNTACTIC': MORPHOSYNTACTIC,
+ 'SEMANTIC': SEMANTIC,
+ 'DISCOURSE': DISCOURSE,
+ 'ACTIVITIES': ACTIVITIES,
+ 'FEEDBACK': FEEDBACK,
+ 'TEXT_TYPES': TEXT_TYPES,
+ 'CURRENT_SITUATION': CURRENT_SITUATION, # Añadir esta línea
+ 'NLP_MODEL': NLP_MODEL
}
\ No newline at end of file
diff --git a/translations/es.py b/translations/es.py
index c71658a3fd48e1ab2125bcfb0736c7f6c68ef285..f9eb8d6d0250a59a830ae38019fe559a6b923b63 100644
--- a/translations/es.py
+++ b/translations/es.py
@@ -1,359 +1,359 @@
-# translations/es.py
-
-COMMON = {
- # A
- 'initial_instruction': "Para comenzar un nuevo análisis semántico, cargue un nuevo archivo de texto (.txt)",
- 'analysis_complete': "Análisis completo y guardado. Para realizar un nuevo análisis, cargue otro archivo.",
- 'current_analysis_message': "Mostrando análisis del archivo: {}. Para realizar un nuevo análisis, cargue otro archivo.",
- 'upload_prompt': "Cargue un archivo para comenzar el análisis",
- 'analysis_completed': "Análisis completado",
- 'analysis_section': "Análisis Semántico",
- 'analyze_document': 'Analizar documento',
- 'analysis_saved_success': 'Análisis guardado con éxito',
- 'analysis_save_error': 'Error al guardar el análisis',
- 'analyze_button': "Analizar texto",
- 'analyzing_doc': "Analizando documento",
- 'activities_message': "Mensajes de las actividades",
- 'activities_placeholder': "Espacio de las actividades",
- 'analysis_placeholder': "Marcador de posición del análisis",
- 'analyze_button': "Analizar",
- 'analysis_types_chart': "Gráfico para el tipo de análisis",
- 'analysis_from': "Análisis realizado el",
- # C
- 'chat_title': "Chat de Análisis",
- 'export_button': "Exportar Análisis Actual",
- 'export_success': "Análisis y chat exportados correctamente.",
- 'export_error': "Hubo un problema al exportar el análisis y el chat.",
- 'get_text': "Obtener texto.",
- 'hello': "Hola",
- # L
- 'logout': "Cerrar sesión.",
- 'loading_data': "Cargando datos",
- 'load_selected_file': 'Cargar archivo seleccionado',
- # N
- 'no_analysis': "No hay análisis disponible. Utiliza el chat para realizar un análisis.",
- 'nothing_to_export': "No hay análisis o chat para exportar.",
- 'results_title': "Resultados del Análisis",
- 'select_language': "Selecciona un idioma",
- 'student_activities': "Actividades del estudiante",
- # T
- 'total_analyses': "Análisis totales",
- # W
- 'welcome': "Bienvenido a AIdeaText"
-}
-
-TABS = {
- 'current_situation_tab': "Mi situación actual",
- 'morpho_tab': "Análisis morfosintáctico",
- 'semantic_live_tab': "Semántica en vivo",
- 'semantic_tab': "Análisis semántico",
- 'discourse_live_tab': "Discurso en vivo",
- 'discourse_tab': "Análisis del discurso",
- 'activities_tab': "Mis actividades",
- 'feedback_tab': "Formulario de comentarios"
-}
-
-CURRENT_SITUATION = {
- 'title': "Mi Situación Actual",
- 'input_prompt': "Escribe o pega tu texto aquí:",
- 'first_analyze_button': "Analizar mi escritura",
- 'processing': "Analizando...",
- 'analysis_error': "Error al analizar el texto",
- 'help': "Analizaremos tu texto para determinar su estado actual",
-
- # Radio buttons para tipo de texto
- 'text_type_header': "Tipo de texto",
- 'text_type_help': "Selecciona el tipo de texto para ajustar los criterios de evaluación",
-
- # Métricas
- 'vocabulary_label': "Vocabulario",
- 'vocabulary_help': "Riqueza y variedad del vocabulario",
- 'structure_label': "Estructura",
- 'structure_help': "Organización y complejidad de oraciones",
- 'cohesion_label': "Cohesión",
- 'cohesion_help': "Conexión y fluidez entre ideas",
- 'clarity_label': "Claridad",
- 'clarity_help': "Facilidad de comprensión del texto",
-
- # Estados de métricas
- 'metric_improvement': "⚠️ Por mejorar",
- 'metric_acceptable': "📈 Aceptable",
- 'metric_optimal': "✅ Óptimo",
- 'metric_target': "Meta: {:.2f}",
-
- # Errores
- 'error_interface': "Ocurrió un error al cargar la interfaz",
- 'error_results': "Error al mostrar los resultados",
- 'error_chart': "Error al mostrar el gráfico"
-}
-
-MORPHOSYNTACTIC = {
- #A
- 'arc_diagram': "Análisis sintáctico: Diagrama de arco",
- #B
- 'tab_text_baseline': "Ingresa la primera versión de tu texto",
- 'tab_iterations': "Produce nuevas versiones de tu primer texto",
-
- # Pestaña 1 texto base
- 'btn_new_morpho_analysis': "Nuevo análisis morfosintático",
- 'btn_analyze_baseline': "Analizar la primera versión de tu texto",
- 'input_baseline_text': "Ingresa el primer texto para analizarlo",
- 'warn_enter_text': "Ingrese un texto primer para analizarlo",
- 'error_processing_baseline': "Error al procesar el texto inicial",
- 'arc_diagram_baseline_label': "Diagrama de arco del texto inicial",
- 'baseline_diagram_not_available': "Diagrama de arco del texto inicial no disponible",
-
- # Pestaña 2 Iteración del texto
- 'info_first_analyze_base': "Verifica la existencia del texto inicial",
- 'iteration_text_subheader': "Nueva versión del texto inicial",
- 'input_iteration_text': "Ingresa una nueva versión del texto inicial y compara los arcos de ambos textos",
- 'btn_analyze_iteration': "Analizar Cambios",
- 'warn_enter_iteration_text': "Ingresa una nueva versión del texto inicial y compara los arcos de ambos textos",
- 'iteration_saved': "Cambios guardados correctamente",
- 'error_iteration': "Error procesando los nuevos cambios"
-}
-
-SEMANTIC = {
- # A
-
- # C
- 'chat_title': "Chat de Análisis Semántico",
- 'chat_placeholder': "Haz una pregunta o usa un comando (/resumen, /entidades, /sentimiento, /temas, /grafo_conceptos, /grafo_entidades, /grafo_temas)",
- 'clear_chat': "Limpiar chat",
- 'conceptual_relations': "Relaciones Conceptuales",
- # D
- 'delete_file': "Borrar archivo",
- # E
- 'error_message': "Hubo un problema al guardar el análisis semántico. Por favor, inténtelo de nuevo.",
- # F
- 'file_uploader': "O cargue un archivo de texto",
- 'file_upload_success': "Archivo subido y guardado exitosamente",
- 'file_upload_error': 'Error al cargar el archivo',
- 'file_section': "Archivos",
- 'file_loaded_success': "Archivo cargado exitosamente",
- 'file_load_error': "Error al cargar el archivo",
- 'file_upload_error': "Error al subir y guardar el archivo",
- 'file_deleted_success': 'Archivo borrado con éxito',
- 'file_delete_error': 'Error al borrar el archivo',
- # G
- 'graph_title': "Visualización de Análisis Semántico",
- # I
- 'identified_entities': "Entidades Identificadas",
- # K
- 'key_concepts': "Conceptos Clave",
- # N
- 'no_analysis': "No hay análisis disponible. Por favor, cargue o seleccione un archivo.",
- 'no_results': "No hay resultados disponibles. Por favor, realice un análisis primero.",
- 'no_file': "Por favor, cargue un archivo para comenzar el análisis.",
- 'no_file_selected': "Por favor, seleccione un archivo para comenzar el análisis.",
- # S
- 'semantic_title': "Análisis Semántico",
- 'semantic_initial_message': "Este es un chatbot de propósito general, pero tiene una función específica para el análisis visual de textos: genera un grafo con las principales entidades del texto. Para producirlo, ingrese un archivo de texto en formato txt, pdf, doc, docx o odt y pulse el botón 'analizar archivo'. Después de la generación del grafo puede interactuar con el chat en función del documento.",
- 'send_button': "Enviar",
- 'select_saved_file': "Seleccionar archivo guardado",
- 'success_message': "Análisis semántico guardado correctamente.",
- 'semantic_analyze_button': 'Análisis Semántico',
- 'semantic_export_button': 'Exportar Análisis Semántico',
- 'semantic_new_button': 'Nuevo Análisis Semántico',
- 'semantic_file_uploader': 'Ingresar un archivo de texto para análisis semántico',
- # T
- 'text_input_label': "Ingrese un texto para analizar (máx. 5,000 palabras):",
- 'text_input_placeholder': "El objetivo de esta aplicación es que mejore sus habilidades de redacción...",
- 'title': "AIdeaText - Análisis semántico",
- # U
- 'upload_file': "Agregar un archivo",
- # W
- 'warning_message': "Por favor, ingrese un texto o cargue un archivo para analizar."
-}
-
-DISCOURSE = {
- 'file1_label': "Documento Patrón",
- 'file2_label': "Documento Comparado",
- 'discourse_title': "AIdeaText - Análisis del discurso",
- 'file_uploader1': "Cargar archivo de texto 1 (Patrón)",
- 'file_uploader2': "Cargar archivo de texto 2 (Comparación)",
- 'discourse_analyze_button': "Comparar textos",
- 'discourse_initial_message': "Este es un chatbot de propósito general, pero tiene una función específica para el análisis visual de textos: genera dos grafos con las principales entidades de cada archivo para hacer una comparación entre ambos textos. Para producirlo, ingrese un archivo primero y otro después en formato txt, pdf, doc, docx o odt y pulse el botón 'analizar archivo'. Después de la generación del grafo puede interactuar con el chat en función del documento.",
- 'analyze_button': "Analizar textos",
- 'comparison': "Comparación de Relaciones Semánticas",
- 'success_message': "Análisis del discurso guardado correctamente.",
- 'error_message': "Hubo un problema al guardar el análisis del discurso. Por favor, inténtelo de nuevo.",
- 'warning_message': "Por favor, cargue ambos archivos para analizar.",
- 'no_results': "No hay resultados disponibles. Por favor, realice un análisis primero.",
- 'key_concepts': "Conceptos Clave",
- 'graph_not_available': "El gráfico no está disponible.",
- 'concepts_not_available': "Los conceptos clave no están disponibles.",
- 'comparison_not_available': "La comparación no está disponible.",
- 'morphosyntax_history': "Historial de morfosintaxis",
- 'analysis_of': "Análisis de"
-}
-
-ACTIVITIES = {
- # Nuevas etiquetas actualizadas
- 'current_situation_activities': "Registros de la función: Mi Situación Actual",
- 'morpho_activities': "Registros de mis análisis morfosintácticos",
- 'semantic_activities': "Registros de mis análisis semánticos",
- 'discourse_activities': "Registros de mis análisis comparados de textos",
- 'chat_activities': "Registros de mis conversaciones con el tutor virtual",
-
- # Mantener otras claves existentes
- 'current_situation_tab': "Mi situación actual",
- 'morpho_tab': "Análisis morfosintáctico",
- 'semantic_tab': "Análisis semántico",
- 'discourse_tab': "Análisis comparado de textos",
- 'activities_tab': "Registro de mis actividades",
- 'feedback_tab': "Formulario de comentarios",
-
- # Resto de las claves que estén en el diccionario ACTIVITIES
- 'analysis_types_chart_title': "Tipos de análisis realizados",
- 'analysis_types_chart_x': "Tipo de análisis",
- 'analysis_types_chart_y': "Cantidad",
- 'analysis_from': "Análisis del",
- 'assistant': "Asistente",
- 'activities_summary': "Resumen de Actividades y Progreso",
- 'chat_history_expander': "Historial de Chat",
- 'chat_from': "Chat del",
- 'combined_graph': "Gráfico combinado",
- 'conceptual_relations_graph': "Gráfico de relaciones conceptuales",
- 'conversation': "Conversación",
- 'discourse_analyses_expander': "Historial de Análisis Comparados de Textos",
- 'discourse_analyses': "Análisis Comparados de Textos",
- 'discourse_history': "Histórico de Análisis Comparados de Textos",
- 'document': "Documento",
- 'data_load_error': "Error al cargar los datos del estudiante",
- 'graph_display_error': "No se pudo mostrar el gráfico",
- 'graph_doc1': "Gráfico documento 1",
- 'graph_doc2': "Gráfico documento 2",
- 'key_concepts': "Conceptos clave",
- 'loading_data': "Cargando datos del estudiante...",
- 'morphological_analysis': "Análisis Morfológico",
- 'morphosyntax_analyses_expander': "Historial de Análisis Morfosintácticos",
- 'morphosyntax_history': "Histórico de Análisis Morfosintácticos",
- 'no_arc_diagram': "No se encontró diagrama de arco para este análisis.",
- 'no_chat_history': "No se encontraron conversaciones con el Tutor Virtual.",
- 'no_data_warning': "No se encontraron datos de análisis para este estudiante.",
- 'progress_of': "Progreso de",
- 'semantic_analyses': "Análisis Semánticos",
- 'semantic_analyses_expander': "Historial de Análisis Semánticos",
- 'semantic_history': "Histórico de Análisis Semánticos",
- 'show_debug_data': "Mostrar datos de depuración",
- 'student_debug_data': "Datos del estudiante (para depuración):",
- 'summary_title': "Resumen de Actividades",
- 'title': "Registro de mis actividades",
- 'timestamp': "Fecha y hora",
- 'total_analyses': "Total de análisis realizados:",
- 'try_analysis': "Intenta realizar algunos análisis de texto primero.",
- 'user': "Usuario",
-
- # Nuevas traducciones específicas para la sección de actividades
- 'diagnosis_tab': "Diagnóstico",
- 'recommendations_tab': "Recomendaciones",
- 'key_metrics': "Métricas clave",
- 'details': "Detalles",
- 'analyzed_text': "Texto analizado",
- 'analysis_date': "Fecha",
- 'academic_article': "Artículo académico",
- 'student_essay': "Trabajo universitario",
- 'general_communication': "Comunicación general",
- 'no_diagnosis': "No hay datos de diagnóstico disponibles",
- 'no_recommendations': "No hay recomendaciones disponibles",
- 'error_current_situation': "Error al mostrar análisis de situación actual",
- 'no_current_situation': "No hay análisis de situación actual registrados",
- 'no_morpho_analyses': "No hay análisis morfosintácticos registrados",
- 'error_morpho': "Error al mostrar análisis morfosintáctico",
- 'no_semantic_analyses': "No hay análisis semánticos registrados",
- 'error_semantic': "Error al mostrar análisis semántico",
- 'no_discourse_analyses': "No hay análisis comparados de textos registrados",
- 'error_discourse': "Error al mostrar análisis comparado de textos",
- 'no_chat_history': "No hay registros de conversaciones con el tutor virtual",
- 'error_chat': "Error al mostrar registros de conversaciones",
- 'error_loading_activities': "Error al cargar las actividades",
- 'chat_date': "Fecha de conversación",
- 'invalid_chat_format': "Formato de chat no válido",
- 'comparison_results': "Resultados de la comparación",
- 'concepts_text_1': "Conceptos Texto 1",
- 'concepts_text_2': "Conceptos Texto 2",
- 'no_visualization': "No hay visualización comparativa disponible",
- 'no_graph': "No hay visualización disponible",
- 'error_loading_graph': "Error al cargar el gráfico",
- 'syntactic_diagrams': "Diagramas sintácticos"
-}
-
-FEEDBACK = {
- 'email': "Correo electrónico",
- 'feedback': "Retroalimentación",
- 'feedback_title': "Formulario de opinión",
- 'feedback_error': "Hubo un problema al enviar el formulario. Por favor, intenta de nuevo.",
- 'feedback_success': "Gracias por tu respuesta",
- 'complete_all_fields': "Por favor, completa todos los campos",
- 'name': "Nombre",
- 'submit': "Enviar"
-}
-
-CHATBOT_TRANSLATIONS = {
- 'chat_title': "Asistente AIdeaText",
- 'input_placeholder': "¿Tienes alguna pregunta?",
- 'initial_message': "¡Hola! Soy tu asistente. ¿En qué puedo ayudarte?",
- 'expand_chat': "Abrir asistente",
- 'clear_chat': "Limpiar chat",
- 'processing': "Procesando...",
- 'error_message': "Lo siento, ocurrió un error"
-}
-
-TEXT_TYPES = {
- 'descriptivo': [
- '¿Qué estás describiendo?',
- '¿Cuáles son sus características principales?',
- '¿Cómo se ve, suena, huele o se siente?',
- '¿Qué lo hace único o especial?'
- ],
- 'narrativo': [
- '¿Quién es el protagonista?',
- '¿Dónde y cuándo ocurre la historia?',
- '¿Qué evento inicia la acción?',
- '¿Qué sucede después?',
- '¿Cómo termina la historia?'
- ],
- 'expositivo': [
- '¿Cuál es el tema principal?',
- '¿Qué aspectos importantes quieres explicar?',
- '¿Puedes dar ejemplos o datos que apoyen tu explicación?',
- '¿Cómo se relaciona este tema con otros conceptos?'
- ],
- 'argumentativo': [
- '¿Cuál es tu tesis o argumento principal?',
- '¿Cuáles son tus argumentos de apoyo?',
- '¿Qué evidencias tienes para respaldar tus argumentos?',
- '¿Cuáles son los contraargumentos y cómo los refutas?',
- '¿Cuál es tu conclusión?'
- ],
- 'instructivo': [
- '¿Qué tarea o proceso estás explicando?',
- '¿Qué materiales o herramientas se necesitan?',
- '¿Cuáles son los pasos a seguir?',
- '¿Hay precauciones o consejos importantes que mencionar?'
- ],
- 'pitch': [
- '¿Qué?',
- '¿Para qué?',
- '¿Para quién?',
- '¿Cómo?'
- ]
-}
-
-# Configuración del modelo de lenguaje para español
-NLP_MODEL = 'es_core_news_lg'
-
-# Esta línea es crucial:
-TRANSLATIONS = {
- 'COMMON': COMMON,
- 'TABS': TABS,
- 'MORPHOSYNTACTIC': MORPHOSYNTACTIC,
- 'SEMANTIC': SEMANTIC,
- 'DISCOURSE': DISCOURSE,
- 'ACTIVITIES': ACTIVITIES,
- 'FEEDBACK': FEEDBACK,
- 'TEXT_TYPES': TEXT_TYPES,
- 'CURRENT_SITUATION': CURRENT_SITUATION,
- 'NLP_MODEL': NLP_MODEL
+# translations/es.py
+
+COMMON = {
+ # A
+ 'initial_instruction': "Para comenzar un nuevo análisis semántico, cargue un nuevo archivo de texto (.txt)",
+ 'analysis_complete': "Análisis completo y guardado. Para realizar un nuevo análisis, cargue otro archivo.",
+ 'current_analysis_message': "Mostrando análisis del archivo: {}. Para realizar un nuevo análisis, cargue otro archivo.",
+ 'upload_prompt': "Cargue un archivo para comenzar el análisis",
+ 'analysis_completed': "Análisis completado",
+ 'analysis_section': "Análisis Semántico",
+ 'analyze_document': 'Analizar documento',
+ 'analysis_saved_success': 'Análisis guardado con éxito',
+ 'analysis_save_error': 'Error al guardar el análisis',
+ 'analyze_button': "Analizar texto",
+ 'analyzing_doc': "Analizando documento",
+ 'activities_message': "Mensajes de las actividades",
+ 'activities_placeholder': "Espacio de las actividades",
+ 'analysis_placeholder': "Marcador de posición del análisis",
+ 'analyze_button': "Analizar",
+ 'analysis_types_chart': "Gráfico para el tipo de análisis",
+ 'analysis_from': "Análisis realizado el",
+ # C
+ 'chat_title': "Chat de Análisis",
+ 'export_button': "Exportar Análisis Actual",
+ 'export_success': "Análisis y chat exportados correctamente.",
+ 'export_error': "Hubo un problema al exportar el análisis y el chat.",
+ 'get_text': "Obtener texto.",
+ 'hello': "Hola",
+ # L
+ 'logout': "Cerrar sesión.",
+ 'loading_data': "Cargando datos",
+ 'load_selected_file': 'Cargar archivo seleccionado',
+ # N
+ 'no_analysis': "No hay análisis disponible. Utiliza el chat para realizar un análisis.",
+ 'nothing_to_export': "No hay análisis o chat para exportar.",
+ 'results_title': "Resultados del Análisis",
+ 'select_language': "Selecciona un idioma",
+ 'student_activities': "Actividades del estudiante",
+ # T
+ 'total_analyses': "Análisis totales",
+ # W
+ 'welcome': "Bienvenido a AIdeaText"
+}
+
+TABS = {
+ 'current_situation_tab': "Mi situación actual",
+ 'morpho_tab': "Análisis morfosintáctico",
+ 'semantic_live_tab': "Semántica en vivo",
+ 'semantic_tab': "Análisis semántico",
+ 'discourse_live_tab': "Discurso en vivo",
+ 'discourse_tab': "Análisis del discurso",
+ 'activities_tab': "Mis actividades",
+ 'feedback_tab': "Formulario de comentarios"
+}
+
+CURRENT_SITUATION = {
+ 'title': "Mi Situación Actual",
+ 'input_prompt': "Escribe o pega tu texto aquí:",
+ 'first_analyze_button': "Analizar mi escritura",
+ 'processing': "Analizando...",
+ 'analysis_error': "Error al analizar el texto",
+ 'help': "Analizaremos tu texto para determinar su estado actual",
+
+ # Radio buttons para tipo de texto
+ 'text_type_header': "Tipo de texto",
+ 'text_type_help': "Selecciona el tipo de texto para ajustar los criterios de evaluación",
+
+ # Métricas
+ 'vocabulary_label': "Vocabulario",
+ 'vocabulary_help': "Riqueza y variedad del vocabulario",
+ 'structure_label': "Estructura",
+ 'structure_help': "Organización y complejidad de oraciones",
+ 'cohesion_label': "Cohesión",
+ 'cohesion_help': "Conexión y fluidez entre ideas",
+ 'clarity_label': "Claridad",
+ 'clarity_help': "Facilidad de comprensión del texto",
+
+ # Estados de métricas
+ 'metric_improvement': "⚠️ Por mejorar",
+ 'metric_acceptable': "📈 Aceptable",
+ 'metric_optimal': "✅ Óptimo",
+ 'metric_target': "Meta: {:.2f}",
+
+ # Errores
+ 'error_interface': "Ocurrió un error al cargar la interfaz",
+ 'error_results': "Error al mostrar los resultados",
+ 'error_chart': "Error al mostrar el gráfico"
+}
+
+MORPHOSYNTACTIC = {
+ #A
+ 'arc_diagram': "Análisis sintáctico: Diagrama de arco",
+ #B
+ 'tab_text_baseline': "Ingresa la primera versión de tu texto",
+ 'tab_iterations': "Produce nuevas versiones de tu primer texto",
+
+ # Pestaña 1 texto base
+ 'btn_new_morpho_analysis': "Nuevo análisis morfosintático",
+ 'btn_analyze_baseline': "Analizar la primera versión de tu texto",
+ 'input_baseline_text': "Ingresa el primer texto para analizarlo",
+ 'warn_enter_text': "Ingrese un texto primer para analizarlo",
+ 'error_processing_baseline': "Error al procesar el texto inicial",
+ 'arc_diagram_baseline_label': "Diagrama de arco del texto inicial",
+ 'baseline_diagram_not_available': "Diagrama de arco del texto inicial no disponible",
+
+ # Pestaña 2 Iteración del texto
+ 'info_first_analyze_base': "Verifica la existencia del texto inicial",
+ 'iteration_text_subheader': "Nueva versión del texto inicial",
+ 'input_iteration_text': "Ingresa una nueva versión del texto inicial y compara los arcos de ambos textos",
+ 'btn_analyze_iteration': "Analizar Cambios",
+ 'warn_enter_iteration_text': "Ingresa una nueva versión del texto inicial y compara los arcos de ambos textos",
+ 'iteration_saved': "Cambios guardados correctamente",
+ 'error_iteration': "Error procesando los nuevos cambios"
+}
+
+SEMANTIC = {
+ # A
+
+ # C
+ 'chat_title': "Chat de Análisis Semántico",
+ 'chat_placeholder': "Haz una pregunta o usa un comando (/resumen, /entidades, /sentimiento, /temas, /grafo_conceptos, /grafo_entidades, /grafo_temas)",
+ 'clear_chat': "Limpiar chat",
+ 'conceptual_relations': "Relaciones Conceptuales",
+ # D
+ 'delete_file': "Borrar archivo",
+ # E
+ 'error_message': "Hubo un problema al guardar el análisis semántico. Por favor, inténtelo de nuevo.",
+ # F
+ 'file_uploader': "O cargue un archivo de texto",
+ 'file_upload_success': "Archivo subido y guardado exitosamente",
+ 'file_upload_error': 'Error al cargar el archivo',
+ 'file_section': "Archivos",
+ 'file_loaded_success': "Archivo cargado exitosamente",
+ 'file_load_error': "Error al cargar el archivo",
+ 'file_upload_error': "Error al subir y guardar el archivo",
+ 'file_deleted_success': 'Archivo borrado con éxito',
+ 'file_delete_error': 'Error al borrar el archivo',
+ # G
+ 'graph_title': "Visualización de Análisis Semántico",
+ # I
+ 'identified_entities': "Entidades Identificadas",
+ # K
+ 'key_concepts': "Conceptos Clave",
+ # N
+ 'no_analysis': "No hay análisis disponible. Por favor, cargue o seleccione un archivo.",
+ 'no_results': "No hay resultados disponibles. Por favor, realice un análisis primero.",
+ 'no_file': "Por favor, cargue un archivo para comenzar el análisis.",
+ 'no_file_selected': "Por favor, seleccione un archivo para comenzar el análisis.",
+ # S
+ 'semantic_title': "Análisis Semántico",
+ 'semantic_initial_message': "Este es un chatbot de propósito general, pero tiene una función específica para el análisis visual de textos: genera un grafo con las principales entidades del texto. Para producirlo, ingrese un archivo de texto en formato txt, pdf, doc, docx o odt y pulse el botón 'analizar archivo'. Después de la generación del grafo puede interactuar con el chat en función del documento.",
+ 'send_button': "Enviar",
+ 'select_saved_file': "Seleccionar archivo guardado",
+ 'success_message': "Análisis semántico guardado correctamente.",
+ 'semantic_analyze_button': 'Análisis Semántico',
+ 'semantic_export_button': 'Exportar Análisis Semántico',
+ 'semantic_new_button': 'Nuevo Análisis Semántico',
+ 'semantic_file_uploader': 'Ingresar un archivo de texto para análisis semántico',
+ # T
+ 'text_input_label': "Ingrese un texto para analizar (máx. 5,000 palabras):",
+ 'text_input_placeholder': "El objetivo de esta aplicación es que mejore sus habilidades de redacción...",
+ 'title': "AIdeaText - Análisis semántico",
+ # U
+ 'upload_file': "Agregar un archivo",
+ # W
+ 'warning_message': "Por favor, ingrese un texto o cargue un archivo para analizar."
+}
+
+DISCOURSE = {
+ 'file1_label': "Documento Patrón",
+ 'file2_label': "Documento Comparado",
+ 'discourse_title': "AIdeaText - Análisis del discurso",
+ 'file_uploader1': "Cargar archivo de texto 1 (Patrón)",
+ 'file_uploader2': "Cargar archivo de texto 2 (Comparación)",
+ 'discourse_analyze_button': "Comparar textos",
+ 'discourse_initial_message': "Este es un chatbot de propósito general, pero tiene una función específica para el análisis visual de textos: genera dos grafos con las principales entidades de cada archivo para hacer una comparación entre ambos textos. Para producirlo, ingrese un archivo primero y otro después en formato txt, pdf, doc, docx o odt y pulse el botón 'analizar archivo'. Después de la generación del grafo puede interactuar con el chat en función del documento.",
+ 'analyze_button': "Analizar textos",
+ 'comparison': "Comparación de Relaciones Semánticas",
+ 'success_message': "Análisis del discurso guardado correctamente.",
+ 'error_message': "Hubo un problema al guardar el análisis del discurso. Por favor, inténtelo de nuevo.",
+ 'warning_message': "Por favor, cargue ambos archivos para analizar.",
+ 'no_results': "No hay resultados disponibles. Por favor, realice un análisis primero.",
+ 'key_concepts': "Conceptos Clave",
+ 'graph_not_available': "El gráfico no está disponible.",
+ 'concepts_not_available': "Los conceptos clave no están disponibles.",
+ 'comparison_not_available': "La comparación no está disponible.",
+ 'morphosyntax_history': "Historial de morfosintaxis",
+ 'analysis_of': "Análisis de"
+}
+
+ACTIVITIES = {
+ # Nuevas etiquetas actualizadas
+ 'current_situation_activities': "Registros de la función: Mi Situación Actual",
+ 'morpho_activities': "Registros de mis análisis morfosintácticos",
+ 'semantic_activities': "Registros de mis análisis semánticos",
+ 'discourse_activities': "Registros de mis análisis comparados de textos",
+ 'chat_activities': "Registros de mis conversaciones con el tutor virtual",
+
+ # Mantener otras claves existentes
+ 'current_situation_tab': "Mi situación actual",
+ 'morpho_tab': "Análisis morfosintáctico",
+ 'semantic_tab': "Análisis semántico",
+ 'discourse_tab': "Análisis comparado de textos",
+ 'activities_tab': "Registro de mis actividades",
+ 'feedback_tab': "Formulario de comentarios",
+
+ # Resto de las claves que estén en el diccionario ACTIVITIES
+ 'analysis_types_chart_title': "Tipos de análisis realizados",
+ 'analysis_types_chart_x': "Tipo de análisis",
+ 'analysis_types_chart_y': "Cantidad",
+ 'analysis_from': "Análisis del",
+ 'assistant': "Asistente",
+ 'activities_summary': "Resumen de Actividades y Progreso",
+ 'chat_history_expander': "Historial de Chat",
+ 'chat_from': "Chat del",
+ 'combined_graph': "Gráfico combinado",
+ 'conceptual_relations_graph': "Gráfico de relaciones conceptuales",
+ 'conversation': "Conversación",
+ 'discourse_analyses_expander': "Historial de Análisis Comparados de Textos",
+ 'discourse_analyses': "Análisis Comparados de Textos",
+ 'discourse_history': "Histórico de Análisis Comparados de Textos",
+ 'document': "Documento",
+ 'data_load_error': "Error al cargar los datos del estudiante",
+ 'graph_display_error': "No se pudo mostrar el gráfico",
+ 'graph_doc1': "Gráfico documento 1",
+ 'graph_doc2': "Gráfico documento 2",
+ 'key_concepts': "Conceptos clave",
+ 'loading_data': "Cargando datos del estudiante...",
+ 'morphological_analysis': "Análisis Morfológico",
+ 'morphosyntax_analyses_expander': "Historial de Análisis Morfosintácticos",
+ 'morphosyntax_history': "Histórico de Análisis Morfosintácticos",
+ 'no_arc_diagram': "No se encontró diagrama de arco para este análisis.",
+ 'no_chat_history': "No se encontraron conversaciones con el Tutor Virtual.",
+ 'no_data_warning': "No se encontraron datos de análisis para este estudiante.",
+ 'progress_of': "Progreso de",
+ 'semantic_analyses': "Análisis Semánticos",
+ 'semantic_analyses_expander': "Historial de Análisis Semánticos",
+ 'semantic_history': "Histórico de Análisis Semánticos",
+ 'show_debug_data': "Mostrar datos de depuración",
+ 'student_debug_data': "Datos del estudiante (para depuración):",
+ 'summary_title': "Resumen de Actividades",
+ 'title': "Registro de mis actividades",
+ 'timestamp': "Fecha y hora",
+ 'total_analyses': "Total de análisis realizados:",
+ 'try_analysis': "Intenta realizar algunos análisis de texto primero.",
+ 'user': "Usuario",
+
+ # Nuevas traducciones específicas para la sección de actividades
+ 'diagnosis_tab': "Diagnóstico",
+ 'recommendations_tab': "Recomendaciones",
+ 'key_metrics': "Métricas clave",
+ 'details': "Detalles",
+ 'analyzed_text': "Texto analizado",
+ 'analysis_date': "Fecha",
+ 'academic_article': "Artículo académico",
+ 'student_essay': "Trabajo universitario",
+ 'general_communication': "Comunicación general",
+ 'no_diagnosis': "No hay datos de diagnóstico disponibles",
+ 'no_recommendations': "No hay recomendaciones disponibles",
+ 'error_current_situation': "Error al mostrar análisis de situación actual",
+ 'no_current_situation': "No hay análisis de situación actual registrados",
+ 'no_morpho_analyses': "No hay análisis morfosintácticos registrados",
+ 'error_morpho': "Error al mostrar análisis morfosintáctico",
+ 'no_semantic_analyses': "No hay análisis semánticos registrados",
+ 'error_semantic': "Error al mostrar análisis semántico",
+ 'no_discourse_analyses': "No hay análisis comparados de textos registrados",
+ 'error_discourse': "Error al mostrar análisis comparado de textos",
+ 'no_chat_history': "No hay registros de conversaciones con el tutor virtual",
+ 'error_chat': "Error al mostrar registros de conversaciones",
+ 'error_loading_activities': "Error al cargar las actividades",
+ 'chat_date': "Fecha de conversación",
+ 'invalid_chat_format': "Formato de chat no válido",
+ 'comparison_results': "Resultados de la comparación",
+ 'concepts_text_1': "Conceptos Texto 1",
+ 'concepts_text_2': "Conceptos Texto 2",
+ 'no_visualization': "No hay visualización comparativa disponible",
+ 'no_graph': "No hay visualización disponible",
+ 'error_loading_graph': "Error al cargar el gráfico",
+ 'syntactic_diagrams': "Diagramas sintácticos"
+}
+
+FEEDBACK = {
+ 'email': "Correo electrónico",
+ 'feedback': "Retroalimentación",
+ 'feedback_title': "Formulario de opinión",
+ 'feedback_error': "Hubo un problema al enviar el formulario. Por favor, intenta de nuevo.",
+ 'feedback_success': "Gracias por tu respuesta",
+ 'complete_all_fields': "Por favor, completa todos los campos",
+ 'name': "Nombre",
+ 'submit': "Enviar"
+}
+
+CHATBOT_TRANSLATIONS = {
+ 'chat_title': "Asistente AIdeaText",
+ 'input_placeholder': "¿Tienes alguna pregunta?",
+ 'initial_message': "¡Hola! Soy tu asistente. ¿En qué puedo ayudarte?",
+ 'expand_chat': "Abrir asistente",
+ 'clear_chat': "Limpiar chat",
+ 'processing': "Procesando...",
+ 'error_message': "Lo siento, ocurrió un error"
+}
+
+TEXT_TYPES = {
+ 'descriptivo': [
+ '¿Qué estás describiendo?',
+ '¿Cuáles son sus características principales?',
+ '¿Cómo se ve, suena, huele o se siente?',
+ '¿Qué lo hace único o especial?'
+ ],
+ 'narrativo': [
+ '¿Quién es el protagonista?',
+ '¿Dónde y cuándo ocurre la historia?',
+ '¿Qué evento inicia la acción?',
+ '¿Qué sucede después?',
+ '¿Cómo termina la historia?'
+ ],
+ 'expositivo': [
+ '¿Cuál es el tema principal?',
+ '¿Qué aspectos importantes quieres explicar?',
+ '¿Puedes dar ejemplos o datos que apoyen tu explicación?',
+ '¿Cómo se relaciona este tema con otros conceptos?'
+ ],
+ 'argumentativo': [
+ '¿Cuál es tu tesis o argumento principal?',
+ '¿Cuáles son tus argumentos de apoyo?',
+ '¿Qué evidencias tienes para respaldar tus argumentos?',
+ '¿Cuáles son los contraargumentos y cómo los refutas?',
+ '¿Cuál es tu conclusión?'
+ ],
+ 'instructivo': [
+ '¿Qué tarea o proceso estás explicando?',
+ '¿Qué materiales o herramientas se necesitan?',
+ '¿Cuáles son los pasos a seguir?',
+ '¿Hay precauciones o consejos importantes que mencionar?'
+ ],
+ 'pitch': [
+ '¿Qué?',
+ '¿Para qué?',
+ '¿Para quién?',
+ '¿Cómo?'
+ ]
+}
+
+# Configuración del modelo de lenguaje para español
+NLP_MODEL = 'es_core_news_lg'
+
+# Esta línea es crucial:
+TRANSLATIONS = {
+ 'COMMON': COMMON,
+ 'TABS': TABS,
+ 'MORPHOSYNTACTIC': MORPHOSYNTACTIC,
+ 'SEMANTIC': SEMANTIC,
+ 'DISCOURSE': DISCOURSE,
+ 'ACTIVITIES': ACTIVITIES,
+ 'FEEDBACK': FEEDBACK,
+ 'TEXT_TYPES': TEXT_TYPES,
+ 'CURRENT_SITUATION': CURRENT_SITUATION,
+ 'NLP_MODEL': NLP_MODEL
}
\ No newline at end of file
diff --git a/translations/fr.py b/translations/fr.py
index 7977eb35e295433110686e13187894884b9de927..627ff528f0e79e0ed282068c3385b528531c97a2 100644
--- a/translations/fr.py
+++ b/translations/fr.py
@@ -1,357 +1,357 @@
-# translations/fr.py
-
-COMMON = {
- # A
- 'initial_instruction': "Pour démarrer une nouvelle analyse sémantique, téléchargez un nouveau fichier texte (.txt)",
- 'analysis_complete': "Analyse terminée et enregistrée. Pour effectuer une nouvelle analyse, téléchargez un autre fichier.",
- 'current_analysis_message': "Affichage de l'analyse du fichier : {}. Pour effectuer une nouvelle analyse, veuillez télécharger un autre fichier.",
- 'upload_prompt': "Joindre un fichier pour démarrer l'analyse",
- 'analysis_completed': "Analyse terminée",
- 'analysis_section': "Analyse Sémantique",
- 'analyze_document': 'Analyser le document',
- 'analysis_saved_success': 'Analyse enregistrée avec succès',
- 'analysis_save_error': 'Erreur lors de l\'enregistrement de l\'analyse',
- 'analyze_button': "Analyser le texte",
- 'analyzing_doc': "Analyse du document",
- 'activities_message': "Messages d'activités",
- 'activities_placeholder': "Espace réservé aux activités",
- 'analysis_placeholder': "Espace réservé à l'analyse",
- 'analyze_button': "Analyser",
- 'analysis_types_chart': "Graphique pour le type d'analyse",
- 'analysis_from': "Analyse réalisée sur",
- # C
- 'chat_title': "Chat d'Analyse",
- 'export_button': "Exporter l'Analyse Actuelle",
- 'export_success': "Analyse et chat exportés avec succès.",
- 'export_error': "Un problème est survenu lors de l'exportation de l'analyse et du chat.",
- 'get_text': "Obtenir du texte.",
- 'hello': "Bonjour",
- # L
- 'logout': "Déconnexion.",
- 'loading_data': "Chargement des données",
- 'load_selected_file': 'Charger le fichier sélectionné',
- # N
- 'no_analysis': "Aucune analyse disponible. Utilisez le chat pour effectuer une analyse.",
- 'nothing_to_export': "Aucune analyse ou chat à exporter.",
- 'results_title': "Résultats de l'Analyse",
- 'select_language': "Sélectionner la langue",
- 'student_activities': "Activités étudiantes",
- # T
- 'total_analyses': "Analyses totales",
- # W
- 'welcome': "Bienvenue à AIdeaText"
-}
-
-TABS = {
- 'current_situation_tab': "Ma situation actuelle",
- 'morpho_tab': "Analyse morphosyntaxique",
- 'semantic_live_tab': "Sémantique en direct",
- 'semantic_tab': "Analyse sémantique",
- 'discourse_live_tab': "Discours en direct",
- 'discourse_tab': "Analyse du discours",
- 'activities_tab': "Mes activités",
- 'feedback_tab': "Formulaire de commentaires"
-}
-
-CURRENT_SITUATION = {
- 'title': "Ma Situation Actuelle",
- 'input_prompt': "Écrivez ou collez votre texte ici :",
- 'first_analyze_button': "Analyser mon écriture",
- 'processing': "Analyse en cours...",
- 'analysis_error': "Erreur lors de l'analyse du texte",
- 'help': "Nous analyserons votre texte pour déterminer son état actuel",
-
- # Radio buttons pour type de texte
- 'text_type_header': "Type de texte",
- 'text_type_help': "Sélectionnez le type de texte pour ajuster les critères d'évaluation",
-
- # Métriques
- 'vocabulary_label': "Vocabulaire",
- 'vocabulary_help': "Richesse et variété du vocabulaire",
- 'structure_label': "Structure",
- 'structure_help': "Organisation et complexité des phrases",
- 'cohesion_label': "Cohésion",
- 'cohesion_help': "Connexion et fluidité entre les idées",
- 'clarity_label': "Clarté",
- 'clarity_help': "Facilité de compréhension du texte",
-
- # États des métriques
- 'metric_improvement': "⚠️ À améliorer",
- 'metric_acceptable': "📈 Acceptable",
- 'metric_optimal': "✅ Optimal",
- 'metric_target': "Objectif : {:.2f}",
-
- # Erreurs
- 'error_interface': "Une erreur s'est produite lors du chargement de l'interface",
- 'error_results': "Erreur lors de l'affichage des résultats",
- 'error_chart': "Erreur lors de l'affichage du graphique"
-}
-
-MORPHOSYNTACTIC = {
- #A
- 'arc_diagram': "Analyse syntaxique : Diagramme en arc",
- #B
- 'tab_text_baseline': "Produire le premier texte",
- 'tab_iterations': "Produire de nouvelles versions du premier texte",
-
- # Pestaña 1 texto base
- 'btn_new_morpho_analysis': "Nouvelle analyse morphosyntaxique",
- 'btn_analyze_baseline': "Analyser le texte saisi",
- 'input_baseline_text': "Saisissez le premier texte à analyser",
- 'warn_enter_text': "Veuillez saisir un texte à analyser",
- 'error_processing_baseline': "Erreur lors du traitement du texte initial",
- 'arc_diagram_baseline_label': "Diagramme en arc du texte initial",
- 'baseline_diagram_not_available': "Diagramme en arc du texte initial non disponible",
-
- # Pestaña 2 Iteración del texto
- 'info_first_analyze_base': "Vérifiez si le texte initial existe",
- 'iteration_text_subheader': "Nouvelle version du texte initial",
- 'input_iteration_text': "Saisissez une nouvelle version du texte initial et comparez les arcs des deux textes",
- 'btn_analyze_iteration': "Analyser les changements",
- 'warn_enter_iteration_text': "Saisissez une nouvelle version du texte initial et comparez les arcs des deux textes",
- 'iteration_saved': "Changements enregistrés avec succès",
- 'error_iteration': "Erreur lors du traitement des nouveaux changements"
-}
-
-SEMANTIC = {
- # C
- 'chat_title': "Chat d'Analyse Sémantique",
- 'chat_placeholder': "Posez une question ou utilisez une commande (/résumé, /entités, /sentiment, /thèmes, /graphe_concepts, /graphe_entités, /graphe_thèmes)",
- 'clear_chat': "Effacer le chat",
- 'conceptual_relations': "Relations Conceptuelles",
- # D
- 'delete_file': "Supprimer le fichier",
- # E
- 'error_message': "Un problème est survenu lors de l'enregistrement de l'analyse sémantique. Veuillez réessayer.",
- # F
- 'file_uploader': "Ou téléchargez un fichier texte",
- 'file_upload_success': "Fichier téléchargé et enregistré avec succès",
- 'file_upload_error': "Erreur lors du téléchargement du fichier",
- 'file_section': "Fichiers",
- 'file_loaded_success': "Fichier chargé avec succès",
- 'file_load_error': "Erreur lors du chargement du fichier",
- 'file_upload_error': "Erreur lors du téléchargement et de l'enregistrement du fichier",
- 'file_deleted_success': "Fichier supprimé avec succès",
- 'file_delete_error': "Erreur lors de la suppression du fichier",
- # G
- 'graph_title': "Visualisation de l'Analyse Sémantique",
- # I
- 'identified_entities': "Entités Identifiées",
- # K
- 'key_concepts': "Concepts Clés",
- # N
- 'no_analysis': "Aucune analyse disponible. Veuillez télécharger ou sélectionner un fichier.",
- 'no_results': "Aucun résultat disponible. Veuillez d'abord effectuer une analyse.",
- 'no_file': "Veuillez télécharger un fichier pour commencer l'analyse.",
- 'no_file_selected': "Veuillez sélectionner une archive pour démarrer l'analyse.",
- # S
- 'semantic_title': "Analyse Sémantique",
- 'semantic_initial_message': "Ceci est un chatbot à usage général, mais il a une fonction spécifique pour l'analyse visuelle de textes : il génère un graphe avec les principales entités du texte. Pour le produire, entrez un fichier texte au format txt, pdf, doc, docx ou odt et appuyez sur le bouton 'analyser le fichier'. Après la génération du graphe, vous pouvez interagir avec le chat en fonction du document.",
- 'send_button': "Envoyer",
- 'select_saved_file': "Sélectionner un fichier enregistré",
- 'success_message': "Analyse sémantique enregistrée avec succès.",
- 'semantic_analyze_button': 'Analyse Sémantique',
- 'semantic_export_button': 'Exporter l\'Analyse Sémantique',
- 'semantic_new_button': 'Nouvelle Analyse Sémantique',
- 'semantic_file_uploader': "Créer un fichier de texte pour l'analyse sémantique",
- # T
- 'text_input_label': "Entrez un texte à analyser (max. 5 000 mots) :",
- 'text_input_placeholder': "L'objectif de cette application est d'améliorer vos compétences en rédaction...",
- 'title': "AIdeaText - Analyse Sémantique",
- # U
- 'upload_file': "Télécharger le fichier",
- # W
- 'warning_message': "Veuillez entrer un texte ou télécharger un fichier à analyser."
-}
-
-DISCOURSE = {
- 'file1_label': "Document Modèle",
- 'file2_label': "Document Comparé",
- 'discourse_title': "AIdeaText - Analyse du discours",
- 'file_uploader1': "Télécharger le fichier texte 1 (Modèle)",
- 'file_uploader2': "Télécharger le fichier texte 2 (Comparaison)",
- 'discourse_analyze_button': "Comparer des textes",
- 'discourse_initial_message': "C'est un chatbot de proposition générale, mais il a une fonction spécifique pour l'analyse visuelle des textes : générer des graphiques avec les principales entités de chaque fichier pour faire une comparaison entre plusieurs textes. Pour produire, insérer un premier fichier et l'autre après au format txt, pdf, doc, docx ou odt et appuyez sur le bouton 'analyser les archives'. Après la génération du graphique, vous pouvez interagir avec le chat en fonction du document.",
- 'analyze_button': "Analyser les textes",
- 'comparison': "Comparaison des Relations Sémantiques",
- 'success_message': "Analyse du discours enregistrée avec succès.",
- 'error_message': "Un problème est survenu lors de l'enregistrement de l'analyse du discours. Veuillez réessayer.",
- 'warning_message': "Veuillez télécharger les deux fichiers à analyser.",
- 'no_results': "Aucun résultat disponible. Veuillez d'abord effectuer une analyse.",
- 'key_concepts': "Concepts Clés",
- 'graph_not_available': "Le graphique n'est pas disponible.",
- 'concepts_not_available': "Les concepts clés ne sont pas disponibles.",
- 'comparison_not_available': "La comparaison n'est pas disponible.",
- 'morphosyntax_history': "Historique morphosyntaxique",
- 'analysis_of': "Analyse de"
-}
-
-ACTIVITIES = {
- # Nouvelles étiquettes mises à jour
- 'current_situation_activities': "Registres de la fonction : Ma Situation Actuelle",
- 'morpho_activities': "Registres de mes analyses morphosyntaxiques",
- 'semantic_activities': "Registres de mes analyses sémantiques",
- 'discourse_activities': "Registres de mes analyses de comparaison de textes",
- 'chat_activities': "Registres de mes conversations avec le tuteur virtuel",
-
- # Maintenir d'autres clés existantes
- 'current_situation_tab': "Ma situation actuelle",
- 'morpho_tab': "Analyse morphosyntaxique",
- 'semantic_tab': "Analyse sémantique",
- 'discourse_tab': "Analyse de comparaison de textes",
- 'activities_tab': "Mon registre d'activités",
- 'feedback_tab': "Formulaire de commentaires",
-
- # Reste des clés qui sont dans le dictionnaire ACTIVITIES
- 'analysis_types_chart_title': "Types d'analyses effectuées",
- 'analysis_types_chart_x': "Type d'analyse",
- 'analysis_types_chart_y': "Nombre",
- 'analysis_from': "Analyse du",
- 'assistant': "Assistant",
- 'activities_summary': "Résumé des Activités et Progrès",
- 'chat_history_expander': "Historique des Conversations",
- 'chat_from': "Conversation du",
- 'combined_graph': "Graphique combiné",
- 'conceptual_relations_graph': "Graphique des relations conceptuelles",
- 'conversation': "Conversation",
- 'discourse_analyses_expander': "Historique des Analyses de Comparaison de Textes", # Mis à jour
- 'discourse_analyses': "Analyses de Comparaison de Textes", # Mis à jour
- 'discourse_history': "Historique des Analyses de Comparaison de Textes", # Mis à jour
- 'document': "Document",
- 'data_load_error': "Erreur lors du chargement des données de l'étudiant",
- 'graph_display_error': "Impossible d'afficher le graphique",
- 'graph_doc1': "Graphique document 1",
- 'graph_doc2': "Graphique document 2",
- 'key_concepts': "Concepts clés",
- 'loading_data': "Chargement des données de l'étudiant...",
- 'morphological_analysis': "Analyse Morphologique",
- 'morphosyntax_analyses_expander': "Historique des Analyses Morphosyntaxiques",
- 'morphosyntax_history': "Historique des Analyses Morphosyntaxiques",
- 'no_arc_diagram': "Aucun diagramme en arc trouvé pour cette analyse.",
- 'no_chat_history': "Aucune conversation avec le Tuteur Virtuel n'a été trouvée.", # Mis à jour
- 'no_data_warning': "Aucune donnée d'analyse trouvée pour cet étudiant.",
- 'progress_of': "Progrès de",
- 'semantic_analyses': "Analyses Sémantiques",
- 'semantic_analyses_expander': "Historique des Analyses Sémantiques",
- 'semantic_history': "Historique des Analyses Sémantiques",
- 'show_debug_data': "Afficher les données de débogage",
- 'student_debug_data': "Données de l'étudiant (pour le débogage) :",
- 'summary_title': "Résumé des Activités",
- 'title': "Mon Registre d'Activités", # Mis à jour
- 'timestamp': "Horodatage",
- 'total_analyses': "Total des analyses effectuées :",
- 'try_analysis': "Essayez d'effectuer d'abord quelques analyses de texte.",
- 'user': "Utilisateur",
-
- # Nouvelles traductions spécifiques pour la section activités
- 'diagnosis_tab': "Diagnostic",
- 'recommendations_tab': "Recommandations",
- 'key_metrics': "Métriques clés",
- 'details': "Détails",
- 'analyzed_text': "Texte analysé",
- 'analysis_date': "Date",
- 'academic_article': "Article académique",
- 'student_essay': "Dissertation d'étudiant",
- 'general_communication': "Communication générale",
- 'no_diagnosis': "Aucune donnée de diagnostic disponible",
- 'no_recommendations': "Aucune recommandation disponible",
- 'error_current_situation': "Erreur lors de l'affichage de l'analyse de la situation actuelle",
- 'no_current_situation': "Aucune analyse de situation actuelle enregistrée",
- 'no_morpho_analyses': "Aucune analyse morphosyntaxique enregistrée",
- 'error_morpho': "Erreur lors de l'affichage de l'analyse morphosyntaxique",
- 'no_semantic_analyses': "Aucune analyse sémantique enregistrée",
- 'error_semantic': "Erreur lors de l'affichage de l'analyse sémantique",
- 'no_discourse_analyses': "Aucune analyse de comparaison de textes enregistrée",
- 'error_discourse': "Erreur lors de l'affichage de l'analyse de comparaison de textes",
- 'no_chat_history': "Aucun enregistrement de conversation avec le tuteur virtuel",
- 'error_chat': "Erreur lors de l'affichage des enregistrements de conversation",
- 'error_loading_activities': "Erreur lors du chargement des activités",
- 'chat_date': "Date de conversation",
- 'invalid_chat_format': "Format de chat invalide",
- 'comparison_results': "Résultats de la comparaison",
- 'concepts_text_1': "Concepts Texte 1",
- 'concepts_text_2': "Concepts Texte 2",
- 'no_visualization': "Aucune visualisation comparative disponible",
- 'no_graph': "Aucune visualisation disponible",
- 'error_loading_graph': "Erreur lors du chargement du graphique",
- 'syntactic_diagrams': "Diagrammes syntaxiques"
-}
-
-FEEDBACK = {
- 'email': "E-mail",
- 'feedback': "Retour",
- 'feedback_title': "Formulaire de commentaires",
- 'feedback_error': "Un problème est survenu lors de l'envoi du formulaire. Veuillez réessayer.",
- 'feedback_success': "Merci pour votre retour",
- 'complete_all_fields': "Veuillez remplir tous les champs",
- 'name': "Nom",
- 'submit': "Envoyer"
-}
-
-CHATBOT_TRANSLATIONS = {
- 'chat_title': "Assistant AIdeaText",
- 'input_placeholder': "Des questions ?",
- 'initial_message': "Bonjour ! Je suis votre assistant. Comment puis-je vous aider ?",
- 'expand_chat': "Ouvrir l'assistant",
- 'clear_chat': "Effacer la conversation",
- 'processing': "Traitement en cours...",
- 'error_message': "Désolé, une erreur s'est produite"
-}
-
-TEXT_TYPES = {
- "descriptif": [
- "Que décrivez-vous ?",
- "Quelles sont ses principales caractéristiques ?",
- "À quoi ressemble-t-il, quel son produit-il, quelle odeur dégage-t-il ou quelle sensation procure-t-il ?",
- "Qu'est-ce qui le rend unique ou spécial ?"
- ],
- "narratif": [
- "Qui est le protagoniste ?",
- "Où et quand se déroule l'histoire ?",
- "Quel événement déclenche l'action ?",
- "Que se passe-t-il ensuite ?",
- "Comment se termine l'histoire ?"
- ],
- "explicatif": [
- "Quel est le sujet principal ?",
- "Quels aspects importants voulez-vous expliquer ?",
- "Pouvez-vous donner des exemples ou des données pour appuyer votre explication ?",
- "Comment ce sujet est-il lié à d'autres concepts ?"
- ],
- "argumentatif": [
- "Quelle est votre thèse ou argument principal ?",
- "Quels sont vos arguments de soutien ?",
- "Quelles preuves avez-vous pour étayer vos arguments ?",
- "Quels sont les contre-arguments et comment les réfutez-vous ?",
- "Quelle est votre conclusion ?"
- ],
- "instructif": [
- "Quelle tâche ou quel processus expliquez-vous ?",
- "Quels matériaux ou outils sont nécessaires ?",
- "Quelles sont les étapes à suivre ?",
- "Y a-t-il des précautions ou des conseils importants à mentionner ?"
- ],
- "pitch": [
- "Quoi ?",
- "Pour quoi ?",
- "Pour qui ?",
- "Comment ?"
- ]
-}
-
-# Configuration du modèle de langage pour le français
-NLP_MODEL = 'fr_core_news_lg'
-
-# Cette ligne est cruciale:
-TRANSLATIONS = {
- 'COMMON': COMMON,
- 'TABS': TABS,
- 'MORPHOSYNTACTIC': MORPHOSYNTACTIC,
- 'SEMANTIC': SEMANTIC,
- 'DISCOURSE': DISCOURSE,
- 'ACTIVITIES': ACTIVITIES,
- 'FEEDBACK': FEEDBACK,
- 'TEXT_TYPES': TEXT_TYPES,
- 'CURRENT_SITUATION': CURRENT_SITUATION,
- 'NLP_MODEL': NLP_MODEL
+# translations/fr.py
+
+COMMON = {
+ # A
+ 'initial_instruction': "Pour démarrer une nouvelle analyse sémantique, téléchargez un nouveau fichier texte (.txt)",
+ 'analysis_complete': "Analyse terminée et enregistrée. Pour effectuer une nouvelle analyse, téléchargez un autre fichier.",
+ 'current_analysis_message': "Affichage de l'analyse du fichier : {}. Pour effectuer une nouvelle analyse, veuillez télécharger un autre fichier.",
+ 'upload_prompt': "Joindre un fichier pour démarrer l'analyse",
+ 'analysis_completed': "Analyse terminée",
+ 'analysis_section': "Analyse Sémantique",
+ 'analyze_document': 'Analyser le document',
+ 'analysis_saved_success': 'Analyse enregistrée avec succès',
+ 'analysis_save_error': 'Erreur lors de l\'enregistrement de l\'analyse',
+ 'analyze_button': "Analyser le texte",
+ 'analyzing_doc': "Analyse du document",
+ 'activities_message': "Messages d'activités",
+ 'activities_placeholder': "Espace réservé aux activités",
+ 'analysis_placeholder': "Espace réservé à l'analyse",
+ 'analyze_button': "Analyser",
+ 'analysis_types_chart': "Graphique pour le type d'analyse",
+ 'analysis_from': "Analyse réalisée sur",
+ # C
+ 'chat_title': "Chat d'Analyse",
+ 'export_button': "Exporter l'Analyse Actuelle",
+ 'export_success': "Analyse et chat exportés avec succès.",
+ 'export_error': "Un problème est survenu lors de l'exportation de l'analyse et du chat.",
+ 'get_text': "Obtenir du texte.",
+ 'hello': "Bonjour",
+ # L
+ 'logout': "Déconnexion.",
+ 'loading_data': "Chargement des données",
+ 'load_selected_file': 'Charger le fichier sélectionné',
+ # N
+ 'no_analysis': "Aucune analyse disponible. Utilisez le chat pour effectuer une analyse.",
+ 'nothing_to_export': "Aucune analyse ou chat à exporter.",
+ 'results_title': "Résultats de l'Analyse",
+ 'select_language': "Sélectionner la langue",
+ 'student_activities': "Activités étudiantes",
+ # T
+ 'total_analyses': "Analyses totales",
+ # W
+ 'welcome': "Bienvenue à AIdeaText"
+}
+
+TABS = {
+ 'current_situation_tab': "Ma situation actuelle",
+ 'morpho_tab': "Analyse morphosyntaxique",
+ 'semantic_live_tab': "Sémantique en direct",
+ 'semantic_tab': "Analyse sémantique",
+ 'discourse_live_tab': "Discours en direct",
+ 'discourse_tab': "Analyse du discours",
+ 'activities_tab': "Mes activités",
+ 'feedback_tab': "Formulaire de commentaires"
+}
+
+CURRENT_SITUATION = {
+ 'title': "Ma Situation Actuelle",
+ 'input_prompt': "Écrivez ou collez votre texte ici :",
+ 'first_analyze_button': "Analyser mon écriture",
+ 'processing': "Analyse en cours...",
+ 'analysis_error': "Erreur lors de l'analyse du texte",
+ 'help': "Nous analyserons votre texte pour déterminer son état actuel",
+
+ # Radio buttons pour type de texte
+ 'text_type_header': "Type de texte",
+ 'text_type_help': "Sélectionnez le type de texte pour ajuster les critères d'évaluation",
+
+ # Métriques
+ 'vocabulary_label': "Vocabulaire",
+ 'vocabulary_help': "Richesse et variété du vocabulaire",
+ 'structure_label': "Structure",
+ 'structure_help': "Organisation et complexité des phrases",
+ 'cohesion_label': "Cohésion",
+ 'cohesion_help': "Connexion et fluidité entre les idées",
+ 'clarity_label': "Clarté",
+ 'clarity_help': "Facilité de compréhension du texte",
+
+ # États des métriques
+ 'metric_improvement': "⚠️ À améliorer",
+ 'metric_acceptable': "📈 Acceptable",
+ 'metric_optimal': "✅ Optimal",
+ 'metric_target': "Objectif : {:.2f}",
+
+ # Erreurs
+ 'error_interface': "Une erreur s'est produite lors du chargement de l'interface",
+ 'error_results': "Erreur lors de l'affichage des résultats",
+ 'error_chart': "Erreur lors de l'affichage du graphique"
+}
+
+MORPHOSYNTACTIC = {
+ #A
+ 'arc_diagram': "Analyse syntaxique : Diagramme en arc",
+ #B
+ 'tab_text_baseline': "Produire le premier texte",
+ 'tab_iterations': "Produire de nouvelles versions du premier texte",
+
+ # Pestaña 1 texto base
+ 'btn_new_morpho_analysis': "Nouvelle analyse morphosyntaxique",
+ 'btn_analyze_baseline': "Analyser le texte saisi",
+ 'input_baseline_text': "Saisissez le premier texte à analyser",
+ 'warn_enter_text': "Veuillez saisir un texte à analyser",
+ 'error_processing_baseline': "Erreur lors du traitement du texte initial",
+ 'arc_diagram_baseline_label': "Diagramme en arc du texte initial",
+ 'baseline_diagram_not_available': "Diagramme en arc du texte initial non disponible",
+
+ # Pestaña 2 Iteración del texto
+ 'info_first_analyze_base': "Vérifiez si le texte initial existe",
+ 'iteration_text_subheader': "Nouvelle version du texte initial",
+ 'input_iteration_text': "Saisissez une nouvelle version du texte initial et comparez les arcs des deux textes",
+ 'btn_analyze_iteration': "Analyser les changements",
+ 'warn_enter_iteration_text': "Saisissez une nouvelle version du texte initial et comparez les arcs des deux textes",
+ 'iteration_saved': "Changements enregistrés avec succès",
+ 'error_iteration': "Erreur lors du traitement des nouveaux changements"
+}
+
+SEMANTIC = {
+ # C
+ 'chat_title': "Chat d'Analyse Sémantique",
+ 'chat_placeholder': "Posez une question ou utilisez une commande (/résumé, /entités, /sentiment, /thèmes, /graphe_concepts, /graphe_entités, /graphe_thèmes)",
+ 'clear_chat': "Effacer le chat",
+ 'conceptual_relations': "Relations Conceptuelles",
+ # D
+ 'delete_file': "Supprimer le fichier",
+ # E
+ 'error_message': "Un problème est survenu lors de l'enregistrement de l'analyse sémantique. Veuillez réessayer.",
+ # F
+ 'file_uploader': "Ou téléchargez un fichier texte",
+ 'file_upload_success': "Fichier téléchargé et enregistré avec succès",
+ 'file_upload_error': "Erreur lors du téléchargement du fichier",
+ 'file_section': "Fichiers",
+ 'file_loaded_success': "Fichier chargé avec succès",
+ 'file_load_error': "Erreur lors du chargement du fichier",
+ 'file_upload_error': "Erreur lors du téléchargement et de l'enregistrement du fichier",
+ 'file_deleted_success': "Fichier supprimé avec succès",
+ 'file_delete_error': "Erreur lors de la suppression du fichier",
+ # G
+ 'graph_title': "Visualisation de l'Analyse Sémantique",
+ # I
+ 'identified_entities': "Entités Identifiées",
+ # K
+ 'key_concepts': "Concepts Clés",
+ # N
+ 'no_analysis': "Aucune analyse disponible. Veuillez télécharger ou sélectionner un fichier.",
+ 'no_results': "Aucun résultat disponible. Veuillez d'abord effectuer une analyse.",
+ 'no_file': "Veuillez télécharger un fichier pour commencer l'analyse.",
+ 'no_file_selected': "Veuillez sélectionner une archive pour démarrer l'analyse.",
+ # S
+ 'semantic_title': "Analyse Sémantique",
+ 'semantic_initial_message': "Ceci est un chatbot à usage général, mais il a une fonction spécifique pour l'analyse visuelle de textes : il génère un graphe avec les principales entités du texte. Pour le produire, entrez un fichier texte au format txt, pdf, doc, docx ou odt et appuyez sur le bouton 'analyser le fichier'. Après la génération du graphe, vous pouvez interagir avec le chat en fonction du document.",
+ 'send_button': "Envoyer",
+ 'select_saved_file': "Sélectionner un fichier enregistré",
+ 'success_message': "Analyse sémantique enregistrée avec succès.",
+ 'semantic_analyze_button': 'Analyse Sémantique',
+ 'semantic_export_button': 'Exporter l\'Analyse Sémantique',
+ 'semantic_new_button': 'Nouvelle Analyse Sémantique',
+ 'semantic_file_uploader': "Créer un fichier de texte pour l'analyse sémantique",
+ # T
+ 'text_input_label': "Entrez un texte à analyser (max. 5 000 mots) :",
+ 'text_input_placeholder': "L'objectif de cette application est d'améliorer vos compétences en rédaction...",
+ 'title': "AIdeaText - Analyse Sémantique",
+ # U
+ 'upload_file': "Télécharger le fichier",
+ # W
+ 'warning_message': "Veuillez entrer un texte ou télécharger un fichier à analyser."
+}
+
+DISCOURSE = {
+ 'file1_label': "Document Modèle",
+ 'file2_label': "Document Comparé",
+ 'discourse_title': "AIdeaText - Analyse du discours",
+ 'file_uploader1': "Télécharger le fichier texte 1 (Modèle)",
+ 'file_uploader2': "Télécharger le fichier texte 2 (Comparaison)",
+ 'discourse_analyze_button': "Comparer des textes",
+ 'discourse_initial_message': "C'est un chatbot de proposition générale, mais il a une fonction spécifique pour l'analyse visuelle des textes : générer des graphiques avec les principales entités de chaque fichier pour faire une comparaison entre plusieurs textes. Pour produire, insérer un premier fichier et l'autre après au format txt, pdf, doc, docx ou odt et appuyez sur le bouton 'analyser les archives'. Après la génération du graphique, vous pouvez interagir avec le chat en fonction du document.",
+ 'analyze_button': "Analyser les textes",
+ 'comparison': "Comparaison des Relations Sémantiques",
+ 'success_message': "Analyse du discours enregistrée avec succès.",
+ 'error_message': "Un problème est survenu lors de l'enregistrement de l'analyse du discours. Veuillez réessayer.",
+ 'warning_message': "Veuillez télécharger les deux fichiers à analyser.",
+ 'no_results': "Aucun résultat disponible. Veuillez d'abord effectuer une analyse.",
+ 'key_concepts': "Concepts Clés",
+ 'graph_not_available': "Le graphique n'est pas disponible.",
+ 'concepts_not_available': "Les concepts clés ne sont pas disponibles.",
+ 'comparison_not_available': "La comparaison n'est pas disponible.",
+ 'morphosyntax_history': "Historique morphosyntaxique",
+ 'analysis_of': "Analyse de"
+}
+
+ACTIVITIES = {
+ # Nouvelles étiquettes mises à jour
+ 'current_situation_activities': "Registres de la fonction : Ma Situation Actuelle",
+ 'morpho_activities': "Registres de mes analyses morphosyntaxiques",
+ 'semantic_activities': "Registres de mes analyses sémantiques",
+ 'discourse_activities': "Registres de mes analyses de comparaison de textes",
+ 'chat_activities': "Registres de mes conversations avec le tuteur virtuel",
+
+ # Maintenir d'autres clés existantes
+ 'current_situation_tab': "Ma situation actuelle",
+ 'morpho_tab': "Analyse morphosyntaxique",
+ 'semantic_tab': "Analyse sémantique",
+ 'discourse_tab': "Analyse de comparaison de textes",
+ 'activities_tab': "Mon registre d'activités",
+ 'feedback_tab': "Formulaire de commentaires",
+
+ # Reste des clés qui sont dans le dictionnaire ACTIVITIES
+ 'analysis_types_chart_title': "Types d'analyses effectuées",
+ 'analysis_types_chart_x': "Type d'analyse",
+ 'analysis_types_chart_y': "Nombre",
+ 'analysis_from': "Analyse du",
+ 'assistant': "Assistant",
+ 'activities_summary': "Résumé des Activités et Progrès",
+ 'chat_history_expander': "Historique des Conversations",
+ 'chat_from': "Conversation du",
+ 'combined_graph': "Graphique combiné",
+ 'conceptual_relations_graph': "Graphique des relations conceptuelles",
+ 'conversation': "Conversation",
+ 'discourse_analyses_expander': "Historique des Analyses de Comparaison de Textes", # Mis à jour
+ 'discourse_analyses': "Analyses de Comparaison de Textes", # Mis à jour
+ 'discourse_history': "Historique des Analyses de Comparaison de Textes", # Mis à jour
+ 'document': "Document",
+ 'data_load_error': "Erreur lors du chargement des données de l'étudiant",
+ 'graph_display_error': "Impossible d'afficher le graphique",
+ 'graph_doc1': "Graphique document 1",
+ 'graph_doc2': "Graphique document 2",
+ 'key_concepts': "Concepts clés",
+ 'loading_data': "Chargement des données de l'étudiant...",
+ 'morphological_analysis': "Analyse Morphologique",
+ 'morphosyntax_analyses_expander': "Historique des Analyses Morphosyntaxiques",
+ 'morphosyntax_history': "Historique des Analyses Morphosyntaxiques",
+ 'no_arc_diagram': "Aucun diagramme en arc trouvé pour cette analyse.",
+ 'no_chat_history': "Aucune conversation avec le Tuteur Virtuel n'a été trouvée.", # Mis à jour
+ 'no_data_warning': "Aucune donnée d'analyse trouvée pour cet étudiant.",
+ 'progress_of': "Progrès de",
+ 'semantic_analyses': "Analyses Sémantiques",
+ 'semantic_analyses_expander': "Historique des Analyses Sémantiques",
+ 'semantic_history': "Historique des Analyses Sémantiques",
+ 'show_debug_data': "Afficher les données de débogage",
+ 'student_debug_data': "Données de l'étudiant (pour le débogage) :",
+ 'summary_title': "Résumé des Activités",
+ 'title': "Mon Registre d'Activités", # Mis à jour
+ 'timestamp': "Horodatage",
+ 'total_analyses': "Total des analyses effectuées :",
+ 'try_analysis': "Essayez d'effectuer d'abord quelques analyses de texte.",
+ 'user': "Utilisateur",
+
+ # Nouvelles traductions spécifiques pour la section activités
+ 'diagnosis_tab': "Diagnostic",
+ 'recommendations_tab': "Recommandations",
+ 'key_metrics': "Métriques clés",
+ 'details': "Détails",
+ 'analyzed_text': "Texte analysé",
+ 'analysis_date': "Date",
+ 'academic_article': "Article académique",
+ 'student_essay': "Dissertation d'étudiant",
+ 'general_communication': "Communication générale",
+ 'no_diagnosis': "Aucune donnée de diagnostic disponible",
+ 'no_recommendations': "Aucune recommandation disponible",
+ 'error_current_situation': "Erreur lors de l'affichage de l'analyse de la situation actuelle",
+ 'no_current_situation': "Aucune analyse de situation actuelle enregistrée",
+ 'no_morpho_analyses': "Aucune analyse morphosyntaxique enregistrée",
+ 'error_morpho': "Erreur lors de l'affichage de l'analyse morphosyntaxique",
+ 'no_semantic_analyses': "Aucune analyse sémantique enregistrée",
+ 'error_semantic': "Erreur lors de l'affichage de l'analyse sémantique",
+ 'no_discourse_analyses': "Aucune analyse de comparaison de textes enregistrée",
+ 'error_discourse': "Erreur lors de l'affichage de l'analyse de comparaison de textes",
+ 'no_chat_history': "Aucun enregistrement de conversation avec le tuteur virtuel",
+ 'error_chat': "Erreur lors de l'affichage des enregistrements de conversation",
+ 'error_loading_activities': "Erreur lors du chargement des activités",
+ 'chat_date': "Date de conversation",
+ 'invalid_chat_format': "Format de chat invalide",
+ 'comparison_results': "Résultats de la comparaison",
+ 'concepts_text_1': "Concepts Texte 1",
+ 'concepts_text_2': "Concepts Texte 2",
+ 'no_visualization': "Aucune visualisation comparative disponible",
+ 'no_graph': "Aucune visualisation disponible",
+ 'error_loading_graph': "Erreur lors du chargement du graphique",
+ 'syntactic_diagrams': "Diagrammes syntaxiques"
+}
+
+FEEDBACK = {
+ 'email': "E-mail",
+ 'feedback': "Retour",
+ 'feedback_title': "Formulaire de commentaires",
+ 'feedback_error': "Un problème est survenu lors de l'envoi du formulaire. Veuillez réessayer.",
+ 'feedback_success': "Merci pour votre retour",
+ 'complete_all_fields': "Veuillez remplir tous les champs",
+ 'name': "Nom",
+ 'submit': "Envoyer"
+}
+
+CHATBOT_TRANSLATIONS = {
+ 'chat_title': "Assistant AIdeaText",
+ 'input_placeholder': "Des questions ?",
+ 'initial_message': "Bonjour ! Je suis votre assistant. Comment puis-je vous aider ?",
+ 'expand_chat': "Ouvrir l'assistant",
+ 'clear_chat': "Effacer la conversation",
+ 'processing': "Traitement en cours...",
+ 'error_message': "Désolé, une erreur s'est produite"
+}
+
+TEXT_TYPES = {
+ "descriptif": [
+ "Que décrivez-vous ?",
+ "Quelles sont ses principales caractéristiques ?",
+ "À quoi ressemble-t-il, quel son produit-il, quelle odeur dégage-t-il ou quelle sensation procure-t-il ?",
+ "Qu'est-ce qui le rend unique ou spécial ?"
+ ],
+ "narratif": [
+ "Qui est le protagoniste ?",
+ "Où et quand se déroule l'histoire ?",
+ "Quel événement déclenche l'action ?",
+ "Que se passe-t-il ensuite ?",
+ "Comment se termine l'histoire ?"
+ ],
+ "explicatif": [
+ "Quel est le sujet principal ?",
+ "Quels aspects importants voulez-vous expliquer ?",
+ "Pouvez-vous donner des exemples ou des données pour appuyer votre explication ?",
+ "Comment ce sujet est-il lié à d'autres concepts ?"
+ ],
+ "argumentatif": [
+ "Quelle est votre thèse ou argument principal ?",
+ "Quels sont vos arguments de soutien ?",
+ "Quelles preuves avez-vous pour étayer vos arguments ?",
+ "Quels sont les contre-arguments et comment les réfutez-vous ?",
+ "Quelle est votre conclusion ?"
+ ],
+ "instructif": [
+ "Quelle tâche ou quel processus expliquez-vous ?",
+ "Quels matériaux ou outils sont nécessaires ?",
+ "Quelles sont les étapes à suivre ?",
+ "Y a-t-il des précautions ou des conseils importants à mentionner ?"
+ ],
+ "pitch": [
+ "Quoi ?",
+ "Pour quoi ?",
+ "Pour qui ?",
+ "Comment ?"
+ ]
+}
+
+# Configuration du modèle de langage pour le français
+NLP_MODEL = 'fr_core_news_lg'
+
+# Cette ligne est cruciale:
+TRANSLATIONS = {
+ 'COMMON': COMMON,
+ 'TABS': TABS,
+ 'MORPHOSYNTACTIC': MORPHOSYNTACTIC,
+ 'SEMANTIC': SEMANTIC,
+ 'DISCOURSE': DISCOURSE,
+ 'ACTIVITIES': ACTIVITIES,
+ 'FEEDBACK': FEEDBACK,
+ 'TEXT_TYPES': TEXT_TYPES,
+ 'CURRENT_SITUATION': CURRENT_SITUATION,
+ 'NLP_MODEL': NLP_MODEL
}
\ No newline at end of file
diff --git a/translations/landing_translations.py b/translations/landing_translations.py
index 45710e6ce55a94db3550c8216e23cea33c35cdf8..f1f8a9e0fbc021a261b3418c921d17bb9fae6396 100644
--- a/translations/landing_translations.py
+++ b/translations/landing_translations.py
@@ -1,224 +1,224 @@
-# landing_translations.py
-
-LANDING_TRANSLATIONS = {
- 'en': {
- # Language selector
- 'select_language': "Select language - Sélectionner la langue - Selecionar idioma - Selecciona tu idioma",
-
- # Auth tabs
- 'login': "Login",
- 'register': "Sign Up",
-
- # Login form
- 'email': "Email",
- 'password': "Password",
- 'login_button': "Login",
- 'invalid_credentials': "Invalid credentials",
-
- # Registration form
- 'name': "Name",
- 'lastname': "Last Name",
- 'institution': "Institution",
- 'current_role': "Role in your institution",
- 'professor': "Professor",
- 'student': "Student",
- 'administrative': "Administrative",
- 'institutional_email': "Institutional email",
- 'interest_reason': "Why are you interested in trying AIdeaText?",
- 'submit_application': "Submit application",
- 'complete_all_fields': "Please complete all fields.",
- 'use_institutional_email': "Please use an institutional email.",
- 'application_sent': "Your application has been sent. We will contact you soon.",
- 'application_error': "There was a problem sending your application. Please try again later.",
-
- # Info tabs
- 'use_cases': "Use Cases",
- 'presentation_videos': "Presentation Videos",
- 'academic_presentations': "Academic Presentations",
- 'event_photos': "Event Photos",
- 'version_control': "Version Control",
-
- # Video selectors
- 'select_use_case': "Select a use case in English or Spanish:",
- 'select_presentation': "Select a presentation:",
-
- # Version control info
- 'latest_version_title': "Latest Version Updates",
- 'version_updates': [
- "Improved interface for a better user experience",
- "Optimization of morphosyntactic analysis",
- "Support for multiple languages",
- "New discourse analysis module",
- "Integrated chat system for support"
- ]
- },
- 'es': {
- # Language selector
- 'select_language': "Selecciona tu idioma - Select language - Sélectionner la langue - Selecionar idioma",
-
- # Auth tabs
- 'login': "Iniciar Sesión",
- 'register': "Registrarse",
-
- # Login form
- 'email': "Correo electrónico",
- 'password': "Contraseña",
- 'login_button': "Iniciar Sesión",
- 'invalid_credentials': "Credenciales incorrectas",
-
- # Registration form
- 'name': "Nombre",
- 'lastname': "Apellidos",
- 'institution': "Institución",
- 'current_role': "Rol en la institución donde labora",
- 'professor': "Profesor",
- 'student': "Estudiante",
- 'administrative': "Administrativo",
- 'institutional_email': "Correo electrónico de su institución",
- 'interest_reason': "¿Por qué estás interesado en probar AIdeaText?",
- 'submit_application': "Enviar solicitud",
- 'complete_all_fields': "Por favor, completa todos los campos.",
- 'use_institutional_email': "Por favor, utiliza un correo electrónico institucional.",
- 'application_sent': "Tu solicitud ha sido enviada. Te contactaremos pronto.",
- 'application_error': "Hubo un problema al enviar tu solicitud. Por favor, intenta de nuevo más tarde.",
-
- # Info tabs
- 'use_cases': "Casos de uso",
- 'presentation_videos': "Videos de presentaciones",
- 'academic_presentations': "Ponencias académicas",
- 'event_photos': "Fotos de eventos",
- 'version_control': "Control de versiones",
-
- # Video selectors
- 'select_use_case': "Selecciona un caso de uso en español o en inglés:",
- 'select_presentation': "Selecciona una conferencia:",
-
- # Version control info
- 'latest_version_title': "Novedades de la versión actual",
- 'version_updates': [
- "Interfaz mejorada para una mejor experiencia de usuario",
- "Optimización del análisis morfosintáctico",
- "Soporte para múltiples idiomas",
- "Nuevo módulo de análisis del discurso",
- "Sistema de chat integrado para soporte"
- ]
- },
- 'fr': {
- # Language selector
- 'select_language': "Sélectionner la langue - Selecciona tu idioma - Select language - Selecionar idioma",
-
- # Auth tabs
- 'login': "Se connecter",
- 'register': "S'inscrire",
-
- # Login form
- 'email': "E-mail",
- 'password': "Mot de passe",
- 'login_button': "Se connecter",
- 'invalid_credentials': "Identifiants incorrects",
-
- # Registration form
- 'name': "Nom",
- 'lastname': "Nom de famille",
- 'institution': "Institution",
- 'current_role': "Rôle dans votre institution",
- 'professor': "Professeur",
- 'student': "Étudiant",
- 'administrative': "Administratif",
- 'institutional_email': "E-mail institutionnel",
- 'interest_reason': "Pourquoi êtes-vous intéressé à essayer AIdeaText?",
- 'submit_application': "Soumettre la demande",
- 'complete_all_fields': "Veuillez remplir tous les champs.",
- 'use_institutional_email': "Veuillez utiliser un e-mail institutionnel.",
- 'application_sent': "Votre demande a été envoyée. Nous vous contacterons bientôt.",
- 'application_error': "Un problème est survenu lors de l'envoi de votre demande. Veuillez réessayer plus tard.",
-
- # Info tabs
- 'use_cases': "Cas d'utilisation",
- 'presentation_videos': "Vidéos de présentation",
- 'academic_presentations': "Présentations académiques",
- 'event_photos': "Photos d'événements",
- 'version_control': "Contrôle de version",
-
- # Video selectors
- 'select_use_case': "Sélectionnez un cas d'utilisation en français ou en anglais:",
- 'select_presentation': "Sélectionnez une conférence:",
-
- # Version control info
- 'latest_version_title': "Mises à jour de la dernière version",
- 'version_updates': [
- "Interface améliorée pour une meilleure expérience utilisateur",
- "Optimisation de l'analyse morphosyntaxique",
- "Support pour plusieurs langues",
- "Nouveau module d'analyse du discours",
- "Système de chat intégré pour le support"
- ]
- },
- 'pt': {
- # Language selector
- 'select_language': "Selecionar idioma - Sélectionner la langue - Selecciona tu idioma - Select language",
-
- # Auth tabs
- 'login': "Entrar",
- 'register': "Registrar-se",
-
- # Login form
- 'email': "Email",
- 'password': "Senha",
- 'login_button': "Entrar",
- 'invalid_credentials': "Credenciais inválidas",
-
- # Registration form
- 'name': "Nome",
- 'lastname': "Sobrenome",
- 'institution': "Instituição",
- 'current_role': "Função na sua instituição",
- 'professor': "Professor",
- 'student': "Estudante",
- 'administrative': "Administrativo",
- 'institutional_email': "Email institucional",
- 'interest_reason': "Por que você está interessado em experimentar o AIdeaText?",
- 'submit_application': "Enviar solicitação",
- 'complete_all_fields': "Por favor, preencha todos os campos.",
- 'use_institutional_email': "Por favor, use um email institucional.",
- 'application_sent': "Sua solicitação foi enviada. Entraremos em contato em breve.",
- 'application_error': "Houve um problema ao enviar sua solicitação. Por favor, tente novamente mais tarde.",
-
- # Info tabs
- 'use_cases': "Casos de uso",
- 'presentation_videos': "Vídeos de apresentação",
- 'academic_presentations': "Apresentações acadêmicas",
- 'event_photos': "Fotos de eventos",
- 'version_control': "Controle de versão",
-
- # Video selectors
- 'select_use_case': "Selecione um caso de uso em português ou inglês:",
- 'select_presentation': "Selecione uma conferência:",
-
- # Version control info
- 'latest_version_title': "Atualizações da versão mais recente",
- 'version_updates': [
- "Interface melhorada para uma melhor experiência do usuário",
- "Otimização da análise morfossintática",
- "Suporte para vários idiomas",
- "Novo módulo de análise do discurso",
- "Sistema de chat integrado para suporte"
- ]
- }
-}
-
-def get_landing_translations(lang_code):
- """
- Get translations for the landing page based on language code.
-
- Args:
- lang_code (str): Language code ('en', 'es', 'fr', 'pt')
-
- Returns:
- dict: Dictionary with translations for the landing page
- """
- # Default to Spanish if language not available
- if lang_code not in LANDING_TRANSLATIONS:
- lang_code = 'es'
-
+# landing_translations.py
+
+LANDING_TRANSLATIONS = {
+ 'en': {
+ # Language selector
+ 'select_language': "Select language - Sélectionner la langue - Selecionar idioma - Selecciona tu idioma",
+
+ # Auth tabs
+ 'login': "Login",
+ 'register': "Sign Up",
+
+ # Login form
+ 'email': "Email",
+ 'password': "Password",
+ 'login_button': "Login",
+ 'invalid_credentials': "Invalid credentials",
+
+ # Registration form
+ 'name': "Name",
+ 'lastname': "Last Name",
+ 'institution': "Institution",
+ 'current_role': "Role in your institution",
+ 'professor': "Professor",
+ 'student': "Student",
+ 'administrative': "Administrative",
+ 'institutional_email': "Institutional email",
+ 'interest_reason': "Why are you interested in trying AIdeaText?",
+ 'submit_application': "Submit application",
+ 'complete_all_fields': "Please complete all fields.",
+ 'use_institutional_email': "Please use an institutional email.",
+ 'application_sent': "Your application has been sent. We will contact you soon.",
+ 'application_error': "There was a problem sending your application. Please try again later.",
+
+ # Info tabs
+ 'use_cases': "Use Cases",
+ 'presentation_videos': "Presentation Videos",
+ 'academic_presentations': "Academic Presentations",
+ 'event_photos': "Event Photos",
+ 'version_control': "Version Control",
+
+ # Video selectors
+ 'select_use_case': "Select a use case in English or Spanish:",
+ 'select_presentation': "Select a presentation:",
+
+ # Version control info
+ 'latest_version_title': "Latest Version Updates",
+ 'version_updates': [
+ "Improved interface for a better user experience",
+ "Optimization of morphosyntactic analysis",
+ "Support for multiple languages",
+ "New discourse analysis module",
+ "Integrated chat system for support"
+ ]
+ },
+ 'es': {
+ # Language selector
+ 'select_language': "Selecciona tu idioma - Select language - Sélectionner la langue - Selecionar idioma",
+
+ # Auth tabs
+ 'login': "Iniciar Sesión",
+ 'register': "Registrarse",
+
+ # Login form
+ 'email': "Correo electrónico",
+ 'password': "Contraseña",
+ 'login_button': "Iniciar Sesión",
+ 'invalid_credentials': "Credenciales incorrectas",
+
+ # Registration form
+ 'name': "Nombre",
+ 'lastname': "Apellidos",
+ 'institution': "Institución",
+ 'current_role': "Rol en la institución donde labora",
+ 'professor': "Profesor",
+ 'student': "Estudiante",
+ 'administrative': "Administrativo",
+ 'institutional_email': "Correo electrónico de su institución",
+ 'interest_reason': "¿Por qué estás interesado en probar AIdeaText?",
+ 'submit_application': "Enviar solicitud",
+ 'complete_all_fields': "Por favor, completa todos los campos.",
+ 'use_institutional_email': "Por favor, utiliza un correo electrónico institucional.",
+ 'application_sent': "Tu solicitud ha sido enviada. Te contactaremos pronto.",
+ 'application_error': "Hubo un problema al enviar tu solicitud. Por favor, intenta de nuevo más tarde.",
+
+ # Info tabs
+ 'use_cases': "Casos de uso",
+ 'presentation_videos': "Videos de presentaciones",
+ 'academic_presentations': "Ponencias académicas",
+ 'event_photos': "Fotos de eventos",
+ 'version_control': "Control de versiones",
+
+ # Video selectors
+ 'select_use_case': "Selecciona un caso de uso en español o en inglés:",
+ 'select_presentation': "Selecciona una conferencia:",
+
+ # Version control info
+ 'latest_version_title': "Novedades de la versión actual",
+ 'version_updates': [
+ "Interfaz mejorada para una mejor experiencia de usuario",
+ "Optimización del análisis morfosintáctico",
+ "Soporte para múltiples idiomas",
+ "Nuevo módulo de análisis del discurso",
+ "Sistema de chat integrado para soporte"
+ ]
+ },
+ 'fr': {
+ # Language selector
+ 'select_language': "Sélectionner la langue - Selecciona tu idioma - Select language - Selecionar idioma",
+
+ # Auth tabs
+ 'login': "Se connecter",
+ 'register': "S'inscrire",
+
+ # Login form
+ 'email': "E-mail",
+ 'password': "Mot de passe",
+ 'login_button': "Se connecter",
+ 'invalid_credentials': "Identifiants incorrects",
+
+ # Registration form
+ 'name': "Nom",
+ 'lastname': "Nom de famille",
+ 'institution': "Institution",
+ 'current_role': "Rôle dans votre institution",
+ 'professor': "Professeur",
+ 'student': "Étudiant",
+ 'administrative': "Administratif",
+ 'institutional_email': "E-mail institutionnel",
+ 'interest_reason': "Pourquoi êtes-vous intéressé à essayer AIdeaText?",
+ 'submit_application': "Soumettre la demande",
+ 'complete_all_fields': "Veuillez remplir tous les champs.",
+ 'use_institutional_email': "Veuillez utiliser un e-mail institutionnel.",
+ 'application_sent': "Votre demande a été envoyée. Nous vous contacterons bientôt.",
+ 'application_error': "Un problème est survenu lors de l'envoi de votre demande. Veuillez réessayer plus tard.",
+
+ # Info tabs
+ 'use_cases': "Cas d'utilisation",
+ 'presentation_videos': "Vidéos de présentation",
+ 'academic_presentations': "Présentations académiques",
+ 'event_photos': "Photos d'événements",
+ 'version_control': "Contrôle de version",
+
+ # Video selectors
+ 'select_use_case': "Sélectionnez un cas d'utilisation en français ou en anglais:",
+ 'select_presentation': "Sélectionnez une conférence:",
+
+ # Version control info
+ 'latest_version_title': "Mises à jour de la dernière version",
+ 'version_updates': [
+ "Interface améliorée pour une meilleure expérience utilisateur",
+ "Optimisation de l'analyse morphosyntaxique",
+ "Support pour plusieurs langues",
+ "Nouveau module d'analyse du discours",
+ "Système de chat intégré pour le support"
+ ]
+ },
+ 'pt': {
+ # Language selector
+ 'select_language': "Selecionar idioma - Sélectionner la langue - Selecciona tu idioma - Select language",
+
+ # Auth tabs
+ 'login': "Entrar",
+ 'register': "Registrar-se",
+
+ # Login form
+ 'email': "Email",
+ 'password': "Senha",
+ 'login_button': "Entrar",
+ 'invalid_credentials': "Credenciais inválidas",
+
+ # Registration form
+ 'name': "Nome",
+ 'lastname': "Sobrenome",
+ 'institution': "Instituição",
+ 'current_role': "Função na sua instituição",
+ 'professor': "Professor",
+ 'student': "Estudante",
+ 'administrative': "Administrativo",
+ 'institutional_email': "Email institucional",
+ 'interest_reason': "Por que você está interessado em experimentar o AIdeaText?",
+ 'submit_application': "Enviar solicitação",
+ 'complete_all_fields': "Por favor, preencha todos os campos.",
+ 'use_institutional_email': "Por favor, use um email institucional.",
+ 'application_sent': "Sua solicitação foi enviada. Entraremos em contato em breve.",
+ 'application_error': "Houve um problema ao enviar sua solicitação. Por favor, tente novamente mais tarde.",
+
+ # Info tabs
+ 'use_cases': "Casos de uso",
+ 'presentation_videos': "Vídeos de apresentação",
+ 'academic_presentations': "Apresentações acadêmicas",
+ 'event_photos': "Fotos de eventos",
+ 'version_control': "Controle de versão",
+
+ # Video selectors
+ 'select_use_case': "Selecione um caso de uso em português ou inglês:",
+ 'select_presentation': "Selecione uma conferência:",
+
+ # Version control info
+ 'latest_version_title': "Atualizações da versão mais recente",
+ 'version_updates': [
+ "Interface melhorada para uma melhor experiência do usuário",
+ "Otimização da análise morfossintática",
+ "Suporte para vários idiomas",
+ "Novo módulo de análise do discurso",
+ "Sistema de chat integrado para suporte"
+ ]
+ }
+}
+
+def get_landing_translations(lang_code):
+ """
+ Get translations for the landing page based on language code.
+
+ Args:
+ lang_code (str): Language code ('en', 'es', 'fr', 'pt')
+
+ Returns:
+ dict: Dictionary with translations for the landing page
+ """
+ # Default to Spanish if language not available
+ if lang_code not in LANDING_TRANSLATIONS:
+ lang_code = 'es'
+
return LANDING_TRANSLATIONS[lang_code]
\ No newline at end of file
diff --git a/translations/pt.py b/translations/pt.py
index 05161e32719f5dba9dd205ffe50f3b08d6e1b1b1..791d96314952eb15e45fd482af19391faf0291ef 100644
--- a/translations/pt.py
+++ b/translations/pt.py
@@ -1,408 +1,408 @@
-# translations/pt.py
-
-COMMON = {
- # A
- 'initial_instruction': "Para iniciar uma nova análise semântica, carregue um novo arquivo de texto (.txt)",
- 'analysis_complete': "Análise completa e salva. Para realizar uma nova análise, carregue outro arquivo.",
- 'current_analysis_message': "Exibindo análise do arquivo: {}. Para realizar uma nova análise, carregue outro arquivo.",
- 'upload_prompt': "Anexe um arquivo para iniciar a análise",
- 'analysis_completed': "Análise concluída",
- 'analysis_section': "Análise Semântica",
- 'analyze_document': 'Analisar documento',
- 'analysis_saved_success': 'Análise salva com sucesso',
- 'analysis_save_error': 'Erro ao salvar a análise',
- 'analyze_button': "Analisar texto",
- 'analyzing_doc': "Analisando documento",
- 'activities_message': "Mensagens de atividades",
- 'activities_placeholder': "Espaço reservado para atividades",
- 'analysis_placeholder': "Espaço reservado para análise",
- 'analyze_button': "Analisar",
- 'analysis_types_chart': "Gráfico de tipos de análise",
- 'analysis_from': "Análise realizada em",
- # C
- 'chat_title': "Chat de Análise",
- 'export_button': "Exportar Análise Atual",
- 'export_success': "Análise e chat exportados com sucesso.",
- 'export_error': "Ocorreu um problema ao exportar a análise e o chat.",
- 'get_text': "Obter texto.",
- 'hello': "Olá",
- # L
- 'logout': "Encerrar sessão.",
- 'loading_data': "Carregando dados",
- 'load_selected_file': 'Carregar arquivo selecionado',
- # N
- 'no_analysis': "Nenhuma análise disponível. Use o chat para realizar uma análise.",
- 'nothing_to_export': "Nenhuma análise ou chat para exportar.",
- 'results_title': "Resultados da Análise",
- 'select_language': "Selecionar idioma",
- 'student_activities': "Atividades do estudante",
- # T
- 'total_analyses': "Total de análises",
- # W
- 'welcome': "Bem-vindo ao AIdeaText"
-}
-
-TABS = {
- 'current_situation_tab': "Situação atual",
- 'morpho_tab': "Análise morfossintática",
- 'semantic_live_tab': "Semântica ao vivo",
- 'semantic_tab': "Análise semântica",
- 'discourse_live_tab': "Discurso ao vivo",
- 'discourse_tab': "Análise do discurso",
- 'activities_tab': "Minhas atividades",
- 'feedback_tab': "Formulário de feedback"
-}
-
-CURRENT_SITUATION = {
- 'title': "Minha Situação Atual",
- 'input_prompt': "Escreva ou cole seu texto aqui:",
- 'first_analyze_button': "Analisar minha escrita",
- 'processing': "Analisando...",
- 'analysis_error': "Erro ao analisar o texto",
- 'help': "Analisaremos seu texto para determinar seu estado atual",
-
- # Radio buttons para tipo de texto
- 'text_type_header': "Tipo de texto",
- 'text_type_help': "Selecione o tipo de texto para ajustar os critérios de avaliação",
-
- # Métricas
- 'vocabulary_label': "Vocabulário",
- 'vocabulary_help': "Riqueza e variedade do vocabulário",
- 'structure_label': "Estrutura",
- 'structure_help': "Organização e complexidade das frases",
- 'cohesion_label': "Coesão",
- 'cohesion_help': "Conexão e fluidez entre ideias",
- 'clarity_label': "Clareza",
- 'clarity_help': "Facilidade de compreensão do texto",
-
- # Estados de métricas
- 'metric_improvement': "⚠️ Precisa melhorar",
- 'metric_acceptable': "📈 Aceitável",
- 'metric_optimal': "✅ Ótimo",
- 'metric_target': "Meta: {:.2f}",
-
- # Errores
- 'error_interface': "Ocorreu um erro ao carregar a interface",
- 'error_results': "Erro ao exibir os resultados",
- 'error_chart': "Erro ao exibir o gráfico"
-}
-
-MORPHOSYNTACTIC = {
- #A
- 'arc_diagram': "Análise sintática: Diagrama de arco",
- #B
- 'tab_text_baseline': "Produzir o primeiro texto",
- 'tab_iterations': "Produzir novas versões do primeiro texto",
-
- # Pestaña 1 texto base
- 'btn_new_morpho_analysis': "Nova análise morfossintática",
- 'btn_analyze_baseline': "Analisar o texto inserido",
- 'input_baseline_text': "Insira o primeiro texto para analisar",
- 'warn_enter_text': "Por favor, insira um texto para analisar",
- 'error_processing_baseline': "Erro ao processar o texto inicial",
- 'arc_diagram_baseline_label': "Diagrama de arco do texto inicial",
- 'baseline_diagram_not_available': "Diagrama de arco do texto inicial não disponível",
-
- # Pestaña 2 Iteración del texto
- 'info_first_analyze_base': "Verifique se o texto inicial existe",
- 'iteration_text_subheader': "Nova versão do texto inicial",
- 'input_iteration_text': "Insira uma nova versão do texto inicial e compare os arcos de ambos os textos",
- 'btn_analyze_iteration': "Analisar mudanças",
- 'warn_enter_iteration_text': "Insira uma nova versão do texto inicial e compare os arcos de ambos os textos",
- 'iteration_saved': "Mudanças salvas com sucesso",
- 'error_iteration': "Erro ao processar as novas mudanças",
-
- #C
- 'count': "Contagem",
- #D
- 'dependency': "Dependência",
- 'dep': "Dependência",
- #E
- 'error_message': "Houve um problema ao salvar a análise. Por favor, tente novamente.",
- 'examples': "Exemplos",
- #G
- 'grammatical_category': "Categoria gramatical",
- #L
- 'lemma': "Lema",
- 'legend': "Legenda: Categorias gramaticais",
- #O
- 'objects': "Objetos",
- #P
- 'pos_analysis': "Análise de Classes Gramaticais",
- 'percentage': "Porcentagem",
- #N
- 'no_results': "Nenhum resultado disponível. Por favor, realize uma análise primeiro.",
- #M
- 'morpho_analyze_button': 'Análise Morfossintática',
- 'morpho_title': "AIdeaText - Análise morfológica",
- 'morpho_initial_message': "Este é um chatbot de propósito geral, mas tem uma função específica para análise visual de texto: geração de diagramas de arco. Para produzi-los, digite o seguinte comando /analisis_morfosintactico [seguido por colchetes dentro dos quais você deve colocar o texto que deseja analisar]",
- 'morpho_input_label': "Digite um texto para analisar (máx. 30 palavras):",
- 'morpho_input_placeholder': "espaço reservado para morfossintaxe",
- 'morphosyntactic_analysis_completed': 'Análise morfossintática concluída. Por favor, revise os resultados na seção seguinte.',
- 'morphological_analysis': "Análise Morfológica",
- 'morphology': "Morfologia",
- 'morph': "Morfologia",
- #R
- 'root': "Raiz",
- 'repeated_words': "Palavras repetidas",
- #S
- 'sentence': "Frase",
- 'success_message': "Análise salva com sucesso.",
- 'sentence_structure': "Estrutura da Frase",
- 'subjects': "Sujeitos",
- #V
- 'verbs': "Verbos",
- #T
- 'title': "AIdeaText - Análise Morfológica e Sintática",
- 'tag': "Etiqueta",
- #W
- 'warning_message': "Por favor, digite um texto para analisar.",
- 'word': "Palavra",
- 'processing': 'Processando...',
- 'error_processing': 'Erro de processamento',
- 'morphosyntactic_analysis_error': 'Erro na análise morfossintática',
- 'morphosyntactic_analysis_completed': 'Análise morfossintática concluída'
-}
-
-SEMANTIC = {
- # C
- 'chat_title': "Chat de Análise Semântica",
- 'chat_placeholder': "Faça uma pergunta ou use um comando (/resumo, /entidades, /sentimento, /tópicos, /grafo_conceitos, /grafo_entidades, /grafo_tópicos)",
- 'clear_chat': "Limpar chat",
- 'conceptual_relations': "Relações Conceituais",
- # D
- 'delete_file': "Excluir arquivo",
- # E
- 'error_message': "Houve um problema ao salvar a análise semântica. Por favor, tente novamente.",
- # F
- 'file_uploader': "Ou carregue um arquivo de texto",
- 'file_upload_success': "Arquivo carregado e salvo com sucesso",
- 'file_upload_error': 'Erro ao carregar arquivo',
- 'file_section': "Arquivos",
- 'file_loaded_success': "Arquivo carregado com sucesso",
- 'file_load_error': "Erro ao carregar arquivo",
- 'file_upload_error': "Erro ao carregar e salvar arquivo",
- 'file_deleted_success': 'Arquivo excluído com sucesso',
- 'file_delete_error': 'Erro ao excluir arquivo',
- # G
- 'graph_title': "Visualização da Análise Semântica",
- # I
- 'identified_entities': "Entidades Identificadas",
- # K
- 'key_concepts': "Conceitos-Chave",
- # N
- 'no_analysis': "Nenhuma análise disponível. Por favor, carregue ou selecione um arquivo.",
- 'no_results': "Nenhum resultado disponível. Por favor, realize uma análise primeiro.",
- 'no_file': "Por favor, carregue um arquivo para iniciar a análise.",
- 'no_file_selected': "Por favor, selecione um arquivo para iniciar a análise.",
- # S
- 'semantic_title': "Análise Semântica",
- 'semantic_initial_message': "Este é um chatbot de propósito geral, mas tem uma função específica para análise visual de texto: gera um grafo com as principais entidades do texto. Para produzi-lo, insira um arquivo de texto em formato txt, pdf, doc, docx ou odt e pressione o botão 'analisar arquivo'. Após a geração do grafo, você pode interagir com o chat com base no documento.",
- 'send_button': "Enviar",
- 'select_saved_file': "Selecionar arquivo salvo",
- 'success_message': "Análise semântica salva com sucesso.",
- 'semantic_analyze_button': 'Análise Semântica',
- 'semantic_export_button': 'Exportar Análise Semântica',
- 'semantic_new_button': 'Nova Análise Semântica',
- 'semantic_file_uploader': 'Carregar um arquivo de texto para análise semântica',
- # T
- 'text_input_label': "Digite um texto para analisar (máx. 5.000 palavras):",
- 'text_input_placeholder': "O objetivo desta aplicação é melhorar suas habilidades de escrita...",
- 'title': "AIdeaText - Análise Semântica",
- # U
- 'upload_file': "Carregar arquivo",
- # W
- 'warning_message': "Por favor, digite um texto ou carregue um arquivo para analisar."
-}
-
-DISCOURSE = {
- 'file1_label': "Documento Padrão",
- 'file2_label': "Documento Comparado",
- 'discourse_title': "AIdeaText - Análise do Discurso",
- 'file_uploader1': "Carregar arquivo de texto 1 (Padrão)",
- 'file_uploader2': "Carregar arquivo de texto 2 (Comparação)",
- 'discourse_analyze_button': "Comparar textos",
- 'discourse_initial_message': "Este é um chatbot de propósito geral, mas tem uma função específica para análise visual de texto: gera dois grafos com as principais entidades de cada arquivo para fazer uma comparação entre ambos os textos. Para produzi-lo, insira um arquivo primeiro e depois outro em formato txt, pdf, doc, docx ou odt e pressione o botão 'analisar arquivo'. Após a geração do grafo, você pode interagir com o chat com base no documento.",
- 'analyze_button': "Analisar textos",
- 'comparison': "Comparação de Relações Semânticas",
- 'success_message': "Análise do discurso salva com sucesso.",
- 'error_message': "Houve um problema ao salvar a análise do discurso. Por favor, tente novamente.",
- 'warning_message': "Por favor, carregue ambos os arquivos para analisar.",
- 'no_results': "Nenhum resultado disponível. Por favor, realize uma análise primeiro.",
- 'key_concepts': "Conceitos-Chave",
- 'graph_not_available': "O grafo não está disponível.",
- 'concepts_not_available': "Os conceitos-chave não estão disponíveis.",
- 'comparison_not_available': "A comparação não está disponível.",
- 'morphosyntax_history': "Histórico morfossintático",
- 'analysis_of': "Análise de"
-}
-
-ACTIVITIES = {
- # Nuevas etiquetas actualizadas
- 'current_situation_activities': "Registros da função: Minha Situação Atual",
- 'morpho_activities': "Registros das minhas análises morfossintáticas",
- 'semantic_activities': "Registros das minhas análises semânticas",
- 'discourse_activities': "Registros das minhas análises de comparação de textos",
- 'chat_activities': "Registros das minhas conversas com o tutor virtual",
-
- # Mantener otras claves existentes
- 'current_situation_tab': "Situação atual",
- 'morpho_tab': "Análise morfossintática",
- 'semantic_tab': "Análise semântica",
- 'discourse_tab': "Análise de comparação de textos",
- 'activities_tab': "Meu registro de atividades",
- 'feedback_tab': "Formulário de feedback",
-
- # Resto de las claves que estén en el diccionario ACTIVITIES
- 'analysis_types_chart_title': "Tipos de análises realizadas",
- 'analysis_types_chart_x': "Tipo de análise",
- 'analysis_types_chart_y': "Contagem",
- 'analysis_from': "Análise de",
- 'assistant': "Assistente",
- 'activities_summary': "Resumo de Atividades e Progresso",
- 'chat_history_expander': "Histórico de Chat",
- 'chat_from': "Chat de",
- 'combined_graph': "Grafo Combinado",
- 'conceptual_relations_graph': "Grafo de Relações Conceituais",
- 'conversation': "Conversa",
- 'discourse_analyses_expander': "Histórico de Análises de Comparação de Textos", # Actualizado
- 'discourse_analyses': "Análises de Comparação de Textos", # Actualizado
- 'discourse_history': "Histórico de Análise de Comparação de Textos", # Actualizado
- 'document': "Documento",
- 'data_load_error': "Erro ao carregar dados do estudante",
- 'graph_display_error': "Não foi possível exibir o grafo",
- 'graph_doc1': "Grafo documento 1",
- 'graph_doc2': "Grafo documento 2",
- 'key_concepts': "Conceitos-chave",
- 'loading_data': "Carregando dados do estudante...",
- 'morphological_analysis': "Análise Morfológica",
- 'morphosyntax_analyses_expander': "Histórico de Análises Morfossintáticas",
- 'morphosyntax_history': "Histórico de Análise Morfossintática",
- 'no_arc_diagram': "Nenhum diagrama de arco encontrado para esta análise.",
- 'no_chat_history': "Nenhuma conversa com o Tutor Virtual foi encontrada.", # Actualizado
- 'no_data_warning': "Nenhum dado de análise encontrado para este estudante.",
- 'progress_of': "Progresso de",
- 'semantic_analyses': "Análises Semânticas",
- 'semantic_analyses_expander': "Histórico de Análises Semânticas",
- 'semantic_history': "Histórico de Análise Semântica",
- 'show_debug_data': "Mostrar dados de depuração",
- 'student_debug_data': "Dados do estudante (para depuração):",
- 'summary_title': "Resumo de Atividades",
- 'title': "Meu Registro de Atividades", # Actualizado
- 'timestamp': "Data e hora",
- 'total_analyses': "Total de análises realizadas:",
- 'try_analysis': "Tente realizar algumas análises de texto primeiro.",
- 'user': "Usuário",
-
- # Nuevas traducciones específicas para la sección de actividades
- 'diagnosis_tab': "Diagnóstico",
- 'recommendations_tab': "Recomendações",
- 'key_metrics': "Métricas chave",
- 'details': "Detalhes",
- 'analyzed_text': "Texto analisado",
- 'analysis_date': "Data",
- 'academic_article': "Artigo acadêmico",
- 'student_essay': "Trabalho acadêmico",
- 'general_communication': "Comunicação geral",
- 'no_diagnosis': "Nenhum dado de diagnóstico disponível",
- 'no_recommendations': "Nenhuma recomendação disponível",
- 'error_current_situation': "Erro ao exibir análise da situação atual",
- 'no_current_situation': "Nenhuma análise de situação atual registrada",
- 'no_morpho_analyses': "Nenhuma análise morfossintática registrada",
- 'error_morpho': "Erro ao exibir análise morfossintática",
- 'no_semantic_analyses': "Nenhuma análise semântica registrada",
- 'error_semantic': "Erro ao exibir análise semântica",
- 'no_discourse_analyses': "Nenhuma análise de comparação de textos registrada",
- 'error_discourse': "Erro ao exibir análise de comparação de textos",
- 'no_chat_history': "Nenhum registro de conversa com o tutor virtual",
- 'error_chat': "Erro ao exibir registros de conversa",
- 'error_loading_activities': "Erro ao carregar atividades",
- 'chat_date': "Data da conversa",
- 'invalid_chat_format': "Formato de chat inválido",
- 'comparison_results': "Resultados da comparação",
- 'concepts_text_1': "Conceitos Texto 1",
- 'concepts_text_2': "Conceitos Texto 2",
- 'no_visualization': "Nenhuma visualização comparativa disponível",
- 'no_graph': "Nenhuma visualização disponível",
- 'error_loading_graph': "Erro ao carregar gráfico",
- 'syntactic_diagrams': "Diagramas sintáticos"
-}
-
-FEEDBACK = {
- 'email': "Email",
- 'feedback': "Feedback",
- 'feedback_title': "Formulário de feedback",
- 'feedback_error': "Houve um problema ao enviar o formulário. Por favor, tente novamente.",
- 'feedback_success': "Obrigado pelo seu feedback",
- 'complete_all_fields': "Por favor, preencha todos os campos",
- 'name': "Nome",
- 'submit': "Enviar"
-}
-
-CHATBOT_TRANSLATIONS = {
- 'chat_title': "Assistente AIdeaText",
- 'input_placeholder': "Alguma pergunta?",
- 'initial_message': "Olá! Sou seu assistente. Como posso ajudar?",
- 'expand_chat': "Abrir assistente",
- 'clear_chat': "Limpar chat",
- 'processing': "Processando...",
- 'error_message': "Desculpe, ocorreu um erro"
-}
-
-TEXT_TYPES = {
- 'descritivo': [
- 'O que você está descrevendo?',
- 'Quais são suas principais características?',
- 'Como é a aparência, som, cheiro ou sensação?',
- 'O que o torna único ou especial?'
- ],
- 'narrativo': [
- 'Quem é o protagonista?',
- 'Onde e quando a história se passa?',
- 'Qual evento inicia a ação?',
- 'O que acontece depois?',
- 'Como a história termina?'
- ],
- 'expositivo': [
- 'Qual é o tema principal?',
- 'Quais aspectos importantes você quer explicar?',
- 'Você pode fornecer exemplos ou dados para apoiar sua explicação?',
- 'Como este tema se relaciona com outros conceitos?'
- ],
- 'argumentativo': [
- 'Qual é sua tese ou argumento principal?',
- 'Quais são seus argumentos de apoio?',
- 'Que evidências você tem para sustentar seus argumentos?',
- 'Quais são os contra-argumentos e como você os refuta?',
- 'Qual é sua conclusão?'
- ],
- 'instrutivo': [
- 'Que tarefa ou processo você está explicando?',
- 'Quais materiais ou ferramentas são necessários?',
- 'Quais são os passos a seguir?',
- 'Existem precauções importantes ou dicas a mencionar?'
- ],
- 'pitch': [
- 'O quê?',
- 'Para quê?',
- 'Para quem?',
- 'Como?'
- ]
-}
-
-# Configuração do modelo de linguagem para Português
-NLP_MODEL = 'pt_core_news_lg'
-
-# Esta linha é crucial:
-TRANSLATIONS = {
- 'COMMON': COMMON,
- 'TABS': TABS,
- 'MORPHOSYNTACTIC': MORPHOSYNTACTIC,
- 'SEMANTIC': SEMANTIC,
- 'DISCOURSE': DISCOURSE,
- 'ACTIVITIES': ACTIVITIES,
- 'FEEDBACK': FEEDBACK,
- 'TEXT_TYPES': TEXT_TYPES,
- 'CURRENT_SITUATION': CURRENT_SITUATION,
- 'NLP_MODEL': NLP_MODEL
+# translations/pt.py
+
+COMMON = {
+ # A
+ 'initial_instruction': "Para iniciar uma nova análise semântica, carregue um novo arquivo de texto (.txt)",
+ 'analysis_complete': "Análise completa e salva. Para realizar uma nova análise, carregue outro arquivo.",
+ 'current_analysis_message': "Exibindo análise do arquivo: {}. Para realizar uma nova análise, carregue outro arquivo.",
+ 'upload_prompt': "Anexe um arquivo para iniciar a análise",
+ 'analysis_completed': "Análise concluída",
+ 'analysis_section': "Análise Semântica",
+ 'analyze_document': 'Analisar documento',
+ 'analysis_saved_success': 'Análise salva com sucesso',
+ 'analysis_save_error': 'Erro ao salvar a análise',
+ 'analyze_button': "Analisar texto",
+ 'analyzing_doc': "Analisando documento",
+ 'activities_message': "Mensagens de atividades",
+ 'activities_placeholder': "Espaço reservado para atividades",
+ 'analysis_placeholder': "Espaço reservado para análise",
+ 'analyze_button': "Analisar",
+ 'analysis_types_chart': "Gráfico de tipos de análise",
+ 'analysis_from': "Análise realizada em",
+ # C
+ 'chat_title': "Chat de Análise",
+ 'export_button': "Exportar Análise Atual",
+ 'export_success': "Análise e chat exportados com sucesso.",
+ 'export_error': "Ocorreu um problema ao exportar a análise e o chat.",
+ 'get_text': "Obter texto.",
+ 'hello': "Olá",
+ # L
+ 'logout': "Encerrar sessão.",
+ 'loading_data': "Carregando dados",
+ 'load_selected_file': 'Carregar arquivo selecionado',
+ # N
+ 'no_analysis': "Nenhuma análise disponível. Use o chat para realizar uma análise.",
+ 'nothing_to_export': "Nenhuma análise ou chat para exportar.",
+ 'results_title': "Resultados da Análise",
+ 'select_language': "Selecionar idioma",
+ 'student_activities': "Atividades do estudante",
+ # T
+ 'total_analyses': "Total de análises",
+ # W
+ 'welcome': "Bem-vindo ao AIdeaText"
+}
+
+TABS = {
+ 'current_situation_tab': "Situação atual",
+ 'morpho_tab': "Análise morfossintática",
+ 'semantic_live_tab': "Semântica ao vivo",
+ 'semantic_tab': "Análise semântica",
+ 'discourse_live_tab': "Discurso ao vivo",
+ 'discourse_tab': "Análise do discurso",
+ 'activities_tab': "Minhas atividades",
+ 'feedback_tab': "Formulário de feedback"
+}
+
+CURRENT_SITUATION = {
+ 'title': "Minha Situação Atual",
+ 'input_prompt': "Escreva ou cole seu texto aqui:",
+ 'first_analyze_button': "Analisar minha escrita",
+ 'processing': "Analisando...",
+ 'analysis_error': "Erro ao analisar o texto",
+ 'help': "Analisaremos seu texto para determinar seu estado atual",
+
+ # Radio buttons para tipo de texto
+ 'text_type_header': "Tipo de texto",
+ 'text_type_help': "Selecione o tipo de texto para ajustar os critérios de avaliação",
+
+ # Métricas
+ 'vocabulary_label': "Vocabulário",
+ 'vocabulary_help': "Riqueza e variedade do vocabulário",
+ 'structure_label': "Estrutura",
+ 'structure_help': "Organização e complexidade das frases",
+ 'cohesion_label': "Coesão",
+ 'cohesion_help': "Conexão e fluidez entre ideias",
+ 'clarity_label': "Clareza",
+ 'clarity_help': "Facilidade de compreensão do texto",
+
+ # Estados de métricas
+ 'metric_improvement': "⚠️ Precisa melhorar",
+ 'metric_acceptable': "📈 Aceitável",
+ 'metric_optimal': "✅ Ótimo",
+ 'metric_target': "Meta: {:.2f}",
+
+ # Errores
+ 'error_interface': "Ocorreu um erro ao carregar a interface",
+ 'error_results': "Erro ao exibir os resultados",
+ 'error_chart': "Erro ao exibir o gráfico"
+}
+
+MORPHOSYNTACTIC = {
+ #A
+ 'arc_diagram': "Análise sintática: Diagrama de arco",
+ #B
+ 'tab_text_baseline': "Produzir o primeiro texto",
+ 'tab_iterations': "Produzir novas versões do primeiro texto",
+
+ # Pestaña 1 texto base
+ 'btn_new_morpho_analysis': "Nova análise morfossintática",
+ 'btn_analyze_baseline': "Analisar o texto inserido",
+ 'input_baseline_text': "Insira o primeiro texto para analisar",
+ 'warn_enter_text': "Por favor, insira um texto para analisar",
+ 'error_processing_baseline': "Erro ao processar o texto inicial",
+ 'arc_diagram_baseline_label': "Diagrama de arco do texto inicial",
+ 'baseline_diagram_not_available': "Diagrama de arco do texto inicial não disponível",
+
+ # Pestaña 2 Iteración del texto
+ 'info_first_analyze_base': "Verifique se o texto inicial existe",
+ 'iteration_text_subheader': "Nova versão do texto inicial",
+ 'input_iteration_text': "Insira uma nova versão do texto inicial e compare os arcos de ambos os textos",
+ 'btn_analyze_iteration': "Analisar mudanças",
+ 'warn_enter_iteration_text': "Insira uma nova versão do texto inicial e compare os arcos de ambos os textos",
+ 'iteration_saved': "Mudanças salvas com sucesso",
+ 'error_iteration': "Erro ao processar as novas mudanças",
+
+ #C
+ 'count': "Contagem",
+ #D
+ 'dependency': "Dependência",
+ 'dep': "Dependência",
+ #E
+ 'error_message': "Houve um problema ao salvar a análise. Por favor, tente novamente.",
+ 'examples': "Exemplos",
+ #G
+ 'grammatical_category': "Categoria gramatical",
+ #L
+ 'lemma': "Lema",
+ 'legend': "Legenda: Categorias gramaticais",
+ #O
+ 'objects': "Objetos",
+ #P
+ 'pos_analysis': "Análise de Classes Gramaticais",
+ 'percentage': "Porcentagem",
+ #N
+ 'no_results': "Nenhum resultado disponível. Por favor, realize uma análise primeiro.",
+ #M
+ 'morpho_analyze_button': 'Análise Morfossintática',
+ 'morpho_title': "AIdeaText - Análise morfológica",
+ 'morpho_initial_message': "Este é um chatbot de propósito geral, mas tem uma função específica para análise visual de texto: geração de diagramas de arco. Para produzi-los, digite o seguinte comando /analisis_morfosintactico [seguido por colchetes dentro dos quais você deve colocar o texto que deseja analisar]",
+ 'morpho_input_label': "Digite um texto para analisar (máx. 30 palavras):",
+ 'morpho_input_placeholder': "espaço reservado para morfossintaxe",
+ 'morphosyntactic_analysis_completed': 'Análise morfossintática concluída. Por favor, revise os resultados na seção seguinte.',
+ 'morphological_analysis': "Análise Morfológica",
+ 'morphology': "Morfologia",
+ 'morph': "Morfologia",
+ #R
+ 'root': "Raiz",
+ 'repeated_words': "Palavras repetidas",
+ #S
+ 'sentence': "Frase",
+ 'success_message': "Análise salva com sucesso.",
+ 'sentence_structure': "Estrutura da Frase",
+ 'subjects': "Sujeitos",
+ #V
+ 'verbs': "Verbos",
+ #T
+ 'title': "AIdeaText - Análise Morfológica e Sintática",
+ 'tag': "Etiqueta",
+ #W
+ 'warning_message': "Por favor, digite um texto para analisar.",
+ 'word': "Palavra",
+ 'processing': 'Processando...',
+ 'error_processing': 'Erro de processamento',
+ 'morphosyntactic_analysis_error': 'Erro na análise morfossintática',
+ 'morphosyntactic_analysis_completed': 'Análise morfossintática concluída'
+}
+
+SEMANTIC = {
+ # C
+ 'chat_title': "Chat de Análise Semântica",
+ 'chat_placeholder': "Faça uma pergunta ou use um comando (/resumo, /entidades, /sentimento, /tópicos, /grafo_conceitos, /grafo_entidades, /grafo_tópicos)",
+ 'clear_chat': "Limpar chat",
+ 'conceptual_relations': "Relações Conceituais",
+ # D
+ 'delete_file': "Excluir arquivo",
+ # E
+ 'error_message': "Houve um problema ao salvar a análise semântica. Por favor, tente novamente.",
+ # F
+ 'file_uploader': "Ou carregue um arquivo de texto",
+ 'file_upload_success': "Arquivo carregado e salvo com sucesso",
+ 'file_upload_error': 'Erro ao carregar arquivo',
+ 'file_section': "Arquivos",
+ 'file_loaded_success': "Arquivo carregado com sucesso",
+ 'file_load_error': "Erro ao carregar arquivo",
+ 'file_upload_error': "Erro ao carregar e salvar arquivo",
+ 'file_deleted_success': 'Arquivo excluído com sucesso',
+ 'file_delete_error': 'Erro ao excluir arquivo',
+ # G
+ 'graph_title': "Visualização da Análise Semântica",
+ # I
+ 'identified_entities': "Entidades Identificadas",
+ # K
+ 'key_concepts': "Conceitos-Chave",
+ # N
+ 'no_analysis': "Nenhuma análise disponível. Por favor, carregue ou selecione um arquivo.",
+ 'no_results': "Nenhum resultado disponível. Por favor, realize uma análise primeiro.",
+ 'no_file': "Por favor, carregue um arquivo para iniciar a análise.",
+ 'no_file_selected': "Por favor, selecione um arquivo para iniciar a análise.",
+ # S
+ 'semantic_title': "Análise Semântica",
+ 'semantic_initial_message': "Este é um chatbot de propósito geral, mas tem uma função específica para análise visual de texto: gera um grafo com as principais entidades do texto. Para produzi-lo, insira um arquivo de texto em formato txt, pdf, doc, docx ou odt e pressione o botão 'analisar arquivo'. Após a geração do grafo, você pode interagir com o chat com base no documento.",
+ 'send_button': "Enviar",
+ 'select_saved_file': "Selecionar arquivo salvo",
+ 'success_message': "Análise semântica salva com sucesso.",
+ 'semantic_analyze_button': 'Análise Semântica',
+ 'semantic_export_button': 'Exportar Análise Semântica',
+ 'semantic_new_button': 'Nova Análise Semântica',
+ 'semantic_file_uploader': 'Carregar um arquivo de texto para análise semântica',
+ # T
+ 'text_input_label': "Digite um texto para analisar (máx. 5.000 palavras):",
+ 'text_input_placeholder': "O objetivo desta aplicação é melhorar suas habilidades de escrita...",
+ 'title': "AIdeaText - Análise Semântica",
+ # U
+ 'upload_file': "Carregar arquivo",
+ # W
+ 'warning_message': "Por favor, digite um texto ou carregue um arquivo para analisar."
+}
+
+DISCOURSE = {
+ 'file1_label': "Documento Padrão",
+ 'file2_label': "Documento Comparado",
+ 'discourse_title': "AIdeaText - Análise do Discurso",
+ 'file_uploader1': "Carregar arquivo de texto 1 (Padrão)",
+ 'file_uploader2': "Carregar arquivo de texto 2 (Comparação)",
+ 'discourse_analyze_button': "Comparar textos",
+ 'discourse_initial_message': "Este é um chatbot de propósito geral, mas tem uma função específica para análise visual de texto: gera dois grafos com as principais entidades de cada arquivo para fazer uma comparação entre ambos os textos. Para produzi-lo, insira um arquivo primeiro e depois outro em formato txt, pdf, doc, docx ou odt e pressione o botão 'analisar arquivo'. Após a geração do grafo, você pode interagir com o chat com base no documento.",
+ 'analyze_button': "Analisar textos",
+ 'comparison': "Comparação de Relações Semânticas",
+ 'success_message': "Análise do discurso salva com sucesso.",
+ 'error_message': "Houve um problema ao salvar a análise do discurso. Por favor, tente novamente.",
+ 'warning_message': "Por favor, carregue ambos os arquivos para analisar.",
+ 'no_results': "Nenhum resultado disponível. Por favor, realize uma análise primeiro.",
+ 'key_concepts': "Conceitos-Chave",
+ 'graph_not_available': "O grafo não está disponível.",
+ 'concepts_not_available': "Os conceitos-chave não estão disponíveis.",
+ 'comparison_not_available': "A comparação não está disponível.",
+ 'morphosyntax_history': "Histórico morfossintático",
+ 'analysis_of': "Análise de"
+}
+
+ACTIVITIES = {
+ # Nuevas etiquetas actualizadas
+ 'current_situation_activities': "Registros da função: Minha Situação Atual",
+ 'morpho_activities': "Registros das minhas análises morfossintáticas",
+ 'semantic_activities': "Registros das minhas análises semânticas",
+ 'discourse_activities': "Registros das minhas análises de comparação de textos",
+ 'chat_activities': "Registros das minhas conversas com o tutor virtual",
+
+ # Mantener otras claves existentes
+ 'current_situation_tab': "Situação atual",
+ 'morpho_tab': "Análise morfossintática",
+ 'semantic_tab': "Análise semântica",
+ 'discourse_tab': "Análise de comparação de textos",
+ 'activities_tab': "Meu registro de atividades",
+ 'feedback_tab': "Formulário de feedback",
+
+ # Resto de las claves que estén en el diccionario ACTIVITIES
+ 'analysis_types_chart_title': "Tipos de análises realizadas",
+ 'analysis_types_chart_x': "Tipo de análise",
+ 'analysis_types_chart_y': "Contagem",
+ 'analysis_from': "Análise de",
+ 'assistant': "Assistente",
+ 'activities_summary': "Resumo de Atividades e Progresso",
+ 'chat_history_expander': "Histórico de Chat",
+ 'chat_from': "Chat de",
+ 'combined_graph': "Grafo Combinado",
+ 'conceptual_relations_graph': "Grafo de Relações Conceituais",
+ 'conversation': "Conversa",
+ 'discourse_analyses_expander': "Histórico de Análises de Comparação de Textos", # Actualizado
+ 'discourse_analyses': "Análises de Comparação de Textos", # Actualizado
+ 'discourse_history': "Histórico de Análise de Comparação de Textos", # Actualizado
+ 'document': "Documento",
+ 'data_load_error': "Erro ao carregar dados do estudante",
+ 'graph_display_error': "Não foi possível exibir o grafo",
+ 'graph_doc1': "Grafo documento 1",
+ 'graph_doc2': "Grafo documento 2",
+ 'key_concepts': "Conceitos-chave",
+ 'loading_data': "Carregando dados do estudante...",
+ 'morphological_analysis': "Análise Morfológica",
+ 'morphosyntax_analyses_expander': "Histórico de Análises Morfossintáticas",
+ 'morphosyntax_history': "Histórico de Análise Morfossintática",
+ 'no_arc_diagram': "Nenhum diagrama de arco encontrado para esta análise.",
+ 'no_chat_history': "Nenhuma conversa com o Tutor Virtual foi encontrada.", # Actualizado
+ 'no_data_warning': "Nenhum dado de análise encontrado para este estudante.",
+ 'progress_of': "Progresso de",
+ 'semantic_analyses': "Análises Semânticas",
+ 'semantic_analyses_expander': "Histórico de Análises Semânticas",
+ 'semantic_history': "Histórico de Análise Semântica",
+ 'show_debug_data': "Mostrar dados de depuração",
+ 'student_debug_data': "Dados do estudante (para depuração):",
+ 'summary_title': "Resumo de Atividades",
+ 'title': "Meu Registro de Atividades", # Actualizado
+ 'timestamp': "Data e hora",
+ 'total_analyses': "Total de análises realizadas:",
+ 'try_analysis': "Tente realizar algumas análises de texto primeiro.",
+ 'user': "Usuário",
+
+ # Nuevas traducciones específicas para la sección de actividades
+ 'diagnosis_tab': "Diagnóstico",
+ 'recommendations_tab': "Recomendações",
+ 'key_metrics': "Métricas chave",
+ 'details': "Detalhes",
+ 'analyzed_text': "Texto analisado",
+ 'analysis_date': "Data",
+ 'academic_article': "Artigo acadêmico",
+ 'student_essay': "Trabalho acadêmico",
+ 'general_communication': "Comunicação geral",
+ 'no_diagnosis': "Nenhum dado de diagnóstico disponível",
+ 'no_recommendations': "Nenhuma recomendação disponível",
+ 'error_current_situation': "Erro ao exibir análise da situação atual",
+ 'no_current_situation': "Nenhuma análise de situação atual registrada",
+ 'no_morpho_analyses': "Nenhuma análise morfossintática registrada",
+ 'error_morpho': "Erro ao exibir análise morfossintática",
+ 'no_semantic_analyses': "Nenhuma análise semântica registrada",
+ 'error_semantic': "Erro ao exibir análise semântica",
+ 'no_discourse_analyses': "Nenhuma análise de comparação de textos registrada",
+ 'error_discourse': "Erro ao exibir análise de comparação de textos",
+ 'no_chat_history': "Nenhum registro de conversa com o tutor virtual",
+ 'error_chat': "Erro ao exibir registros de conversa",
+ 'error_loading_activities': "Erro ao carregar atividades",
+ 'chat_date': "Data da conversa",
+ 'invalid_chat_format': "Formato de chat inválido",
+ 'comparison_results': "Resultados da comparação",
+ 'concepts_text_1': "Conceitos Texto 1",
+ 'concepts_text_2': "Conceitos Texto 2",
+ 'no_visualization': "Nenhuma visualização comparativa disponível",
+ 'no_graph': "Nenhuma visualização disponível",
+ 'error_loading_graph': "Erro ao carregar gráfico",
+ 'syntactic_diagrams': "Diagramas sintáticos"
+}
+
+FEEDBACK = {
+ 'email': "Email",
+ 'feedback': "Feedback",
+ 'feedback_title': "Formulário de feedback",
+ 'feedback_error': "Houve um problema ao enviar o formulário. Por favor, tente novamente.",
+ 'feedback_success': "Obrigado pelo seu feedback",
+ 'complete_all_fields': "Por favor, preencha todos os campos",
+ 'name': "Nome",
+ 'submit': "Enviar"
+}
+
+CHATBOT_TRANSLATIONS = {
+ 'chat_title': "Assistente AIdeaText",
+ 'input_placeholder': "Alguma pergunta?",
+ 'initial_message': "Olá! Sou seu assistente. Como posso ajudar?",
+ 'expand_chat': "Abrir assistente",
+ 'clear_chat': "Limpar chat",
+ 'processing': "Processando...",
+ 'error_message': "Desculpe, ocorreu um erro"
+}
+
+TEXT_TYPES = {
+ 'descritivo': [
+ 'O que você está descrevendo?',
+ 'Quais são suas principais características?',
+ 'Como é a aparência, som, cheiro ou sensação?',
+ 'O que o torna único ou especial?'
+ ],
+ 'narrativo': [
+ 'Quem é o protagonista?',
+ 'Onde e quando a história se passa?',
+ 'Qual evento inicia a ação?',
+ 'O que acontece depois?',
+ 'Como a história termina?'
+ ],
+ 'expositivo': [
+ 'Qual é o tema principal?',
+ 'Quais aspectos importantes você quer explicar?',
+ 'Você pode fornecer exemplos ou dados para apoiar sua explicação?',
+ 'Como este tema se relaciona com outros conceitos?'
+ ],
+ 'argumentativo': [
+ 'Qual é sua tese ou argumento principal?',
+ 'Quais são seus argumentos de apoio?',
+ 'Que evidências você tem para sustentar seus argumentos?',
+ 'Quais são os contra-argumentos e como você os refuta?',
+ 'Qual é sua conclusão?'
+ ],
+ 'instrutivo': [
+ 'Que tarefa ou processo você está explicando?',
+ 'Quais materiais ou ferramentas são necessários?',
+ 'Quais são os passos a seguir?',
+ 'Existem precauções importantes ou dicas a mencionar?'
+ ],
+ 'pitch': [
+ 'O quê?',
+ 'Para quê?',
+ 'Para quem?',
+ 'Como?'
+ ]
+}
+
+# Configuração do modelo de linguagem para Português
+NLP_MODEL = 'pt_core_news_lg'
+
+# Esta linha é crucial:
+TRANSLATIONS = {
+ 'COMMON': COMMON,
+ 'TABS': TABS,
+ 'MORPHOSYNTACTIC': MORPHOSYNTACTIC,
+ 'SEMANTIC': SEMANTIC,
+ 'DISCOURSE': DISCOURSE,
+ 'ACTIVITIES': ACTIVITIES,
+ 'FEEDBACK': FEEDBACK,
+ 'TEXT_TYPES': TEXT_TYPES,
+ 'CURRENT_SITUATION': CURRENT_SITUATION,
+ 'NLP_MODEL': NLP_MODEL
}
\ No newline at end of file
diff --git a/translations/recommendations.py b/translations/recommendations.py
index 0e2122768b84a84cb6b09e94a85639c33268b512..4280b32b219a28806c474cd4f0f9cbb09f7dcc20 100644
--- a/translations/recommendations.py
+++ b/translations/recommendations.py
@@ -1,413 +1,413 @@
-# translations/recommendations.py
-
-# Recomendaciones en español
-RECOMMENDATIONS_ES = {
- 'vocabulary': {
- 'low': [
- "Amplía tu vocabulario utilizando sinónimos para palabras repetidas.",
- "Incorpora terminología específica relacionada con el tema.",
- "Lee textos similares para familiarizarte con el vocabulario del campo."
- ],
- 'medium': [
- "Mejora la precisión léxica usando términos más específicos.",
- "Considera incluir algunos términos técnicos relevantes.",
- "Evita repeticiones innecesarias usando sinónimos."
- ],
- 'high': [
- "Mantén el excelente nivel de vocabulario.",
- "Asegúrate de que los términos técnicos sean comprensibles para tu audiencia."
- ]
- },
- 'structure': {
- 'low': [
- "Simplifica tus oraciones largas dividiéndolas en varias más cortas.",
- "Varía la estructura de tus oraciones para mantener el interés.",
- "Organiza tus ideas siguiendo una secuencia lógica (introducción, desarrollo, conclusión)."
- ],
- 'medium': [
- "Equilibra oraciones simples y compuestas para mejorar el ritmo.",
- "Asegúrate de que cada párrafo desarrolle una idea central.",
- "Considera usar conectores para transiciones más suaves entre ideas."
- ],
- 'high': [
- "Mantén la excelente estructura de tu texto.",
- "Considera ocasionalmente variar el ritmo con alguna oración más breve o más compleja."
- ]
- },
- 'cohesion': {
- 'low': [
- "Añade conectores (además, sin embargo, por lo tanto) para relacionar ideas.",
- "Mantén la consistencia temática entre párrafos.",
- "Usa referencias explícitas a ideas mencionadas anteriormente."
- ],
- 'medium': [
- "Mejora las transiciones entre párrafos para una progresión más fluida.",
- "Utiliza términos del mismo campo semántico para reforzar la unidad temática.",
- "Considera usar palabras clave de forma recurrente."
- ],
- 'high': [
- "Mantén la excelente cohesión de tu texto.",
- "Asegúrate de que las referencias a conceptos previos sean claras."
- ]
- },
- 'clarity': {
- 'low': [
- "Acorta las oraciones demasiado largas o complejas.",
- "Evita ambigüedades usando términos precisos.",
- "Añade ejemplos concretos para ilustrar conceptos abstractos."
- ],
- 'medium': [
- "Elimina información innecesaria que pueda distraer.",
- "Ordena las ideas de lo más simple a lo más complejo.",
- "Utiliza un lenguaje directo y evita circunloquios."
- ],
- 'high': [
- "Mantén la excelente claridad de tu texto.",
- "Considera si algún término técnico podría necesitar una breve explicación."
- ]
- },
- 'academic_article': [
- "Asegúrate de fundamentar tus afirmaciones con evidencia.",
- "Mantén un tono formal y objetivo.",
- "Incluye referencias bibliográficas para apoyar tus argumentos."
- ],
- 'student_essay': [
- "Asegúrate de tener una tesis clara y argumentos que la apoyen.",
- "Conecta tus ideas con lo aprendido en clase o lecturas asignadas.",
- "Concluye sintetizando tus principales puntos y su relevancia."
- ],
- 'general_communication': [
- "Adapta tu lenguaje a la audiencia específica.",
- "Mantén un equilibrio entre ser informativo y mantener el interés.",
- "Considera incluir algún elemento visual si es apropiado."
- ],
- 'priority_intro': "Esta es el área donde puedes enfocar tus esfuerzos para mejorar más rápidamente.",
- 'detailed_recommendations': "Recomendaciones detalladas",
- 'save_button': "Guardar análisis y recomendaciones",
- 'save_success': "Análisis y recomendaciones guardados correctamente",
- 'save_error': "No se pudo guardar el análisis y recomendaciones",
- 'area_priority': "Área prioritaria",
- 'dimension_names': {
- 'vocabulary': 'Vocabulario',
- 'structure': 'Estructura',
- 'cohesion': 'Cohesión',
- 'clarity': 'Claridad',
- 'general': 'General'
- },
- 'text_types': {
- 'academic_article': 'Artículo Académico',
- 'student_essay': 'Trabajo Universitario',
- 'general_communication': 'Comunicación General'
- }
-}
-
-# Recomendaciones en inglés
-RECOMMENDATIONS_EN = {
- 'vocabulary': {
- 'low': [
- "Expand your vocabulary by using synonyms for repeated words.",
- "Incorporate specific terminology related to the topic.",
- "Read similar texts to familiarize yourself with the field's vocabulary."
- ],
- 'medium': [
- "Improve lexical precision by using more specific terms.",
- "Consider including some relevant technical terms.",
- "Avoid unnecessary repetitions by using synonyms."
- ],
- 'high': [
- "Maintain the excellent vocabulary level.",
- "Ensure that technical terms are understandable for your audience."
- ]
- },
- 'structure': {
- 'low': [
- "Simplify your long sentences by dividing them into shorter ones.",
- "Vary your sentence structure to maintain interest.",
- "Organize your ideas following a logical sequence (introduction, development, conclusion)."
- ],
- 'medium': [
- "Balance simple and compound sentences to improve rhythm.",
- "Make sure each paragraph develops a central idea.",
- "Consider using connectors for smoother transitions between ideas."
- ],
- 'high': [
- "Maintain the excellent structure of your text.",
- "Consider occasionally varying the rhythm with shorter or more complex sentences."
- ]
- },
- 'cohesion': {
- 'low': [
- "Add connectors (additionally, however, therefore) to relate ideas.",
- "Maintain thematic consistency between paragraphs.",
- "Use explicit references to previously mentioned ideas."
- ],
- 'medium': [
- "Improve transitions between paragraphs for a more fluid progression.",
- "Use terms from the same semantic field to reinforce thematic unity.",
- "Consider using key words recurrently."
- ],
- 'high': [
- "Maintain the excellent cohesion of your text.",
- "Ensure that references to previous concepts are clear."
- ]
- },
- 'clarity': {
- 'low': [
- "Shorten overly long or complex sentences.",
- "Avoid ambiguities by using precise terms.",
- "Add concrete examples to illustrate abstract concepts."
- ],
- 'medium': [
- "Eliminate unnecessary information that may distract.",
- "Order ideas from simplest to most complex.",
- "Use direct language and avoid circumlocutions."
- ],
- 'high': [
- "Maintain the excellent clarity of your text.",
- "Consider whether any technical term might need a brief explanation."
- ]
- },
- 'academic_article': [
- "Make sure to support your claims with evidence.",
- "Maintain a formal and objective tone.",
- "Include bibliographic references to support your arguments."
- ],
- 'student_essay': [
- "Make sure you have a clear thesis and arguments that support it.",
- "Connect your ideas with what you've learned in class or assigned readings.",
- "Conclude by synthesizing your main points and their relevance."
- ],
- 'general_communication': [
- "Adapt your language to the specific audience.",
- "Maintain a balance between being informative and maintaining interest.",
- "Consider including a visual element if appropriate."
- ],
- 'priority_intro': "This is the area where you can focus your efforts to improve more quickly.",
- 'detailed_recommendations': "Detailed recommendations",
- 'save_button': "Save analysis and recommendations",
- 'save_success': "Analysis and recommendations saved successfully",
- 'save_error': "Could not save the analysis and recommendations",
- 'area_priority': "Priority area",
- 'dimension_names': {
- 'vocabulary': 'Vocabulary',
- 'structure': 'Structure',
- 'cohesion': 'Cohesion',
- 'clarity': 'Clarity',
- 'general': 'General'
- },
- 'text_types': {
- 'academic_article': 'Academic Article',
- 'student_essay': 'Student Essay',
- 'general_communication': 'General Communication'
- }
-}
-
-# Recomendaciones en francés
-RECOMMENDATIONS_FR = {
- 'vocabulary': {
- 'low': [
- "Élargissez votre vocabulaire en utilisant des synonymes pour les mots répétés.",
- "Incorporez une terminologie spécifique liée au sujet.",
- "Lisez des textes similaires pour vous familiariser avec le vocabulaire du domaine."
- ],
- 'medium': [
- "Améliorez la précision lexicale en utilisant des termes plus spécifiques.",
- "Envisagez d'inclure quelques termes techniques pertinents.",
- "Évitez les répétitions inutiles en utilisant des synonymes."
- ],
- 'high': [
- "Maintenez l'excellent niveau de vocabulaire.",
- "Assurez-vous que les termes techniques sont compréhensibles pour votre public."
- ]
- },
- 'structure': {
- 'low': [
- "Simplifiez vos longues phrases en les divisant en plusieurs plus courtes.",
- "Variez la structure de vos phrases pour maintenir l'intérêt.",
- "Organisez vos idées selon une séquence logique (introduction, développement, conclusion)."
- ],
- 'medium': [
- "Équilibrez les phrases simples et composées pour améliorer le rythme.",
- "Assurez-vous que chaque paragraphe développe une idée centrale.",
- "Envisagez d'utiliser des connecteurs pour des transitions plus fluides entre les idées."
- ],
- 'high': [
- "Maintenez l'excellente structure de votre texte.",
- "Envisagez parfois de varier le rythme avec une phrase plus courte ou plus complexe."
- ]
- },
- 'cohesion': {
- 'low': [
- "Ajoutez des connecteurs (de plus, cependant, par conséquent) pour relier les idées.",
- "Maintenez la cohérence thématique entre les paragraphes.",
- "Utilisez des références explicites aux idées mentionnées précédemment."
- ],
- 'medium': [
- "Améliorez les transitions entre les paragraphes pour une progression plus fluide.",
- "Utilisez des termes du même champ sémantique pour renforcer l'unité thématique.",
- "Envisagez d'utiliser des mots-clés de manière récurrente."
- ],
- 'high': [
- "Maintenez l'excellente cohésion de votre texte.",
- "Assurez-vous que les références aux concepts précédents sont claires."
- ]
- },
- 'clarity': {
- 'low': [
- "Raccourcissez les phrases trop longues ou complexes.",
- "Évitez les ambiguïtés en utilisant des termes précis.",
- "Ajoutez des exemples concrets pour illustrer des concepts abstraits."
- ],
- 'medium': [
- "Éliminez les informations inutiles qui peuvent distraire.",
- "Organisez les idées du plus simple au plus complexe.",
- "Utilisez un langage direct et évitez les circonlocutions."
- ],
- 'high': [
- "Maintenez l'excellente clarté de votre texte.",
- "Envisagez si un terme technique pourrait nécessiter une brève explication."
- ]
- },
- 'academic_article': [
- "Assurez-vous d'étayer vos affirmations par des preuves.",
- "Maintenez un ton formel et objectif.",
- "Incluez des références bibliographiques pour soutenir vos arguments."
- ],
- 'student_essay': [
- "Assurez-vous d'avoir une thèse claire et des arguments qui la soutiennent.",
- "Connectez vos idées avec ce que vous avez appris en classe ou dans les lectures assignées.",
- "Concluez en synthétisant vos points principaux et leur pertinence."
- ],
- 'general_communication': [
- "Adaptez votre langage à l'audience spécifique.",
- "Maintenez un équilibre entre être informatif et maintenir l'intérêt.",
- "Envisagez d'inclure un élément visuel si approprié."
- ],
- 'priority_intro': "C'est le domaine où vous pouvez concentrer vos efforts pour vous améliorer plus rapidement.",
- 'detailed_recommendations': "Recommandations détaillées",
- 'save_button': "Enregistrer l'analyse et les recommandations",
- 'save_success': "Analyse et recommandations enregistrées avec succès",
- 'save_error': "Impossible d'enregistrer l'analyse et les recommandations",
- 'area_priority': "Domaine prioritaire",
- 'dimension_names': {
- 'vocabulary': 'Vocabulaire',
- 'structure': 'Structure',
- 'cohesion': 'Cohésion',
- 'clarity': 'Clarté',
- 'general': 'Général'
- },
- 'text_types': {
- 'academic_article': 'Article Académique',
- 'student_essay': 'Devoir Universitaire',
- 'general_communication': 'Communication Générale'
- }
-}
-
-# Recomendaciones en portugués
-RECOMMENDATIONS_PT = {
- 'vocabulary': {
- 'low': [
- "Amplie seu vocabulário usando sinônimos para palavras repetidas.",
- "Incorpore terminologia específica relacionada ao tema.",
- "Leia textos semelhantes para se familiarizar com o vocabulário do campo."
- ],
- 'medium': [
- "Melhore a precisão lexical usando termos mais específicos.",
- "Considere incluir alguns termos técnicos relevantes.",
- "Evite repetições desnecessárias usando sinônimos."
- ],
- 'high': [
- "Mantenha o excelente nível de vocabulário.",
- "Certifique-se de que os termos técnicos sejam compreensíveis para seu público."
- ]
- },
- 'structure': {
- 'low': [
- "Simplifique suas frases longas dividindo-as em várias mais curtas.",
- "Varie a estrutura de suas frases para manter o interesse.",
- "Organize suas ideias seguindo uma sequência lógica (introdução, desenvolvimento, conclusão)."
- ],
- 'medium': [
- "Equilibre frases simples e compostas para melhorar o ritmo.",
- "Certifique-se de que cada parágrafo desenvolva uma ideia central.",
- "Considere usar conectores para transições mais suaves entre ideias."
- ],
- 'high': [
- "Mantenha a excelente estrutura do seu texto.",
- "Considere ocasionalmente variar o ritmo com alguma frase mais breve ou mais complexa."
- ]
- },
- 'cohesion': {
- 'low': [
- "Adicione conectores (além disso, no entanto, portanto) para relacionar ideias.",
- "Mantenha a consistência temática entre parágrafos.",
- "Use referências explícitas a ideias mencionadas anteriormente."
- ],
- 'medium': [
- "Melhore as transições entre parágrafos para uma progressão mais fluida.",
- "Utilize termos do mesmo campo semântico para reforçar a unidade temática.",
- "Considere usar palavras-chave de forma recorrente."
- ],
- 'high': [
- "Mantenha a excelente coesão do seu texto.",
- "Certifique-se de que as referências a conceitos anteriores sejam claras."
- ]
- },
- 'clarity': {
- 'low': [
- "Encurte as frases muito longas ou complexas.",
- "Evite ambiguidades usando termos precisos.",
- "Adicione exemplos concretos para ilustrar conceitos abstratos."
- ],
- 'medium': [
- "Elimine informações desnecessárias que possam distrair.",
- "Ordene as ideias do mais simples ao mais complexo.",
- "Utilize uma linguagem direta e evite circunlóquios."
- ],
- 'high': [
- "Mantenha a excelente clareza do seu texto.",
- "Considere se algum termo técnico pode precisar de uma breve explicação."
- ]
- },
- 'academic_article': [
- "Certifique-se de fundamentar suas afirmações com evidências.",
- "Mantenha um tom formal e objetivo.",
- "Inclua referências bibliográficas para apoiar seus argumentos."
- ],
- 'student_essay': [
- "Certifique-se de ter uma tese clara e argumentos que a apoiem.",
- "Conecte suas ideias com o que você aprendeu em aula ou leituras designadas.",
- "Conclua sintetizando seus principais pontos e sua relevância."
- ],
- 'general_communication': [
- "Adapte sua linguagem ao público específico.",
- "Mantenha um equilíbrio entre ser informativo e manter o interesse.",
- "Considere incluir algum elemento visual, se apropriado."
- ],
- 'priority_intro': "Esta é a área onde você pode concentrar seus esforços para melhorar mais rapidamente.",
- 'detailed_recommendations': "Recomendações detalhadas",
- 'save_button': "Salvar análise e recomendações",
- 'save_success': "Análise e recomendações salvas com sucesso",
- 'save_error': "Não foi possível salvar a análise e recomendações",
- 'area_priority': "Área prioritária",
- 'dimension_names': {
- 'vocabulary': 'Vocabulário',
- 'structure': 'Estrutura',
- 'cohesion': 'Coesão',
- 'clarity': 'Clareza',
- 'general': 'Geral'
- },
- 'text_types': {
- 'academic_article': 'Artigo Acadêmico',
- 'student_essay': 'Trabalho Universitário',
- 'general_communication': 'Comunicação Geral'
- }
-}
-
-# Diccionario que mapea los códigos de idioma a sus respectivas recomendaciones
-RECOMMENDATIONS = {
- 'es': RECOMMENDATIONS_ES,
- 'en': RECOMMENDATIONS_EN,
- 'fr': RECOMMENDATIONS_FR,
- 'pt': RECOMMENDATIONS_PT
+# translations/recommendations.py
+
+# Recomendaciones en español
+RECOMMENDATIONS_ES = {
+ 'vocabulary': {
+ 'low': [
+ "Amplía tu vocabulario utilizando sinónimos para palabras repetidas.",
+ "Incorpora terminología específica relacionada con el tema.",
+ "Lee textos similares para familiarizarte con el vocabulario del campo."
+ ],
+ 'medium': [
+ "Mejora la precisión léxica usando términos más específicos.",
+ "Considera incluir algunos términos técnicos relevantes.",
+ "Evita repeticiones innecesarias usando sinónimos."
+ ],
+ 'high': [
+ "Mantén el excelente nivel de vocabulario.",
+ "Asegúrate de que los términos técnicos sean comprensibles para tu audiencia."
+ ]
+ },
+ 'structure': {
+ 'low': [
+ "Simplifica tus oraciones largas dividiéndolas en varias más cortas.",
+ "Varía la estructura de tus oraciones para mantener el interés.",
+ "Organiza tus ideas siguiendo una secuencia lógica (introducción, desarrollo, conclusión)."
+ ],
+ 'medium': [
+ "Equilibra oraciones simples y compuestas para mejorar el ritmo.",
+ "Asegúrate de que cada párrafo desarrolle una idea central.",
+ "Considera usar conectores para transiciones más suaves entre ideas."
+ ],
+ 'high': [
+ "Mantén la excelente estructura de tu texto.",
+ "Considera ocasionalmente variar el ritmo con alguna oración más breve o más compleja."
+ ]
+ },
+ 'cohesion': {
+ 'low': [
+ "Añade conectores (además, sin embargo, por lo tanto) para relacionar ideas.",
+ "Mantén la consistencia temática entre párrafos.",
+ "Usa referencias explícitas a ideas mencionadas anteriormente."
+ ],
+ 'medium': [
+ "Mejora las transiciones entre párrafos para una progresión más fluida.",
+ "Utiliza términos del mismo campo semántico para reforzar la unidad temática.",
+ "Considera usar palabras clave de forma recurrente."
+ ],
+ 'high': [
+ "Mantén la excelente cohesión de tu texto.",
+ "Asegúrate de que las referencias a conceptos previos sean claras."
+ ]
+ },
+ 'clarity': {
+ 'low': [
+ "Acorta las oraciones demasiado largas o complejas.",
+ "Evita ambigüedades usando términos precisos.",
+ "Añade ejemplos concretos para ilustrar conceptos abstractos."
+ ],
+ 'medium': [
+ "Elimina información innecesaria que pueda distraer.",
+ "Ordena las ideas de lo más simple a lo más complejo.",
+ "Utiliza un lenguaje directo y evita circunloquios."
+ ],
+ 'high': [
+ "Mantén la excelente claridad de tu texto.",
+ "Considera si algún término técnico podría necesitar una breve explicación."
+ ]
+ },
+ 'academic_article': [
+ "Asegúrate de fundamentar tus afirmaciones con evidencia.",
+ "Mantén un tono formal y objetivo.",
+ "Incluye referencias bibliográficas para apoyar tus argumentos."
+ ],
+ 'student_essay': [
+ "Asegúrate de tener una tesis clara y argumentos que la apoyen.",
+ "Conecta tus ideas con lo aprendido en clase o lecturas asignadas.",
+ "Concluye sintetizando tus principales puntos y su relevancia."
+ ],
+ 'general_communication': [
+ "Adapta tu lenguaje a la audiencia específica.",
+ "Mantén un equilibrio entre ser informativo y mantener el interés.",
+ "Considera incluir algún elemento visual si es apropiado."
+ ],
+ 'priority_intro': "Esta es el área donde puedes enfocar tus esfuerzos para mejorar más rápidamente.",
+ 'detailed_recommendations': "Recomendaciones detalladas",
+ 'save_button': "Guardar análisis y recomendaciones",
+ 'save_success': "Análisis y recomendaciones guardados correctamente",
+ 'save_error': "No se pudo guardar el análisis y recomendaciones",
+ 'area_priority': "Área prioritaria",
+ 'dimension_names': {
+ 'vocabulary': 'Vocabulario',
+ 'structure': 'Estructura',
+ 'cohesion': 'Cohesión',
+ 'clarity': 'Claridad',
+ 'general': 'General'
+ },
+ 'text_types': {
+ 'academic_article': 'Artículo Académico',
+ 'student_essay': 'Trabajo Universitario',
+ 'general_communication': 'Comunicación General'
+ }
+}
+
+# Recomendaciones en inglés
+RECOMMENDATIONS_EN = {
+ 'vocabulary': {
+ 'low': [
+ "Expand your vocabulary by using synonyms for repeated words.",
+ "Incorporate specific terminology related to the topic.",
+ "Read similar texts to familiarize yourself with the field's vocabulary."
+ ],
+ 'medium': [
+ "Improve lexical precision by using more specific terms.",
+ "Consider including some relevant technical terms.",
+ "Avoid unnecessary repetitions by using synonyms."
+ ],
+ 'high': [
+ "Maintain the excellent vocabulary level.",
+ "Ensure that technical terms are understandable for your audience."
+ ]
+ },
+ 'structure': {
+ 'low': [
+ "Simplify your long sentences by dividing them into shorter ones.",
+ "Vary your sentence structure to maintain interest.",
+ "Organize your ideas following a logical sequence (introduction, development, conclusion)."
+ ],
+ 'medium': [
+ "Balance simple and compound sentences to improve rhythm.",
+ "Make sure each paragraph develops a central idea.",
+ "Consider using connectors for smoother transitions between ideas."
+ ],
+ 'high': [
+ "Maintain the excellent structure of your text.",
+ "Consider occasionally varying the rhythm with shorter or more complex sentences."
+ ]
+ },
+ 'cohesion': {
+ 'low': [
+ "Add connectors (additionally, however, therefore) to relate ideas.",
+ "Maintain thematic consistency between paragraphs.",
+ "Use explicit references to previously mentioned ideas."
+ ],
+ 'medium': [
+ "Improve transitions between paragraphs for a more fluid progression.",
+ "Use terms from the same semantic field to reinforce thematic unity.",
+ "Consider using key words recurrently."
+ ],
+ 'high': [
+ "Maintain the excellent cohesion of your text.",
+ "Ensure that references to previous concepts are clear."
+ ]
+ },
+ 'clarity': {
+ 'low': [
+ "Shorten overly long or complex sentences.",
+ "Avoid ambiguities by using precise terms.",
+ "Add concrete examples to illustrate abstract concepts."
+ ],
+ 'medium': [
+ "Eliminate unnecessary information that may distract.",
+ "Order ideas from simplest to most complex.",
+ "Use direct language and avoid circumlocutions."
+ ],
+ 'high': [
+ "Maintain the excellent clarity of your text.",
+ "Consider whether any technical term might need a brief explanation."
+ ]
+ },
+ 'academic_article': [
+ "Make sure to support your claims with evidence.",
+ "Maintain a formal and objective tone.",
+ "Include bibliographic references to support your arguments."
+ ],
+ 'student_essay': [
+ "Make sure you have a clear thesis and arguments that support it.",
+ "Connect your ideas with what you've learned in class or assigned readings.",
+ "Conclude by synthesizing your main points and their relevance."
+ ],
+ 'general_communication': [
+ "Adapt your language to the specific audience.",
+ "Maintain a balance between being informative and maintaining interest.",
+ "Consider including a visual element if appropriate."
+ ],
+ 'priority_intro': "This is the area where you can focus your efforts to improve more quickly.",
+ 'detailed_recommendations': "Detailed recommendations",
+ 'save_button': "Save analysis and recommendations",
+ 'save_success': "Analysis and recommendations saved successfully",
+ 'save_error': "Could not save the analysis and recommendations",
+ 'area_priority': "Priority area",
+ 'dimension_names': {
+ 'vocabulary': 'Vocabulary',
+ 'structure': 'Structure',
+ 'cohesion': 'Cohesion',
+ 'clarity': 'Clarity',
+ 'general': 'General'
+ },
+ 'text_types': {
+ 'academic_article': 'Academic Article',
+ 'student_essay': 'Student Essay',
+ 'general_communication': 'General Communication'
+ }
+}
+
+# Recomendaciones en francés
+RECOMMENDATIONS_FR = {
+ 'vocabulary': {
+ 'low': [
+ "Élargissez votre vocabulaire en utilisant des synonymes pour les mots répétés.",
+ "Incorporez une terminologie spécifique liée au sujet.",
+ "Lisez des textes similaires pour vous familiariser avec le vocabulaire du domaine."
+ ],
+ 'medium': [
+ "Améliorez la précision lexicale en utilisant des termes plus spécifiques.",
+ "Envisagez d'inclure quelques termes techniques pertinents.",
+ "Évitez les répétitions inutiles en utilisant des synonymes."
+ ],
+ 'high': [
+ "Maintenez l'excellent niveau de vocabulaire.",
+ "Assurez-vous que les termes techniques sont compréhensibles pour votre public."
+ ]
+ },
+ 'structure': {
+ 'low': [
+ "Simplifiez vos longues phrases en les divisant en plusieurs plus courtes.",
+ "Variez la structure de vos phrases pour maintenir l'intérêt.",
+ "Organisez vos idées selon une séquence logique (introduction, développement, conclusion)."
+ ],
+ 'medium': [
+ "Équilibrez les phrases simples et composées pour améliorer le rythme.",
+ "Assurez-vous que chaque paragraphe développe une idée centrale.",
+ "Envisagez d'utiliser des connecteurs pour des transitions plus fluides entre les idées."
+ ],
+ 'high': [
+ "Maintenez l'excellente structure de votre texte.",
+ "Envisagez parfois de varier le rythme avec une phrase plus courte ou plus complexe."
+ ]
+ },
+ 'cohesion': {
+ 'low': [
+ "Ajoutez des connecteurs (de plus, cependant, par conséquent) pour relier les idées.",
+ "Maintenez la cohérence thématique entre les paragraphes.",
+ "Utilisez des références explicites aux idées mentionnées précédemment."
+ ],
+ 'medium': [
+ "Améliorez les transitions entre les paragraphes pour une progression plus fluide.",
+ "Utilisez des termes du même champ sémantique pour renforcer l'unité thématique.",
+ "Envisagez d'utiliser des mots-clés de manière récurrente."
+ ],
+ 'high': [
+ "Maintenez l'excellente cohésion de votre texte.",
+ "Assurez-vous que les références aux concepts précédents sont claires."
+ ]
+ },
+ 'clarity': {
+ 'low': [
+ "Raccourcissez les phrases trop longues ou complexes.",
+ "Évitez les ambiguïtés en utilisant des termes précis.",
+ "Ajoutez des exemples concrets pour illustrer des concepts abstraits."
+ ],
+ 'medium': [
+ "Éliminez les informations inutiles qui peuvent distraire.",
+ "Organisez les idées du plus simple au plus complexe.",
+ "Utilisez un langage direct et évitez les circonlocutions."
+ ],
+ 'high': [
+ "Maintenez l'excellente clarté de votre texte.",
+ "Envisagez si un terme technique pourrait nécessiter une brève explication."
+ ]
+ },
+ 'academic_article': [
+ "Assurez-vous d'étayer vos affirmations par des preuves.",
+ "Maintenez un ton formel et objectif.",
+ "Incluez des références bibliographiques pour soutenir vos arguments."
+ ],
+ 'student_essay': [
+ "Assurez-vous d'avoir une thèse claire et des arguments qui la soutiennent.",
+ "Connectez vos idées avec ce que vous avez appris en classe ou dans les lectures assignées.",
+ "Concluez en synthétisant vos points principaux et leur pertinence."
+ ],
+ 'general_communication': [
+ "Adaptez votre langage à l'audience spécifique.",
+ "Maintenez un équilibre entre être informatif et maintenir l'intérêt.",
+ "Envisagez d'inclure un élément visuel si approprié."
+ ],
+ 'priority_intro': "C'est le domaine où vous pouvez concentrer vos efforts pour vous améliorer plus rapidement.",
+ 'detailed_recommendations': "Recommandations détaillées",
+ 'save_button': "Enregistrer l'analyse et les recommandations",
+ 'save_success': "Analyse et recommandations enregistrées avec succès",
+ 'save_error': "Impossible d'enregistrer l'analyse et les recommandations",
+ 'area_priority': "Domaine prioritaire",
+ 'dimension_names': {
+ 'vocabulary': 'Vocabulaire',
+ 'structure': 'Structure',
+ 'cohesion': 'Cohésion',
+ 'clarity': 'Clarté',
+ 'general': 'Général'
+ },
+ 'text_types': {
+ 'academic_article': 'Article Académique',
+ 'student_essay': 'Devoir Universitaire',
+ 'general_communication': 'Communication Générale'
+ }
+}
+
+# Recomendaciones en portugués
+RECOMMENDATIONS_PT = {
+ 'vocabulary': {
+ 'low': [
+ "Amplie seu vocabulário usando sinônimos para palavras repetidas.",
+ "Incorpore terminologia específica relacionada ao tema.",
+ "Leia textos semelhantes para se familiarizar com o vocabulário do campo."
+ ],
+ 'medium': [
+ "Melhore a precisão lexical usando termos mais específicos.",
+ "Considere incluir alguns termos técnicos relevantes.",
+ "Evite repetições desnecessárias usando sinônimos."
+ ],
+ 'high': [
+ "Mantenha o excelente nível de vocabulário.",
+ "Certifique-se de que os termos técnicos sejam compreensíveis para seu público."
+ ]
+ },
+ 'structure': {
+ 'low': [
+ "Simplifique suas frases longas dividindo-as em várias mais curtas.",
+ "Varie a estrutura de suas frases para manter o interesse.",
+ "Organize suas ideias seguindo uma sequência lógica (introdução, desenvolvimento, conclusão)."
+ ],
+ 'medium': [
+ "Equilibre frases simples e compostas para melhorar o ritmo.",
+ "Certifique-se de que cada parágrafo desenvolva uma ideia central.",
+ "Considere usar conectores para transições mais suaves entre ideias."
+ ],
+ 'high': [
+ "Mantenha a excelente estrutura do seu texto.",
+ "Considere ocasionalmente variar o ritmo com alguma frase mais breve ou mais complexa."
+ ]
+ },
+ 'cohesion': {
+ 'low': [
+ "Adicione conectores (além disso, no entanto, portanto) para relacionar ideias.",
+ "Mantenha a consistência temática entre parágrafos.",
+ "Use referências explícitas a ideias mencionadas anteriormente."
+ ],
+ 'medium': [
+ "Melhore as transições entre parágrafos para uma progressão mais fluida.",
+ "Utilize termos do mesmo campo semântico para reforçar a unidade temática.",
+ "Considere usar palavras-chave de forma recorrente."
+ ],
+ 'high': [
+ "Mantenha a excelente coesão do seu texto.",
+ "Certifique-se de que as referências a conceitos anteriores sejam claras."
+ ]
+ },
+ 'clarity': {
+ 'low': [
+ "Encurte as frases muito longas ou complexas.",
+ "Evite ambiguidades usando termos precisos.",
+ "Adicione exemplos concretos para ilustrar conceitos abstratos."
+ ],
+ 'medium': [
+ "Elimine informações desnecessárias que possam distrair.",
+ "Ordene as ideias do mais simples ao mais complexo.",
+ "Utilize uma linguagem direta e evite circunlóquios."
+ ],
+ 'high': [
+ "Mantenha a excelente clareza do seu texto.",
+ "Considere se algum termo técnico pode precisar de uma breve explicação."
+ ]
+ },
+ 'academic_article': [
+ "Certifique-se de fundamentar suas afirmações com evidências.",
+ "Mantenha um tom formal e objetivo.",
+ "Inclua referências bibliográficas para apoiar seus argumentos."
+ ],
+ 'student_essay': [
+ "Certifique-se de ter uma tese clara e argumentos que a apoiem.",
+ "Conecte suas ideias com o que você aprendeu em aula ou leituras designadas.",
+ "Conclua sintetizando seus principais pontos e sua relevância."
+ ],
+ 'general_communication': [
+ "Adapte sua linguagem ao público específico.",
+ "Mantenha um equilíbrio entre ser informativo e manter o interesse.",
+ "Considere incluir algum elemento visual, se apropriado."
+ ],
+ 'priority_intro': "Esta é a área onde você pode concentrar seus esforços para melhorar mais rapidamente.",
+ 'detailed_recommendations': "Recomendações detalhadas",
+ 'save_button': "Salvar análise e recomendações",
+ 'save_success': "Análise e recomendações salvas com sucesso",
+ 'save_error': "Não foi possível salvar a análise e recomendações",
+ 'area_priority': "Área prioritária",
+ 'dimension_names': {
+ 'vocabulary': 'Vocabulário',
+ 'structure': 'Estrutura',
+ 'cohesion': 'Coesão',
+ 'clarity': 'Clareza',
+ 'general': 'Geral'
+ },
+ 'text_types': {
+ 'academic_article': 'Artigo Acadêmico',
+ 'student_essay': 'Trabalho Universitário',
+ 'general_communication': 'Comunicação Geral'
+ }
+}
+
+# Diccionario que mapea los códigos de idioma a sus respectivas recomendaciones
+RECOMMENDATIONS = {
+ 'es': RECOMMENDATIONS_ES,
+ 'en': RECOMMENDATIONS_EN,
+ 'fr': RECOMMENDATIONS_FR,
+ 'pt': RECOMMENDATIONS_PT
}
\ No newline at end of file