AIdeaText commited on
Commit
bc14470
verified
2 Parent(s): a96a5a5 fe4fe74

Merge branch #AIdeaText/v3' into 'AIdeaText/v4'

Browse files
README.md CHANGED
@@ -4,7 +4,7 @@ emoji: 馃憖
4
  colorFrom: blue
5
  colorTo: purple
6
  sdk: streamlit
7
- sdk_version: 1.40.0
8
  app_file: app.py
9
  pinned: true
10
  license: mit
 
4
  colorFrom: blue
5
  colorTo: purple
6
  sdk: streamlit
7
+ sdk_version: 1.40.1
8
  app_file: app.py
9
  pinned: true
10
  license: mit
modules/__init__.py CHANGED
@@ -12,19 +12,22 @@ def load_auth_functions():
12
  def load_database_functions():
13
 
14
  from .database.database_init import (
15
- initialize_database_connections
 
 
16
  )
17
 
 
18
  from .database.sql_db import (
19
  create_student_user,
20
  get_student_user,
21
  update_student_user,
22
  delete_student_user,
23
  store_application_request,
24
- store_student_feedback,
25
- record_login,
26
- record_logout,
27
- get_recent_sessions,
28
  get_user_total_time
29
  )
30
 
@@ -64,6 +67,8 @@ def load_database_functions():
64
 
65
  return {
66
  'initialize_database_connections': initialize_database_connections,
 
 
67
  'create_student_user': create_student_user,
68
  'get_student_user': get_student_user,
69
  'update_student_user': update_student_user,
 
12
  def load_database_functions():
13
 
14
  from .database.database_init import (
15
+ initialize_database_connections,
16
+ get_container,
17
+ get_mongodb
18
  )
19
 
20
+ # Importar funciones SQL
21
  from .database.sql_db import (
22
  create_student_user,
23
  get_student_user,
24
  update_student_user,
25
  delete_student_user,
26
  store_application_request,
27
+ store_student_feedback,
28
+ record_login,
29
+ record_logout,
30
+ get_recent_sessions,
31
  get_user_total_time
32
  )
33
 
 
67
 
68
  return {
69
  'initialize_database_connections': initialize_database_connections,
70
+ 'get_container': get_container,
71
+ 'get_mongodb': get_mongodb,
72
  'create_student_user': create_student_user,
73
  'get_student_user': get_student_user,
74
  'update_student_user': update_student_user,
modules/admin/admin_ui.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import streamlit as st
2
  from datetime import datetime
3
  from ..database.sql_db import (
 
1
+ #modules/admin/admin_ui.py
2
+
3
  import streamlit as st
4
  from datetime import datetime
5
  from ..database.sql_db import (
modules/auth/auth.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import os
2
  import streamlit as st
3
  from azure.cosmos import CosmosClient, exceptions
@@ -14,7 +16,9 @@ from ..database.sql_db import (
14
  record_login,
15
  record_logout
16
  )
 
17
  import logging
 
18
  from datetime import datetime, timezone
19
 
20
  logger = logging.getLogger(__name__)
@@ -39,12 +43,10 @@ if not endpoint or not key:
39
 
40
  key = clean_and_validate_key(key)
41
 
 
42
  def authenticate_user(username, password):
43
- """
44
- Autentica un usuario y registra el inicio de sesi贸n
45
- """
46
  try:
47
- # Primero intentar obtener usuario general
48
  user_item = get_user(username)
49
 
50
  if not user_item:
@@ -54,16 +56,18 @@ def authenticate_user(username, password):
54
  if verify_password(user_item['password'], password):
55
  logger.info(f"Usuario autenticado: {username}, Rol: {user_item['role']}")
56
 
57
- # Registrar la sesi贸n
58
  try:
59
  session_id = record_login(username)
60
  if session_id:
61
  st.session_state.session_id = session_id
 
62
  st.session_state.login_time = datetime.now(timezone.utc).isoformat()
 
 
 
63
  except Exception as e:
64
  logger.error(f"Error al registrar inicio de sesi贸n: {str(e)}")
65
- # Continuar aunque falle el registro de sesi贸n
66
-
67
  return True, user_item['role']
68
 
69
  logger.warning(f"Contrase帽a incorrecta para usuario: {username}")
@@ -150,16 +154,18 @@ def logout():
150
  """Cierra la sesi贸n del usuario"""
151
  try:
152
  if 'session_id' in st.session_state and 'username' in st.session_state:
153
- record_logout(
154
  st.session_state.username,
155
  st.session_state.session_id
156
  )
157
- logger.info(f"Sesi贸n cerrada: {st.session_state.username}")
158
-
159
- st.session_state.clear()
 
160
 
161
  except Exception as e:
162
  logger.error(f"Error en logout: {str(e)}")
 
163
  st.session_state.clear()
164
 
165
  def hash_password(password):
 
1
+ ##########modules/auth/auth.py
2
+
3
  import os
4
  import streamlit as st
5
  from azure.cosmos import CosmosClient, exceptions
 
16
  record_login,
17
  record_logout
18
  )
19
+
20
  import logging
21
+
22
  from datetime import datetime, timezone
23
 
24
  logger = logging.getLogger(__name__)
 
43
 
44
  key = clean_and_validate_key(key)
45
 
46
+
47
  def authenticate_user(username, password):
48
+ """Autentica un usuario y registra el inicio de sesi贸n"""
 
 
49
  try:
 
50
  user_item = get_user(username)
51
 
52
  if not user_item:
 
56
  if verify_password(user_item['password'], password):
57
  logger.info(f"Usuario autenticado: {username}, Rol: {user_item['role']}")
58
 
 
59
  try:
60
  session_id = record_login(username)
61
  if session_id:
62
  st.session_state.session_id = session_id
63
+ st.session_state.username = username
64
  st.session_state.login_time = datetime.now(timezone.utc).isoformat()
65
+ logger.info(f"Sesi贸n iniciada: {session_id}")
66
+ else:
67
+ logger.warning("No se pudo registrar la sesi贸n")
68
  except Exception as e:
69
  logger.error(f"Error al registrar inicio de sesi贸n: {str(e)}")
70
+
 
71
  return True, user_item['role']
72
 
73
  logger.warning(f"Contrase帽a incorrecta para usuario: {username}")
 
154
  """Cierra la sesi贸n del usuario"""
155
  try:
156
  if 'session_id' in st.session_state and 'username' in st.session_state:
157
+ success = record_logout(
158
  st.session_state.username,
159
  st.session_state.session_id
160
  )
161
+ if success:
162
+ logger.info(f"Sesi贸n cerrada: {st.session_state.username}")
163
+ else:
164
+ logger.warning(f"Error al registrar cierre de sesi贸n: {st.session_state.username}")
165
 
166
  except Exception as e:
167
  logger.error(f"Error en logout: {str(e)}")
168
+ finally:
169
  st.session_state.clear()
170
 
171
  def hash_password(password):
modules/database/database_init.py CHANGED
@@ -1,80 +1,116 @@
1
- import os
2
- import logging
3
- from azure.cosmos import CosmosClient
4
- from pymongo import MongoClient
5
- import certifi
6
-
7
- logging.basicConfig(level=logging.DEBUG)
8
- logger = logging.getLogger(__name__)
9
-
10
- # Variables globales para Cosmos DB SQL API
11
- cosmos_client = None
12
- user_database = None
13
- user_container = None
14
- application_requests_container = None
15
- user_feedback_container = None
16
-
17
- # Variables globales para Cosmos DB MongoDB API
18
- mongo_client = None
19
- mongo_db = None
20
-
21
- def initialize_cosmos_sql_connection():
22
- global cosmos_client, user_database, user_container, application_requests_container, user_feedback_container
23
- try:
24
- cosmos_endpoint = os.environ.get("COSMOS_ENDPOINT")
25
- cosmos_key = os.environ.get("COSMOS_KEY")
26
-
27
- if not cosmos_endpoint or not cosmos_key:
28
- raise ValueError("COSMOS_ENDPOINT and COSMOS_KEY environment variables must be set")
29
-
30
- cosmos_client = CosmosClient(cosmos_endpoint, cosmos_key)
31
- user_database = cosmos_client.get_database_client("user_database")
32
- user_container = user_database.get_container_client("users")
33
- application_requests_container = user_database.get_container_client("application_requests")
34
- user_feedback_container = user_database.get_container_client("user_feedback")
35
-
36
- logger.info("Conexi贸n a Cosmos DB SQL API exitosa")
37
- return True
38
- except Exception as e:
39
- logger.error(f"Error al conectar con Cosmos DB SQL API: {str(e)}", exc_info=True)
40
- return False
41
-
42
- def initialize_mongodb_connection():
43
- global mongo_client, mongo_db
44
- try:
45
- cosmos_mongodb_connection_string = os.getenv("MONGODB_CONNECTION_STRING")
46
- if not cosmos_mongodb_connection_string:
47
- raise ValueError("MONGODB_CONNECTION_STRING environment variable is not set")
48
-
49
- mongo_client = MongoClient(cosmos_mongodb_connection_string,
50
- tls=True,
51
- tlsCAFile=certifi.where(),
52
- retryWrites=False,
53
- serverSelectionTimeoutMS=5000,
54
- connectTimeoutMS=10000,
55
- socketTimeoutMS=10000)
56
-
57
- mongo_client.admin.command('ping')
58
-
59
- mongo_db = mongo_client['aideatext_db']
60
-
61
- logger.info("Conexi贸n a Cosmos DB MongoDB API exitosa")
62
- return True
63
- except Exception as e:
64
- logger.error(f"Error al conectar con Cosmos DB MongoDB API: {str(e)}", exc_info=True)
65
- return False
66
-
67
- def initialize_database_connections():
68
- sql_success = initialize_cosmos_sql_connection()
69
- mongodb_success = initialize_mongodb_connection()
70
- return sql_success and mongodb_success
71
-
72
- def get_sql_containers():
73
- if user_container is None or application_requests_container is None or user_feedback_container is None:
74
- initialize_cosmos_sql_connection()
75
- return user_container, application_requests_container, user_feedback_container
76
-
77
- def get_mongodb():
78
- if mongo_db is None:
79
- initialize_mongodb_connection()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  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/sql_db.py CHANGED
@@ -1,6 +1,6 @@
1
  #modules/database/sql_db.py
2
 
3
- from .database_init import get_sql_containers
4
  from datetime import datetime, timezone
5
  import logging
6
  import bcrypt
@@ -9,12 +9,15 @@ import uuid
9
  logger = logging.getLogger(__name__)
10
 
11
  def get_user(username, role=None):
12
- user_container, _, _ = get_sql_containers()
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(user_container.query_items(query=query, enable_cross_partition_query=True))
 
 
 
18
  return items[0] if items else None
19
  except Exception as e:
20
  logger.error(f"Error al obtener usuario {username}: {str(e)}")
@@ -30,19 +33,28 @@ def get_teacher_user(username):
30
  return get_user(username, role='Profesor')
31
 
32
  def create_user(username, password, role, additional_info=None):
33
- user_container, _, _ = get_sql_containers()
 
 
 
 
 
34
  try:
35
- hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
36
  user_data = {
37
  'id': username,
38
- 'password': hashed_password,
39
  'role': role,
40
  'timestamp': datetime.now(timezone.utc).isoformat(),
41
  'additional_info': additional_info or {}
42
  }
43
- user_container.create_item(body=user_data)
 
 
 
 
44
  logger.info(f"Usuario {role} creado: {username}")
45
  return True
 
46
  except Exception as e:
47
  logger.error(f"Error al crear usuario {role}: {str(e)}")
48
  return False
@@ -58,32 +70,47 @@ def create_admin_user(username, password, additional_info=None):
58
 
59
  ############-- Funciones de control del tiempo de sesi贸n ##################
60
 
 
 
61
  def record_login(username):
62
  """Registra el inicio de sesi贸n de un usuario"""
63
  try:
64
- container = get_container("user_sessions")
65
  if not container:
 
66
  return None
67
 
 
68
  session_doc = {
69
- "id": str(uuid.uuid4()),
70
  "type": "session",
71
  "username": username,
72
  "loginTime": datetime.now(timezone.utc).isoformat(),
 
 
73
  "partitionKey": username
74
  }
75
 
76
- result = container.create_item(body=session_doc)
77
- return result['id']
 
 
 
 
 
78
  except Exception as e:
79
  logger.error(f"Error registrando login: {str(e)}")
80
  return None
81
 
 
 
 
82
  def record_logout(username, session_id):
83
  """Registra el cierre de sesi贸n y calcula la duraci贸n"""
84
  try:
85
- container = get_container("user_sessions")
86
  if not container:
 
87
  return False
88
 
89
  # Obtener la sesi贸n actual
@@ -93,65 +120,88 @@ def record_logout(username, session_id):
93
  {"name": "@username", "value": username}
94
  ]
95
 
96
- sessions = list(container.query_items(
97
  query=query,
98
  parameters=params,
99
  enable_cross_partition_query=True
100
  ))
101
 
102
- if not sessions:
 
103
  return False
104
 
105
- session = sessions[0]
106
  login_time = datetime.fromisoformat(session['loginTime'].rstrip('Z'))
107
  logout_time = datetime.now(timezone.utc)
108
  duration = int((logout_time - login_time).total_seconds())
109
 
110
- # Actualizar el documento
111
  session.update({
112
  "logoutTime": logout_time.isoformat(),
113
  "sessionDuration": duration
114
  })
115
 
116
- container.upsert_item(body=session)
 
 
 
 
117
  return True
118
  except Exception as e:
119
  logger.error(f"Error registrando logout: {str(e)}")
120
  return False
121
 
 
 
 
122
  def get_recent_sessions(limit=10):
123
  """Obtiene las sesiones m谩s recientes"""
124
  try:
125
- container = get_container("user_sessions")
126
  if not container:
 
127
  return []
128
 
129
  query = """
130
  SELECT c.username, c.loginTime, c.logoutTime, c.sessionDuration
131
  FROM c
132
  WHERE c.type = 'session'
133
- AND c.logoutTime != null
134
  ORDER BY c.loginTime DESC
135
  OFFSET 0 LIMIT @limit
136
  """
137
 
138
- params = [{"name": "@limit", "value": limit}]
139
-
140
- sessions = container.query_items(
141
  query=query,
142
- parameters=params,
143
  enable_cross_partition_query=True
144
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
- return list(sessions)
147
  except Exception as e:
148
  logger.error(f"Error obteniendo sesiones recientes: {str(e)}")
149
  return []
150
 
 
 
 
 
151
  def get_user_total_time(username):
152
  """Obtiene el tiempo total que un usuario ha pasado en la plataforma"""
153
  try:
154
- container = get_container("user_sessions")
155
  if not container:
156
  return None
157
 
@@ -160,21 +210,19 @@ def get_user_total_time(username):
160
  FROM c
161
  WHERE c.type = 'session'
162
  AND c.username = @username
163
- AND c.logoutTime != null
164
  """
165
 
166
- params = [{"name": "@username", "value": username}]
167
-
168
  result = list(container.query_items(
169
  query=query,
170
- parameters=params,
171
  enable_cross_partition_query=True
172
  ))
173
 
174
- return result[0] if result else 0
175
  except Exception as e:
176
  logger.error(f"Error obteniendo tiempo total: {str(e)}")
177
- return None
178
 
179
  ###########################################################
180
  def update_student_user(username, new_info):
 
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
 
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)}")
 
33
  return get_user(username, role='Profesor')
34
 
35
  def create_user(username, password, role, additional_info=None):
36
+ """Crea un nuevo usuario"""
37
+ container = get_container("users")
38
+ if not container:
39
+ logger.error("No se pudo obtener el contenedor de usuarios")
40
+ return False
41
+
42
  try:
 
43
  user_data = {
44
  'id': username,
45
+ 'password': password,
46
  'role': role,
47
  'timestamp': datetime.now(timezone.utc).isoformat(),
48
  'additional_info': additional_info or {}
49
  }
50
+
51
+ container.create_item(
52
+ body=user_data,
53
+ partition_key=username
54
+ )
55
  logger.info(f"Usuario {role} creado: {username}")
56
  return True
57
+
58
  except Exception as e:
59
  logger.error(f"Error al crear usuario {role}: {str(e)}")
60
  return False
 
70
 
71
  ############-- Funciones de control del tiempo de sesi贸n ##################
72
 
73
+ ###########################################################
74
+
75
  def record_login(username):
76
  """Registra el inicio de sesi贸n de un usuario"""
77
  try:
78
+ container = get_container("users_sessions")
79
  if not container:
80
+ logger.error("No se pudo obtener el contenedor users_sessions")
81
  return None
82
 
83
+ session_id = str(uuid.uuid4())
84
  session_doc = {
85
+ "id": session_id,
86
  "type": "session",
87
  "username": username,
88
  "loginTime": datetime.now(timezone.utc).isoformat(),
89
+ "additional_info": {},
90
+ # El campo para partition key debe estar en el documento
91
  "partitionKey": username
92
  }
93
 
94
+ # Crear el documento usando options
95
+ result = container.create_item(
96
+ body=session_doc,
97
+ enable_cross_partition_query=True
98
+ )
99
+ logger.info(f"Sesi贸n {session_id} registrada para {username}")
100
+ return session_id
101
  except Exception as e:
102
  logger.error(f"Error registrando login: {str(e)}")
103
  return None
104
 
105
+
106
+ ###########################################################
107
+
108
  def record_logout(username, session_id):
109
  """Registra el cierre de sesi贸n y calcula la duraci贸n"""
110
  try:
111
+ container = get_container("users_sessions")
112
  if not container:
113
+ logger.error("No se pudo obtener el contenedor users_sessions")
114
  return False
115
 
116
  # Obtener la sesi贸n actual
 
120
  {"name": "@username", "value": username}
121
  ]
122
 
123
+ items = list(container.query_items(
124
  query=query,
125
  parameters=params,
126
  enable_cross_partition_query=True
127
  ))
128
 
129
+ if not items:
130
+ logger.warning(f"Sesi贸n no encontrada: {session_id}")
131
  return False
132
 
133
+ session = items[0]
134
  login_time = datetime.fromisoformat(session['loginTime'].rstrip('Z'))
135
  logout_time = datetime.now(timezone.utc)
136
  duration = int((logout_time - login_time).total_seconds())
137
 
 
138
  session.update({
139
  "logoutTime": logout_time.isoformat(),
140
  "sessionDuration": duration
141
  })
142
 
143
+ # Actualizar el documento
144
+ container.upsert_item(
145
+ body=session
146
+ )
147
+ logger.info(f"Sesi贸n {session_id} cerrada para {username}, duraci贸n: {duration}s")
148
  return True
149
  except Exception as e:
150
  logger.error(f"Error registrando logout: {str(e)}")
151
  return False
152
 
153
+
154
+ ###########################################################
155
+
156
  def get_recent_sessions(limit=10):
157
  """Obtiene las sesiones m谩s recientes"""
158
  try:
159
+ container = get_container("users_sessions")
160
  if not container:
161
+ logger.error("No se pudo obtener el contenedor users_sessions")
162
  return []
163
 
164
  query = """
165
  SELECT c.username, c.loginTime, c.logoutTime, c.sessionDuration
166
  FROM c
167
  WHERE c.type = 'session'
 
168
  ORDER BY c.loginTime DESC
169
  OFFSET 0 LIMIT @limit
170
  """
171
 
172
+ sessions = list(container.query_items(
 
 
173
  query=query,
174
+ parameters=[{"name": "@limit", "value": limit}],
175
  enable_cross_partition_query=True
176
+ ))
177
+
178
+ # Validar y limpiar los datos
179
+ clean_sessions = []
180
+ for session in sessions:
181
+ try:
182
+ clean_sessions.append({
183
+ "username": session["username"],
184
+ "loginTime": session["loginTime"],
185
+ "logoutTime": session.get("logoutTime", "Activo"),
186
+ "sessionDuration": session.get("sessionDuration", 0)
187
+ })
188
+ except KeyError as e:
189
+ logger.warning(f"Sesi贸n con datos incompletos: {e}")
190
+ continue
191
 
192
+ return clean_sessions
193
  except Exception as e:
194
  logger.error(f"Error obteniendo sesiones recientes: {str(e)}")
195
  return []
196
 
197
+
198
+
199
+ ###########################################################
200
+
201
  def get_user_total_time(username):
202
  """Obtiene el tiempo total que un usuario ha pasado en la plataforma"""
203
  try:
204
+ container = get_container("users_sessions")
205
  if not container:
206
  return None
207
 
 
210
  FROM c
211
  WHERE c.type = 'session'
212
  AND c.username = @username
213
+ AND IS_DEFINED(c.sessionDuration)
214
  """
215
 
 
 
216
  result = list(container.query_items(
217
  query=query,
218
+ parameters=[{"name": "@username", "value": username}],
219
  enable_cross_partition_query=True
220
  ))
221
 
222
+ return result[0] if result and result[0] is not None else 0
223
  except Exception as e:
224
  logger.error(f"Error obteniendo tiempo total: {str(e)}")
225
+ return 0
226
 
227
  ###########################################################
228
  def update_student_user(username, new_info):