AIdeaText commited on
Commit
3ed079d
·
verified ·
1 Parent(s): a55e716

Update modules/discourse/discourse_interface.py

Browse files
Files changed (1) hide show
  1. modules/discourse/discourse_interface.py +280 -280
modules/discourse/discourse_interface.py CHANGED
@@ -1,281 +1,281 @@
1
- # modules/discourse/discourse/discourse_interface.py
2
-
3
- import streamlit as st
4
- import pandas as pd
5
- import plotly.graph_objects as go
6
- import logging
7
- from ..utils.widget_utils import generate_unique_key
8
- from .discourse_process import perform_discourse_analysis
9
- from ..database.chat_mongo_db import store_chat_history
10
- from ..database.discourse_mongo_db import store_student_discourse_result
11
-
12
- logger = logging.getLogger(__name__)
13
-
14
- def display_discourse_interface(lang_code, nlp_models, discourse_t):
15
- """
16
- Interfaz para el análisis del discurso
17
- Args:
18
- lang_code: Código del idioma actual
19
- nlp_models: Modelos de spaCy cargados
20
- discourse_t: Diccionario de traducciones
21
- """
22
- try:
23
- # 1. Inicializar estado si no existe
24
- if 'discourse_state' not in st.session_state:
25
- st.session_state.discourse_state = {
26
- 'analysis_count': 0,
27
- 'last_analysis': None,
28
- 'current_files': None
29
- }
30
-
31
- # 2. Título y descripción
32
- st.subheader(discourse_t.get('discourse_title', 'Análisis del Discurso'))
33
- st.info(discourse_t.get('initial_instruction',
34
- 'Cargue dos archivos de texto para realizar un análisis comparativo del discurso.'))
35
-
36
- # 3. Área de carga de archivos
37
- col1, col2 = st.columns(2)
38
- with col1:
39
- st.markdown(discourse_t.get('file1_label', "**Documento 1 (Patrón)**"))
40
- uploaded_file1 = st.file_uploader(
41
- discourse_t.get('file_uploader1', "Cargar archivo 1"),
42
- type=['txt'],
43
- key=f"discourse_file1_{st.session_state.discourse_state['analysis_count']}"
44
- )
45
-
46
- with col2:
47
- st.markdown(discourse_t.get('file2_label', "**Documento 2 (Comparación)**"))
48
- uploaded_file2 = st.file_uploader(
49
- discourse_t.get('file_uploader2', "Cargar archivo 2"),
50
- type=['txt'],
51
- key=f"discourse_file2_{st.session_state.discourse_state['analysis_count']}"
52
- )
53
-
54
- # 4. Botón de análisis
55
- col1, col2, col3 = st.columns([1,2,1])
56
- with col1:
57
- analyze_button = st.button(
58
- discourse_t.get('discourse_analyze_button', 'Analizar Discurso'),
59
- key=generate_unique_key("discourse", "analyze_button"),
60
- type="primary",
61
- icon="🔍",
62
- disabled=not (uploaded_file1 and uploaded_file2),
63
- use_container_width=True
64
- )
65
-
66
- # 5. Proceso de análisis
67
- if analyze_button and uploaded_file1 and uploaded_file2:
68
- try:
69
- with st.spinner(discourse_t.get('processing', 'Procesando análisis...')):
70
- # Leer contenido de archivos
71
- text1 = uploaded_file1.getvalue().decode('utf-8')
72
- text2 = uploaded_file2.getvalue().decode('utf-8')
73
-
74
- # Realizar análisis
75
- result = perform_discourse_analysis(
76
- text1,
77
- text2,
78
- nlp_models[lang_code],
79
- lang_code
80
- )
81
-
82
- if result['success']:
83
- # Guardar estado
84
- st.session_state.discourse_result = result
85
- st.session_state.discourse_state['analysis_count'] += 1
86
- st.session_state.discourse_state['current_files'] = (
87
- uploaded_file1.name,
88
- uploaded_file2.name
89
- )
90
-
91
- # Guardar en base de datos
92
- if store_student_discourse_result(
93
- st.session_state.username,
94
- text1,
95
- text2,
96
- result
97
- ):
98
- st.success(discourse_t.get('success_message', 'Análisis guardado correctamente'))
99
-
100
- # Mostrar resultados
101
- display_discourse_results(result, lang_code, discourse_t)
102
- else:
103
- st.error(discourse_t.get('error_message', 'Error al guardar el análisis'))
104
- else:
105
- st.error(discourse_t.get('analysis_error', 'Error en el análisis'))
106
-
107
- except Exception as e:
108
- logger.error(f"Error en análisis del discurso: {str(e)}")
109
- st.error(discourse_t.get('error_processing', f'Error procesando archivos: {str(e)}'))
110
-
111
- # 6. Mostrar resultados previos
112
- elif 'discourse_result' in st.session_state and st.session_state.discourse_result is not None:
113
- if st.session_state.discourse_state.get('current_files'):
114
- st.info(
115
- discourse_t.get('current_analysis_message', 'Mostrando análisis de los archivos: {} y {}')
116
- .format(*st.session_state.discourse_state['current_files'])
117
- )
118
- display_discourse_results(
119
- st.session_state.discourse_result,
120
- lang_code,
121
- discourse_t
122
- )
123
-
124
- except Exception as e:
125
- logger.error(f"Error general en interfaz del discurso: {str(e)}")
126
- st.error(discourse_t.get('general_error', 'Se produjo un error. Por favor, intente de nuevo.'))
127
-
128
-
129
-
130
- #####################################################################################################################
131
-
132
- def display_discourse_results(result, lang_code, discourse_t):
133
- """
134
- Muestra los resultados del análisis del discurso
135
- """
136
- if not result.get('success'):
137
- st.warning(discourse_t.get('no_results', 'No hay resultados disponibles'))
138
- return
139
-
140
- # Estilo CSS
141
- st.markdown("""
142
- <style>
143
- .concepts-container {
144
- display: flex;
145
- flex-wrap: nowrap;
146
- gap: 8px;
147
- padding: 12px;
148
- background-color: #f8f9fa;
149
- border-radius: 8px;
150
- overflow-x: auto;
151
- margin-bottom: 15px;
152
- white-space: nowrap;
153
- }
154
- .concept-item {
155
- background-color: white;
156
- border-radius: 4px;
157
- padding: 6px 10px;
158
- display: inline-flex;
159
- align-items: center;
160
- gap: 4px;
161
- box-shadow: 0 1px 2px rgba(0,0,0,0.1);
162
- flex-shrink: 0;
163
- }
164
- .concept-name {
165
- font-weight: 500;
166
- color: #1f2937;
167
- font-size: 0.85em;
168
- }
169
- .concept-freq {
170
- color: #6b7280;
171
- font-size: 0.75em;
172
- }
173
- .graph-container {
174
- background-color: white;
175
- padding: 15px;
176
- border-radius: 8px;
177
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
178
- margin-top: 10px;
179
- }
180
- </style>
181
- """, unsafe_allow_html=True)
182
-
183
- col1, col2 = st.columns(2)
184
-
185
- # Documento 1
186
- with col1:
187
- st.subheader(discourse_t.get('doc1_title', 'Documento 1'))
188
- st.markdown(discourse_t.get('key_concepts', 'Conceptos Clave'))
189
- if 'key_concepts1' in result:
190
- concepts_html = f"""
191
- <div class="concepts-container">
192
- {''.join([
193
- f'<div class="concept-item"><span class="concept-name">{concept}</span>'
194
- f'<span class="concept-freq">({freq:.2f})</span></div>'
195
- for concept, freq in result['key_concepts1']
196
- ])}
197
- </div>
198
- """
199
- st.markdown(concepts_html, unsafe_allow_html=True)
200
-
201
- if 'graph1' in result:
202
- st.markdown('<div class="graph-container">', unsafe_allow_html=True)
203
- st.pyplot(result['graph1'])
204
-
205
- # Botones y controles
206
- button_col1, spacer_col1 = st.columns([1,4])
207
- with button_col1:
208
- if 'graph1_bytes' in result:
209
- st.download_button(
210
- label="📥 " + discourse_t.get('download_graph', "Download"),
211
- data=result['graph1_bytes'],
212
- file_name="discourse_graph1.png",
213
- mime="image/png",
214
- use_container_width=True
215
- )
216
-
217
- # Interpretación como texto normal sin expander
218
- st.markdown("**📊 Interpretación del grafo:**")
219
- st.markdown("""
220
- - 🔀 Las flechas indican la dirección de la relación entre conceptos
221
- - 🎨 Los colores más intensos indican conceptos más centrales en el texto
222
- - ⭕ El tamaño de los nodos representa la frecuencia del concepto
223
- - ↔️ El grosor de las líneas indica la fuerza de la conexión
224
- """)
225
-
226
- st.markdown('</div>', unsafe_allow_html=True)
227
- else:
228
- st.warning(discourse_t.get('graph_not_available', 'Gráfico no disponible'))
229
- else:
230
- st.warning(discourse_t.get('concepts_not_available', 'Conceptos no disponibles'))
231
-
232
- # Documento 2
233
- with col2:
234
- st.subheader(discourse_t.get('doc2_title', 'Documento 2'))
235
- st.markdown(discourse_t.get('key_concepts', 'Conceptos Clave'))
236
- if 'key_concepts2' in result:
237
- concepts_html = f"""
238
- <div class="concepts-container">
239
- {''.join([
240
- f'<div class="concept-item"><span class="concept-name">{concept}</span>'
241
- f'<span class="concept-freq">({freq:.2f})</span></div>'
242
- for concept, freq in result['key_concepts2']
243
- ])}
244
- </div>
245
- """
246
- st.markdown(concepts_html, unsafe_allow_html=True)
247
-
248
- if 'graph2' in result:
249
- st.markdown('<div class="graph-container">', unsafe_allow_html=True)
250
- st.pyplot(result['graph2'])
251
-
252
- # Botones y controles
253
- button_col2, spacer_col2 = st.columns([1,4])
254
- with button_col2:
255
- if 'graph2_bytes' in result:
256
- st.download_button(
257
- label="📥 " + discourse_t.get('download_graph', "Download"),
258
- data=result['graph2_bytes'],
259
- file_name="discourse_graph2.png",
260
- mime="image/png",
261
- use_container_width=True
262
- )
263
-
264
- # Interpretación como texto normal sin expander
265
- st.markdown("**📊 Interpretación del grafo:**")
266
- st.markdown("""
267
- - 🔀 Las flechas indican la dirección de la relación entre conceptos
268
- - 🎨 Los colores más intensos indican conceptos más centrales en el texto
269
- - ⭕ El tamaño de los nodos representa la frecuencia del concepto
270
- - ↔️ El grosor de las líneas indica la fuerza de la conexión
271
- """)
272
-
273
- st.markdown('</div>', unsafe_allow_html=True)
274
- else:
275
- st.warning(discourse_t.get('graph_not_available', 'Gráfico no disponible'))
276
- else:
277
- st.warning(discourse_t.get('concepts_not_available', 'Conceptos no disponibles'))
278
-
279
- # Nota informativa sobre la comparación
280
- st.info(discourse_t.get('comparison_note',
281
  'La funcionalidad de comparación detallada estará disponible en una próxima actualización.'))
 
1
+ # modules/discourse/discourse/discourse_interface.py
2
+
3
+ import streamlit as st
4
+ import pandas as pd
5
+ import plotly.graph_objects as go
6
+ import logging
7
+ from ..utils.widget_utils import generate_unique_key
8
+ from .discourse_process import perform_discourse_analysis
9
+ from ..database.chat_mongo_db import store_chat_history
10
+ from ..database.discourse_mongo_db import store_student_discourse_result
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ def display_discourse_interface(lang_code, nlp_models, discourse_t):
15
+ """
16
+ Interfaz para el análisis del discurso
17
+ Args:
18
+ lang_code: Código del idioma actual
19
+ nlp_models: Modelos de spaCy cargados
20
+ discourse_t: Diccionario de traducciones
21
+ """
22
+ try:
23
+ # 1. Inicializar estado si no existe
24
+ if 'discourse_state' not in st.session_state:
25
+ st.session_state.discourse_state = {
26
+ 'analysis_count': 0,
27
+ 'last_analysis': None,
28
+ 'current_files': None
29
+ }
30
+
31
+ # 2. Título y descripción
32
+ st.subheader(discourse_t.get('discourse_title', 'Análisis del Discurso'))
33
+ st.info(discourse_t.get('initial_instruction',
34
+ 'Cargue dos archivos de texto para realizar un análisis comparativo del discurso.'))
35
+
36
+ # 3. Área de carga de archivos
37
+ col1, col2 = st.columns(2)
38
+ with col1:
39
+ st.markdown(discourse_t.get('file1_label', "**Documento 1 (Patrón)**"))
40
+ uploaded_file1 = st.file_uploader(
41
+ discourse_t.get('file_uploader1', "Cargar archivo 1"),
42
+ type=['txt'],
43
+ key=f"discourse_file1_{st.session_state.discourse_state['analysis_count']}"
44
+ )
45
+
46
+ with col2:
47
+ st.markdown(discourse_t.get('file2_label', "**Documento 2 (Comparación)**"))
48
+ uploaded_file2 = st.file_uploader(
49
+ discourse_t.get('file_uploader2', "Cargar archivo 2"),
50
+ type=['txt'],
51
+ key=f"discourse_file2_{st.session_state.discourse_state['analysis_count']}"
52
+ )
53
+
54
+ # 4. Botón de análisis
55
+ col1, col2, col3 = st.columns([1,2,1])
56
+ with col1:
57
+ analyze_button = st.button(
58
+ discourse_t.get('discourse_analyze_button', 'Comparar textos'),
59
+ key=generate_unique_key("discourse", "analyze_button"),
60
+ type="primary",
61
+ icon="🔍",
62
+ disabled=not (uploaded_file1 and uploaded_file2),
63
+ use_container_width=True
64
+ )
65
+
66
+ # 5. Proceso de análisis
67
+ if analyze_button and uploaded_file1 and uploaded_file2:
68
+ try:
69
+ with st.spinner(discourse_t.get('processing', 'Procesando análisis...')):
70
+ # Leer contenido de archivos
71
+ text1 = uploaded_file1.getvalue().decode('utf-8')
72
+ text2 = uploaded_file2.getvalue().decode('utf-8')
73
+
74
+ # Realizar análisis
75
+ result = perform_discourse_analysis(
76
+ text1,
77
+ text2,
78
+ nlp_models[lang_code],
79
+ lang_code
80
+ )
81
+
82
+ if result['success']:
83
+ # Guardar estado
84
+ st.session_state.discourse_result = result
85
+ st.session_state.discourse_state['analysis_count'] += 1
86
+ st.session_state.discourse_state['current_files'] = (
87
+ uploaded_file1.name,
88
+ uploaded_file2.name
89
+ )
90
+
91
+ # Guardar en base de datos
92
+ if store_student_discourse_result(
93
+ st.session_state.username,
94
+ text1,
95
+ text2,
96
+ result
97
+ ):
98
+ st.success(discourse_t.get('success_message', 'Análisis guardado correctamente'))
99
+
100
+ # Mostrar resultados
101
+ display_discourse_results(result, lang_code, discourse_t)
102
+ else:
103
+ st.error(discourse_t.get('error_message', 'Error al guardar el análisis'))
104
+ else:
105
+ st.error(discourse_t.get('analysis_error', 'Error en el análisis'))
106
+
107
+ except Exception as e:
108
+ logger.error(f"Error en análisis del discurso: {str(e)}")
109
+ st.error(discourse_t.get('error_processing', f'Error procesando archivos: {str(e)}'))
110
+
111
+ # 6. Mostrar resultados previos
112
+ elif 'discourse_result' in st.session_state and st.session_state.discourse_result is not None:
113
+ if st.session_state.discourse_state.get('current_files'):
114
+ st.info(
115
+ discourse_t.get('current_analysis_message', 'Mostrando análisis de los archivos: {} y {}')
116
+ .format(*st.session_state.discourse_state['current_files'])
117
+ )
118
+ display_discourse_results(
119
+ st.session_state.discourse_result,
120
+ lang_code,
121
+ discourse_t
122
+ )
123
+
124
+ except Exception as e:
125
+ logger.error(f"Error general en interfaz del discurso: {str(e)}")
126
+ st.error(discourse_t.get('general_error', 'Se produjo un error. Por favor, intente de nuevo.'))
127
+
128
+
129
+
130
+ #####################################################################################################################
131
+
132
+ def display_discourse_results(result, lang_code, discourse_t):
133
+ """
134
+ Muestra los resultados del análisis del discurso
135
+ """
136
+ if not result.get('success'):
137
+ st.warning(discourse_t.get('no_results', 'No hay resultados disponibles'))
138
+ return
139
+
140
+ # Estilo CSS
141
+ st.markdown("""
142
+ <style>
143
+ .concepts-container {
144
+ display: flex;
145
+ flex-wrap: nowrap;
146
+ gap: 8px;
147
+ padding: 12px;
148
+ background-color: #f8f9fa;
149
+ border-radius: 8px;
150
+ overflow-x: auto;
151
+ margin-bottom: 15px;
152
+ white-space: nowrap;
153
+ }
154
+ .concept-item {
155
+ background-color: white;
156
+ border-radius: 4px;
157
+ padding: 6px 10px;
158
+ display: inline-flex;
159
+ align-items: center;
160
+ gap: 4px;
161
+ box-shadow: 0 1px 2px rgba(0,0,0,0.1);
162
+ flex-shrink: 0;
163
+ }
164
+ .concept-name {
165
+ font-weight: 500;
166
+ color: #1f2937;
167
+ font-size: 0.85em;
168
+ }
169
+ .concept-freq {
170
+ color: #6b7280;
171
+ font-size: 0.75em;
172
+ }
173
+ .graph-container {
174
+ background-color: white;
175
+ padding: 15px;
176
+ border-radius: 8px;
177
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
178
+ margin-top: 10px;
179
+ }
180
+ </style>
181
+ """, unsafe_allow_html=True)
182
+
183
+ col1, col2 = st.columns(2)
184
+
185
+ # Documento 1
186
+ with col1:
187
+ st.subheader(discourse_t.get('doc1_title', 'Documento 1'))
188
+ st.markdown(discourse_t.get('key_concepts', 'Conceptos Clave'))
189
+ if 'key_concepts1' in result:
190
+ concepts_html = f"""
191
+ <div class="concepts-container">
192
+ {''.join([
193
+ f'<div class="concept-item"><span class="concept-name">{concept}</span>'
194
+ f'<span class="concept-freq">({freq:.2f})</span></div>'
195
+ for concept, freq in result['key_concepts1']
196
+ ])}
197
+ </div>
198
+ """
199
+ st.markdown(concepts_html, unsafe_allow_html=True)
200
+
201
+ if 'graph1' in result:
202
+ st.markdown('<div class="graph-container">', unsafe_allow_html=True)
203
+ st.pyplot(result['graph1'])
204
+
205
+ # Botones y controles
206
+ button_col1, spacer_col1 = st.columns([1,4])
207
+ with button_col1:
208
+ if 'graph1_bytes' in result:
209
+ st.download_button(
210
+ label="📥 " + discourse_t.get('download_graph', "Download"),
211
+ data=result['graph1_bytes'],
212
+ file_name="discourse_graph1.png",
213
+ mime="image/png",
214
+ use_container_width=True
215
+ )
216
+
217
+ # Interpretación como texto normal sin expander
218
+ st.markdown("**📊 Interpretación del grafo:**")
219
+ st.markdown("""
220
+ - 🔀 Las flechas indican la dirección de la relación entre conceptos
221
+ - 🎨 Los colores más intensos indican conceptos más centrales en el texto
222
+ - ⭕ El tamaño de los nodos representa la frecuencia del concepto
223
+ - ↔️ El grosor de las líneas indica la fuerza de la conexión
224
+ """)
225
+
226
+ st.markdown('</div>', unsafe_allow_html=True)
227
+ else:
228
+ st.warning(discourse_t.get('graph_not_available', 'Gráfico no disponible'))
229
+ else:
230
+ st.warning(discourse_t.get('concepts_not_available', 'Conceptos no disponibles'))
231
+
232
+ # Documento 2
233
+ with col2:
234
+ st.subheader(discourse_t.get('doc2_title', 'Documento 2'))
235
+ st.markdown(discourse_t.get('key_concepts', 'Conceptos Clave'))
236
+ if 'key_concepts2' in result:
237
+ concepts_html = f"""
238
+ <div class="concepts-container">
239
+ {''.join([
240
+ f'<div class="concept-item"><span class="concept-name">{concept}</span>'
241
+ f'<span class="concept-freq">({freq:.2f})</span></div>'
242
+ for concept, freq in result['key_concepts2']
243
+ ])}
244
+ </div>
245
+ """
246
+ st.markdown(concepts_html, unsafe_allow_html=True)
247
+
248
+ if 'graph2' in result:
249
+ st.markdown('<div class="graph-container">', unsafe_allow_html=True)
250
+ st.pyplot(result['graph2'])
251
+
252
+ # Botones y controles
253
+ button_col2, spacer_col2 = st.columns([1,4])
254
+ with button_col2:
255
+ if 'graph2_bytes' in result:
256
+ st.download_button(
257
+ label="📥 " + discourse_t.get('download_graph', "Download"),
258
+ data=result['graph2_bytes'],
259
+ file_name="discourse_graph2.png",
260
+ mime="image/png",
261
+ use_container_width=True
262
+ )
263
+
264
+ # Interpretación como texto normal sin expander
265
+ st.markdown("**📊 Interpretación del grafo:**")
266
+ st.markdown("""
267
+ - 🔀 Las flechas indican la dirección de la relación entre conceptos
268
+ - 🎨 Los colores más intensos indican conceptos más centrales en el texto
269
+ - ⭕ El tamaño de los nodos representa la frecuencia del concepto
270
+ - ↔️ El grosor de las líneas indica la fuerza de la conexión
271
+ """)
272
+
273
+ st.markdown('</div>', unsafe_allow_html=True)
274
+ else:
275
+ st.warning(discourse_t.get('graph_not_available', 'Gráfico no disponible'))
276
+ else:
277
+ st.warning(discourse_t.get('concepts_not_available', 'Conceptos no disponibles'))
278
+
279
+ # Nota informativa sobre la comparación
280
+ st.info(discourse_t.get('comparison_note',
281
  'La funcionalidad de comparación detallada estará disponible en una próxima actualización.'))