AIdeaText commited on
Commit
c0d5df9
·
verified ·
1 Parent(s): 3ee8d84

Update modules/semantic/semantic_live_interface.py

Browse files
modules/semantic/semantic_live_interface.py CHANGED
@@ -1,197 +1,236 @@
1
- # modules/semantic/semantic_live_interface.py
2
- import streamlit as st
3
- from streamlit_float import *
4
- from streamlit_antd_components import *
5
- import pandas as pd
6
- import logging
7
-
8
- # Configuración del logger
9
- logger = logging.getLogger(__name__)
10
-
11
- # Importaciones locales
12
- from .semantic_process import (
13
- process_semantic_input,
14
- format_semantic_results
15
- )
16
-
17
- from ..utils.widget_utils import generate_unique_key
18
- from ..database.semantic_mongo_db import store_student_semantic_result
19
- from ..database.chat_mongo_db import store_chat_history, get_chat_history
20
-
21
- def display_semantic_live_interface(lang_code, nlp_models, semantic_t):
22
- """
23
- Interfaz para el análisis semántico en vivo con proporciones de columna ajustadas
24
- """
25
- try:
26
- # 1. Inicializar el estado de la sesión de manera más robusta
27
- if 'semantic_live_state' not in st.session_state:
28
- st.session_state.semantic_live_state = {
29
- 'analysis_count': 0,
30
- 'current_text': '',
31
- 'last_result': None,
32
- 'text_changed': False
33
- }
34
-
35
- # 2. Función para manejar cambios en el texto
36
- def on_text_change():
37
- current_text = st.session_state.semantic_live_text
38
- st.session_state.semantic_live_state['current_text'] = current_text
39
- st.session_state.semantic_live_state['text_changed'] = True
40
-
41
- # 3. Crear columnas con nueva proporción (1:3)
42
- input_col, result_col = st.columns([1, 3])
43
-
44
- # Columna izquierda: Entrada de texto
45
- with input_col:
46
- st.subheader(semantic_t.get('enter_text', 'Ingrese su texto'))
47
-
48
- # Área de texto con manejo de eventos
49
- text_input = st.text_area(
50
- semantic_t.get('text_input_label', 'Escriba o pegue su texto aquí'),
51
- height=500,
52
- key="semantic_live_text",
53
- value=st.session_state.semantic_live_state.get('current_text', ''),
54
- on_change=on_text_change,
55
- label_visibility="collapsed" # Oculta el label para mayor estabilidad
56
- )
57
-
58
- # Botón de análisis y procesamiento
59
- analyze_button = st.button(
60
- semantic_t.get('analyze_button', 'Analizar'),
61
- key="semantic_live_analyze",
62
- type="primary",
63
- icon="🔍",
64
- disabled=not text_input,
65
- use_container_width=True
66
- )
67
-
68
- if analyze_button and text_input:
69
- try:
70
- with st.spinner(semantic_t.get('processing', 'Procesando...')):
71
- analysis_result = process_semantic_input(
72
- text_input,
73
- lang_code,
74
- nlp_models,
75
- semantic_t
76
- )
77
-
78
- if analysis_result['success']:
79
- st.session_state.semantic_live_state['last_result'] = analysis_result
80
- st.session_state.semantic_live_state['analysis_count'] += 1
81
- st.session_state.semantic_live_state['text_changed'] = False
82
-
83
- store_student_semantic_result(
84
- st.session_state.username,
85
- text_input,
86
- analysis_result['analysis']
87
- )
88
- else:
89
- st.error(analysis_result.get('message', 'Error en el análisis'))
90
-
91
- except Exception as e:
92
- logger.error(f"Error en análisis: {str(e)}")
93
- st.error(semantic_t.get('error_processing', 'Error al procesar el texto'))
94
-
95
- # Columna derecha: Visualización de resultados
96
- with result_col:
97
- st.subheader(semantic_t.get('live_results', 'Resultados en vivo'))
98
-
99
- if 'last_result' in st.session_state.semantic_live_state and \
100
- st.session_state.semantic_live_state['last_result'] is not None:
101
-
102
- analysis = st.session_state.semantic_live_state['last_result']['analysis']
103
-
104
- if 'key_concepts' in analysis and analysis['key_concepts'] and \
105
- 'concept_graph' in analysis and analysis['concept_graph'] is not None:
106
-
107
- st.markdown("""
108
- <style>
109
- .unified-container {
110
- background-color: white;
111
- border-radius: 10px;
112
- overflow: hidden;
113
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
114
- width: 100%;
115
- margin-bottom: 1rem;
116
- }
117
- .concept-table {
118
- display: flex;
119
- flex-wrap: nowrap; /* Evita el wrap */
120
- gap: 6px; /* Reducido el gap */
121
- padding: 10px;
122
- background-color: #f8f9fa;
123
- overflow-x: auto; /* Permite scroll horizontal si es necesario */
124
- white-space: nowrap; /* Mantiene todo en una línea */
125
- }
126
- .concept-item {
127
- background-color: white;
128
- border-radius: 4px;
129
- padding: 4px 8px; /* Padding reducido */
130
- display: inline-flex; /* Cambiado a inline-flex */
131
- align-items: center;
132
- gap: 4px; /* Gap reducido */
133
- box-shadow: 0 1px 2px rgba(0,0,0,0.1);
134
- flex-shrink: 0; /* Evita que los items se encojan */
135
- }
136
- .concept-name {
137
- font-weight: 500;
138
- color: #1f2937;
139
- font-size: 0.8em; /* Tamaño de fuente reducido */
140
- }
141
- .concept-freq {
142
- color: #6b7280;
143
- font-size: 0.75em; /* Tamaño de fuente reducido */
144
- }
145
- .graph-section {
146
- padding: 20px;
147
- background-color: white;
148
- }
149
- </style>
150
- """, unsafe_allow_html=True)
151
-
152
- with st.container():
153
- # Conceptos en una sola línea
154
- concepts_html = """
155
- <div class="unified-container">
156
- <div class="concept-table">
157
- """
158
- concepts_html += ''.join(
159
- f'<div class="concept-item"><span class="concept-name">{concept}</span>'
160
- f'<span class="concept-freq">({freq:.2f})</span></div>'
161
- for concept, freq in analysis['key_concepts']
162
- )
163
- concepts_html += "</div></div>"
164
- st.markdown(concepts_html, unsafe_allow_html=True)
165
-
166
- # Grafo
167
- if 'concept_graph' in analysis and analysis['concept_graph'] is not None:
168
- st.image(
169
- analysis['concept_graph'],
170
- use_container_width=True
171
- )
172
-
173
- # Botones y controles
174
- button_col, spacer_col = st.columns([1,5])
175
- with button_col:
176
- st.download_button(
177
- label="📥 " + semantic_t.get('download_graph', "Download"),
178
- data=analysis['concept_graph'],
179
- file_name="semantic_live_graph.png",
180
- mime="image/png",
181
- use_container_width=True
182
- )
183
-
184
- with st.expander("📊 " + semantic_t.get('graph_help', "Graph Interpretation")):
185
- st.markdown("""
186
- - 🔀 Las flechas indican la dirección de la relación entre conceptos
187
- - 🎨 Los colores más intensos indican conceptos más centrales en el texto
188
- - ⭕ El tamaño de los nodos representa la frecuencia del concepto
189
- - ↔️ El grosor de las líneas indica la fuerza de la conexión
190
- """)
191
- else:
192
- st.info(semantic_t.get('no_graph', 'No hay datos para mostrar'))
193
-
194
- except Exception as e:
195
- logger.error(f"Error general en interfaz semántica en vivo: {str(e)}")
196
- st.error(semantic_t.get('general_error', "Se produjo un error. Por favor, intente de nuevo."))
197
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # modules/semantic/semantic_live_interface.py
2
+ import streamlit as st
3
+ from streamlit_float import *
4
+ from streamlit_antd_components import *
5
+ import pandas as pd
6
+ import logging
7
+
8
+ # Configuración del logger
9
+ logger = logging.getLogger(__name__)
10
+
11
+ # Importaciones locales
12
+ from .semantic_process import (
13
+ process_semantic_input,
14
+ format_semantic_results
15
+ )
16
+
17
+ from ..utils.widget_utils import generate_unique_key
18
+ from ..database.semantic_mongo_db import store_student_semantic_result
19
+ from ..database.chat_mongo_db import store_chat_history, get_chat_history
20
+
21
+ def display_semantic_live_interface(lang_code, nlp_models, semantic_t):
22
+ """
23
+ Interfaz para el análisis semántico en vivo con proporciones de columna ajustadas
24
+ """
25
+ try:
26
+ # 1. Inicializar el estado de la sesión de manera más robusta
27
+ if 'semantic_live_state' not in st.session_state:
28
+ st.session_state.semantic_live_state = {
29
+ 'analysis_count': 0,
30
+ 'current_text': '',
31
+ 'last_result': None,
32
+ 'text_changed': False,
33
+ 'pending_analysis': False # Nuevo flag para análisis pendiente
34
+ }
35
+
36
+ # 2. Función para manejar cambios en el texto
37
+ def on_text_change():
38
+ current_text = st.session_state.semantic_live_text
39
+ st.session_state.semantic_live_state['current_text'] = current_text
40
+ st.session_state.semantic_live_state['text_changed'] = True
41
+
42
+ # 3. Crear columnas con nueva proporción (1:3)
43
+ input_col, result_col = st.columns([1, 3])
44
+
45
+ # Columna izquierda: Entrada de texto
46
+ with input_col:
47
+ st.subheader(semantic_t.get('enter_text', 'Ingrese su texto'))
48
+
49
+ # Área de texto con manejo de eventos
50
+ text_input = st.text_area(
51
+ semantic_t.get('text_input_label', 'Escriba o pegue su texto aquí'),
52
+ height=500,
53
+ key="semantic_live_text",
54
+ value=st.session_state.semantic_live_state.get('current_text', ''),
55
+ on_change=on_text_change,
56
+ label_visibility="collapsed"
57
+ )
58
+
59
+ # Botón de análisis y procesamiento
60
+ analyze_button = st.button(
61
+ semantic_t.get('analyze_button', 'Analizar'),
62
+ key="semantic_live_analyze",
63
+ type="primary",
64
+ icon="🔍",
65
+ disabled=not text_input,
66
+ use_container_width=True
67
+ )
68
+
69
+ # 4. Procesar análisis cuando se presiona el botón
70
+ if analyze_button and text_input:
71
+ st.session_state.semantic_live_state['pending_analysis'] = True
72
+ st.rerun()
73
+
74
+ # 5. Manejar análisis pendiente
75
+ if st.session_state.semantic_live_state.get('pending_analysis', False):
76
+ try:
77
+ with st.spinner(semantic_t.get('processing', 'Procesando...')):
78
+ analysis_result = process_semantic_input(
79
+ text_input,
80
+ lang_code,
81
+ nlp_models,
82
+ semantic_t
83
+ )
84
+
85
+ if analysis_result['success']:
86
+ st.session_state.semantic_live_state['last_result'] = analysis_result
87
+ st.session_state.semantic_live_state['analysis_count'] += 1
88
+ st.session_state.semantic_live_state['text_changed'] = False
89
+
90
+ # Guardar en la colección live
91
+ store_result = store_student_semantic_live_result(
92
+ st.session_state.username,
93
+ text_input,
94
+ analysis_result['analysis'],
95
+ lang_code
96
+ )
97
+
98
+ if not store_result:
99
+ st.error(semantic_t.get('error_saving', 'Error al guardar el análisis'))
100
+ else:
101
+ st.success(semantic_t.get('analysis_saved', 'Análisis guardado correctamente'))
102
+ else:
103
+ st.error(analysis_result.get('message', 'Error en el análisis'))
104
+
105
+ except Exception as e:
106
+ logger.error(f"Error en análisis: {str(e)}")
107
+ st.error(semantic_t.get('error_processing', 'Error al procesar el texto'))
108
+ finally:
109
+ st.session_state.semantic_live_state['pending_analysis'] = False
110
+
111
+ # Columna derecha: Visualización de resultados
112
+ with result_col:
113
+ st.subheader(semantic_t.get('live_results', 'Resultados en vivo'))
114
+
115
+ if 'last_result' in st.session_state.semantic_live_state and \
116
+ st.session_state.semantic_live_state['last_result'] is not None:
117
+
118
+ analysis = st.session_state.semantic_live_state['last_result']['analysis']
119
+
120
+ if 'key_concepts' in analysis and analysis['key_concepts'] and \
121
+ 'concept_graph' in analysis and analysis['concept_graph'] is not None:
122
+
123
+ st.markdown("""
124
+ <style>
125
+ .unified-container {
126
+ background-color: white;
127
+ border-radius: 10px;
128
+ overflow: hidden;
129
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
130
+ width: 100%;
131
+ margin-bottom: 1rem;
132
+ }
133
+ .concept-table {
134
+ display: flex;
135
+ flex-wrap: nowrap;
136
+ gap: 6px;
137
+ padding: 10px;
138
+ background-color: #f8f9fa;
139
+ overflow-x: auto;
140
+ white-space: nowrap;
141
+ }
142
+ .concept-item {
143
+ background-color: white;
144
+ border-radius: 4px;
145
+ padding: 4px 8px;
146
+ display: inline-flex;
147
+ align-items: center;
148
+ gap: 4px;
149
+ box-shadow: 0 1px 2px rgba(0,0,0,0.1);
150
+ flex-shrink: 0;
151
+ }
152
+ .concept-name {
153
+ font-weight: 500;
154
+ color: #1f2937;
155
+ font-size: 0.8em;
156
+ }
157
+ .concept-freq {
158
+ color: #6b7280;
159
+ font-size: 0.75em;
160
+ }
161
+ .graph-section {
162
+ padding: 20px;
163
+ background-color: white;
164
+ }
165
+ </style>
166
+ """, unsafe_allow_html=True)
167
+
168
+ with st.container():
169
+ # Conceptos en una sola línea
170
+ concepts_html = """
171
+ <div class="unified-container">
172
+ <div class="concept-table">
173
+ """
174
+ concepts_html += ''.join(
175
+ f'<div class="concept-item"><span class="concept-name">{concept}</span>'
176
+ f'<span class="concept-freq">({freq:.2f})</span></div>'
177
+ for concept, freq in analysis['key_concepts']
178
+ )
179
+ concepts_html += "</div></div>"
180
+ st.markdown(concepts_html, unsafe_allow_html=True)
181
+
182
+ # Grafo
183
+ if 'concept_graph' in analysis and analysis['concept_graph'] is not None:
184
+ st.image(
185
+ analysis['concept_graph'],
186
+ use_container_width=True
187
+ )
188
+
189
+ # Controles en dos columnas
190
+ col1, col2 = st.columns([1, 3])
191
+
192
+ with col1:
193
+ # Botón para consultar con el asistente (NUEVO)
194
+ if st.button("💬 Consultar con Asistente",
195
+ key="semantic_live_chat_button",
196
+ use_container_width=True):
197
+ if 'last_result' not in st.session_state.semantic_live_state:
198
+ st.error("Primero complete el análisis semántico")
199
+ else:
200
+ st.session_state.semantic_agent_data = {
201
+ 'text': st.session_state.semantic_live_state['current_text'],
202
+ 'metrics': analysis,
203
+ 'graph_data': analysis.get('concept_graph')
204
+ }
205
+ st.session_state.semantic_agent_active = True
206
+ st.rerun()
207
+
208
+ # Botón de descarga
209
+ st.download_button(
210
+ label="📥 " + semantic_t.get('download_graph', "Descargar"),
211
+ data=analysis['concept_graph'],
212
+ file_name="semantic_live_graph.png",
213
+ mime="image/png",
214
+ use_container_width=True
215
+ )
216
+
217
+ # Notificación si el agente está activo
218
+ if st.session_state.get('semantic_agent_active', False):
219
+ st.success(semantic_t.get('semantic_agent_ready_message',
220
+ 'El agente virtual está listo. Abre el chat en la barra lateral.'))
221
+
222
+ with st.expander("📊 " + semantic_t.get('graph_help', "Interpretación del gráfico")):
223
+ st.markdown("""
224
+ - 🔀 Las flechas indican la dirección de la relación entre conceptos
225
+ - 🎨 Los colores más intensos indican conceptos más centrales
226
+ - ⭕ El tamaño de los nodos representa la frecuencia del concepto
227
+ - ↔️ El grosor de las líneas indica la fuerza de la conexión
228
+ """)
229
+ else:
230
+ st.info(semantic_t.get('no_graph', 'No hay datos para mostrar'))
231
+ else:
232
+ st.info(semantic_t.get('analysis_prompt', 'Realice un análisis para ver los resultados'))
233
+
234
+ except Exception as e:
235
+ logger.error(f"Error general en interfaz semántica en vivo: {str(e)}")
236
+ st.error(semantic_t.get('general_error', "Se produjo un error. Por favor, intente de nuevo."))