AIdeaText commited on
Commit
82bf6fc
verified
1 Parent(s): 3a8d21a

Upload 9 files

Browse files
.gitattributes CHANGED
@@ -1,35 +1,35 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,14 +1,14 @@
1
- ---
2
- title: G1a
3
- emoji: 馃弳
4
- colorFrom: red
5
- colorTo: red
6
- sdk: gradio
7
- sdk_version: 5.9.1
8
- app_file: app.py
9
- pinned: false
10
- license: mit
11
- short_description: AIdeaText Gradio version
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ ---
2
+ title: G1a
3
+ emoji: 馃弳
4
+ colorFrom: red
5
+ colorTo: red
6
+ sdk: gradio
7
+ sdk_version: 5.9.1
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ short_description: AIdeaText Gradio version
12
+ ---
13
+
14
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py CHANGED
@@ -1,64 +1,105 @@
1
- import gradio as gr
2
- import os
3
- import logging
4
-
5
- from modules.ui.landing_ui import create_landing_interface
6
- from modules.ui.login_ui import create_login_interface
7
- from modules.database.database_init import initialize_database_connections
8
-
9
- # Configuraci贸n b谩sica
10
- logging.basicConfig(level=logging.INFO)
11
- logger = logging.getLogger(__name__)
12
-
13
- # Verificar variables de entorno
14
- COSMOS_ENDPOINT = os.getenv("COSMOS_ENDPOINT")
15
- COSMOS_KEY = os.getenv("COSMOS_KEY")
16
- if not COSMOS_ENDPOINT or not COSMOS_KEY:
17
- raise ValueError("Faltan variables de entorno: COSMOS_ENDPOINT y COSMOS_KEY.")
18
-
19
- # Inicializar la conexi贸n a la base de datos
20
- if not initialize_database_connections():
21
- raise ValueError("No se pudo inicializar la conexi贸n a la base de datos.")
22
-
23
- def main_interface():
24
- """
25
- Crea la interfaz principal con redirecci贸n al login o landing.
26
- """
27
- with gr.Blocks() as app_interface:
28
- # Contenedores para las p谩ginas
29
- landing_container = gr.Group(visible=True)
30
- login_container = gr.Group(visible=False)
31
-
32
- # Crear interfaces
33
- landing_page = create_landing_interface()
34
- login_page = create_login_interface()
35
-
36
- # Manejo de navegaci贸n
37
- def handle_navigation(page="landing"):
38
- """
39
- Redirige entre Landing Page y Login.
40
- """
41
- if page == "login":
42
- return {
43
- landing_container: gr.update(visible=False),
44
- login_container: gr.update(visible=True),
45
- }
46
- return {
47
- landing_container: gr.update(visible=True),
48
- login_container: gr.update(visible=False),
49
- }
50
-
51
- # Landing Page
52
- with landing_container:
53
- landing_page(navigate_to_login=lambda: handle_navigation("login"))
54
-
55
- # Login Page
56
- with login_container:
57
- login_page(navigate_back=lambda: handle_navigation("landing"))
58
-
59
- return app_interface
60
-
61
- # Lanzar la aplicaci贸n
62
- if __name__ == "__main__":
63
- app = main_interface()
64
- app.launch(server_name="0.0.0.0", server_port=7860, auth=None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import logging
4
+
5
+ from modules.auth.auth import (
6
+ # Autenticaci贸n
7
+ authenticate_user,
8
+ authenticate_student,
9
+ authenticate_admin,
10
+ # Manejo de contrase帽as
11
+ hash_password,
12
+ verify_password,
13
+ # Manejo de usuarios
14
+ register_student,
15
+ update_student_info,
16
+ delete_student,
17
+ # Interfaz de Gradio
18
+ create_auth_interface,
19
+ create_user_page
20
+ )
21
+
22
+ from modules.database.database_init import initialize_database_connections
23
+ import spaces
24
+ import torch
25
+
26
+ zero = torch.Tensor([0]).cuda()
27
+ print(zero.device) # <-- 'cpu' 馃
28
+
29
+ @spaces.GPU
30
+ def greet(n):
31
+ print(zero.device) # <-- 'cuda:0' 馃
32
+ return f"Hello {zero + n} Tensor"
33
+
34
+ # Configuraci贸n b谩sica
35
+ logging.basicConfig(level=logging.INFO)
36
+ logger = logging.getLogger(__name__)
37
+
38
+ # Verificar variables de entorno
39
+ COSMOS_ENDPOINT = os.getenv("COSMOS_ENDPOINT")
40
+ COSMOS_KEY = os.getenv("COSMOS_KEY")
41
+ if not COSMOS_ENDPOINT or not COSMOS_KEY:
42
+ raise ValueError("Faltan variables de entorno: COSMOS_ENDPOINT y COSMOS_KEY.")
43
+
44
+ # Inicializar la conexi贸n a la base de datos
45
+ if not initialize_database_connections():
46
+ raise ValueError("No se pudo inicializar la conexi贸n a la base de datos.")
47
+
48
+ # Crear la interfaz de login
49
+ # app = create_auth_interface()
50
+
51
+ # Crear la interfaz de usuario y login
52
+ def main_interface():
53
+ """
54
+ Crea la interfaz principal con redirecci贸n al login o user page.
55
+ """
56
+ with gr.Blocks() as app_interface:
57
+ # Contenedores para manejo de redirecci贸n
58
+ login_container = gr.Group(visible=True)
59
+ user_container = gr.Group(visible=False)
60
+
61
+ # Crear login_page y user_page
62
+ login_page = create_auth_interface()
63
+ user_page = create_user_page()
64
+
65
+ # Manejo de login exitoso
66
+ def handle_login_redirect(username=None, role=None):
67
+ """
68
+ Redirige entre la p谩gina de login y la p谩gina de usuario.
69
+ Args:
70
+ username (str): Nombre de usuario.
71
+ role (str): Rol del usuario.
72
+ Returns:
73
+ dict: Actualizaci贸n de visibilidad de los contenedores.
74
+ """
75
+ if username and role:
76
+ return {
77
+ login_container: gr.update(visible=False),
78
+ user_container: gr.update(visible=True)
79
+ }
80
+ return {
81
+ login_container: gr.update(visible=True),
82
+ user_container: gr.update(visible=False)
83
+ }
84
+
85
+ # Contenedor de Login
86
+ with login_container:
87
+ login_page.render() # Renderiza la p谩gina de login
88
+
89
+ # Contenedor de Usuario
90
+ with user_container:
91
+ user_page.render() # Renderiza la p谩gina de usuario
92
+
93
+ # Conectar el manejo del login al evento `load`
94
+ login_page.load(
95
+ fn=handle_login_redirect,
96
+ inputs=[],
97
+ outputs=[login_container, user_container]
98
+ )
99
+
100
+ return app_interface
101
+
102
+ # Lanzar la aplicaci贸n
103
+ if __name__ == "__main__":
104
+ app = main_interface()
105
+ app.launch(server_name="0.0.0.0", server_port=7860, auth=None)
app_Old-6-1-2025.py CHANGED
@@ -1,14 +1,14 @@
1
- import gradio as gr
2
- import spaces
3
- import torch
4
-
5
- zero = torch.Tensor([0]).cuda()
6
- print(zero.device) # <-- 'cpu' 馃
7
-
8
- @spaces.GPU
9
- def greet(n):
10
- print(zero.device) # <-- 'cuda:0' 馃
11
- return f"Hello {zero + n} Tensor"
12
-
13
- demo = gr.Interface(fn=greet, inputs=gr.Number(), outputs=gr.Text())
14
  demo.launch()
 
1
+ import gradio as gr
2
+ import spaces
3
+ import torch
4
+
5
+ zero = torch.Tensor([0]).cuda()
6
+ print(zero.device) # <-- 'cpu' 馃
7
+
8
+ @spaces.GPU
9
+ def greet(n):
10
+ print(zero.device) # <-- 'cuda:0' 馃
11
+ return f"Hello {zero + n} Tensor"
12
+
13
+ demo = gr.Interface(fn=greet, inputs=gr.Number(), outputs=gr.Text())
14
  demo.launch()
modules/auth/auth.py CHANGED
@@ -1,32 +1,217 @@
1
- # modules/auth/auth.py
2
-
3
- import os
4
- import logging
5
- import bcrypt
6
- from azure.cosmos import CosmosClient
7
- from modules.database.sql_db import get_user
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
- # Verificar las variables de entorno
12
- COSMOS_ENDPOINT = os.getenv("COSMOS_ENDPOINT")
13
- COSMOS_KEY = os.getenv("COSMOS_KEY")
14
- if not COSMOS_ENDPOINT or not COSMOS_KEY:
15
- raise ValueError("Las variables de entorno COSMOS_ENDPOINT y COSMOS_KEY no est谩n configuradas.")
16
-
17
- cosmos_client = CosmosClient(COSMOS_ENDPOINT, COSMOS_KEY)
18
-
19
- def authenticate_user(username: str, password: str):
20
- """
21
- Autentica un usuario utilizando la base de datos.
22
- """
23
- try:
24
- user = get_user(username)
25
- if user and bcrypt.checkpw(password.encode("utf-8"), user["password"].encode("utf-8")):
26
- logger.info(f"Usuario autenticado: {username}")
27
- return True, user["role"]
28
- logger.warning(f"Credenciales incorrectas para: {username}")
29
- return False, None
30
- except Exception as e:
31
- logger.error(f"Error autenticando a {username}: {e}")
32
- return False, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # modules/auth/auth.py
2
+
3
+ import gradio as gr
4
+ import os
5
+ import logging
6
+ import bcrypt
7
+ import base64
8
+ from azure.cosmos import CosmosClient
9
+ from azure.cosmos.exceptions import CosmosHttpResponseError
10
+ from datetime import datetime, timezone
11
+
12
+ from ..database.sql_db import (
13
+ get_user,
14
+ get_student_user,
15
+ create_student_user,
16
+ update_student_user,
17
+ delete_student_user,
18
+ record_login,
19
+ record_logout,
20
+ )
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Verificar las variables de entorno necesarias
25
+ COSMOS_ENDPOINT = os.getenv("COSMOS_ENDPOINT")
26
+ COSMOS_KEY = os.getenv("COSMOS_KEY")
27
+
28
+ if not COSMOS_ENDPOINT or not COSMOS_KEY:
29
+ raise ValueError("Las variables de entorno COSMOS_ENDPOINT y COSMOS_KEY no est谩n configuradas.")
30
+
31
+ # Validar y limpiar la clave de Cosmos DB
32
+ def clean_and_validate_key(key):
33
+ key = key.strip()
34
+ while len(key) % 4 != 0:
35
+ key += "="
36
+ try:
37
+ base64.b64decode(key)
38
+ return key
39
+ except Exception:
40
+ raise ValueError("La clave proporcionada no es v谩lida.")
41
+
42
+ COSMOS_KEY = clean_and_validate_key(COSMOS_KEY)
43
+ cosmos_client = CosmosClient(COSMOS_ENDPOINT, COSMOS_KEY)
44
+
45
+ ########################################
46
+ # 脥NDICE DE FUNCIONES (Referencia consolidada)
47
+ ########################################
48
+ # Autenticaci贸n
49
+ # - authenticate_user
50
+ # - authenticate_student
51
+ # - authenticate_admin
52
+
53
+ # Manejo de contrase帽as
54
+ # - hash_password
55
+ # - verify_password
56
+
57
+ # Manejo de usuarios
58
+ # - register_student
59
+ # - update_student_info
60
+ # - delete_student
61
+
62
+ # Interfaz de Gradio
63
+ # - create_auth_interface
64
+ # - create_user_page
65
+
66
+ ########################################
67
+ # Funciones de Autenticaci贸n
68
+ ########################################
69
+
70
+ def authenticate_user(username: str, password: str) -> tuple[bool, str | None]:
71
+ """
72
+ Autentica un usuario utilizando la base de datos.
73
+ """
74
+ try:
75
+ user = get_user(username)
76
+ if user and verify_password(user["password"], password):
77
+ logger.info(f"Usuario autenticado: {username}, Rol: {user['role']}")
78
+ return True, user["role"]
79
+ logger.warning(f"Credenciales incorrectas para el usuario: {username}")
80
+ return False, None
81
+ except Exception as e:
82
+ logger.error(f"Error autenticando al usuario {username}: {str(e)}")
83
+ return False, None
84
+
85
+ def authenticate_student(username, password):
86
+ """Autentica a un estudiante."""
87
+ success, role = authenticate_user(username, password)
88
+ return (success, role) if role == "Estudiante" else (False, None)
89
+
90
+ def authenticate_admin(username, password):
91
+ """Autentica a un administrador."""
92
+ success, role = authenticate_user(username, password)
93
+ return (success, role) if role == "Administrador" else (False, None)
94
+
95
+ ########################################
96
+ # Registro y Manejo de Usuarios
97
+ ########################################
98
+
99
+ def register_student(username: str, password: str, additional_info=None) -> bool:
100
+ """
101
+ Registra un nuevo estudiante.
102
+ """
103
+ try:
104
+ if get_student_user(username):
105
+ logger.warning(f"El estudiante {username} ya existe.")
106
+ return False
107
+
108
+ hashed_password = hash_password(password)
109
+ additional_info = additional_info or {}
110
+ additional_info["role"] = "Estudiante"
111
+
112
+ create_student_user(username, hashed_password, additional_info)
113
+ logger.info(f"Estudiante registrado: {username}")
114
+ return True
115
+ except Exception as e:
116
+ logger.error(f"Error registrando al estudiante {username}: {str(e)}")
117
+ return False
118
+
119
+ def update_student_info(username: str, new_info: dict) -> bool:
120
+ """
121
+ Actualiza la informaci贸n de un estudiante.
122
+ """
123
+ try:
124
+ if "password" in new_info:
125
+ new_info["password"] = hash_password(new_info["password"])
126
+ return update_student_user(username, new_info)
127
+ except Exception as e:
128
+ logger.error(f"Error actualizando informaci贸n del estudiante {username}: {str(e)}")
129
+ return False
130
+
131
+ def delete_student(username: str) -> bool:
132
+ """
133
+ Elimina un estudiante de la base de datos.
134
+ """
135
+ try:
136
+ return delete_student_user(username)
137
+ except Exception as e:
138
+ logger.error(f"Error al eliminar estudiante {username}: {str(e)}")
139
+ return False
140
+
141
+ ########################################
142
+ # Manejo de Contrase帽as
143
+ ########################################
144
+
145
+ def hash_password(password: str) -> str:
146
+ """Hashea una contrase帽a utilizando bcrypt."""
147
+ return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
148
+
149
+ def verify_password(stored_password: str, provided_password: str) -> bool:
150
+ """Verifica que una contrase帽a coincida con su hash."""
151
+ return bcrypt.checkpw(provided_password.encode("utf-8"), stored_password.encode("utf-8"))
152
+
153
+ ########################################
154
+ # Interfaz Gradio para Login
155
+ ########################################
156
+
157
+ def create_auth_interface():
158
+ """
159
+ Crea la interfaz de autenticaci贸n.
160
+ """
161
+ with gr.Blocks() as auth_interface:
162
+ gr.Markdown("# Login")
163
+ username = gr.Textbox(label="Usuario")
164
+ password = gr.Textbox(label="Contrase帽a", type="password")
165
+ login_btn = gr.Button("Iniciar Sesi贸n")
166
+ message = gr.Markdown()
167
+
168
+ def handle_login(user, pwd):
169
+ success, role = authenticate_user(user, pwd)
170
+ return f"Bienvenido, {user} ({role})" if success else "Credenciales incorrectas."
171
+
172
+ login_btn.click(fn=handle_login, inputs=[username, password], outputs=message)
173
+ return auth_interface
174
+
175
+ ########################################
176
+ # P谩gina de Usuario
177
+ ########################################
178
+
179
+ def create_user_page():
180
+ """
181
+ Crea una p谩gina de usuario simple tras el inicio de sesi贸n.
182
+ """
183
+ with gr.Blocks() as user_page:
184
+ gr.Markdown("# Bienvenido a la P谩gina de Usuario")
185
+ gr.Markdown("Esta p谩gina est谩 disponible despu茅s de un inicio de sesi贸n exitoso.")
186
+
187
+ username = gr.Textbox(label="Usuario", interactive=False)
188
+ role = gr.Textbox(label="Rol", interactive=False)
189
+
190
+ def load_user_info():
191
+ return "UsuarioPrueba", "Estudiante" # Simula datos de sesi贸n
192
+
193
+ user_page.load(fn=load_user_info, inputs=[], outputs=[username, role])
194
+
195
+ gr.Button("Cerrar Sesi贸n").click(
196
+ fn=lambda: "Sesi贸n cerrada",
197
+ inputs=[],
198
+ outputs=[user_page]
199
+ )
200
+ return user_page
201
+
202
+ ########################################
203
+ # Exportar Funciones
204
+ ########################################
205
+
206
+ __all__ = [
207
+ "create_auth_interface",
208
+ "create_user_page",
209
+ "register_student",
210
+ "authenticate_user",
211
+ "authenticate_student",
212
+ "authenticate_admin",
213
+ "update_student_info",
214
+ "delete_student",
215
+ "hash_password",
216
+ "verify_password",
217
+ ]
modules/database/database_init.py CHANGED
@@ -1,116 +1,116 @@
1
- # 1. modules/database/database_init.py
2
-
3
- import os
4
- import logging
5
- from azure.cosmos import CosmosClient
6
- from pymongo import MongoClient
7
- import certifi
8
-
9
- logging.basicConfig(level=logging.DEBUG)
10
- logger = logging.getLogger(__name__)
11
-
12
- # Variables globales para Cosmos DB SQL API
13
- cosmos_client = None
14
- user_database = None
15
- user_container = None
16
- application_requests_container = None
17
- user_feedback_container = None
18
- user_sessions_container = None
19
-
20
- # Variables globales para Cosmos DB MongoDB API
21
- mongo_client = None
22
- mongo_db = None
23
-
24
- ###################################################################
25
- def verify_container_partition_key(container, expected_path):
26
- """Verifica la configuraci贸n de partition key de un contenedor"""
27
- try:
28
- container_props = container.read()
29
- partition_key_paths = container_props['partitionKey']['paths']
30
- logger.info(f"Container: {container.id}, Partition Key Paths: {partition_key_paths}")
31
- return expected_path in partition_key_paths
32
- except Exception as e:
33
- logger.error(f"Error verificando partition key en {container.id}: {str(e)}")
34
- return False
35
-
36
- ###################################################################
37
- def get_container(container_name):
38
- """Obtiene un contenedor espec铆fico"""
39
- global user_container, user_sessions_container
40
-
41
- if not initialize_cosmos_sql_connection():
42
- logger.error("No se pudo inicializar la conexi贸n")
43
- return None
44
-
45
- containers = {
46
- "users": user_container,
47
- "users_sessions": user_sessions_container
48
- }
49
-
50
- return containers.get(container_name)
51
-
52
- ###################################################################
53
- def initialize_cosmos_sql_connection():
54
- """Inicializa la conexi贸n a Cosmos DB SQL API"""
55
- global cosmos_client, user_database, user_container, user_sessions_container
56
-
57
- try:
58
- if cosmos_client and user_database and user_container and user_sessions_container:
59
- return True
60
-
61
- cosmos_endpoint = os.environ.get("COSMOS_ENDPOINT")
62
- cosmos_key = os.environ.get("COSMOS_KEY")
63
-
64
- if not cosmos_endpoint or not cosmos_key:
65
- raise ValueError("COSMOS_ENDPOINT y COSMOS_KEY deben estar configurados")
66
-
67
- cosmos_client = CosmosClient(cosmos_endpoint, cosmos_key)
68
- user_database = cosmos_client.get_database_client("user_database")
69
-
70
- # Inicializar contenedores manteniendo la estructura existente
71
- user_container = user_database.get_container_client("users")
72
- user_sessions_container = user_database.get_container_client("users_sessions")
73
-
74
- logger.info("Conexi贸n a Cosmos DB SQL API exitosa")
75
- return True
76
-
77
- except Exception as e:
78
- logger.error(f"Error al conectar con Cosmos DB SQL API: {str(e)}")
79
- return False
80
-
81
- ###################################################################
82
- def initialize_mongodb_connection():
83
- """Inicializa la conexi贸n a MongoDB"""
84
- global mongo_client, mongo_db
85
- try:
86
- connection_string = os.getenv("MONGODB_CONNECTION_STRING")
87
- if not connection_string:
88
- raise ValueError("MONGODB_CONNECTION_STRING debe estar configurado")
89
-
90
- mongo_client = MongoClient(
91
- connection_string,
92
- tls=True,
93
- tlsCAFile=certifi.where(),
94
- retryWrites=False,
95
- serverSelectionTimeoutMS=5000,
96
- connectTimeoutMS=10000,
97
- socketTimeoutMS=10000
98
- )
99
-
100
- mongo_db = mongo_client['aideatext_db']
101
- return True
102
- except Exception as e:
103
- logger.error(f"Error conectando a MongoDB: {str(e)}")
104
- return False
105
-
106
- ###################################################################
107
- def initialize_database_connections():
108
- """Inicializa todas las conexiones"""
109
- return initialize_cosmos_sql_connection() and initialize_mongodb_connection()
110
-
111
- ###################################################################
112
- def get_mongodb():
113
- """Obtiene la conexi贸n MongoDB"""
114
- if mongo_db is None:
115
- initialize_mongodb_connection()
116
  return mongo_db
 
1
+ # 1. modules/database/database_init.py
2
+
3
+ import os
4
+ import logging
5
+ from azure.cosmos import CosmosClient
6
+ from pymongo import MongoClient
7
+ import certifi
8
+
9
+ logging.basicConfig(level=logging.DEBUG)
10
+ logger = logging.getLogger(__name__)
11
+
12
+ # Variables globales para Cosmos DB SQL API
13
+ cosmos_client = None
14
+ user_database = None
15
+ user_container = None
16
+ application_requests_container = None
17
+ user_feedback_container = None
18
+ user_sessions_container = None
19
+
20
+ # Variables globales para Cosmos DB MongoDB API
21
+ mongo_client = None
22
+ mongo_db = None
23
+
24
+ ###################################################################
25
+ def verify_container_partition_key(container, expected_path):
26
+ """Verifica la configuraci贸n de partition key de un contenedor"""
27
+ try:
28
+ container_props = container.read()
29
+ partition_key_paths = container_props['partitionKey']['paths']
30
+ logger.info(f"Container: {container.id}, Partition Key Paths: {partition_key_paths}")
31
+ return expected_path in partition_key_paths
32
+ except Exception as e:
33
+ logger.error(f"Error verificando partition key en {container.id}: {str(e)}")
34
+ return False
35
+
36
+ ###################################################################
37
+ def get_container(container_name):
38
+ """Obtiene un contenedor espec铆fico"""
39
+ global user_container, user_sessions_container
40
+
41
+ if not initialize_cosmos_sql_connection():
42
+ logger.error("No se pudo inicializar la conexi贸n")
43
+ return None
44
+
45
+ containers = {
46
+ "users": user_container,
47
+ "users_sessions": user_sessions_container
48
+ }
49
+
50
+ return containers.get(container_name)
51
+
52
+ ###################################################################
53
+ def initialize_cosmos_sql_connection():
54
+ """Inicializa la conexi贸n a Cosmos DB SQL API"""
55
+ global cosmos_client, user_database, user_container, user_sessions_container
56
+
57
+ try:
58
+ if cosmos_client and user_database and user_container and user_sessions_container:
59
+ return True
60
+
61
+ cosmos_endpoint = os.environ.get("COSMOS_ENDPOINT")
62
+ cosmos_key = os.environ.get("COSMOS_KEY")
63
+
64
+ if not cosmos_endpoint or not cosmos_key:
65
+ raise ValueError("COSMOS_ENDPOINT y COSMOS_KEY deben estar configurados")
66
+
67
+ cosmos_client = CosmosClient(cosmos_endpoint, cosmos_key)
68
+ user_database = cosmos_client.get_database_client("user_database")
69
+
70
+ # Inicializar contenedores manteniendo la estructura existente
71
+ user_container = user_database.get_container_client("users")
72
+ user_sessions_container = user_database.get_container_client("users_sessions")
73
+
74
+ logger.info("Conexi贸n a Cosmos DB SQL API exitosa")
75
+ return True
76
+
77
+ except Exception as e:
78
+ logger.error(f"Error al conectar con Cosmos DB SQL API: {str(e)}")
79
+ return False
80
+
81
+ ###################################################################
82
+ def initialize_mongodb_connection():
83
+ """Inicializa la conexi贸n a MongoDB"""
84
+ global mongo_client, mongo_db
85
+ try:
86
+ connection_string = os.getenv("MONGODB_CONNECTION_STRING")
87
+ if not connection_string:
88
+ raise ValueError("MONGODB_CONNECTION_STRING debe estar configurado")
89
+
90
+ mongo_client = MongoClient(
91
+ connection_string,
92
+ tls=True,
93
+ tlsCAFile=certifi.where(),
94
+ retryWrites=False,
95
+ serverSelectionTimeoutMS=5000,
96
+ connectTimeoutMS=10000,
97
+ socketTimeoutMS=10000
98
+ )
99
+
100
+ mongo_db = mongo_client['aideatext_db']
101
+ return True
102
+ except Exception as e:
103
+ logger.error(f"Error conectando a MongoDB: {str(e)}")
104
+ return False
105
+
106
+ ###################################################################
107
+ def initialize_database_connections():
108
+ """Inicializa todas las conexiones"""
109
+ return initialize_cosmos_sql_connection() and initialize_mongodb_connection()
110
+
111
+ ###################################################################
112
+ def get_mongodb():
113
+ """Obtiene la conexi贸n MongoDB"""
114
+ if mongo_db is None:
115
+ initialize_mongodb_connection()
116
  return mongo_db
modules/database/mongo_db.py CHANGED
@@ -1,64 +1,64 @@
1
-
2
-
3
- from .database_init import get_mongodb
4
- import logging
5
-
6
- logger = logging.getLogger(__name__)
7
-
8
- def get_collection(collection_name):
9
- try:
10
- db = get_mongodb()
11
- if db is None:
12
- logger.error(f"No se pudo obtener la base de datos para {collection_name}")
13
- return None
14
-
15
- collection = db[collection_name]
16
- logger.info(f"Colecci贸n {collection_name} obtenida exitosamente")
17
- return collection
18
-
19
- except Exception as e:
20
- logger.error(f"Error al obtener colecci贸n {collection_name}: {str(e)}")
21
- return None
22
-
23
- def insert_document(collection_name, document):
24
- collection = get_collection(collection_name)
25
- try:
26
- result = collection.insert_one(document)
27
- logger.info(f"Documento insertado en {collection_name} con ID: {result.inserted_id}")
28
- return result.inserted_id
29
- except Exception as e:
30
- logger.error(f"Error al insertar documento en {collection_name}: {str(e)}")
31
- return None
32
-
33
- def find_documents(collection_name, query, sort=None, limit=None):
34
- collection = get_collection(collection_name)
35
- try:
36
- cursor = collection.find(query)
37
- if sort:
38
- cursor = cursor.sort(sort)
39
- if limit:
40
- cursor = cursor.limit(limit)
41
- return list(cursor)
42
- except Exception as e:
43
- logger.error(f"Error al buscar documentos en {collection_name}: {str(e)}")
44
- return []
45
-
46
- def update_document(collection_name, query, update):
47
- collection = get_collection(collection_name)
48
- try:
49
- result = collection.update_one(query, update)
50
- logger.info(f"Documento actualizado en {collection_name}: {result.modified_count} modificado(s)")
51
- return result.modified_count
52
- except Exception as e:
53
- logger.error(f"Error al actualizar documento en {collection_name}: {str(e)}")
54
- return 0
55
-
56
- def delete_document(collection_name, query):
57
- collection = get_collection(collection_name)
58
- try:
59
- result = collection.delete_one(query)
60
- logger.info(f"Documento eliminado de {collection_name}: {result.deleted_count} eliminado(s)")
61
- return result.deleted_count
62
- except Exception as e:
63
- logger.error(f"Error al eliminar documento de {collection_name}: {str(e)}")
64
  return 0
 
1
+
2
+
3
+ from .database_init import get_mongodb
4
+ import logging
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ def get_collection(collection_name):
9
+ try:
10
+ db = get_mongodb()
11
+ if db is None:
12
+ logger.error(f"No se pudo obtener la base de datos para {collection_name}")
13
+ return None
14
+
15
+ collection = db[collection_name]
16
+ logger.info(f"Colecci贸n {collection_name} obtenida exitosamente")
17
+ return collection
18
+
19
+ except Exception as e:
20
+ logger.error(f"Error al obtener colecci贸n {collection_name}: {str(e)}")
21
+ return None
22
+
23
+ def insert_document(collection_name, document):
24
+ collection = get_collection(collection_name)
25
+ try:
26
+ result = collection.insert_one(document)
27
+ logger.info(f"Documento insertado en {collection_name} con ID: {result.inserted_id}")
28
+ return result.inserted_id
29
+ except Exception as e:
30
+ logger.error(f"Error al insertar documento en {collection_name}: {str(e)}")
31
+ return None
32
+
33
+ def find_documents(collection_name, query, sort=None, limit=None):
34
+ collection = get_collection(collection_name)
35
+ try:
36
+ cursor = collection.find(query)
37
+ if sort:
38
+ cursor = cursor.sort(sort)
39
+ if limit:
40
+ cursor = cursor.limit(limit)
41
+ return list(cursor)
42
+ except Exception as e:
43
+ logger.error(f"Error al buscar documentos en {collection_name}: {str(e)}")
44
+ return []
45
+
46
+ def update_document(collection_name, query, update):
47
+ collection = get_collection(collection_name)
48
+ try:
49
+ result = collection.update_one(query, update)
50
+ logger.info(f"Documento actualizado en {collection_name}: {result.modified_count} modificado(s)")
51
+ return result.modified_count
52
+ except Exception as e:
53
+ logger.error(f"Error al actualizar documento en {collection_name}: {str(e)}")
54
+ return 0
55
+
56
+ def delete_document(collection_name, query):
57
+ collection = get_collection(collection_name)
58
+ try:
59
+ result = collection.delete_one(query)
60
+ logger.info(f"Documento eliminado de {collection_name}: {result.deleted_count} eliminado(s)")
61
+ return result.deleted_count
62
+ except Exception as e:
63
+ logger.error(f"Error al eliminar documento de {collection_name}: {str(e)}")
64
  return 0
modules/database/sql_db.py CHANGED
@@ -1,307 +1,307 @@
1
- #modules/database/sql_db.py
2
-
3
- from .database_init import get_container
4
- from datetime import datetime, timezone
5
- import logging
6
- import bcrypt
7
- import uuid
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
- def get_user(username, role=None):
12
- container = get_container("users")
13
- try:
14
- query = f"SELECT * FROM c WHERE c.id = '{username}'"
15
- if role:
16
- query += f" AND c.role = '{role}'"
17
- items = list(container.query_items(
18
- query=query,
19
- enable_cross_partition_query=True
20
- ))
21
- return items[0] if items else None
22
- except Exception as e:
23
- logger.error(f"Error al obtener usuario {username}: {str(e)}")
24
- return None
25
-
26
- def get_admin_user(username):
27
- return get_user(username, role='Administrador')
28
-
29
- def get_student_user(username):
30
- return get_user(username, role='Estudiante')
31
-
32
- def get_teacher_user(username):
33
- return get_user(username, role='Profesor')
34
-
35
- #################################################################################
36
- #El error ocurre porque el par谩metro `partition_key`
37
- #no es soportado en la versi贸n actual del SDK.
38
- #Modifiquemos la funci贸n `create_user()` para incluir la partici贸n en el cuerpo del documento:
39
-
40
- def create_user(username, password, role, additional_info=None):
41
- container = get_container("users")
42
- if not container:
43
- logger.error("No se pudo obtener el contenedor de usuarios")
44
- return False
45
-
46
- try:
47
- # Generar salt y hash de la contrase帽a
48
- salt = bcrypt.gensalt()
49
- hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
50
-
51
- user_data = {
52
- 'id': username,
53
- 'password': hashed.decode('utf-8'),
54
- 'salt': salt.decode('utf-8'),
55
- 'role': role,
56
- 'timestamp': datetime.now(timezone.utc).isoformat(),
57
- 'additional_info': additional_info or {},
58
- # La partici贸n va dentro del documento
59
- 'partitionKey': username
60
- }
61
-
62
- container.create_item(body=user_data)
63
- logger.info(f"Usuario {role} creado: {username}")
64
- return True
65
-
66
- except Exception as e:
67
- logger.error(f"Error al crear usuario {role}: {str(e)}")
68
- return False
69
-
70
- #########################################################################
71
- def create_student_user(username, password, additional_info=None):
72
- return create_user(username, password, 'Estudiante', additional_info)
73
-
74
- def create_teacher_user(username, password, additional_info=None):
75
- return create_user(username, password, 'Profesor', additional_info)
76
-
77
- def create_admin_user(username, password, additional_info=None):
78
- return create_user(username, password, 'Administrador', additional_info)
79
-
80
- ############-- Funciones de control del tiempo de sesi贸n ##################
81
-
82
- ###########################################################
83
-
84
- def record_login(username):
85
- """Registra el inicio de sesi贸n de un usuario"""
86
- try:
87
- container = get_container("users_sessions")
88
- if not container:
89
- logger.error("No se pudo obtener el contenedor users_sessions")
90
- return None
91
-
92
- session_id = str(uuid.uuid4())
93
- session_doc = {
94
- "id": session_id,
95
- "type": "session",
96
- "username": username,
97
- "loginTime": datetime.now(timezone.utc).isoformat(),
98
- "additional_info": {},
99
- # El campo para partition key debe estar en el documento
100
- "partitionKey": username
101
- }
102
-
103
- # Crear el documento usando options
104
- result = container.create_item(
105
- body=session_doc,
106
- enable_cross_partition_query=True
107
- )
108
- logger.info(f"Sesi贸n {session_id} registrada para {username}")
109
- return session_id
110
- except Exception as e:
111
- logger.error(f"Error registrando login: {str(e)}")
112
- return None
113
-
114
-
115
- ###########################################################
116
-
117
- def record_logout(username, session_id):
118
- """Registra el cierre de sesi贸n y calcula la duraci贸n"""
119
- try:
120
- container = get_container("users_sessions")
121
- if not container:
122
- logger.error("No se pudo obtener el contenedor users_sessions")
123
- return False
124
-
125
- # Obtener la sesi贸n actual
126
- query = "SELECT * FROM c WHERE c.id = @id AND c.username = @username"
127
- params = [
128
- {"name": "@id", "value": session_id},
129
- {"name": "@username", "value": username}
130
- ]
131
-
132
- items = list(container.query_items(
133
- query=query,
134
- parameters=params,
135
- enable_cross_partition_query=True
136
- ))
137
-
138
- if not items:
139
- logger.warning(f"Sesi贸n no encontrada: {session_id}")
140
- return False
141
-
142
- session = items[0]
143
- login_time = datetime.fromisoformat(session['loginTime'].rstrip('Z'))
144
- logout_time = datetime.now(timezone.utc)
145
- duration = int((logout_time - login_time).total_seconds())
146
-
147
- session.update({
148
- "logoutTime": logout_time.isoformat(),
149
- "sessionDuration": duration
150
- })
151
-
152
- # Actualizar el documento
153
- container.upsert_item(
154
- body=session
155
- )
156
- logger.info(f"Sesi贸n {session_id} cerrada para {username}, duraci贸n: {duration}s")
157
- return True
158
- except Exception as e:
159
- logger.error(f"Error registrando logout: {str(e)}")
160
- return False
161
-
162
-
163
- ###########################################################
164
-
165
- def get_recent_sessions(limit=10):
166
- """Obtiene las sesiones m谩s recientes"""
167
- try:
168
- container = get_container("users_sessions")
169
- if not container:
170
- logger.error("No se pudo obtener el contenedor users_sessions")
171
- return []
172
-
173
- query = """
174
- SELECT c.username, c.loginTime, c.logoutTime, c.sessionDuration
175
- FROM c
176
- WHERE c.type = 'session'
177
- ORDER BY c.loginTime DESC
178
- OFFSET 0 LIMIT @limit
179
- """
180
-
181
- sessions = list(container.query_items(
182
- query=query,
183
- parameters=[{"name": "@limit", "value": limit}],
184
- enable_cross_partition_query=True
185
- ))
186
-
187
- # Validar y limpiar los datos
188
- clean_sessions = []
189
- for session in sessions:
190
- try:
191
- clean_sessions.append({
192
- "username": session["username"],
193
- "loginTime": session["loginTime"],
194
- "logoutTime": session.get("logoutTime", "Activo"),
195
- "sessionDuration": session.get("sessionDuration", 0)
196
- })
197
- except KeyError as e:
198
- logger.warning(f"Sesi贸n con datos incompletos: {e}")
199
- continue
200
-
201
- return clean_sessions
202
- except Exception as e:
203
- logger.error(f"Error obteniendo sesiones recientes: {str(e)}")
204
- return []
205
-
206
-
207
-
208
- ###########################################################
209
-
210
- def get_user_total_time(username):
211
- """Obtiene el tiempo total que un usuario ha pasado en la plataforma"""
212
- try:
213
- container = get_container("users_sessions")
214
- if not container:
215
- return None
216
-
217
- query = """
218
- SELECT VALUE SUM(c.sessionDuration)
219
- FROM c
220
- WHERE c.type = 'session'
221
- AND c.username = @username
222
- AND IS_DEFINED(c.sessionDuration)
223
- """
224
-
225
- result = list(container.query_items(
226
- query=query,
227
- parameters=[{"name": "@username", "value": username}],
228
- enable_cross_partition_query=True
229
- ))
230
-
231
- return result[0] if result and result[0] is not None else 0
232
- except Exception as e:
233
- logger.error(f"Error obteniendo tiempo total: {str(e)}")
234
- return 0
235
-
236
- ###########################################################
237
- def update_student_user(username, new_info):
238
- user_container, _, _ = get_sql_containers()
239
- try:
240
- user = get_student_user(username)
241
- if user:
242
- user['additional_info'].update(new_info)
243
- user_container.upsert_item(body=user)
244
- logger.info(f"Informaci贸n del estudiante actualizada: {username}")
245
- return True
246
- else:
247
- logger.warning(f"Intento de actualizar estudiante no existente: {username}")
248
- return False
249
- except Exception as e:
250
- logger.error(f"Error al actualizar informaci贸n del estudiante {username}: {str(e)}")
251
- return False
252
-
253
-
254
- def delete_student_user(username):
255
- user_container, _, _ = get_sql_containers()
256
- try:
257
- user = get_student_user(username)
258
- if user:
259
- user_container.delete_item(item=user['id'], partition_key=username)
260
- logger.info(f"Estudiante eliminado: {username}")
261
- return True
262
- else:
263
- logger.warning(f"Intento de eliminar estudiante no existente: {username}")
264
- return False
265
- except Exception as e:
266
- logger.error(f"Error al eliminar estudiante {username}: {str(e)}")
267
- return False
268
-
269
- def store_application_request(name, lastname, email, institution, current_role, desired_role, reason):
270
- _, application_requests_container, _ = get_sql_containers()
271
- try:
272
- application_request = {
273
- "id": str(uuid.uuid4()),
274
- "name": name,
275
- "lastname": lastname,
276
- "email": email,
277
- "institution": institution,
278
- "current_role": current_role,
279
- "desired_role": desired_role,
280
- "reason": reason,
281
- "requestDate": datetime.utcnow().isoformat()
282
- }
283
- application_requests_container.create_item(body=application_request)
284
- logger.info(f"Solicitud de aplicaci贸n almacenada para el email: {email}")
285
- return True
286
- except Exception as e:
287
- logger.error(f"Error al almacenar la solicitud de aplicaci贸n: {str(e)}")
288
- return False
289
-
290
- def store_student_feedback(username, name, email, feedback):
291
- _, _, user_feedback_container = get_sql_containers()
292
- try:
293
- feedback_item = {
294
- "id": str(uuid.uuid4()),
295
- "username": username,
296
- "name": name,
297
- "email": email,
298
- "feedback": feedback,
299
- "role": "Estudiante",
300
- 'timestamp': datetime.now(timezone.utc).isoformat(),
301
- }
302
- result = user_feedback_container.create_item(body=feedback_item)
303
- logger.info(f"Feedback de estudiante almacenado con ID: {result['id']} para el usuario: {username}")
304
- return True
305
- except Exception as e:
306
- logger.error(f"Error al almacenar el feedback del estudiante {username}: {str(e)}")
307
  return False
 
1
+ #modules/database/sql_db.py
2
+
3
+ from .database_init import get_container
4
+ from datetime import datetime, timezone
5
+ import logging
6
+ import bcrypt
7
+ import uuid
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ def get_user(username, role=None):
12
+ container = get_container("users")
13
+ try:
14
+ query = f"SELECT * FROM c WHERE c.id = '{username}'"
15
+ if role:
16
+ query += f" AND c.role = '{role}'"
17
+ items = list(container.query_items(
18
+ query=query,
19
+ enable_cross_partition_query=True
20
+ ))
21
+ return items[0] if items else None
22
+ except Exception as e:
23
+ logger.error(f"Error al obtener usuario {username}: {str(e)}")
24
+ return None
25
+
26
+ def get_admin_user(username):
27
+ return get_user(username, role='Administrador')
28
+
29
+ def get_student_user(username):
30
+ return get_user(username, role='Estudiante')
31
+
32
+ def get_teacher_user(username):
33
+ return get_user(username, role='Profesor')
34
+
35
+ #################################################################################
36
+ #El error ocurre porque el par谩metro `partition_key`
37
+ #no es soportado en la versi贸n actual del SDK.
38
+ #Modifiquemos la funci贸n `create_user()` para incluir la partici贸n en el cuerpo del documento:
39
+
40
+ def create_user(username, password, role, additional_info=None):
41
+ container = get_container("users")
42
+ if not container:
43
+ logger.error("No se pudo obtener el contenedor de usuarios")
44
+ return False
45
+
46
+ try:
47
+ # Generar salt y hash de la contrase帽a
48
+ salt = bcrypt.gensalt()
49
+ hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
50
+
51
+ user_data = {
52
+ 'id': username,
53
+ 'password': hashed.decode('utf-8'),
54
+ 'salt': salt.decode('utf-8'),
55
+ 'role': role,
56
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
57
+ 'additional_info': additional_info or {},
58
+ # La partici贸n va dentro del documento
59
+ 'partitionKey': username
60
+ }
61
+
62
+ container.create_item(body=user_data)
63
+ logger.info(f"Usuario {role} creado: {username}")
64
+ return True
65
+
66
+ except Exception as e:
67
+ logger.error(f"Error al crear usuario {role}: {str(e)}")
68
+ return False
69
+
70
+ #########################################################################
71
+ def create_student_user(username, password, additional_info=None):
72
+ return create_user(username, password, 'Estudiante', additional_info)
73
+
74
+ def create_teacher_user(username, password, additional_info=None):
75
+ return create_user(username, password, 'Profesor', additional_info)
76
+
77
+ def create_admin_user(username, password, additional_info=None):
78
+ return create_user(username, password, 'Administrador', additional_info)
79
+
80
+ ############-- Funciones de control del tiempo de sesi贸n ##################
81
+
82
+ ###########################################################
83
+
84
+ def record_login(username):
85
+ """Registra el inicio de sesi贸n de un usuario"""
86
+ try:
87
+ container = get_container("users_sessions")
88
+ if not container:
89
+ logger.error("No se pudo obtener el contenedor users_sessions")
90
+ return None
91
+
92
+ session_id = str(uuid.uuid4())
93
+ session_doc = {
94
+ "id": session_id,
95
+ "type": "session",
96
+ "username": username,
97
+ "loginTime": datetime.now(timezone.utc).isoformat(),
98
+ "additional_info": {},
99
+ # El campo para partition key debe estar en el documento
100
+ "partitionKey": username
101
+ }
102
+
103
+ # Crear el documento usando options
104
+ result = container.create_item(
105
+ body=session_doc,
106
+ enable_cross_partition_query=True
107
+ )
108
+ logger.info(f"Sesi贸n {session_id} registrada para {username}")
109
+ return session_id
110
+ except Exception as e:
111
+ logger.error(f"Error registrando login: {str(e)}")
112
+ return None
113
+
114
+
115
+ ###########################################################
116
+
117
+ def record_logout(username, session_id):
118
+ """Registra el cierre de sesi贸n y calcula la duraci贸n"""
119
+ try:
120
+ container = get_container("users_sessions")
121
+ if not container:
122
+ logger.error("No se pudo obtener el contenedor users_sessions")
123
+ return False
124
+
125
+ # Obtener la sesi贸n actual
126
+ query = "SELECT * FROM c WHERE c.id = @id AND c.username = @username"
127
+ params = [
128
+ {"name": "@id", "value": session_id},
129
+ {"name": "@username", "value": username}
130
+ ]
131
+
132
+ items = list(container.query_items(
133
+ query=query,
134
+ parameters=params,
135
+ enable_cross_partition_query=True
136
+ ))
137
+
138
+ if not items:
139
+ logger.warning(f"Sesi贸n no encontrada: {session_id}")
140
+ return False
141
+
142
+ session = items[0]
143
+ login_time = datetime.fromisoformat(session['loginTime'].rstrip('Z'))
144
+ logout_time = datetime.now(timezone.utc)
145
+ duration = int((logout_time - login_time).total_seconds())
146
+
147
+ session.update({
148
+ "logoutTime": logout_time.isoformat(),
149
+ "sessionDuration": duration
150
+ })
151
+
152
+ # Actualizar el documento
153
+ container.upsert_item(
154
+ body=session
155
+ )
156
+ logger.info(f"Sesi贸n {session_id} cerrada para {username}, duraci贸n: {duration}s")
157
+ return True
158
+ except Exception as e:
159
+ logger.error(f"Error registrando logout: {str(e)}")
160
+ return False
161
+
162
+
163
+ ###########################################################
164
+
165
+ def get_recent_sessions(limit=10):
166
+ """Obtiene las sesiones m谩s recientes"""
167
+ try:
168
+ container = get_container("users_sessions")
169
+ if not container:
170
+ logger.error("No se pudo obtener el contenedor users_sessions")
171
+ return []
172
+
173
+ query = """
174
+ SELECT c.username, c.loginTime, c.logoutTime, c.sessionDuration
175
+ FROM c
176
+ WHERE c.type = 'session'
177
+ ORDER BY c.loginTime DESC
178
+ OFFSET 0 LIMIT @limit
179
+ """
180
+
181
+ sessions = list(container.query_items(
182
+ query=query,
183
+ parameters=[{"name": "@limit", "value": limit}],
184
+ enable_cross_partition_query=True
185
+ ))
186
+
187
+ # Validar y limpiar los datos
188
+ clean_sessions = []
189
+ for session in sessions:
190
+ try:
191
+ clean_sessions.append({
192
+ "username": session["username"],
193
+ "loginTime": session["loginTime"],
194
+ "logoutTime": session.get("logoutTime", "Activo"),
195
+ "sessionDuration": session.get("sessionDuration", 0)
196
+ })
197
+ except KeyError as e:
198
+ logger.warning(f"Sesi贸n con datos incompletos: {e}")
199
+ continue
200
+
201
+ return clean_sessions
202
+ except Exception as e:
203
+ logger.error(f"Error obteniendo sesiones recientes: {str(e)}")
204
+ return []
205
+
206
+
207
+
208
+ ###########################################################
209
+
210
+ def get_user_total_time(username):
211
+ """Obtiene el tiempo total que un usuario ha pasado en la plataforma"""
212
+ try:
213
+ container = get_container("users_sessions")
214
+ if not container:
215
+ return None
216
+
217
+ query = """
218
+ SELECT VALUE SUM(c.sessionDuration)
219
+ FROM c
220
+ WHERE c.type = 'session'
221
+ AND c.username = @username
222
+ AND IS_DEFINED(c.sessionDuration)
223
+ """
224
+
225
+ result = list(container.query_items(
226
+ query=query,
227
+ parameters=[{"name": "@username", "value": username}],
228
+ enable_cross_partition_query=True
229
+ ))
230
+
231
+ return result[0] if result and result[0] is not None else 0
232
+ except Exception as e:
233
+ logger.error(f"Error obteniendo tiempo total: {str(e)}")
234
+ return 0
235
+
236
+ ###########################################################
237
+ def update_student_user(username, new_info):
238
+ user_container, _, _ = get_sql_containers()
239
+ try:
240
+ user = get_student_user(username)
241
+ if user:
242
+ user['additional_info'].update(new_info)
243
+ user_container.upsert_item(body=user)
244
+ logger.info(f"Informaci贸n del estudiante actualizada: {username}")
245
+ return True
246
+ else:
247
+ logger.warning(f"Intento de actualizar estudiante no existente: {username}")
248
+ return False
249
+ except Exception as e:
250
+ logger.error(f"Error al actualizar informaci贸n del estudiante {username}: {str(e)}")
251
+ return False
252
+
253
+
254
+ def delete_student_user(username):
255
+ user_container, _, _ = get_sql_containers()
256
+ try:
257
+ user = get_student_user(username)
258
+ if user:
259
+ user_container.delete_item(item=user['id'], partition_key=username)
260
+ logger.info(f"Estudiante eliminado: {username}")
261
+ return True
262
+ else:
263
+ logger.warning(f"Intento de eliminar estudiante no existente: {username}")
264
+ return False
265
+ except Exception as e:
266
+ logger.error(f"Error al eliminar estudiante {username}: {str(e)}")
267
+ return False
268
+
269
+ def store_application_request(name, lastname, email, institution, current_role, desired_role, reason):
270
+ _, application_requests_container, _ = get_sql_containers()
271
+ try:
272
+ application_request = {
273
+ "id": str(uuid.uuid4()),
274
+ "name": name,
275
+ "lastname": lastname,
276
+ "email": email,
277
+ "institution": institution,
278
+ "current_role": current_role,
279
+ "desired_role": desired_role,
280
+ "reason": reason,
281
+ "requestDate": datetime.utcnow().isoformat()
282
+ }
283
+ application_requests_container.create_item(body=application_request)
284
+ logger.info(f"Solicitud de aplicaci贸n almacenada para el email: {email}")
285
+ return True
286
+ except Exception as e:
287
+ logger.error(f"Error al almacenar la solicitud de aplicaci贸n: {str(e)}")
288
+ return False
289
+
290
+ def store_student_feedback(username, name, email, feedback):
291
+ _, _, user_feedback_container = get_sql_containers()
292
+ try:
293
+ feedback_item = {
294
+ "id": str(uuid.uuid4()),
295
+ "username": username,
296
+ "name": name,
297
+ "email": email,
298
+ "feedback": feedback,
299
+ "role": "Estudiante",
300
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
301
+ }
302
+ result = user_feedback_container.create_item(body=feedback_item)
303
+ logger.info(f"Feedback de estudiante almacenado con ID: {result['id']} para el usuario: {username}")
304
+ return True
305
+ except Exception as e:
306
+ logger.error(f"Error al almacenar el feedback del estudiante {username}: {str(e)}")
307
  return False
requirements.txt CHANGED
@@ -1,54 +1,54 @@
1
- gradio
2
- azure-cosmos
3
- bcrypt
4
-
5
- anthropic
6
- azure-identity
7
- azure-cosmos
8
- antiword
9
- certifi
10
- cairosvg
11
- python-dotenv
12
- drawSvg
13
- docx2txt
14
-
15
- # Modelos de spaCy desde Hugging Face
16
- # https://huggingface.co/spacy/es_core_news_lg/resolve/main/es_core_news_lg-any-py3-none-any.whl
17
- # https://huggingface.co/spacy/en_core_web_lg/resolve/main/en_core_web_lg-any-py3-none-any.whl
18
- # https://huggingface.co/spacy/fr_core_news_lg/resolve/main/fr_core_news_lg-any-py3-none-any.whl
19
-
20
- # Enlaces alternativos desde GitHub (comentados)
21
- es-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/es_core_news_lg-3.7.0/es_core_news_lg-3.7.0-py3-none-any.whl
22
- en-core-web-lg @ https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.7.1/en_core_web_lg-3.7.1-py3-none-any.whl
23
- fr-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/fr_core_news_lg-3.7.0/fr_core_news_lg-3.7.0-py3-none-any.whl
24
-
25
- numpy
26
- networkx
27
- matplotlib
28
- odfpy
29
- plotly
30
- pydantic
31
- python-dateutil
32
- pandas
33
- python-docx
34
- #pywin32
35
- pymssql
36
- python-dotenv
37
- pymongo
38
- PyPDF2
39
- rlPyCairo
40
- requests
41
- reportlab
42
- spacy>=3.7.0,<3.8.0
43
- #spacy
44
- spacy-streamlit
45
- seaborn
46
- squarify
47
- scipy
48
- sentencepiece
49
- scikit-learn
50
- svglib
51
- transformers
52
- torch
53
- tqdm
54
  thinc
 
1
+ gradio
2
+ azure-cosmos
3
+ bcrypt
4
+
5
+ anthropic
6
+ azure-identity
7
+ azure-cosmos
8
+ antiword
9
+ certifi
10
+ cairosvg
11
+ python-dotenv
12
+ drawSvg
13
+ docx2txt
14
+
15
+ # Modelos de spaCy desde Hugging Face
16
+ # https://huggingface.co/spacy/es_core_news_lg/resolve/main/es_core_news_lg-any-py3-none-any.whl
17
+ # https://huggingface.co/spacy/en_core_web_lg/resolve/main/en_core_web_lg-any-py3-none-any.whl
18
+ # https://huggingface.co/spacy/fr_core_news_lg/resolve/main/fr_core_news_lg-any-py3-none-any.whl
19
+
20
+ # Enlaces alternativos desde GitHub (comentados)
21
+ es-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/es_core_news_lg-3.7.0/es_core_news_lg-3.7.0-py3-none-any.whl
22
+ en-core-web-lg @ https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.7.1/en_core_web_lg-3.7.1-py3-none-any.whl
23
+ fr-core-news-lg @ https://github.com/explosion/spacy-models/releases/download/fr_core_news_lg-3.7.0/fr_core_news_lg-3.7.0-py3-none-any.whl
24
+
25
+ numpy
26
+ networkx
27
+ matplotlib
28
+ odfpy
29
+ plotly
30
+ pydantic
31
+ python-dateutil
32
+ pandas
33
+ python-docx
34
+ #pywin32
35
+ pymssql
36
+ python-dotenv
37
+ pymongo
38
+ PyPDF2
39
+ rlPyCairo
40
+ requests
41
+ reportlab
42
+ spacy>=3.7.0,<3.8.0
43
+ #spacy
44
+ spacy-streamlit
45
+ seaborn
46
+ squarify
47
+ scipy
48
+ sentencepiece
49
+ scikit-learn
50
+ svglib
51
+ transformers
52
+ torch
53
+ tqdm
54
  thinc