AIdeaText commited on
Commit
5bb2e31
·
verified ·
1 Parent(s): dd88e34

Create ui.py

Browse files
Files changed (1) hide show
  1. modules/ui/ui.py +445 -0
modules/ui/ui.py ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # modules/ui/ui.py
2
+ import streamlit as st
3
+ from PIL import Image
4
+ import base64
5
+ from streamlit_player import st_player
6
+ import logging
7
+ from datetime import datetime
8
+ from dateutil.parser import parse
9
+
10
+ #########################################################
11
+ # Configuración de estilo CSS para el carrusel
12
+ st.markdown("""
13
+ <style>
14
+ .carousel-container {
15
+ position: relative;
16
+ max-width: 800px;
17
+ margin: 0 auto;
18
+ }
19
+ .carousel-image {
20
+ width: 100%;
21
+ border-radius: 10px;
22
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
23
+ display: none;
24
+ }
25
+ .carousel-image.active {
26
+ display: block;
27
+ animation: fadeIn 0.5s;
28
+ }
29
+ @keyframes fadeIn {
30
+ from {opacity: 0.4;}
31
+ to {opacity: 1;}
32
+ }
33
+ .carousel-caption {
34
+ text-align: center;
35
+ margin-top: 10px;
36
+ font-size: 1.1em;
37
+ color: #333;
38
+ }
39
+ .carousel-nav {
40
+ display: flex;
41
+ justify-content: center;
42
+ margin-top: 15px;
43
+ }
44
+ .carousel-dot {
45
+ height: 12px;
46
+ width: 12px;
47
+ margin: 0 5px;
48
+ background-color: #bbb;
49
+ border-radius: 50%;
50
+ display: inline-block;
51
+ cursor: pointer;
52
+ }
53
+ .carousel-dot.active {
54
+ background-color: #717171;
55
+ }
56
+ </style>
57
+ """, unsafe_allow_html=True)
58
+
59
+ # Datos del carrusel (imágenes y descripciones)
60
+ eventos = [
61
+ {
62
+ "imagen": "assets/img/socialmedia/WebSummit_ShowCase_2025.png",
63
+ "titulo": "WebSummitRio 2025, Brasil, april, 27-30",
64
+ "descripcion": "AIdeaText showcase"
65
+ },
66
+
67
+ {
68
+ "imagen": "assets/img/socialmedia/image_pycon_2024.png",
69
+ "titulo": "PyCon 2024, Colombia, june, 7-9",
70
+ "descripcion": "AIdeaText showcase"
71
+ }
72
+ ]
73
+
74
+ def initialize_carousel_state():
75
+ """Inicializa todas las variables de estado necesarias para el carrusel"""
76
+ if not hasattr(st.session_state, 'current_event'):
77
+ st.session_state.current_event = 0
78
+ if not hasattr(st.session_state, 'carousel_initialized'):
79
+ st.session_state.carousel_initialized = True
80
+
81
+ #####################
82
+ def show_carousel():
83
+ """Muestra el carrusel de imágenes con inicialización segura del estado"""
84
+ try:
85
+ # Inicialización segura del estado
86
+ initialize_carousel_state()
87
+
88
+ # Verificación adicional
89
+ if not hasattr(st.session_state, 'current_event'):
90
+ st.session_state.current_event = 0
91
+ st.rerun()
92
+
93
+ current_idx = st.session_state.current_event
94
+
95
+ with st.container():
96
+ #st.markdown("<h2 style='text-align: center; margin-bottom: 30px;'>Eventos Relevantes</h2>",
97
+ # unsafe_allow_html=True)
98
+
99
+ # Controles de navegación
100
+ col1, col2, col3 = st.columns([1, 6, 1])
101
+
102
+ with col1:
103
+ if st.button("◀", key="carousel_prev"):
104
+ st.session_state.current_event = (current_idx - 1) % len(eventos)
105
+ st.rerun()
106
+
107
+ with col2:
108
+ event = eventos[current_idx]
109
+ img = Image.open(event["imagen"]) if isinstance(event["imagen"], str) else event["imagen"]
110
+ st.image(
111
+ img,
112
+ use_container_width=True, # <-- Cambio realizado aquí
113
+ caption=f"{event['titulo']} - {event['descripcion']}"
114
+ )
115
+
116
+ with col3:
117
+ if st.button("▶", key="carousel_next"):
118
+ st.session_state.current_event = (current_idx + 1) % len(eventos)
119
+ st.rerun()
120
+
121
+ # Indicadores de posición
122
+ st.markdown("<div class='carousel-nav'>", unsafe_allow_html=True)
123
+ cols = st.columns(len(eventos))
124
+ for i, col in enumerate(cols):
125
+ with col:
126
+ if st.button("•", key=f"carousel_dot_{i}"):
127
+ st.session_state.current_event = i
128
+ st.rerun()
129
+ st.markdown("</div>", unsafe_allow_html=True)
130
+
131
+ except Exception as e:
132
+ st.error(f"Error al mostrar el carrusel: {str(e)}")
133
+ logger.error(f"Error en show_carousel: {str(e)}")
134
+
135
+ #########################################################
136
+
137
+
138
+ # Configura el logger PRIMERO, antes de cualquier uso
139
+ logging.basicConfig(level=logging.INFO)
140
+ logger = logging.getLogger(__name__)
141
+
142
+ # Importaciones locales
143
+ from session_state import initialize_session_state, logout
144
+ from translations import get_translations, get_landing_translations
145
+ from ..auth.auth import authenticate_user, authenticate_student, authenticate_admin
146
+ from ..database.sql_db import store_application_request
147
+
148
+ # Intento de importación con logger YA DEFINIDO
149
+ try:
150
+ from .user_page import user_page
151
+ except ImportError as e:
152
+ logger.error(f"No se pudo importar user_page: {str(e)}. Asegúrate de que el archivo existe.")
153
+
154
+ # Función de respaldo
155
+ def user_page(lang_code, t):
156
+ st.error("La página de usuario no está disponible. Por favor, contacta al administrador.")
157
+
158
+ from ..admin.admin_ui import admin_page
159
+
160
+ #############################################################
161
+ def main():
162
+ logger.info(f"Entrando en main() - Página actual: {st.session_state.page}")
163
+
164
+ if 'nlp_models' not in st.session_state:
165
+ logger.error("Los modelos NLP no están inicializados.")
166
+ st.error("Los modelos NLP no están inicializados. Por favor, reinicie la aplicación.")
167
+ return
168
+
169
+ lang_code = st.session_state.get('lang_code', 'es')
170
+ t = get_translations(lang_code)
171
+
172
+ logger.info(f"Página actual antes de la lógica de enrutamiento: {st.session_state.page}")
173
+
174
+ if st.session_state.get('logged_out', False):
175
+ st.session_state.logged_out = False
176
+ st.session_state.page = 'login'
177
+ st.rerun()
178
+
179
+ if not st.session_state.get('logged_in', False):
180
+ logger.info("Usuario no ha iniciado sesión. Mostrando página de login/registro")
181
+ login_register_page(lang_code, t)
182
+ elif st.session_state.page == 'user':
183
+ if st.session_state.role == 'Administrador':
184
+ logger.info("Redirigiendo a la página de administrador")
185
+ st.session_state.page = 'Admin'
186
+ st.rerun()
187
+ else:
188
+ logger.info("Renderizando página de usuario")
189
+ user_page(lang_code, t)
190
+ elif st.session_state.page == "Admin":
191
+ logger.info("Renderizando página de administrador")
192
+ admin_page()
193
+ else:
194
+ logger.error(f"Página no reconocida: {st.session_state.page}")
195
+ st.error(t.get('unrecognized_page', 'Página no reconocida'))
196
+ # Redirigir a la página de usuario en caso de error
197
+ st.session_state.page = 'user'
198
+ st.rerun()
199
+
200
+ logger.info(f"Saliendo de main() - Estado final de la sesión: {st.session_state}")
201
+
202
+ #############################################################
203
+ #############################################################
204
+ def login_register_page(lang_code, t):
205
+ # Obtener traducciones específicas para landing page
206
+ landing_t = get_landing_translations(lang_code)
207
+
208
+ # Language selection dropdown at the top
209
+ languages = {'Español': 'es', 'English': 'en', 'Français': 'fr', 'Português': 'pt'}
210
+
211
+ # Estilo personalizado para mejorar el espaciado y alineación
212
+ st.markdown("""
213
+ <style>
214
+ div.row-widget.stHorizontalBlock {
215
+ align-items: center;
216
+ }
217
+ </style>
218
+ """, unsafe_allow_html=True)
219
+
220
+ # Crear contenedor para logos y selector de idioma usando columnas de Streamlit
221
+ col1, col2, col3, col4, col5 = st.columns([0.25, 0.25, 1, 1, 1])
222
+
223
+ with col1:
224
+ # Logo de ALPHA
225
+ st.image("https://huggingface.co/spaces/AIdeaText/v3/resolve/main/assets/img/ALPHA_Startup%20Badges.png", width=100)
226
+
227
+ with col2:
228
+ # Logo de AIdeaText
229
+ st.image("https://huggingface.co/spaces/AIdeaText/v3/resolve/main/assets/img/AIdeaText_Logo_vectores.png", width=100)
230
+
231
+ with col5:
232
+ # Selector de idioma
233
+ selected_lang = st.selectbox(
234
+ landing_t['select_language'],
235
+ list(languages.keys()),
236
+ index=list(languages.values()).index(lang_code),
237
+ key=f"landing_language_selector_{lang_code}"
238
+ )
239
+ new_lang_code = languages[selected_lang]
240
+ if lang_code != new_lang_code:
241
+ st.session_state.lang_code = new_lang_code
242
+ st.rerun()
243
+
244
+ # Main content with columns
245
+ left_column, right_column = st.columns([1, 3])
246
+
247
+ with left_column:
248
+ tab1, tab2 = st.tabs([landing_t['login'], landing_t['register']])
249
+
250
+ with tab1:
251
+ login_form(lang_code, landing_t)
252
+
253
+ with tab2:
254
+ register_form(lang_code, landing_t)
255
+
256
+ with right_column:
257
+ display_videos_and_info(lang_code, landing_t)
258
+
259
+ #############################################################
260
+ #############################################################
261
+ def login_form(lang_code, landing_t):
262
+ with st.form("login_form"):
263
+ username = st.text_input(landing_t['email'])
264
+ password = st.text_input(landing_t['password'], type="password")
265
+ submit_button = st.form_submit_button(landing_t['login_button'])
266
+
267
+ if submit_button:
268
+ success, role = authenticate_user(username, password)
269
+ if success:
270
+ st.session_state.logged_in = True
271
+ st.session_state.username = username
272
+ st.session_state.role = role
273
+ if role == 'Administrador':
274
+ st.session_state.page = 'Admin'
275
+ else:
276
+ st.session_state.page = 'user'
277
+ logger.info(f"Usuario autenticado: {username}, Rol: {role}")
278
+ st.rerun()
279
+ else:
280
+ st.error(landing_t['invalid_credentials'])
281
+
282
+
283
+ #############################################################
284
+ #############################################################
285
+ def register_form(lang_code, landing_t):
286
+ name = st.text_input(landing_t['name'])
287
+ lastname = st.text_input(landing_t['lastname'])
288
+ institution = st.text_input(landing_t['institution'])
289
+ current_role = st.selectbox(landing_t['current_role'],
290
+ [landing_t['professor'], landing_t['student'], landing_t['administrative']])
291
+
292
+ # Definimos el rol por defecto como estudiante
293
+ desired_role = landing_t['student']
294
+
295
+ email = st.text_input(landing_t['institutional_email'])
296
+ reason = st.text_area(landing_t['interest_reason'])
297
+
298
+ if st.button(landing_t['submit_application']):
299
+ logger.info(f"Intentando enviar solicitud para {email}")
300
+ logger.debug(f"Datos del formulario: name={name}, lastname={lastname}, email={email}, institution={institution}, current_role={current_role}, desired_role={desired_role}, reason={reason}")
301
+
302
+ if not name or not lastname or not email or not institution or not reason:
303
+ logger.warning("Envío de formulario incompleto")
304
+ st.error(landing_t['complete_all_fields'])
305
+ elif not is_institutional_email(email):
306
+ logger.warning(f"Email no institucional utilizado: {email}")
307
+ st.error(landing_t['use_institutional_email'])
308
+ else:
309
+ logger.info(f"Intentando almacenar solicitud para {email}")
310
+ success = store_application_request(name, lastname, email, institution, current_role, desired_role, reason)
311
+ if success:
312
+ st.success(landing_t['application_sent'])
313
+ logger.info(f"Solicitud almacenada exitosamente para {email}")
314
+ else:
315
+ st.error(landing_t['application_error'])
316
+ logger.error(f"Error al almacenar solicitud para {email}")
317
+
318
+
319
+ #############################################################
320
+ #############################################################
321
+ def is_institutional_email(email):
322
+ forbidden_domains = ['gmail.com', 'hotmail.com', 'yahoo.com', 'outlook.com']
323
+ return not any(domain in email.lower() for domain in forbidden_domains)
324
+
325
+ #############################################################
326
+ #############################################################
327
+ def display_videos_and_info(lang_code, landing_t):
328
+ # Crear tabs para cada sección
329
+ tab_gallery, tab_use_case, tab_videos, tab_events, tab_news = st.tabs([
330
+ landing_t['event_photos'],
331
+ landing_t['use_cases'],
332
+ landing_t['presentation_videos'],
333
+ landing_t['academic_presentations'],
334
+ landing_t['version_control']
335
+ ])
336
+
337
+ # Tab de Casos de uso
338
+ with tab_use_case:
339
+ use_case_videos = {
340
+ "Español - Semantic Analysis": "https://https://youtu.be/_4WMufl6MTA"
341
+ }
342
+
343
+ selected_title = st.selectbox(landing_t['select_use_case'], list(use_case_videos.keys()))
344
+ if selected_title in use_case_videos:
345
+ try:
346
+ st_player(use_case_videos[selected_title])
347
+ except Exception as e:
348
+ st.error(f"Error al cargar el video: {str(e)}")
349
+
350
+ # Tab de Videos
351
+ with tab_videos:
352
+ videos = {
353
+ "Reel AIdeaText": "https://youtu.be/hXnwUvN1Q9Q",
354
+ "Presentación en SENDA, UNAM. Ciudad de México, México" : "https://www.youtube.com/watch?v=XFLvjST2cE0",
355
+ "Presentación en PyCon 2024. Colombia, Medellín": "https://www.youtube.com/watch?v=Jn545-IKx5Q",
356
+ "Presentación en Fundación Ser Maaestro. Lima, Perú": "https://www.youtube.com/watch?v=imc4TI1q164",
357
+ "Presentación en Explora del IFE, TEC de Monterrey, Nuevo León, México": "https://www.youtube.com/watch?v=Fqi4Di_Rj_s",
358
+ "Entrevista con el Dr. Guillermo Ruíz. Lima, Perú": "https://www.youtube.com/watch?v=_ch8cRja3oc",
359
+ "Demo de la versión de escritorio.": "https://www.youtube.com/watch?v=nP6eXbog-ZY"
360
+ }
361
+
362
+ selected_title = st.selectbox(landing_t['select_presentation'], list(videos.keys()))
363
+ if selected_title in videos:
364
+ try:
365
+ st_player(videos[selected_title])
366
+ except Exception as e:
367
+ st.error(f"Error al cargar el video: {str(e)}")
368
+
369
+ # Tab de Eventos
370
+ with tab_events:
371
+ st.markdown("""
372
+ ## 2025
373
+
374
+ **El Agente Cognitivo Vinculante como Innovación en el Aprendizaje Adaptativo: el caso de AIdeaText**
375
+ IFE CONFERENCE 2025. Organizado por el Instituto para el Futuro de la Educación del TEC de Monterrey.
376
+ Nuevo León, México. Del 28 al 30 enero 2025
377
+
378
+ ## 2024
379
+ [1]
380
+ AIdeaText, AIdeaText, recurso digital que emplea la técnica de Análisis de Resonancia Central para perfeccionar textos académicos**
381
+ V Temporada SENDA - Organizado por el Seminario de Entornos y Narrativas Digitales en la Academia del
382
+ Instituto de Investigaciones Antropológicas (IIA) de la Universidad Autonóma de México (UNAM). 22 noviembre 2024
383
+
384
+ [2]
385
+ Aproximación al Agente Cognitivo Vinculante (ACV) desde la Teoría del Actor Red (TAR)**
386
+ Congreso HeETI 2024: Horizontes Expandidos de la Educación, la Tecnología y la Innovación
387
+ Universidad el Claustro de Sor Juana. Del 25 al 27 septiembre 2024
388
+
389
+ [3]
390
+ AIdeaText, visualización de mapas semánticos**
391
+ PyCon 2024, Organizado por el grupo de desarrolladores independientes de Python.
392
+ Universidad EAFIT, Medellín, Colombia. Del 7 al 9 de junio de 2024.
393
+
394
+ ## 2023
395
+ **Aproximación al Agente Cognitivo Vinculante (ACV) desde la Teoría del Actor Red (TAR)**
396
+ [1]
397
+ XVII Congreso Nacional de Investigación Educativa - VII Encuentro de Estudiantes de Posgrado Educación.
398
+ Consejo Mexicano de Investigación Educativa (COMIE)
399
+ Villahermosa, Tabasco, México.
400
+ Del 4 al 8 de diciembre 2023
401
+
402
+ [2]
403
+ XXXI Encuentro Internacional de Educación a Distancia
404
+ Universidad de Guadalajara. Jalisco, México.
405
+ Del 27 al 30 noviembre 2023
406
+
407
+ [3]
408
+ IV Temporada SENDA - Seminario de Entornos y Narrativas Digitales en la Academia
409
+ Instituto de Investigaciones Antropológicas (IIA), UNAM.
410
+ 22 noviembre 2023
411
+
412
+ [4]
413
+ 1er Congreso Internacional de Educación Digital
414
+ Instituto Politécnico Nacional, sede Zacatecas. México.
415
+ Del 23 al 24 de noviembre de 2023
416
+
417
+ [5]
418
+ La cuestión de la centralidad del maestro frente a las tecnologías digitales generativas**
419
+ Innova Fórum: Ecosistemas de Aprendizaje
420
+ Universidad de Guadalajara. Jalisco, México.
421
+ Del 16 al 18 de mayo 2023
422
+ """)
423
+
424
+ ##########################################################################################
425
+
426
+ # Tab de Galería
427
+ with tab_gallery:
428
+ show_carousel()
429
+
430
+ #############################################################
431
+ #############################################################
432
+ # Tab de Novedades - Usar contenido traducido
433
+ with tab_news:
434
+ st.markdown(f"### {landing_t['latest_version_title']}")
435
+ for update in landing_t['version_updates']:
436
+ st.markdown(f"- {update}")
437
+
438
+ #############################################################
439
+ #############################################################
440
+ # Definición de __all__ para especificar qué se exporta
441
+ __all__ = ['main', 'login_register_page', 'initialize_session_state']
442
+
443
+ # Bloque de ejecución condicional
444
+ if __name__ == "__main__":
445
+ main()