AIdeaText commited on
Commit
f185a6d
·
verified ·
1 Parent(s): a75f74b

Update modules/ui/ui.py

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