AIdeaText commited on
Commit
be0c7a2
·
verified ·
1 Parent(s): 1fc131a

Upload 16 files

Browse files
modules/database/chat_mongo_db.py CHANGED
@@ -1,116 +1,116 @@
1
- # /modules/database/chat_mongo_db.py
2
- from .mongo_db import insert_document, find_documents, get_collection
3
- from datetime import datetime, timezone
4
- import logging
5
-
6
- logger = logging.getLogger(__name__)
7
- COLLECTION_NAME = 'chat_history-v3'
8
-
9
- def get_chat_history(username: str, analysis_type: str = 'sidebar', limit: int = None) -> list:
10
- """
11
- Recupera el historial del chat.
12
-
13
- Args:
14
- username: Nombre del usuario
15
- analysis_type: Tipo de análisis ('sidebar' por defecto)
16
- limit: Límite de conversaciones a recuperar
17
-
18
- Returns:
19
- list: Lista de conversaciones con formato
20
- """
21
- try:
22
- query = {
23
- "username": username,
24
- "analysis_type": analysis_type
25
- }
26
-
27
- collection = get_collection(COLLECTION_NAME)
28
- if collection is None:
29
- logger.error("No se pudo obtener la colección de chat")
30
- return []
31
-
32
- # Obtener y formatear conversaciones
33
- cursor = collection.find(query).sort("timestamp", -1)
34
- if limit:
35
- cursor = cursor.limit(limit)
36
-
37
- conversations = []
38
- for chat in cursor:
39
- try:
40
- formatted_chat = {
41
- 'timestamp': chat['timestamp'],
42
- 'messages': [
43
- {
44
- 'role': msg.get('role', 'unknown'),
45
- 'content': msg.get('content', '')
46
- }
47
- for msg in chat.get('messages', [])
48
- ]
49
- }
50
- conversations.append(formatted_chat)
51
- except Exception as e:
52
- logger.error(f"Error formateando chat: {str(e)}")
53
- continue
54
-
55
- return conversations
56
-
57
- except Exception as e:
58
- logger.error(f"Error al recuperar historial de chat: {str(e)}")
59
- return []
60
-
61
- def store_chat_history(username: str, messages: list, analysis_type: str = 'sidebar') -> bool:
62
- """
63
- Guarda el historial del chat.
64
-
65
- Args:
66
- username: Nombre del usuario
67
- messages: Lista de mensajes a guardar
68
- analysis_type: Tipo de análisis
69
-
70
- Returns:
71
- bool: True si se guardó correctamente
72
- """
73
- try:
74
- collection = get_collection(COLLECTION_NAME)
75
- if collection is None:
76
- logger.error("No se pudo obtener la colección de chat")
77
- return False
78
-
79
- # Formatear mensajes antes de guardar
80
- formatted_messages = [
81
- {
82
- 'role': msg.get('role', 'unknown'),
83
- 'content': msg.get('content', ''),
84
- 'timestamp': datetime.now(timezone.utc).isoformat()
85
- }
86
- for msg in messages
87
- ]
88
-
89
- chat_document = {
90
- 'username': username,
91
- 'timestamp': datetime.now(timezone.utc).isoformat(),
92
- 'messages': formatted_messages,
93
- 'analysis_type': analysis_type
94
- }
95
-
96
- result = collection.insert_one(chat_document)
97
- if result.inserted_id:
98
- logger.info(f"Historial de chat guardado con ID: {result.inserted_id} para el usuario: {username}")
99
- return True
100
-
101
- logger.error("No se pudo insertar el documento")
102
- return False
103
-
104
- except Exception as e:
105
- logger.error(f"Error al guardar historial de chat: {str(e)}")
106
- return False
107
-
108
-
109
- #def get_chat_history(username, analysis_type=None, limit=10):
110
- # query = {"username": username}
111
- # if analysis_type:
112
- # query["analysis_type"] = analysis_type
113
-
114
- # return find_documents(COLLECTION_NAME, query, sort=[("timestamp", -1)], limit=limit)
115
-
116
  # Agregar funciones para actualizar y eliminar chat si es necesario
 
1
+ # /modules/database/chat_mongo_db.py
2
+ from .mongo_db import insert_document, find_documents, get_collection
3
+ from datetime import datetime, timezone
4
+ import logging
5
+
6
+ logger = logging.getLogger(__name__)
7
+ COLLECTION_NAME = 'chat_history-v3'
8
+
9
+ def get_chat_history(username: str, analysis_type: str = 'sidebar', limit: int = None) -> list:
10
+ """
11
+ Recupera el historial del chat.
12
+
13
+ Args:
14
+ username: Nombre del usuario
15
+ analysis_type: Tipo de análisis ('sidebar' por defecto)
16
+ limit: Límite de conversaciones a recuperar
17
+
18
+ Returns:
19
+ list: Lista de conversaciones con formato
20
+ """
21
+ try:
22
+ query = {
23
+ "username": username,
24
+ "analysis_type": analysis_type
25
+ }
26
+
27
+ collection = get_collection(COLLECTION_NAME)
28
+ if collection is None:
29
+ logger.error("No se pudo obtener la colección de chat")
30
+ return []
31
+
32
+ # Obtener y formatear conversaciones
33
+ cursor = collection.find(query).sort("timestamp", -1)
34
+ if limit:
35
+ cursor = cursor.limit(limit)
36
+
37
+ conversations = []
38
+ for chat in cursor:
39
+ try:
40
+ formatted_chat = {
41
+ 'timestamp': chat['timestamp'],
42
+ 'messages': [
43
+ {
44
+ 'role': msg.get('role', 'unknown'),
45
+ 'content': msg.get('content', '')
46
+ }
47
+ for msg in chat.get('messages', [])
48
+ ]
49
+ }
50
+ conversations.append(formatted_chat)
51
+ except Exception as e:
52
+ logger.error(f"Error formateando chat: {str(e)}")
53
+ continue
54
+
55
+ return conversations
56
+
57
+ except Exception as e:
58
+ logger.error(f"Error al recuperar historial de chat: {str(e)}")
59
+ return []
60
+
61
+ def store_chat_history(username: str, messages: list, analysis_type: str = 'sidebar') -> bool:
62
+ """
63
+ Guarda el historial del chat.
64
+
65
+ Args:
66
+ username: Nombre del usuario
67
+ messages: Lista de mensajes a guardar
68
+ analysis_type: Tipo de análisis
69
+
70
+ Returns:
71
+ bool: True si se guardó correctamente
72
+ """
73
+ try:
74
+ collection = get_collection(COLLECTION_NAME)
75
+ if collection is None:
76
+ logger.error("No se pudo obtener la colección de chat")
77
+ return False
78
+
79
+ # Formatear mensajes antes de guardar
80
+ formatted_messages = [
81
+ {
82
+ 'role': msg.get('role', 'unknown'),
83
+ 'content': msg.get('content', ''),
84
+ 'timestamp': datetime.now(timezone.utc).isoformat()
85
+ }
86
+ for msg in messages
87
+ ]
88
+
89
+ chat_document = {
90
+ 'username': username,
91
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
92
+ 'messages': formatted_messages,
93
+ 'analysis_type': analysis_type
94
+ }
95
+
96
+ result = collection.insert_one(chat_document)
97
+ if result.inserted_id:
98
+ logger.info(f"Historial de chat guardado con ID: {result.inserted_id} para el usuario: {username}")
99
+ return True
100
+
101
+ logger.error("No se pudo insertar el documento")
102
+ return False
103
+
104
+ except Exception as e:
105
+ logger.error(f"Error al guardar historial de chat: {str(e)}")
106
+ return False
107
+
108
+
109
+ #def get_chat_history(username, analysis_type=None, limit=10):
110
+ # query = {"username": username}
111
+ # if analysis_type:
112
+ # query["analysis_type"] = analysis_type
113
+
114
+ # return find_documents(COLLECTION_NAME, query, sort=[("timestamp", -1)], limit=limit)
115
+
116
  # Agregar funciones para actualizar y eliminar chat si es necesario
modules/database/claude_recommendations_mongo_db.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # modules/database/claude_recommendations_mongo_db.py
2
+ from datetime import datetime, timezone, timedelta
3
+ import logging
4
+ from .mongo_db import get_collection
5
+
6
+ logger = logging.getLogger(__name__)
7
+ COLLECTION_NAME = 'student_claude_recommendations'
8
+
9
+ def store_claude_recommendation(username, text, metrics, text_type, recommendations):
10
+ """
11
+ Guarda las recomendaciones generadas por Claude AI.
12
+
13
+ Args:
14
+ username: Nombre del usuario
15
+ text: Texto analizado
16
+ metrics: Métricas del análisis
17
+ text_type: Tipo de texto (academic_article, university_work, general_communication)
18
+ recommendations: Recomendaciones generadas por Claude
19
+
20
+ Returns:
21
+ bool: True si se guardó correctamente, False en caso contrario
22
+ """
23
+ try:
24
+ # Verificar parámetros
25
+ if not all([username, text, recommendations]):
26
+ logger.error("Faltan parámetros requeridos para guardar recomendaciones de Claude")
27
+ return False
28
+
29
+ collection = get_collection(COLLECTION_NAME)
30
+ if collection is None:
31
+ logger.error("No se pudo obtener la colección de recomendaciones de Claude")
32
+ return False
33
+
34
+ # Crear documento
35
+ document = {
36
+ 'username': username,
37
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
38
+ 'text': text,
39
+ 'metrics': metrics or {},
40
+ 'text_type': text_type,
41
+ 'recommendations': recommendations,
42
+ 'analysis_type': 'claude_recommendation'
43
+ }
44
+
45
+ # Insertar documento
46
+ result = collection.insert_one(document)
47
+ if result.inserted_id:
48
+ logger.info(f"""
49
+ Recomendaciones de Claude guardadas:
50
+ - Usuario: {username}
51
+ - ID: {result.inserted_id}
52
+ - Tipo de texto: {text_type}
53
+ - Longitud del texto: {len(text)}
54
+ """)
55
+
56
+ # Verificar almacenamiento
57
+ storage_verified = verify_recommendation_storage(username)
58
+ if not storage_verified:
59
+ logger.warning("Verificación de almacenamiento de recomendaciones falló")
60
+
61
+ return True
62
+
63
+ logger.error("No se pudo insertar el documento de recomendaciones")
64
+ return False
65
+
66
+ except Exception as e:
67
+ logger.error(f"Error guardando recomendaciones de Claude: {str(e)}")
68
+ return False
69
+
70
+ def verify_recommendation_storage(username):
71
+ """
72
+ Verifica que las recomendaciones se están guardando correctamente.
73
+
74
+ Args:
75
+ username: Nombre del usuario
76
+
77
+ Returns:
78
+ bool: True si la verificación es exitosa, False en caso contrario
79
+ """
80
+ try:
81
+ collection = get_collection(COLLECTION_NAME)
82
+ if collection is None:
83
+ logger.error("No se pudo obtener la colección para verificación de recomendaciones")
84
+ return False
85
+
86
+ # Buscar documentos recientes del usuario
87
+ timestamp_threshold = (datetime.now(timezone.utc) - timedelta(minutes=5)).isoformat()
88
+ recent_docs = collection.find({
89
+ 'username': username,
90
+ 'timestamp': {'$gte': timestamp_threshold}
91
+ }).sort('timestamp', -1).limit(1)
92
+
93
+ docs = list(recent_docs)
94
+ if docs:
95
+ logger.info(f"""
96
+ Último documento de recomendaciones guardado:
97
+ - ID: {docs[0]['_id']}
98
+ - Timestamp: {docs[0]['timestamp']}
99
+ - Tipo de texto: {docs[0].get('text_type', 'N/A')}
100
+ """)
101
+ return True
102
+
103
+ logger.warning(f"No se encontraron documentos recientes de recomendaciones para {username}")
104
+ return False
105
+
106
+ except Exception as e:
107
+ logger.error(f"Error verificando almacenamiento de recomendaciones: {str(e)}")
108
+ return False
109
+
110
+ def get_claude_recommendations(username, limit=10):
111
+ """
112
+ Obtiene las recomendaciones más recientes de Claude para un usuario.
113
+
114
+ Args:
115
+ username: Nombre del usuario
116
+ limit: Número máximo de recomendaciones a recuperar
117
+
118
+ Returns:
119
+ list: Lista de recomendaciones
120
+ """
121
+ try:
122
+ collection = get_collection(COLLECTION_NAME)
123
+ if collection is None:
124
+ logger.error("No se pudo obtener la colección de recomendaciones")
125
+ return []
126
+
127
+ results = collection.find(
128
+ {'username': username}
129
+ ).sort('timestamp', -1).limit(limit)
130
+
131
+ recommendations = list(results)
132
+ logger.info(f"Recuperadas {len(recommendations)} recomendaciones de Claude para {username}")
133
+ return recommendations
134
+
135
+ except Exception as e:
136
+ logger.error(f"Error obteniendo recomendaciones de Claude: {str(e)}")
137
+ return []
modules/database/current_situation_mongo_db.py CHANGED
@@ -1,112 +1,137 @@
1
- # modules/database/current_situation_mongo_db.py
2
- from datetime import datetime, timezone, timedelta
3
- import logging
4
- from .mongo_db import get_collection
5
-
6
- logger = logging.getLogger(__name__)
7
- COLLECTION_NAME = 'student_current_situation'
8
-
9
- # En modules/database/current_situation_mongo_db.py
10
-
11
- def store_current_situation_result(username, text, metrics, feedback):
12
- """
13
- Guarda los resultados del análisis de situación actual.
14
- """
15
- try:
16
- # Verificar parámetros
17
- if not all([username, text, metrics]):
18
- logger.error("Faltan parámetros requeridos")
19
- return False
20
-
21
- collection = get_collection(COLLECTION_NAME)
22
- if collection is None:
23
- logger.error("No se pudo obtener la colección")
24
- return False
25
-
26
- # Crear documento
27
- document = {
28
- 'username': username,
29
- 'timestamp': datetime.now(timezone.utc).isoformat(),
30
- 'text': text,
31
- 'metrics': metrics,
32
- 'feedback': feedback or {},
33
- 'analysis_type': 'current_situation'
34
- }
35
-
36
- # Insertar documento y verificar
37
- result = collection.insert_one(document)
38
- if result.inserted_id:
39
- logger.info(f"""
40
- Análisis de situación actual guardado:
41
- - Usuario: {username}
42
- - ID: {result.inserted_id}
43
- - Longitud texto: {len(text)}
44
- """)
45
-
46
- # Verificar almacenamiento
47
- storage_verified = verify_storage(username)
48
- if not storage_verified:
49
- logger.warning("Verificación de almacenamiento falló")
50
-
51
- return True
52
-
53
- logger.error("No se pudo insertar el documento")
54
- return False
55
-
56
- except Exception as e:
57
- logger.error(f"Error guardando análisis de situación actual: {str(e)}")
58
- return False
59
-
60
- def verify_storage(username):
61
- """
62
- Verifica que los datos se están guardando correctamente.
63
- """
64
- try:
65
- collection = get_collection(COLLECTION_NAME)
66
- if collection is None:
67
- logger.error("No se pudo obtener la colección para verificación")
68
- return False
69
-
70
- # Buscar documentos recientes del usuario
71
- timestamp_threshold = (datetime.now(timezone.utc) - timedelta(minutes=5)).isoformat()
72
-
73
- recent_docs = collection.find({
74
- 'username': username,
75
- 'timestamp': {'$gte': timestamp_threshold}
76
- }).sort('timestamp', -1).limit(1)
77
-
78
- docs = list(recent_docs)
79
- if docs:
80
- logger.info(f"""
81
- Último documento guardado:
82
- - ID: {docs[0]['_id']}
83
- - Timestamp: {docs[0]['timestamp']}
84
- - Métricas guardadas: {bool(docs[0].get('metrics'))}
85
- """)
86
- return True
87
-
88
- logger.warning(f"No se encontraron documentos recientes para {username}")
89
- return False
90
-
91
- except Exception as e:
92
- logger.error(f"Error verificando almacenamiento: {str(e)}")
93
- return False
94
-
95
- def get_recent_situation_analysis(username, limit=5):
96
- """
97
- Obtiene los análisis más recientes de un usuario.
98
- """
99
- try:
100
- collection = get_collection(COLLECTION_NAME)
101
- if collection is None:
102
- return []
103
-
104
- results = collection.find(
105
- {'username': username}
106
- ).sort('timestamp', -1).limit(limit)
107
-
108
- return list(results)
109
-
110
- except Exception as e:
111
- logger.error(f"Error obteniendo análisis recientes: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  return []
 
1
+ # modules/database/current_situation_mongo_db.py
2
+ from datetime import datetime, timezone, timedelta
3
+ import logging
4
+ from .mongo_db import get_collection
5
+
6
+ logger = logging.getLogger(__name__)
7
+ COLLECTION_NAME = 'student_current_situation'
8
+
9
+ # En modules/database/current_situation_mongo_db.py
10
+
11
+ def store_current_situation_result(username, text, metrics, feedback):
12
+ """
13
+ Guarda los resultados del análisis de situación actual.
14
+ """
15
+ try:
16
+ # Verificar parámetros
17
+ if not all([username, text, metrics]):
18
+ logger.error("Faltan parámetros requeridos")
19
+ return False
20
+
21
+ collection = get_collection(COLLECTION_NAME)
22
+ if collection is None:
23
+ logger.error("No se pudo obtener la colección")
24
+ return False
25
+
26
+ # Crear documento
27
+ document = {
28
+ 'username': username,
29
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
30
+ 'text': text,
31
+ 'metrics': metrics,
32
+ 'feedback': feedback or {},
33
+ 'analysis_type': 'current_situation'
34
+ }
35
+
36
+ # Insertar documento y verificar
37
+ result = collection.insert_one(document)
38
+ if result.inserted_id:
39
+ logger.info(f"""
40
+ Análisis de situación actual guardado:
41
+ - Usuario: {username}
42
+ - ID: {result.inserted_id}
43
+ - Longitud texto: {len(text)}
44
+ """)
45
+
46
+ # Verificar almacenamiento
47
+ storage_verified = verify_storage(username)
48
+ if not storage_verified:
49
+ logger.warning("Verificación de almacenamiento falló")
50
+
51
+ return True
52
+
53
+ logger.error("No se pudo insertar el documento")
54
+ return False
55
+
56
+ except Exception as e:
57
+ logger.error(f"Error guardando análisis de situación actual: {str(e)}")
58
+ return False
59
+
60
+ def verify_storage(username):
61
+ """
62
+ Verifica que los datos se están guardando correctamente.
63
+ """
64
+ try:
65
+ collection = get_collection(COLLECTION_NAME)
66
+ if collection is None:
67
+ logger.error("No se pudo obtener la colección para verificación")
68
+ return False
69
+
70
+ # Buscar documentos recientes del usuario
71
+ timestamp_threshold = (datetime.now(timezone.utc) - timedelta(minutes=5)).isoformat()
72
+
73
+ recent_docs = collection.find({
74
+ 'username': username,
75
+ 'timestamp': {'$gte': timestamp_threshold}
76
+ }).sort('timestamp', -1).limit(1)
77
+
78
+ docs = list(recent_docs)
79
+ if docs:
80
+ logger.info(f"""
81
+ Último documento guardado:
82
+ - ID: {docs[0]['_id']}
83
+ - Timestamp: {docs[0]['timestamp']}
84
+ - Métricas guardadas: {bool(docs[0].get('metrics'))}
85
+ """)
86
+ return True
87
+
88
+ logger.warning(f"No se encontraron documentos recientes para {username}")
89
+ return False
90
+
91
+ except Exception as e:
92
+ logger.error(f"Error verificando almacenamiento: {str(e)}")
93
+ return False
94
+
95
+ def get_current_situation_analysis(username, limit=5):
96
+ """
97
+ Obtiene los análisis de situación actual de un usuario.
98
+ """
99
+ try:
100
+ collection = get_collection(COLLECTION_NAME)
101
+ if collection is None:
102
+ logger.error("No se pudo obtener la colección")
103
+ return []
104
+
105
+ # Buscar documentos
106
+ query = {'username': username, 'analysis_type': 'current_situation'}
107
+ cursor = collection.find(query).sort('timestamp', -1)
108
+
109
+ # Aplicar límite si se especifica
110
+ if limit:
111
+ cursor = cursor.limit(limit)
112
+
113
+ # Convertir cursor a lista
114
+ return list(cursor)
115
+
116
+ except Exception as e:
117
+ logger.error(f"Error obteniendo análisis de situación actual: {str(e)}")
118
+ return []
119
+
120
+ def get_recent_situation_analysis(username, limit=5):
121
+ """
122
+ Obtiene los análisis más recientes de un usuario.
123
+ """
124
+ try:
125
+ collection = get_collection(COLLECTION_NAME)
126
+ if collection is None:
127
+ return []
128
+
129
+ results = collection.find(
130
+ {'username': username}
131
+ ).sort('timestamp', -1).limit(limit)
132
+
133
+ return list(results)
134
+
135
+ except Exception as e:
136
+ logger.error(f"Error obteniendo análisis recientes: {str(e)}")
137
  return []
modules/database/database_init.py CHANGED
@@ -1,188 +1,188 @@
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
- logger.info(f"Solicitando contenedor: {container_name}")
40
-
41
- if not initialize_cosmos_sql_connection():
42
- logger.error("No se pudo inicializar la conexión")
43
- return None
44
-
45
- # Verificar estado de los contenedores
46
- containers_status = {
47
- "users": user_container is not None,
48
- "users_sessions": user_sessions_container is not None,
49
- "application_requests": application_requests_container is not None,
50
- "user_feedback": user_feedback_container is not None # Añadido
51
- }
52
-
53
- logger.info(f"Estado actual de los contenedores: {containers_status}")
54
-
55
- # Mapear nombres a contenedores
56
- containers = {
57
- "users": user_container,
58
- "users_sessions": user_sessions_container,
59
- "application_requests": application_requests_container,
60
- "user_feedback": user_feedback_container # Añadido
61
- }
62
-
63
- container = containers.get(container_name)
64
-
65
- if container is None:
66
- logger.error(f"Contenedor '{container_name}' no encontrado o no inicializado")
67
- logger.error(f"Contenedores disponibles: {[k for k, v in containers_status.items() if v]}")
68
- return None
69
-
70
- logger.info(f"Contenedor '{container_name}' obtenido exitosamente")
71
- return container
72
- ###################################################################
73
-
74
- def initialize_cosmos_sql_connection():
75
- """Inicializa la conexión a Cosmos DB SQL API"""
76
- global cosmos_client, user_database, user_container, user_sessions_container, application_requests_container, user_feedback_container # Añadida aquí user_feedback_container
77
-
78
- try:
79
- # Verificar conexión existente
80
- if all([
81
- cosmos_client,
82
- user_database,
83
- user_container,
84
- user_sessions_container,
85
- application_requests_container,
86
- user_feedback_container
87
- ]):
88
- logger.debug("Todas las conexiones ya están inicializadas")
89
- return True
90
-
91
- # Obtener credenciales
92
- cosmos_endpoint = os.environ.get("COSMOS_ENDPOINT")
93
- cosmos_key = os.environ.get("COSMOS_KEY")
94
-
95
- if not cosmos_endpoint or not cosmos_key:
96
- raise ValueError("COSMOS_ENDPOINT y COSMOS_KEY deben estar configurados")
97
-
98
- # Inicializar cliente y base de datos
99
- cosmos_client = CosmosClient(cosmos_endpoint, cosmos_key)
100
- user_database = cosmos_client.get_database_client("user_database")
101
-
102
- # Inicializar contenedores
103
- try:
104
- user_container = user_database.get_container_client("users")
105
- logger.info("Contenedor 'users' inicializado correctamente")
106
- except Exception as e:
107
- logger.error(f"Error inicializando contenedor 'users': {str(e)}")
108
- user_container = None
109
-
110
- try:
111
- user_sessions_container = user_database.get_container_client("users_sessions")
112
- logger.info("Contenedor 'users_sessions' inicializado correctamente")
113
- except Exception as e:
114
- logger.error(f"Error inicializando contenedor 'users_sessions': {str(e)}")
115
- user_sessions_container = None
116
-
117
- try:
118
- application_requests_container = user_database.get_container_client("application_requests")
119
- logger.info("Contenedor 'application_requests' inicializado correctamente")
120
- except Exception as e:
121
- logger.error(f"Error inicializando contenedor 'application_requests': {str(e)}")
122
- application_requests_container = None
123
-
124
- try:
125
- user_feedback_container = user_database.get_container_client("user_feedback")
126
- logger.info("Contenedor 'user_feedback' inicializado correctamente")
127
- except Exception as e:
128
- logger.error(f"Error inicializando contenedor 'user_feedback': {str(e)}")
129
- user_feedback_container = None
130
-
131
- # Verificar el estado de los contenedores
132
- containers_status = {
133
- 'users': user_container is not None,
134
- 'users_sessions': user_sessions_container is not None,
135
- 'application_requests': application_requests_container is not None,
136
- 'user_feedback': user_feedback_container is not None
137
- }
138
-
139
- logger.info(f"Estado de los contenedores: {containers_status}")
140
-
141
- if all(containers_status.values()):
142
- logger.info("Todos los contenedores inicializados correctamente")
143
- return True
144
- else:
145
- logger.error("No se pudieron inicializar todos los contenedores")
146
- return False
147
-
148
- except Exception as e:
149
- logger.error(f"Error al conectar con Cosmos DB SQL API: {str(e)}")
150
- return False
151
-
152
-
153
- ###################################################################
154
- def initialize_mongodb_connection():
155
- """Inicializa la conexión a MongoDB"""
156
- global mongo_client, mongo_db
157
- try:
158
- connection_string = os.getenv("MONGODB_CONNECTION_STRING")
159
- if not connection_string:
160
- raise ValueError("MONGODB_CONNECTION_STRING debe estar configurado")
161
-
162
- mongo_client = MongoClient(
163
- connection_string,
164
- tls=True,
165
- tlsCAFile=certifi.where(),
166
- retryWrites=False,
167
- serverSelectionTimeoutMS=5000,
168
- connectTimeoutMS=10000,
169
- socketTimeoutMS=10000
170
- )
171
-
172
- mongo_db = mongo_client['aideatext_db']
173
- return True
174
- except Exception as e:
175
- logger.error(f"Error conectando a MongoDB: {str(e)}")
176
- return False
177
-
178
- ###################################################################
179
- def initialize_database_connections():
180
- """Inicializa todas las conexiones"""
181
- return initialize_cosmos_sql_connection() and initialize_mongodb_connection()
182
-
183
- ###################################################################
184
- def get_mongodb():
185
- """Obtiene la conexión MongoDB"""
186
- if mongo_db is None:
187
- initialize_mongodb_connection()
188
  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
+ logger.info(f"Solicitando contenedor: {container_name}")
40
+
41
+ if not initialize_cosmos_sql_connection():
42
+ logger.error("No se pudo inicializar la conexión")
43
+ return None
44
+
45
+ # Verificar estado de los contenedores
46
+ containers_status = {
47
+ "users": user_container is not None,
48
+ "users_sessions": user_sessions_container is not None,
49
+ "application_requests": application_requests_container is not None,
50
+ "user_feedback": user_feedback_container is not None # Añadido
51
+ }
52
+
53
+ logger.info(f"Estado actual de los contenedores: {containers_status}")
54
+
55
+ # Mapear nombres a contenedores
56
+ containers = {
57
+ "users": user_container,
58
+ "users_sessions": user_sessions_container,
59
+ "application_requests": application_requests_container,
60
+ "user_feedback": user_feedback_container # Añadido
61
+ }
62
+
63
+ container = containers.get(container_name)
64
+
65
+ if container is None:
66
+ logger.error(f"Contenedor '{container_name}' no encontrado o no inicializado")
67
+ logger.error(f"Contenedores disponibles: {[k for k, v in containers_status.items() if v]}")
68
+ return None
69
+
70
+ logger.info(f"Contenedor '{container_name}' obtenido exitosamente")
71
+ return container
72
+ ###################################################################
73
+
74
+ def initialize_cosmos_sql_connection():
75
+ """Inicializa la conexión a Cosmos DB SQL API"""
76
+ global cosmos_client, user_database, user_container, user_sessions_container, application_requests_container, user_feedback_container # Añadida aquí user_feedback_container
77
+
78
+ try:
79
+ # Verificar conexión existente
80
+ if all([
81
+ cosmos_client,
82
+ user_database,
83
+ user_container,
84
+ user_sessions_container,
85
+ application_requests_container,
86
+ user_feedback_container
87
+ ]):
88
+ logger.debug("Todas las conexiones ya están inicializadas")
89
+ return True
90
+
91
+ # Obtener credenciales
92
+ cosmos_endpoint = os.environ.get("COSMOS_ENDPOINT")
93
+ cosmos_key = os.environ.get("COSMOS_KEY")
94
+
95
+ if not cosmos_endpoint or not cosmos_key:
96
+ raise ValueError("COSMOS_ENDPOINT y COSMOS_KEY deben estar configurados")
97
+
98
+ # Inicializar cliente y base de datos
99
+ cosmos_client = CosmosClient(cosmos_endpoint, cosmos_key)
100
+ user_database = cosmos_client.get_database_client("user_database")
101
+
102
+ # Inicializar contenedores
103
+ try:
104
+ user_container = user_database.get_container_client("users")
105
+ logger.info("Contenedor 'users' inicializado correctamente")
106
+ except Exception as e:
107
+ logger.error(f"Error inicializando contenedor 'users': {str(e)}")
108
+ user_container = None
109
+
110
+ try:
111
+ user_sessions_container = user_database.get_container_client("users_sessions")
112
+ logger.info("Contenedor 'users_sessions' inicializado correctamente")
113
+ except Exception as e:
114
+ logger.error(f"Error inicializando contenedor 'users_sessions': {str(e)}")
115
+ user_sessions_container = None
116
+
117
+ try:
118
+ application_requests_container = user_database.get_container_client("application_requests")
119
+ logger.info("Contenedor 'application_requests' inicializado correctamente")
120
+ except Exception as e:
121
+ logger.error(f"Error inicializando contenedor 'application_requests': {str(e)}")
122
+ application_requests_container = None
123
+
124
+ try:
125
+ user_feedback_container = user_database.get_container_client("user_feedback")
126
+ logger.info("Contenedor 'user_feedback' inicializado correctamente")
127
+ except Exception as e:
128
+ logger.error(f"Error inicializando contenedor 'user_feedback': {str(e)}")
129
+ user_feedback_container = None
130
+
131
+ # Verificar el estado de los contenedores
132
+ containers_status = {
133
+ 'users': user_container is not None,
134
+ 'users_sessions': user_sessions_container is not None,
135
+ 'application_requests': application_requests_container is not None,
136
+ 'user_feedback': user_feedback_container is not None
137
+ }
138
+
139
+ logger.info(f"Estado de los contenedores: {containers_status}")
140
+
141
+ if all(containers_status.values()):
142
+ logger.info("Todos los contenedores inicializados correctamente")
143
+ return True
144
+ else:
145
+ logger.error("No se pudieron inicializar todos los contenedores")
146
+ return False
147
+
148
+ except Exception as e:
149
+ logger.error(f"Error al conectar con Cosmos DB SQL API: {str(e)}")
150
+ return False
151
+
152
+
153
+ ###################################################################
154
+ def initialize_mongodb_connection():
155
+ """Inicializa la conexión a MongoDB"""
156
+ global mongo_client, mongo_db
157
+ try:
158
+ connection_string = os.getenv("MONGODB_CONNECTION_STRING")
159
+ if not connection_string:
160
+ raise ValueError("MONGODB_CONNECTION_STRING debe estar configurado")
161
+
162
+ mongo_client = MongoClient(
163
+ connection_string,
164
+ tls=True,
165
+ tlsCAFile=certifi.where(),
166
+ retryWrites=False,
167
+ serverSelectionTimeoutMS=5000,
168
+ connectTimeoutMS=10000,
169
+ socketTimeoutMS=10000
170
+ )
171
+
172
+ mongo_db = mongo_client['aideatext_db']
173
+ return True
174
+ except Exception as e:
175
+ logger.error(f"Error conectando a MongoDB: {str(e)}")
176
+ return False
177
+
178
+ ###################################################################
179
+ def initialize_database_connections():
180
+ """Inicializa todas las conexiones"""
181
+ return initialize_cosmos_sql_connection() and initialize_mongodb_connection()
182
+
183
+ ###################################################################
184
+ def get_mongodb():
185
+ """Obtiene la conexión MongoDB"""
186
+ if mongo_db is None:
187
+ initialize_mongodb_connection()
188
  return mongo_db
modules/database/discourse_mongo_db.py CHANGED
@@ -1,173 +1,173 @@
1
- # modules/database/discourse_mongo_db.py
2
- # Importaciones estándar
3
- import io
4
- import base64
5
- from datetime import datetime, timezone
6
- import logging
7
-
8
- # Importaciones de terceros
9
- import matplotlib.pyplot as plt
10
-
11
- from .mongo_db import (
12
- get_collection,
13
- insert_document,
14
- find_documents,
15
- update_document,
16
- delete_document
17
- )
18
-
19
- # Configuración del logger
20
- logger = logging.getLogger(__name__)
21
- COLLECTION_NAME = 'student_discourse_analysis'
22
-
23
- ########################################################################
24
- def store_student_discourse_result(username, text1, text2, analysis_result):
25
- """
26
- Guarda el resultado del análisis de discurso comparativo en MongoDB.
27
- """
28
- try:
29
- # Los gráficos ya vienen en bytes, solo necesitamos codificar a base64
30
- graph1_data = None
31
- graph2_data = None
32
- combined_graph_data = None
33
-
34
- if 'graph1' in analysis_result and analysis_result['graph1'] is not None:
35
- try:
36
- graph1_data = base64.b64encode(analysis_result['graph1']).decode('utf-8')
37
- except Exception as e:
38
- logger.error(f"Error al codificar gráfico 1: {str(e)}")
39
-
40
- if 'graph2' in analysis_result and analysis_result['graph2'] is not None:
41
- try:
42
- graph2_data = base64.b64encode(analysis_result['graph2']).decode('utf-8')
43
- except Exception as e:
44
- logger.error(f"Error al codificar gráfico 2: {str(e)}")
45
-
46
- if 'combined_graph' in analysis_result and analysis_result['combined_graph'] is not None:
47
- try:
48
- combined_graph_data = base64.b64encode(analysis_result['combined_graph']).decode('utf-8')
49
- except Exception as e:
50
- logger.error(f"Error al codificar gráfico combinado: {str(e)}")
51
-
52
- # Crear documento para MongoDB
53
- analysis_document = {
54
- 'username': username,
55
- 'timestamp': datetime.now(timezone.utc).isoformat(),
56
- 'text1': text1,
57
- 'text2': text2,
58
- 'analysis_type': 'discourse',
59
- 'key_concepts1': analysis_result.get('key_concepts1', []),
60
- 'key_concepts2': analysis_result.get('key_concepts2', []),
61
- 'graph1': graph1_data,
62
- 'graph2': graph2_data,
63
- 'combined_graph': combined_graph_data
64
- }
65
-
66
- # Insertar en MongoDB
67
- result = insert_document(COLLECTION_NAME, analysis_document)
68
- if result:
69
- logger.info(f"Análisis del discurso guardado con ID: {result} para el usuario: {username}")
70
- return True
71
-
72
- logger.error("No se pudo insertar el documento en MongoDB")
73
- return False
74
-
75
- except Exception as e:
76
- logger.error(f"Error al guardar el análisis del discurso: {str(e)}")
77
- return False
78
-
79
-
80
-
81
- #################################################################################
82
- def get_student_discourse_analysis(username, limit=10):
83
- """
84
- Recupera los análisis del discurso de un estudiante.
85
- """
86
- try:
87
- # Obtener la colección
88
- collection = get_collection(COLLECTION_NAME)
89
- if collection is None: # Cambiado de if not collection a if collection is None
90
- logger.error("No se pudo obtener la colección discourse")
91
- return []
92
-
93
- # Consulta
94
- query = {
95
- "username": username,
96
- "analysis_type": "discourse"
97
- }
98
-
99
- # Campos a recuperar
100
- projection = {
101
- "timestamp": 1,
102
- "combined_graph": 1,
103
- "_id": 1
104
- }
105
-
106
- # Ejecutar consulta
107
- try:
108
- cursor = collection.find(query, projection).sort("timestamp", -1)
109
- if limit:
110
- cursor = cursor.limit(limit)
111
-
112
- # Convertir cursor a lista
113
- results = list(cursor)
114
- logger.info(f"Recuperados {len(results)} análisis del discurso para {username}")
115
- return results
116
-
117
- except Exception as db_error:
118
- logger.error(f"Error en la consulta a MongoDB: {str(db_error)}")
119
- return []
120
-
121
- except Exception as e:
122
- logger.error(f"Error recuperando análisis del discurso: {str(e)}")
123
- return []
124
- #####################################################################################
125
-
126
- def get_student_discourse_data(username):
127
- """
128
- Obtiene un resumen de los análisis del discurso de un estudiante.
129
- """
130
- try:
131
- analyses = get_student_discourse_analysis(username, limit=None)
132
- formatted_analyses = []
133
-
134
- for analysis in analyses:
135
- formatted_analysis = {
136
- 'timestamp': analysis['timestamp'],
137
- 'text1': analysis.get('text1', ''),
138
- 'text2': analysis.get('text2', ''),
139
- 'key_concepts1': analysis.get('key_concepts1', []),
140
- 'key_concepts2': analysis.get('key_concepts2', [])
141
- }
142
- formatted_analyses.append(formatted_analysis)
143
-
144
- return {'entries': formatted_analyses}
145
-
146
- except Exception as e:
147
- logger.error(f"Error al obtener datos del discurso: {str(e)}")
148
- return {'entries': []}
149
-
150
- ###########################################################################
151
- def update_student_discourse_analysis(analysis_id, update_data):
152
- """
153
- Actualiza un análisis del discurso existente.
154
- """
155
- try:
156
- query = {"_id": analysis_id}
157
- update = {"$set": update_data}
158
- return update_document(COLLECTION_NAME, query, update)
159
- except Exception as e:
160
- logger.error(f"Error al actualizar análisis del discurso: {str(e)}")
161
- return False
162
-
163
- ###########################################################################
164
- def delete_student_discourse_analysis(analysis_id):
165
- """
166
- Elimina un análisis del discurso.
167
- """
168
- try:
169
- query = {"_id": analysis_id}
170
- return delete_document(COLLECTION_NAME, query)
171
- except Exception as e:
172
- logger.error(f"Error al eliminar análisis del discurso: {str(e)}")
173
  return False
 
1
+ # modules/database/discourse_mongo_db.py
2
+ # Importaciones estándar
3
+ import io
4
+ import base64
5
+ from datetime import datetime, timezone
6
+ import logging
7
+
8
+ # Importaciones de terceros
9
+ import matplotlib.pyplot as plt
10
+
11
+ from .mongo_db import (
12
+ get_collection,
13
+ insert_document,
14
+ find_documents,
15
+ update_document,
16
+ delete_document
17
+ )
18
+
19
+ # Configuración del logger
20
+ logger = logging.getLogger(__name__)
21
+ COLLECTION_NAME = 'student_discourse_analysis'
22
+
23
+ ########################################################################
24
+ def store_student_discourse_result(username, text1, text2, analysis_result):
25
+ """
26
+ Guarda el resultado del análisis de discurso comparativo en MongoDB.
27
+ """
28
+ try:
29
+ # Los gráficos ya vienen en bytes, solo necesitamos codificar a base64
30
+ graph1_data = None
31
+ graph2_data = None
32
+ combined_graph_data = None
33
+
34
+ if 'graph1' in analysis_result and analysis_result['graph1'] is not None:
35
+ try:
36
+ graph1_data = base64.b64encode(analysis_result['graph1']).decode('utf-8')
37
+ except Exception as e:
38
+ logger.error(f"Error al codificar gráfico 1: {str(e)}")
39
+
40
+ if 'graph2' in analysis_result and analysis_result['graph2'] is not None:
41
+ try:
42
+ graph2_data = base64.b64encode(analysis_result['graph2']).decode('utf-8')
43
+ except Exception as e:
44
+ logger.error(f"Error al codificar gráfico 2: {str(e)}")
45
+
46
+ if 'combined_graph' in analysis_result and analysis_result['combined_graph'] is not None:
47
+ try:
48
+ combined_graph_data = base64.b64encode(analysis_result['combined_graph']).decode('utf-8')
49
+ except Exception as e:
50
+ logger.error(f"Error al codificar gráfico combinado: {str(e)}")
51
+
52
+ # Crear documento para MongoDB
53
+ analysis_document = {
54
+ 'username': username,
55
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
56
+ 'text1': text1,
57
+ 'text2': text2,
58
+ 'analysis_type': 'discourse',
59
+ 'key_concepts1': analysis_result.get('key_concepts1', []),
60
+ 'key_concepts2': analysis_result.get('key_concepts2', []),
61
+ 'graph1': graph1_data,
62
+ 'graph2': graph2_data,
63
+ 'combined_graph': combined_graph_data
64
+ }
65
+
66
+ # Insertar en MongoDB
67
+ result = insert_document(COLLECTION_NAME, analysis_document)
68
+ if result:
69
+ logger.info(f"Análisis del discurso guardado con ID: {result} para el usuario: {username}")
70
+ return True
71
+
72
+ logger.error("No se pudo insertar el documento en MongoDB")
73
+ return False
74
+
75
+ except Exception as e:
76
+ logger.error(f"Error al guardar el análisis del discurso: {str(e)}")
77
+ return False
78
+
79
+
80
+
81
+ #################################################################################
82
+ def get_student_discourse_analysis(username, limit=10):
83
+ """
84
+ Recupera los análisis del discurso de un estudiante.
85
+ """
86
+ try:
87
+ # Obtener la colección
88
+ collection = get_collection(COLLECTION_NAME)
89
+ if collection is None: # Cambiado de if not collection a if collection is None
90
+ logger.error("No se pudo obtener la colección discourse")
91
+ return []
92
+
93
+ # Consulta
94
+ query = {
95
+ "username": username,
96
+ "analysis_type": "discourse"
97
+ }
98
+
99
+ # Campos a recuperar
100
+ projection = {
101
+ "timestamp": 1,
102
+ "combined_graph": 1,
103
+ "_id": 1
104
+ }
105
+
106
+ # Ejecutar consulta
107
+ try:
108
+ cursor = collection.find(query, projection).sort("timestamp", -1)
109
+ if limit:
110
+ cursor = cursor.limit(limit)
111
+
112
+ # Convertir cursor a lista
113
+ results = list(cursor)
114
+ logger.info(f"Recuperados {len(results)} análisis del discurso para {username}")
115
+ return results
116
+
117
+ except Exception as db_error:
118
+ logger.error(f"Error en la consulta a MongoDB: {str(db_error)}")
119
+ return []
120
+
121
+ except Exception as e:
122
+ logger.error(f"Error recuperando análisis del discurso: {str(e)}")
123
+ return []
124
+ #####################################################################################
125
+
126
+ def get_student_discourse_data(username):
127
+ """
128
+ Obtiene un resumen de los análisis del discurso de un estudiante.
129
+ """
130
+ try:
131
+ analyses = get_student_discourse_analysis(username, limit=None)
132
+ formatted_analyses = []
133
+
134
+ for analysis in analyses:
135
+ formatted_analysis = {
136
+ 'timestamp': analysis['timestamp'],
137
+ 'text1': analysis.get('text1', ''),
138
+ 'text2': analysis.get('text2', ''),
139
+ 'key_concepts1': analysis.get('key_concepts1', []),
140
+ 'key_concepts2': analysis.get('key_concepts2', [])
141
+ }
142
+ formatted_analyses.append(formatted_analysis)
143
+
144
+ return {'entries': formatted_analyses}
145
+
146
+ except Exception as e:
147
+ logger.error(f"Error al obtener datos del discurso: {str(e)}")
148
+ return {'entries': []}
149
+
150
+ ###########################################################################
151
+ def update_student_discourse_analysis(analysis_id, update_data):
152
+ """
153
+ Actualiza un análisis del discurso existente.
154
+ """
155
+ try:
156
+ query = {"_id": analysis_id}
157
+ update = {"$set": update_data}
158
+ return update_document(COLLECTION_NAME, query, update)
159
+ except Exception as e:
160
+ logger.error(f"Error al actualizar análisis del discurso: {str(e)}")
161
+ return False
162
+
163
+ ###########################################################################
164
+ def delete_student_discourse_analysis(analysis_id):
165
+ """
166
+ Elimina un análisis del discurso.
167
+ """
168
+ try:
169
+ query = {"_id": analysis_id}
170
+ return delete_document(COLLECTION_NAME, query)
171
+ except Exception as e:
172
+ logger.error(f"Error al eliminar análisis del discurso: {str(e)}")
173
  return False
modules/database/mongo_db.py CHANGED
@@ -1,63 +1,62 @@
1
- #modules/database/mongo_db.py
2
- from .database_init import get_mongodb
3
- import logging
4
-
5
- logger = logging.getLogger(__name__)
6
-
7
- def get_collection(collection_name):
8
- try:
9
- db = get_mongodb()
10
- if db is None:
11
- logger.error(f"No se pudo obtener la base de datos para {collection_name}")
12
- return None
13
-
14
- collection = db[collection_name]
15
- logger.info(f"Colección {collection_name} obtenida exitosamente")
16
- return collection
17
-
18
- except Exception as e:
19
- logger.error(f"Error al obtener colección {collection_name}: {str(e)}")
20
- return None
21
-
22
- def insert_document(collection_name, document):
23
- collection = get_collection(collection_name)
24
- try:
25
- result = collection.insert_one(document)
26
- logger.info(f"Documento insertado en {collection_name} con ID: {result.inserted_id}")
27
- return result.inserted_id
28
- except Exception as e:
29
- logger.error(f"Error al insertar documento en {collection_name}: {str(e)}")
30
- return None
31
-
32
- def find_documents(collection_name, query, sort=None, limit=None):
33
- collection = get_collection(collection_name)
34
- try:
35
- cursor = collection.find(query)
36
- if sort:
37
- cursor = cursor.sort(sort)
38
- if limit:
39
- cursor = cursor.limit(limit)
40
- return list(cursor)
41
- except Exception as e:
42
- logger.error(f"Error al buscar documentos en {collection_name}: {str(e)}")
43
- return []
44
-
45
- def update_document(collection_name, query, update):
46
- collection = get_collection(collection_name)
47
- try:
48
- result = collection.update_one(query, update)
49
- logger.info(f"Documento actualizado en {collection_name}: {result.modified_count} modificado(s)")
50
- return result.modified_count
51
- except Exception as e:
52
- logger.error(f"Error al actualizar documento en {collection_name}: {str(e)}")
53
- return 0
54
-
55
- def delete_document(collection_name, query):
56
- collection = get_collection(collection_name)
57
- try:
58
- result = collection.delete_one(query)
59
- logger.info(f"Documento eliminado de {collection_name}: {result.deleted_count} eliminado(s)")
60
- return result.deleted_count
61
- except Exception as e:
62
- logger.error(f"Error al eliminar documento de {collection_name}: {str(e)}")
63
  return 0
 
1
+ from .database_init import get_mongodb
2
+ import logging
3
+
4
+ logger = logging.getLogger(__name__)
5
+
6
+ def get_collection(collection_name):
7
+ try:
8
+ db = get_mongodb()
9
+ if db is None:
10
+ logger.error(f"No se pudo obtener la base de datos para {collection_name}")
11
+ return None
12
+
13
+ collection = db[collection_name]
14
+ logger.info(f"Colección {collection_name} obtenida exitosamente")
15
+ return collection
16
+
17
+ except Exception as e:
18
+ logger.error(f"Error al obtener colección {collection_name}: {str(e)}")
19
+ return None
20
+
21
+ def insert_document(collection_name, document):
22
+ collection = get_collection(collection_name)
23
+ try:
24
+ result = collection.insert_one(document)
25
+ logger.info(f"Documento insertado en {collection_name} con ID: {result.inserted_id}")
26
+ return result.inserted_id
27
+ except Exception as e:
28
+ logger.error(f"Error al insertar documento en {collection_name}: {str(e)}")
29
+ return None
30
+
31
+ def find_documents(collection_name, query, sort=None, limit=None):
32
+ collection = get_collection(collection_name)
33
+ try:
34
+ cursor = collection.find(query)
35
+ if sort:
36
+ cursor = cursor.sort(sort)
37
+ if limit:
38
+ cursor = cursor.limit(limit)
39
+ return list(cursor)
40
+ except Exception as e:
41
+ logger.error(f"Error al buscar documentos en {collection_name}: {str(e)}")
42
+ return []
43
+
44
+ def update_document(collection_name, query, update):
45
+ collection = get_collection(collection_name)
46
+ try:
47
+ result = collection.update_one(query, update)
48
+ logger.info(f"Documento actualizado en {collection_name}: {result.modified_count} modificado(s)")
49
+ return result.modified_count
50
+ except Exception as e:
51
+ logger.error(f"Error al actualizar documento en {collection_name}: {str(e)}")
52
+ return 0
53
+
54
+ def delete_document(collection_name, query):
55
+ collection = get_collection(collection_name)
56
+ try:
57
+ result = collection.delete_one(query)
58
+ logger.info(f"Documento eliminado de {collection_name}: {result.deleted_count} eliminado(s)")
59
+ return result.deleted_count
60
+ except Exception as e:
61
+ logger.error(f"Error al eliminar documento de {collection_name}: {str(e)}")
 
62
  return 0
modules/database/morphosyntax_iterative_mongo_db.py CHANGED
@@ -1,171 +1,171 @@
1
- # modules/database/morphosyntax_iterative_mongo_db.py
2
-
3
-
4
- from datetime import datetime, timezone
5
- import logging
6
- from bson import ObjectId # <--- Importar ObjectId
7
- from .mongo_db import get_collection, insert_document, find_documents, update_document, delete_document
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
- BASE_COLLECTION = 'student_morphosyntax_analysis_base'
12
- ITERATION_COLLECTION = 'student_morphosyntax_iterations'
13
-
14
- def store_student_morphosyntax_base(username, text, arc_diagrams):
15
- """Almacena el análisis morfosintáctico base y retorna su ObjectId."""
16
- try:
17
- base_document = {
18
- 'username': username,
19
- 'timestamp': datetime.now(timezone.utc).isoformat(),
20
- 'text': text,
21
- 'arc_diagrams': arc_diagrams,
22
- 'analysis_type': 'morphosyntax_base',
23
- 'has_iterations': False
24
- }
25
- collection = get_collection(BASE_COLLECTION)
26
- result = collection.insert_one(base_document)
27
-
28
- logger.info(f"Análisis base guardado para {username}")
29
- # Retornamos el ObjectId directamente (NO str)
30
- return result.inserted_id
31
-
32
- except Exception as e:
33
- logger.error(f"Error almacenando análisis base: {str(e)}")
34
- return None
35
-
36
- def store_student_morphosyntax_iteration(username, base_id, original_text, iteration_text, arc_diagrams):
37
- """
38
- Almacena una iteración de análisis morfosintáctico.
39
- base_id: ObjectId de la base (o string convertible a ObjectId).
40
- """
41
- try:
42
- # Convertir a ObjectId si viene como string
43
- if isinstance(base_id, str):
44
- base_id = ObjectId(base_id)
45
-
46
- iteration_document = {
47
- 'username': username,
48
- 'base_id': base_id, # Guardar el ObjectId en la iteración
49
- 'timestamp': datetime.now(timezone.utc).isoformat(),
50
- 'original_text': original_text,
51
- 'iteration_text': iteration_text,
52
- 'arc_diagrams': arc_diagrams,
53
- 'analysis_type': 'morphosyntax_iteration'
54
- }
55
- collection = get_collection(ITERATION_COLLECTION)
56
- result = collection.insert_one(iteration_document)
57
-
58
- # Actualizar documento base (usando ObjectId)
59
- base_collection = get_collection(BASE_COLLECTION)
60
- base_collection.update_one(
61
- {'_id': base_id, 'username': username},
62
- {'$set': {'has_iterations': True}}
63
- )
64
-
65
- logger.info(f"Iteración guardada para {username}, base_id: {base_id}")
66
- return result.inserted_id # Retornar el ObjectId de la iteración
67
-
68
- except Exception as e:
69
- logger.error(f"Error almacenando iteración: {str(e)}")
70
- return None
71
-
72
- def get_student_morphosyntax_analysis(username, limit=10):
73
- """
74
- Obtiene los análisis base y sus iteraciones.
75
- Returns: Lista de análisis con sus iteraciones.
76
- """
77
- try:
78
- base_collection = get_collection(BASE_COLLECTION)
79
- base_query = {
80
- "username": username,
81
- "analysis_type": "morphosyntax_base"
82
- }
83
- base_analyses = list(
84
- base_collection.find(base_query).sort("timestamp", -1).limit(limit)
85
- )
86
-
87
- # Para cada análisis base, obtener sus iteraciones
88
- iteration_collection = get_collection(ITERATION_COLLECTION)
89
- for analysis in base_analyses:
90
- base_id = analysis['_id']
91
- # Buscar iteraciones con base_id = ObjectId
92
- iterations = list(
93
- iteration_collection.find({"base_id": base_id}).sort("timestamp", -1)
94
- )
95
- analysis['iterations'] = iterations
96
-
97
- return base_analyses
98
-
99
- except Exception as e:
100
- logger.error(f"Error obteniendo análisis: {str(e)}")
101
- return []
102
-
103
- def update_student_morphosyntax_analysis(analysis_id, is_base, update_data):
104
- """
105
- Actualiza un análisis base o iteración.
106
- analysis_id puede ser un ObjectId o string.
107
- """
108
- from bson import ObjectId
109
-
110
- try:
111
- collection_name = BASE_COLLECTION if is_base else ITERATION_COLLECTION
112
- collection = get_collection(collection_name)
113
-
114
- if isinstance(analysis_id, str):
115
- analysis_id = ObjectId(analysis_id)
116
-
117
- query = {"_id": analysis_id}
118
- update = {"$set": update_data}
119
-
120
- result = update_document(collection_name, query, update)
121
- return result
122
-
123
- except Exception as e:
124
- logger.error(f"Error actualizando análisis: {str(e)}")
125
- return False
126
-
127
- def delete_student_morphosyntax_analysis(analysis_id, is_base):
128
- """
129
- Elimina un análisis base o iteración.
130
- Si es base, también elimina todas sus iteraciones.
131
- """
132
- from bson import ObjectId
133
-
134
- try:
135
- if isinstance(analysis_id, str):
136
- analysis_id = ObjectId(analysis_id)
137
-
138
- if is_base:
139
- # Eliminar iteraciones vinculadas
140
- iteration_collection = get_collection(ITERATION_COLLECTION)
141
- iteration_collection.delete_many({"base_id": analysis_id})
142
-
143
- # Luego eliminar el análisis base
144
- collection = get_collection(BASE_COLLECTION)
145
- else:
146
- collection = get_collection(ITERATION_COLLECTION)
147
-
148
- query = {"_id": analysis_id}
149
- result = delete_document(collection.name, query)
150
- return result
151
-
152
- except Exception as e:
153
- logger.error(f"Error eliminando análisis: {str(e)}")
154
- return False
155
-
156
- def get_student_morphosyntax_data(username):
157
- """
158
- Obtiene todos los datos de análisis morfosintáctico de un estudiante.
159
- Returns: Diccionario con todos los análisis y sus iteraciones.
160
- """
161
- try:
162
- analyses = get_student_morphosyntax_analysis(username, limit=None)
163
- return {
164
- 'entries': analyses,
165
- 'total_analyses': len(analyses),
166
- 'has_iterations': any(a.get('has_iterations', False) for a in analyses)
167
- }
168
-
169
- except Exception as e:
170
- logger.error(f"Error obteniendo datos del estudiante: {str(e)}")
171
- return {'entries': [], 'total_analyses': 0, 'has_iterations': False}
 
1
+ # modules/database/morphosyntax_iterative_mongo_db.py
2
+
3
+
4
+ from datetime import datetime, timezone
5
+ import logging
6
+ from bson import ObjectId # <--- Importar ObjectId
7
+ from .mongo_db import get_collection, insert_document, find_documents, update_document, delete_document
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ BASE_COLLECTION = 'student_morphosyntax_analysis_base'
12
+ ITERATION_COLLECTION = 'student_morphosyntax_iterations'
13
+
14
+ def store_student_morphosyntax_base(username, text, arc_diagrams):
15
+ """Almacena el análisis morfosintáctico base y retorna su ObjectId."""
16
+ try:
17
+ base_document = {
18
+ 'username': username,
19
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
20
+ 'text': text,
21
+ 'arc_diagrams': arc_diagrams,
22
+ 'analysis_type': 'morphosyntax_base',
23
+ 'has_iterations': False
24
+ }
25
+ collection = get_collection(BASE_COLLECTION)
26
+ result = collection.insert_one(base_document)
27
+
28
+ logger.info(f"Análisis base guardado para {username}")
29
+ # Retornamos el ObjectId directamente (NO str)
30
+ return result.inserted_id
31
+
32
+ except Exception as e:
33
+ logger.error(f"Error almacenando análisis base: {str(e)}")
34
+ return None
35
+
36
+ def store_student_morphosyntax_iteration(username, base_id, original_text, iteration_text, arc_diagrams):
37
+ """
38
+ Almacena una iteración de análisis morfosintáctico.
39
+ base_id: ObjectId de la base (o string convertible a ObjectId).
40
+ """
41
+ try:
42
+ # Convertir a ObjectId si viene como string
43
+ if isinstance(base_id, str):
44
+ base_id = ObjectId(base_id)
45
+
46
+ iteration_document = {
47
+ 'username': username,
48
+ 'base_id': base_id, # Guardar el ObjectId en la iteración
49
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
50
+ 'original_text': original_text,
51
+ 'iteration_text': iteration_text,
52
+ 'arc_diagrams': arc_diagrams,
53
+ 'analysis_type': 'morphosyntax_iteration'
54
+ }
55
+ collection = get_collection(ITERATION_COLLECTION)
56
+ result = collection.insert_one(iteration_document)
57
+
58
+ # Actualizar documento base (usando ObjectId)
59
+ base_collection = get_collection(BASE_COLLECTION)
60
+ base_collection.update_one(
61
+ {'_id': base_id, 'username': username},
62
+ {'$set': {'has_iterations': True}}
63
+ )
64
+
65
+ logger.info(f"Iteración guardada para {username}, base_id: {base_id}")
66
+ return result.inserted_id # Retornar el ObjectId de la iteración
67
+
68
+ except Exception as e:
69
+ logger.error(f"Error almacenando iteración: {str(e)}")
70
+ return None
71
+
72
+ def get_student_morphosyntax_analysis(username, limit=10):
73
+ """
74
+ Obtiene los análisis base y sus iteraciones.
75
+ Returns: Lista de análisis con sus iteraciones.
76
+ """
77
+ try:
78
+ base_collection = get_collection(BASE_COLLECTION)
79
+ base_query = {
80
+ "username": username,
81
+ "analysis_type": "morphosyntax_base"
82
+ }
83
+ base_analyses = list(
84
+ base_collection.find(base_query).sort("timestamp", -1).limit(limit)
85
+ )
86
+
87
+ # Para cada análisis base, obtener sus iteraciones
88
+ iteration_collection = get_collection(ITERATION_COLLECTION)
89
+ for analysis in base_analyses:
90
+ base_id = analysis['_id']
91
+ # Buscar iteraciones con base_id = ObjectId
92
+ iterations = list(
93
+ iteration_collection.find({"base_id": base_id}).sort("timestamp", -1)
94
+ )
95
+ analysis['iterations'] = iterations
96
+
97
+ return base_analyses
98
+
99
+ except Exception as e:
100
+ logger.error(f"Error obteniendo análisis: {str(e)}")
101
+ return []
102
+
103
+ def update_student_morphosyntax_analysis(analysis_id, is_base, update_data):
104
+ """
105
+ Actualiza un análisis base o iteración.
106
+ analysis_id puede ser un ObjectId o string.
107
+ """
108
+ from bson import ObjectId
109
+
110
+ try:
111
+ collection_name = BASE_COLLECTION if is_base else ITERATION_COLLECTION
112
+ collection = get_collection(collection_name)
113
+
114
+ if isinstance(analysis_id, str):
115
+ analysis_id = ObjectId(analysis_id)
116
+
117
+ query = {"_id": analysis_id}
118
+ update = {"$set": update_data}
119
+
120
+ result = update_document(collection_name, query, update)
121
+ return result
122
+
123
+ except Exception as e:
124
+ logger.error(f"Error actualizando análisis: {str(e)}")
125
+ return False
126
+
127
+ def delete_student_morphosyntax_analysis(analysis_id, is_base):
128
+ """
129
+ Elimina un análisis base o iteración.
130
+ Si es base, también elimina todas sus iteraciones.
131
+ """
132
+ from bson import ObjectId
133
+
134
+ try:
135
+ if isinstance(analysis_id, str):
136
+ analysis_id = ObjectId(analysis_id)
137
+
138
+ if is_base:
139
+ # Eliminar iteraciones vinculadas
140
+ iteration_collection = get_collection(ITERATION_COLLECTION)
141
+ iteration_collection.delete_many({"base_id": analysis_id})
142
+
143
+ # Luego eliminar el análisis base
144
+ collection = get_collection(BASE_COLLECTION)
145
+ else:
146
+ collection = get_collection(ITERATION_COLLECTION)
147
+
148
+ query = {"_id": analysis_id}
149
+ result = delete_document(collection.name, query)
150
+ return result
151
+
152
+ except Exception as e:
153
+ logger.error(f"Error eliminando análisis: {str(e)}")
154
+ return False
155
+
156
+ def get_student_morphosyntax_data(username):
157
+ """
158
+ Obtiene todos los datos de análisis morfosintáctico de un estudiante.
159
+ Returns: Diccionario con todos los análisis y sus iteraciones.
160
+ """
161
+ try:
162
+ analyses = get_student_morphosyntax_analysis(username, limit=None)
163
+ return {
164
+ 'entries': analyses,
165
+ 'total_analyses': len(analyses),
166
+ 'has_iterations': any(a.get('has_iterations', False) for a in analyses)
167
+ }
168
+
169
+ except Exception as e:
170
+ logger.error(f"Error obteniendo datos del estudiante: {str(e)}")
171
+ return {'entries': [], 'total_analyses': 0, 'has_iterations': False}
modules/database/semantic_export.py CHANGED
@@ -1,78 +1,78 @@
1
- from io import BytesIO
2
- from reportlab.lib import colors
3
- from reportlab.lib.pagesizes import letter
4
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, PageBreak
5
- from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
6
- from reportlab.lib.units import cm
7
- from svglib.svglib import svg2rlg
8
- from reportlab.graphics import renderPM
9
- import base64
10
- import cairosvg
11
- from reportlab.graphics import renderPDF
12
- from reportlab.lib.utils import ImageReader
13
-
14
- #importaciones locales
15
- from .semantic_mongo_db import get_student_semantic_data
16
- from .chat_db import get_chat_history
17
-
18
- # Placeholder para el logo
19
- LOGO_PATH = "assets\img\logo_92x92.png" # Reemplaza esto con la ruta real de tu logo
20
-
21
- # Definir el tamaño de página carta manualmente (612 x 792 puntos)
22
- LETTER_SIZE = (612, 792)
23
-
24
- def add_logo(canvas, doc):
25
- logo = Image(LOGO_PATH, width=2*cm, height=2*cm)
26
- logo.drawOn(canvas, doc.leftMargin, doc.height + doc.topMargin - 0.5*cm)
27
-
28
- def export_user_interactions(username, analysis_type):
29
- # Obtener historial de chat (que ahora incluye los análisis morfosintácticos)
30
- chat_history = get_chat_history(username, analysis_type)
31
-
32
- # Crear un PDF
33
- buffer = BytesIO()
34
- doc = SimpleDocTemplate(
35
- buffer,
36
- pagesize=letter,
37
- rightMargin=2*cm,
38
- leftMargin=2*cm,
39
- topMargin=2*cm,
40
- bottomMargin=2*cm
41
- )
42
-
43
- story = []
44
- styles = getSampleStyleSheet()
45
-
46
- # Título
47
- story.append(Paragraph(f"Interacciones de {username} - Análisis {analysis_type}", styles['Title']))
48
- story.append(Spacer(1, 0.5*cm))
49
-
50
- # Historial del chat y análisis
51
- for entry in chat_history:
52
- for message in entry['messages']:
53
- role = message['role']
54
- content = message['content']
55
- story.append(Paragraph(f"<b>{role.capitalize()}:</b> {content}", styles['BodyText']))
56
- story.append(Spacer(1, 0.25*cm))
57
-
58
- # Si hay visualizaciones (diagramas SVG), convertirlas a imagen y añadirlas
59
- if 'visualizations' in message and message['visualizations']:
60
- for svg in message['visualizations']:
61
- drawing = svg2rlg(BytesIO(svg.encode('utf-8')))
62
- img_data = BytesIO()
63
- renderPM.drawToFile(drawing, img_data, fmt="PNG")
64
- img_data.seek(0)
65
- img = Image(img_data, width=15*cm, height=7.5*cm)
66
- story.append(img)
67
- story.append(Spacer(1, 0.5*cm))
68
-
69
- story.append(PageBreak())
70
-
71
- # Construir el PDF
72
- doc.build(story)
73
- buffer.seek(0)
74
- return buffer
75
-
76
- # Uso en Streamlit:
77
- # pdf_buffer = export_user_interactions(username, 'morphosyntax')
78
  # st.download_button(label="Descargar PDF", data=pdf_buffer, file_name="interacciones.pdf", mime="application/pdf")
 
1
+ from io import BytesIO
2
+ from reportlab.lib import colors
3
+ from reportlab.lib.pagesizes import letter
4
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, PageBreak
5
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
6
+ from reportlab.lib.units import cm
7
+ from svglib.svglib import svg2rlg
8
+ from reportlab.graphics import renderPM
9
+ import base64
10
+ import cairosvg
11
+ from reportlab.graphics import renderPDF
12
+ from reportlab.lib.utils import ImageReader
13
+
14
+ #importaciones locales
15
+ from .semantic_mongo_db import get_student_semantic_data
16
+ from .chat_db import get_chat_history
17
+
18
+ # Placeholder para el logo
19
+ LOGO_PATH = "assets\img\logo_92x92.png" # Reemplaza esto con la ruta real de tu logo
20
+
21
+ # Definir el tamaño de página carta manualmente (612 x 792 puntos)
22
+ LETTER_SIZE = (612, 792)
23
+
24
+ def add_logo(canvas, doc):
25
+ logo = Image(LOGO_PATH, width=2*cm, height=2*cm)
26
+ logo.drawOn(canvas, doc.leftMargin, doc.height + doc.topMargin - 0.5*cm)
27
+
28
+ def export_user_interactions(username, analysis_type):
29
+ # Obtener historial de chat (que ahora incluye los análisis morfosintácticos)
30
+ chat_history = get_chat_history(username, analysis_type)
31
+
32
+ # Crear un PDF
33
+ buffer = BytesIO()
34
+ doc = SimpleDocTemplate(
35
+ buffer,
36
+ pagesize=letter,
37
+ rightMargin=2*cm,
38
+ leftMargin=2*cm,
39
+ topMargin=2*cm,
40
+ bottomMargin=2*cm
41
+ )
42
+
43
+ story = []
44
+ styles = getSampleStyleSheet()
45
+
46
+ # Título
47
+ story.append(Paragraph(f"Interacciones de {username} - Análisis {analysis_type}", styles['Title']))
48
+ story.append(Spacer(1, 0.5*cm))
49
+
50
+ # Historial del chat y análisis
51
+ for entry in chat_history:
52
+ for message in entry['messages']:
53
+ role = message['role']
54
+ content = message['content']
55
+ story.append(Paragraph(f"<b>{role.capitalize()}:</b> {content}", styles['BodyText']))
56
+ story.append(Spacer(1, 0.25*cm))
57
+
58
+ # Si hay visualizaciones (diagramas SVG), convertirlas a imagen y añadirlas
59
+ if 'visualizations' in message and message['visualizations']:
60
+ for svg in message['visualizations']:
61
+ drawing = svg2rlg(BytesIO(svg.encode('utf-8')))
62
+ img_data = BytesIO()
63
+ renderPM.drawToFile(drawing, img_data, fmt="PNG")
64
+ img_data.seek(0)
65
+ img = Image(img_data, width=15*cm, height=7.5*cm)
66
+ story.append(img)
67
+ story.append(Spacer(1, 0.5*cm))
68
+
69
+ story.append(PageBreak())
70
+
71
+ # Construir el PDF
72
+ doc.build(story)
73
+ buffer.seek(0)
74
+ return buffer
75
+
76
+ # Uso en Streamlit:
77
+ # pdf_buffer = export_user_interactions(username, 'morphosyntax')
78
  # st.download_button(label="Descargar PDF", data=pdf_buffer, file_name="interacciones.pdf", mime="application/pdf")
modules/database/semantic_mongo_db.py CHANGED
@@ -1,160 +1,160 @@
1
- #/modules/database/semantic_mongo_db.py
2
-
3
- # Importaciones estándar
4
- import io
5
- import base64
6
- from datetime import datetime, timezone
7
- import logging
8
-
9
- # Importaciones de terceros
10
- import matplotlib.pyplot as plt
11
-
12
- # Importaciones locales
13
- from .mongo_db import (
14
- get_collection,
15
- insert_document,
16
- find_documents,
17
- update_document,
18
- delete_document
19
- )
20
-
21
- # Configuración del logger
22
- logger = logging.getLogger(__name__) # Cambiado de name a __name__
23
- COLLECTION_NAME = 'student_semantic_analysis'
24
-
25
- def store_student_semantic_result(username, text, analysis_result):
26
- """
27
- Guarda el resultado del análisis semántico en MongoDB.
28
- """
29
- try:
30
- # El gráfico ya viene en bytes, solo necesitamos codificarlo a base64
31
- concept_graph_data = None
32
- if 'concept_graph' in analysis_result and analysis_result['concept_graph'] is not None:
33
- try:
34
- # Ya está en bytes, solo codificar a base64
35
- concept_graph_data = base64.b64encode(analysis_result['concept_graph']).decode('utf-8')
36
- except Exception as e:
37
- logger.error(f"Error al codificar gráfico conceptual: {str(e)}")
38
-
39
- # Crear documento para MongoDB
40
- analysis_document = {
41
- 'username': username,
42
- 'timestamp': datetime.now(timezone.utc).isoformat(),
43
- 'text': text,
44
- 'analysis_type': 'semantic',
45
- 'key_concepts': analysis_result.get('key_concepts', []),
46
- 'concept_graph': concept_graph_data
47
- }
48
-
49
- # Insertar en MongoDB
50
- result = insert_document(COLLECTION_NAME, analysis_document)
51
- if result:
52
- logger.info(f"Análisis semántico guardado con ID: {result} para el usuario: {username}")
53
- return True
54
-
55
- logger.error("No se pudo insertar el documento en MongoDB")
56
- return False
57
-
58
- except Exception as e:
59
- logger.error(f"Error al guardar el análisis semántico: {str(e)}")
60
- return False
61
-
62
- ####################################################################################
63
- def get_student_semantic_analysis(username, limit=10):
64
- """
65
- Recupera los análisis semánticos de un estudiante.
66
- """
67
- try:
68
- # Obtener la colección
69
- collection = get_collection(COLLECTION_NAME)
70
- if collection is None: # Cambiado de if not collection a if collection is None
71
- logger.error("No se pudo obtener la colección semantic")
72
- return []
73
-
74
- # Consulta
75
- query = {
76
- "username": username,
77
- "analysis_type": "semantic"
78
- }
79
-
80
- # Campos a recuperar
81
- projection = {
82
- "timestamp": 1,
83
- "concept_graph": 1,
84
- "_id": 1
85
- }
86
-
87
- # Ejecutar consulta
88
- try:
89
- cursor = collection.find(query, projection).sort("timestamp", -1)
90
- if limit:
91
- cursor = cursor.limit(limit)
92
-
93
- # Convertir cursor a lista
94
- results = list(cursor)
95
- logger.info(f"Recuperados {len(results)} análisis semánticos para {username}")
96
- return results
97
-
98
- except Exception as db_error:
99
- logger.error(f"Error en la consulta a MongoDB: {str(db_error)}")
100
- return []
101
-
102
- except Exception as e:
103
- logger.error(f"Error recuperando análisis semántico: {str(e)}")
104
- return []
105
- ####################################################################################################
106
-
107
-
108
- def update_student_semantic_analysis(analysis_id, update_data):
109
- """
110
- Actualiza un análisis semántico existente.
111
- Args:
112
- analysis_id: ID del análisis a actualizar
113
- update_data: Datos a actualizar
114
- """
115
- query = {"_id": analysis_id}
116
- update = {"$set": update_data}
117
- return update_document(COLLECTION_NAME, query, update)
118
-
119
- def delete_student_semantic_analysis(analysis_id):
120
- """
121
- Elimina un análisis semántico.
122
- Args:
123
- analysis_id: ID del análisis a eliminar
124
- """
125
- query = {"_id": analysis_id}
126
- return delete_document(COLLECTION_NAME, query)
127
-
128
- def get_student_semantic_data(username):
129
- """
130
- Obtiene todos los análisis semánticos de un estudiante.
131
- Args:
132
- username: Nombre del usuario
133
- Returns:
134
- dict: Diccionario con todos los análisis del estudiante
135
- """
136
- analyses = get_student_semantic_analysis(username, limit=None)
137
-
138
- formatted_analyses = []
139
- for analysis in analyses:
140
- formatted_analysis = {
141
- 'timestamp': analysis['timestamp'],
142
- 'text': analysis['text'],
143
- 'key_concepts': analysis['key_concepts'],
144
- 'entities': analysis['entities']
145
- # No incluimos los gráficos en el resumen general
146
- }
147
- formatted_analyses.append(formatted_analysis)
148
-
149
- return {
150
- 'entries': formatted_analyses
151
- }
152
-
153
- # Exportar las funciones necesarias
154
- __all__ = [
155
- 'store_student_semantic_result',
156
- 'get_student_semantic_analysis',
157
- 'update_student_semantic_analysis',
158
- 'delete_student_semantic_analysis',
159
- 'get_student_semantic_data'
160
  ]
 
1
+ #/modules/database/semantic_mongo_db.py
2
+
3
+ # Importaciones estándar
4
+ import io
5
+ import base64
6
+ from datetime import datetime, timezone
7
+ import logging
8
+
9
+ # Importaciones de terceros
10
+ import matplotlib.pyplot as plt
11
+
12
+ # Importaciones locales
13
+ from .mongo_db import (
14
+ get_collection,
15
+ insert_document,
16
+ find_documents,
17
+ update_document,
18
+ delete_document
19
+ )
20
+
21
+ # Configuración del logger
22
+ logger = logging.getLogger(__name__) # Cambiado de name a __name__
23
+ COLLECTION_NAME = 'student_semantic_analysis'
24
+
25
+ def store_student_semantic_result(username, text, analysis_result):
26
+ """
27
+ Guarda el resultado del análisis semántico en MongoDB.
28
+ """
29
+ try:
30
+ # El gráfico ya viene en bytes, solo necesitamos codificarlo a base64
31
+ concept_graph_data = None
32
+ if 'concept_graph' in analysis_result and analysis_result['concept_graph'] is not None:
33
+ try:
34
+ # Ya está en bytes, solo codificar a base64
35
+ concept_graph_data = base64.b64encode(analysis_result['concept_graph']).decode('utf-8')
36
+ except Exception as e:
37
+ logger.error(f"Error al codificar gráfico conceptual: {str(e)}")
38
+
39
+ # Crear documento para MongoDB
40
+ analysis_document = {
41
+ 'username': username,
42
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
43
+ 'text': text,
44
+ 'analysis_type': 'semantic',
45
+ 'key_concepts': analysis_result.get('key_concepts', []),
46
+ 'concept_graph': concept_graph_data
47
+ }
48
+
49
+ # Insertar en MongoDB
50
+ result = insert_document(COLLECTION_NAME, analysis_document)
51
+ if result:
52
+ logger.info(f"Análisis semántico guardado con ID: {result} para el usuario: {username}")
53
+ return True
54
+
55
+ logger.error("No se pudo insertar el documento en MongoDB")
56
+ return False
57
+
58
+ except Exception as e:
59
+ logger.error(f"Error al guardar el análisis semántico: {str(e)}")
60
+ return False
61
+
62
+ ####################################################################################
63
+ def get_student_semantic_analysis(username, limit=10):
64
+ """
65
+ Recupera los análisis semánticos de un estudiante.
66
+ """
67
+ try:
68
+ # Obtener la colección
69
+ collection = get_collection(COLLECTION_NAME)
70
+ if collection is None: # Cambiado de if not collection a if collection is None
71
+ logger.error("No se pudo obtener la colección semantic")
72
+ return []
73
+
74
+ # Consulta
75
+ query = {
76
+ "username": username,
77
+ "analysis_type": "semantic"
78
+ }
79
+
80
+ # Campos a recuperar
81
+ projection = {
82
+ "timestamp": 1,
83
+ "concept_graph": 1,
84
+ "_id": 1
85
+ }
86
+
87
+ # Ejecutar consulta
88
+ try:
89
+ cursor = collection.find(query, projection).sort("timestamp", -1)
90
+ if limit:
91
+ cursor = cursor.limit(limit)
92
+
93
+ # Convertir cursor a lista
94
+ results = list(cursor)
95
+ logger.info(f"Recuperados {len(results)} análisis semánticos para {username}")
96
+ return results
97
+
98
+ except Exception as db_error:
99
+ logger.error(f"Error en la consulta a MongoDB: {str(db_error)}")
100
+ return []
101
+
102
+ except Exception as e:
103
+ logger.error(f"Error recuperando análisis semántico: {str(e)}")
104
+ return []
105
+ ####################################################################################################
106
+
107
+
108
+ def update_student_semantic_analysis(analysis_id, update_data):
109
+ """
110
+ Actualiza un análisis semántico existente.
111
+ Args:
112
+ analysis_id: ID del análisis a actualizar
113
+ update_data: Datos a actualizar
114
+ """
115
+ query = {"_id": analysis_id}
116
+ update = {"$set": update_data}
117
+ return update_document(COLLECTION_NAME, query, update)
118
+
119
+ def delete_student_semantic_analysis(analysis_id):
120
+ """
121
+ Elimina un análisis semántico.
122
+ Args:
123
+ analysis_id: ID del análisis a eliminar
124
+ """
125
+ query = {"_id": analysis_id}
126
+ return delete_document(COLLECTION_NAME, query)
127
+
128
+ def get_student_semantic_data(username):
129
+ """
130
+ Obtiene todos los análisis semánticos de un estudiante.
131
+ Args:
132
+ username: Nombre del usuario
133
+ Returns:
134
+ dict: Diccionario con todos los análisis del estudiante
135
+ """
136
+ analyses = get_student_semantic_analysis(username, limit=None)
137
+
138
+ formatted_analyses = []
139
+ for analysis in analyses:
140
+ formatted_analysis = {
141
+ 'timestamp': analysis['timestamp'],
142
+ 'text': analysis['text'],
143
+ 'key_concepts': analysis['key_concepts'],
144
+ 'entities': analysis['entities']
145
+ # No incluimos los gráficos en el resumen general
146
+ }
147
+ formatted_analyses.append(formatted_analysis)
148
+
149
+ return {
150
+ 'entries': formatted_analyses
151
+ }
152
+
153
+ # Exportar las funciones necesarias
154
+ __all__ = [
155
+ 'store_student_semantic_result',
156
+ 'get_student_semantic_analysis',
157
+ 'update_student_semantic_analysis',
158
+ 'delete_student_semantic_analysis',
159
+ 'get_student_semantic_data'
160
  ]
modules/database/sql_db.py CHANGED
@@ -1,323 +1,323 @@
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
- #########################################
12
- def get_user(username, role=None):
13
- container = get_container("users")
14
- try:
15
- query = f"SELECT * FROM c WHERE c.id = '{username}'"
16
- if role:
17
- query += f" AND c.role = '{role}'"
18
- items = list(container.query_items(query=query))
19
- return items[0] if items else None
20
- except Exception as e:
21
- logger.error(f"Error al obtener usuario {username}: {str(e)}")
22
- return None
23
-
24
-
25
- #########################################
26
- def get_admin_user(username):
27
- return get_user(username, role='Administrador')
28
-
29
-
30
- #########################################
31
- def get_student_user(username):
32
- return get_user(username, role='Estudiante')
33
-
34
-
35
- #########################################
36
- def get_teacher_user(username):
37
- return get_user(username, role='Profesor')
38
-
39
-
40
- #########################################
41
- def create_user(username, password, role, additional_info=None):
42
- """Crea un nuevo usuario"""
43
- container = get_container("users")
44
- if not container:
45
- logger.error("No se pudo obtener el contenedor de usuarios")
46
- return False
47
-
48
- try:
49
- user_data = {
50
- 'id': username,
51
- 'password': password,
52
- 'role': role,
53
- 'timestamp': datetime.now(timezone.utc).isoformat(),
54
- 'additional_info': additional_info or {},
55
- 'partitionKey': username # Agregar partition key
56
- }
57
-
58
- # Crear item sin especificar partition_key en el método
59
- container.create_item(body=user_data)
60
- logger.info(f"Usuario {role} creado: {username}")
61
- return True
62
-
63
- except Exception as e:
64
- logger.error(f"Error al crear usuario {role}: {str(e)}")
65
- return False
66
-
67
- #########################################
68
- def create_student_user(username, password, additional_info=None):
69
- return create_user(username, password, 'Estudiante', additional_info)
70
-
71
- #########################################
72
- def create_teacher_user(username, password, additional_info=None):
73
- return create_user(username, password, 'Profesor', additional_info)
74
-
75
- #########################################
76
- def create_admin_user(username, password, additional_info=None):
77
- return create_user(username, password, 'Administrador', additional_info)
78
-
79
- #########################################
80
- def record_login(username):
81
- """Registra el inicio de sesión de un usuario"""
82
- try:
83
- container = get_container("users_sessions")
84
- if not container:
85
- logger.error("No se pudo obtener el contenedor users_sessions")
86
- return None
87
-
88
- session_id = str(uuid.uuid4())
89
- session_doc = {
90
- "id": session_id,
91
- "type": "session",
92
- "username": username,
93
- "loginTime": datetime.now(timezone.utc).isoformat(),
94
- "additional_info": {},
95
- "partitionKey": username
96
- }
97
-
98
- result = container.create_item(body=session_doc)
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
- def record_logout(username, session_id):
107
- """Registra el cierre de sesión y calcula la duración"""
108
- try:
109
- container = get_container("users_sessions")
110
- if not container:
111
- logger.error("No se pudo obtener el contenedor users_sessions")
112
- return False
113
-
114
- query = "SELECT * FROM c WHERE c.id = @id AND c.username = @username"
115
- params = [
116
- {"name": "@id", "value": session_id},
117
- {"name": "@username", "value": username}
118
- ]
119
-
120
- items = list(container.query_items(
121
- query=query,
122
- parameters=params
123
- ))
124
-
125
- if not items:
126
- logger.warning(f"Sesión no encontrada: {session_id}")
127
- return False
128
-
129
- session = items[0]
130
- login_time = datetime.fromisoformat(session['loginTime'].rstrip('Z'))
131
- logout_time = datetime.now(timezone.utc)
132
- duration = int((logout_time - login_time).total_seconds())
133
-
134
- session.update({
135
- "logoutTime": logout_time.isoformat(),
136
- "sessionDuration": duration,
137
- "partitionKey": username
138
- })
139
-
140
- container.upsert_item(body=session)
141
- logger.info(f"Sesión {session_id} cerrada para {username}, duración: {duration}s")
142
- return True
143
- except Exception as e:
144
- logger.error(f"Error registrando logout: {str(e)}")
145
- return False
146
-
147
- #########################################
148
- def get_recent_sessions(limit=10):
149
- """Obtiene las sesiones más recientes"""
150
- try:
151
- container = get_container("users_sessions")
152
- if not container:
153
- logger.error("No se pudo obtener el contenedor users_sessions")
154
- return []
155
-
156
- query = """
157
- SELECT c.username, c.loginTime, c.logoutTime, c.sessionDuration
158
- FROM c
159
- WHERE c.type = 'session'
160
- ORDER BY c.loginTime DESC
161
- OFFSET 0 LIMIT @limit
162
- """
163
-
164
- sessions = list(container.query_items(
165
- query=query,
166
- parameters=[{"name": "@limit", "value": limit}],
167
- enable_cross_partition_query=True # Agregar este parámetro
168
- ))
169
-
170
- clean_sessions = []
171
- for session in sessions:
172
- try:
173
- clean_sessions.append({
174
- "username": session["username"],
175
- "loginTime": session["loginTime"],
176
- "logoutTime": session.get("logoutTime", "Activo"),
177
- "sessionDuration": session.get("sessionDuration", 0)
178
- })
179
- except KeyError as e:
180
- logger.warning(f"Sesión con datos incompletos: {e}")
181
- continue
182
-
183
- return clean_sessions
184
- except Exception as e:
185
- logger.error(f"Error obteniendo sesiones recientes: {str(e)}")
186
- return []
187
-
188
- #########################################
189
- def get_user_total_time(username):
190
- """Obtiene el tiempo total que un usuario ha pasado en la plataforma"""
191
- try:
192
- container = get_container("users_sessions")
193
- if not container:
194
- return None
195
-
196
- query = """
197
- SELECT VALUE SUM(c.sessionDuration)
198
- FROM c
199
- WHERE c.type = 'session'
200
- AND c.username = @username
201
- AND IS_DEFINED(c.sessionDuration)
202
- """
203
-
204
- result = list(container.query_items(
205
- query=query,
206
- parameters=[{"name": "@username", "value": username}]
207
- ))
208
-
209
- return result[0] if result and result[0] is not None else 0
210
- except Exception as e:
211
- logger.error(f"Error obteniendo tiempo total: {str(e)}")
212
- return 0
213
-
214
- #########################################
215
- def update_student_user(username, new_info):
216
- container = get_container("users")
217
- try:
218
- user = get_student_user(username)
219
- if user:
220
- user['additional_info'].update(new_info)
221
- user['partitionKey'] = username
222
- container.upsert_item(body=user)
223
- logger.info(f"Información del estudiante actualizada: {username}")
224
- return True
225
- else:
226
- logger.warning(f"Intento de actualizar estudiante no existente: {username}")
227
- return False
228
- except Exception as e:
229
- logger.error(f"Error al actualizar información del estudiante {username}: {str(e)}")
230
- return False
231
-
232
- #########################################
233
- def delete_student_user(username):
234
- container = get_container("users")
235
- try:
236
- user = get_student_user(username)
237
- if user:
238
- # El ID es suficiente para eliminación ya que partitionKey está en el documento
239
- container.delete_item(item=user['id'])
240
- logger.info(f"Estudiante eliminado: {username}")
241
- return True
242
- else:
243
- logger.warning(f"Intento de eliminar estudiante no existente: {username}")
244
- return False
245
- except Exception as e:
246
- logger.error(f"Error al eliminar estudiante {username}: {str(e)}")
247
- return False
248
-
249
- #########################################
250
- def store_application_request(name, lastname, email, institution, current_role, desired_role, reason):
251
- """Almacena una solicitud de aplicación"""
252
- try:
253
- # Obtener el contenedor usando get_container() que sí funciona
254
- container = get_container("application_requests")
255
- if not container:
256
- logger.error("No se pudo obtener el contenedor de solicitudes")
257
- return False
258
-
259
- # Crear documento con la solicitud
260
- # Nótese que incluimos email como partition key en el cuerpo del documento
261
- application_request = {
262
- "id": str(uuid.uuid4()),
263
- "name": name,
264
- "lastname": lastname,
265
- "email": email,
266
- "institution": institution,
267
- "current_role": current_role,
268
- "desired_role": desired_role,
269
- "reason": reason,
270
- "requestDate": datetime.utcnow().isoformat(),
271
- # El campo para partition key debe estar en el documento
272
- "partitionKey": email
273
- }
274
-
275
- # Crear el item en el contenedor - sin el parámetro enable_cross_partition_query
276
- container.create_item(
277
- body=application_request # Solo pasamos el body
278
- )
279
- logger.info(f"Solicitud de aplicación almacenada para: {email}")
280
- return True
281
-
282
- except Exception as e:
283
- logger.error(f"Error al almacenar la solicitud de aplicación: {str(e)}")
284
- logger.error(f"Detalles del error: {str(e)}")
285
- return False
286
-
287
-
288
- ################################################################
289
- def store_student_feedback(username, name, email, feedback):
290
- """Almacena el feedback de un estudiante"""
291
- try:
292
- # Obtener el contenedor - verificar disponibilidad
293
- logger.info(f"Intentando obtener contenedor user_feedback para usuario: {username}")
294
- container = get_container("user_feedback")
295
- if not container:
296
- logger.error("No se pudo obtener el contenedor user_feedback")
297
- return False
298
-
299
- # Crear documento de feedback - asegurar que el username esté como partition key
300
- feedback_item = {
301
- "id": str(uuid.uuid4()),
302
- "username": username, # Campo regular
303
- "name": name,
304
- "email": email,
305
- "feedback": feedback,
306
- "role": "Estudiante",
307
- "timestamp": datetime.now(timezone.utc).isoformat(),
308
- "partitionKey": username # Campo de partición
309
- }
310
-
311
- # Crear el item - sin el parámetro enable_cross_partition_query
312
- logger.info(f"Intentando almacenar feedback para usuario: {username}")
313
- result = container.create_item(
314
- body=feedback_item # Solo el body, no parámetros adicionales
315
- )
316
-
317
- logger.info(f"Feedback almacenado exitosamente para el usuario: {username}")
318
- return True
319
-
320
- except Exception as e:
321
- logger.error(f"Error al almacenar el feedback del estudiante {username}")
322
- logger.error(f"Detalles del error: {str(e)}")
323
  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
+ #########################################
12
+ def get_user(username, role=None):
13
+ container = get_container("users")
14
+ try:
15
+ query = f"SELECT * FROM c WHERE c.id = '{username}'"
16
+ if role:
17
+ query += f" AND c.role = '{role}'"
18
+ items = list(container.query_items(query=query))
19
+ return items[0] if items else None
20
+ except Exception as e:
21
+ logger.error(f"Error al obtener usuario {username}: {str(e)}")
22
+ return None
23
+
24
+
25
+ #########################################
26
+ def get_admin_user(username):
27
+ return get_user(username, role='Administrador')
28
+
29
+
30
+ #########################################
31
+ def get_student_user(username):
32
+ return get_user(username, role='Estudiante')
33
+
34
+
35
+ #########################################
36
+ def get_teacher_user(username):
37
+ return get_user(username, role='Profesor')
38
+
39
+
40
+ #########################################
41
+ def create_user(username, password, role, additional_info=None):
42
+ """Crea un nuevo usuario"""
43
+ container = get_container("users")
44
+ if not container:
45
+ logger.error("No se pudo obtener el contenedor de usuarios")
46
+ return False
47
+
48
+ try:
49
+ user_data = {
50
+ 'id': username,
51
+ 'password': password,
52
+ 'role': role,
53
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
54
+ 'additional_info': additional_info or {},
55
+ 'partitionKey': username # Agregar partition key
56
+ }
57
+
58
+ # Crear item sin especificar partition_key en el método
59
+ container.create_item(body=user_data)
60
+ logger.info(f"Usuario {role} creado: {username}")
61
+ return True
62
+
63
+ except Exception as e:
64
+ logger.error(f"Error al crear usuario {role}: {str(e)}")
65
+ return False
66
+
67
+ #########################################
68
+ def create_student_user(username, password, additional_info=None):
69
+ return create_user(username, password, 'Estudiante', additional_info)
70
+
71
+ #########################################
72
+ def create_teacher_user(username, password, additional_info=None):
73
+ return create_user(username, password, 'Profesor', additional_info)
74
+
75
+ #########################################
76
+ def create_admin_user(username, password, additional_info=None):
77
+ return create_user(username, password, 'Administrador', additional_info)
78
+
79
+ #########################################
80
+ def record_login(username):
81
+ """Registra el inicio de sesión de un usuario"""
82
+ try:
83
+ container = get_container("users_sessions")
84
+ if not container:
85
+ logger.error("No se pudo obtener el contenedor users_sessions")
86
+ return None
87
+
88
+ session_id = str(uuid.uuid4())
89
+ session_doc = {
90
+ "id": session_id,
91
+ "type": "session",
92
+ "username": username,
93
+ "loginTime": datetime.now(timezone.utc).isoformat(),
94
+ "additional_info": {},
95
+ "partitionKey": username
96
+ }
97
+
98
+ result = container.create_item(body=session_doc)
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
+ def record_logout(username, session_id):
107
+ """Registra el cierre de sesión y calcula la duración"""
108
+ try:
109
+ container = get_container("users_sessions")
110
+ if not container:
111
+ logger.error("No se pudo obtener el contenedor users_sessions")
112
+ return False
113
+
114
+ query = "SELECT * FROM c WHERE c.id = @id AND c.username = @username"
115
+ params = [
116
+ {"name": "@id", "value": session_id},
117
+ {"name": "@username", "value": username}
118
+ ]
119
+
120
+ items = list(container.query_items(
121
+ query=query,
122
+ parameters=params
123
+ ))
124
+
125
+ if not items:
126
+ logger.warning(f"Sesión no encontrada: {session_id}")
127
+ return False
128
+
129
+ session = items[0]
130
+ login_time = datetime.fromisoformat(session['loginTime'].rstrip('Z'))
131
+ logout_time = datetime.now(timezone.utc)
132
+ duration = int((logout_time - login_time).total_seconds())
133
+
134
+ session.update({
135
+ "logoutTime": logout_time.isoformat(),
136
+ "sessionDuration": duration,
137
+ "partitionKey": username
138
+ })
139
+
140
+ container.upsert_item(body=session)
141
+ logger.info(f"Sesión {session_id} cerrada para {username}, duración: {duration}s")
142
+ return True
143
+ except Exception as e:
144
+ logger.error(f"Error registrando logout: {str(e)}")
145
+ return False
146
+
147
+ #########################################
148
+ def get_recent_sessions(limit=10):
149
+ """Obtiene las sesiones más recientes"""
150
+ try:
151
+ container = get_container("users_sessions")
152
+ if not container:
153
+ logger.error("No se pudo obtener el contenedor users_sessions")
154
+ return []
155
+
156
+ query = """
157
+ SELECT c.username, c.loginTime, c.logoutTime, c.sessionDuration
158
+ FROM c
159
+ WHERE c.type = 'session'
160
+ ORDER BY c.loginTime DESC
161
+ OFFSET 0 LIMIT @limit
162
+ """
163
+
164
+ sessions = list(container.query_items(
165
+ query=query,
166
+ parameters=[{"name": "@limit", "value": limit}],
167
+ enable_cross_partition_query=True # Agregar este parámetro
168
+ ))
169
+
170
+ clean_sessions = []
171
+ for session in sessions:
172
+ try:
173
+ clean_sessions.append({
174
+ "username": session["username"],
175
+ "loginTime": session["loginTime"],
176
+ "logoutTime": session.get("logoutTime", "Activo"),
177
+ "sessionDuration": session.get("sessionDuration", 0)
178
+ })
179
+ except KeyError as e:
180
+ logger.warning(f"Sesión con datos incompletos: {e}")
181
+ continue
182
+
183
+ return clean_sessions
184
+ except Exception as e:
185
+ logger.error(f"Error obteniendo sesiones recientes: {str(e)}")
186
+ return []
187
+
188
+ #########################################
189
+ def get_user_total_time(username):
190
+ """Obtiene el tiempo total que un usuario ha pasado en la plataforma"""
191
+ try:
192
+ container = get_container("users_sessions")
193
+ if not container:
194
+ return None
195
+
196
+ query = """
197
+ SELECT VALUE SUM(c.sessionDuration)
198
+ FROM c
199
+ WHERE c.type = 'session'
200
+ AND c.username = @username
201
+ AND IS_DEFINED(c.sessionDuration)
202
+ """
203
+
204
+ result = list(container.query_items(
205
+ query=query,
206
+ parameters=[{"name": "@username", "value": username}]
207
+ ))
208
+
209
+ return result[0] if result and result[0] is not None else 0
210
+ except Exception as e:
211
+ logger.error(f"Error obteniendo tiempo total: {str(e)}")
212
+ return 0
213
+
214
+ #########################################
215
+ def update_student_user(username, new_info):
216
+ container = get_container("users")
217
+ try:
218
+ user = get_student_user(username)
219
+ if user:
220
+ user['additional_info'].update(new_info)
221
+ user['partitionKey'] = username
222
+ container.upsert_item(body=user)
223
+ logger.info(f"Información del estudiante actualizada: {username}")
224
+ return True
225
+ else:
226
+ logger.warning(f"Intento de actualizar estudiante no existente: {username}")
227
+ return False
228
+ except Exception as e:
229
+ logger.error(f"Error al actualizar información del estudiante {username}: {str(e)}")
230
+ return False
231
+
232
+ #########################################
233
+ def delete_student_user(username):
234
+ container = get_container("users")
235
+ try:
236
+ user = get_student_user(username)
237
+ if user:
238
+ # El ID es suficiente para eliminación ya que partitionKey está en el documento
239
+ container.delete_item(item=user['id'])
240
+ logger.info(f"Estudiante eliminado: {username}")
241
+ return True
242
+ else:
243
+ logger.warning(f"Intento de eliminar estudiante no existente: {username}")
244
+ return False
245
+ except Exception as e:
246
+ logger.error(f"Error al eliminar estudiante {username}: {str(e)}")
247
+ return False
248
+
249
+ #########################################
250
+ def store_application_request(name, lastname, email, institution, current_role, desired_role, reason):
251
+ """Almacena una solicitud de aplicación"""
252
+ try:
253
+ # Obtener el contenedor usando get_container() que sí funciona
254
+ container = get_container("application_requests")
255
+ if not container:
256
+ logger.error("No se pudo obtener el contenedor de solicitudes")
257
+ return False
258
+
259
+ # Crear documento con la solicitud
260
+ # Nótese que incluimos email como partition key en el cuerpo del documento
261
+ application_request = {
262
+ "id": str(uuid.uuid4()),
263
+ "name": name,
264
+ "lastname": lastname,
265
+ "email": email,
266
+ "institution": institution,
267
+ "current_role": current_role,
268
+ "desired_role": desired_role,
269
+ "reason": reason,
270
+ "requestDate": datetime.utcnow().isoformat(),
271
+ # El campo para partition key debe estar en el documento
272
+ "partitionKey": email
273
+ }
274
+
275
+ # Crear el item en el contenedor - sin el parámetro enable_cross_partition_query
276
+ container.create_item(
277
+ body=application_request # Solo pasamos el body
278
+ )
279
+ logger.info(f"Solicitud de aplicación almacenada para: {email}")
280
+ return True
281
+
282
+ except Exception as e:
283
+ logger.error(f"Error al almacenar la solicitud de aplicación: {str(e)}")
284
+ logger.error(f"Detalles del error: {str(e)}")
285
+ return False
286
+
287
+
288
+ ################################################################
289
+ def store_student_feedback(username, name, email, feedback):
290
+ """Almacena el feedback de un estudiante"""
291
+ try:
292
+ # Obtener el contenedor - verificar disponibilidad
293
+ logger.info(f"Intentando obtener contenedor user_feedback para usuario: {username}")
294
+ container = get_container("user_feedback")
295
+ if not container:
296
+ logger.error("No se pudo obtener el contenedor user_feedback")
297
+ return False
298
+
299
+ # Crear documento de feedback - asegurar que el username esté como partition key
300
+ feedback_item = {
301
+ "id": str(uuid.uuid4()),
302
+ "username": username, # Campo regular
303
+ "name": name,
304
+ "email": email,
305
+ "feedback": feedback,
306
+ "role": "Estudiante",
307
+ "timestamp": datetime.now(timezone.utc).isoformat(),
308
+ "partitionKey": username # Campo de partición
309
+ }
310
+
311
+ # Crear el item - sin el parámetro enable_cross_partition_query
312
+ logger.info(f"Intentando almacenar feedback para usuario: {username}")
313
+ result = container.create_item(
314
+ body=feedback_item # Solo el body, no parámetros adicionales
315
+ )
316
+
317
+ logger.info(f"Feedback almacenado exitosamente para el usuario: {username}")
318
+ return True
319
+
320
+ except Exception as e:
321
+ logger.error(f"Error al almacenar el feedback del estudiante {username}")
322
+ logger.error(f"Detalles del error: {str(e)}")
323
  return False
modules/database/writing_progress_mongo_db.py CHANGED
@@ -1,141 +1,141 @@
1
- # modules/database/writing_progress_mongo_db.py
2
-
3
- from .mongo_db import get_collection, insert_document
4
- from datetime import datetime, timezone
5
- import logging
6
-
7
- logger = logging.getLogger(__name__)
8
- COLLECTION_NAME = 'writing_progress'
9
-
10
- def store_writing_baseline(username, metrics, text):
11
- """
12
- Guarda la línea base de escritura de un usuario.
13
- Args:
14
- username: ID del usuario
15
- metrics: Diccionario con métricas iniciales
16
- text: Texto analizado
17
- """
18
- try:
19
- document = {
20
- 'username': username,
21
- 'type': 'baseline',
22
- 'metrics': metrics,
23
- 'text': text,
24
- 'timestamp': datetime.now(timezone.utc).isoformat(),
25
- 'iteration': 0 # Línea base siempre es iteración 0
26
- }
27
-
28
- # Verificar si ya existe una línea base
29
- collection = get_collection(COLLECTION_NAME)
30
- existing = collection.find_one({
31
- 'username': username,
32
- 'type': 'baseline'
33
- })
34
-
35
- if existing:
36
- # Actualizar línea base existente
37
- result = collection.update_one(
38
- {'_id': existing['_id']},
39
- {'$set': document}
40
- )
41
- success = result.modified_count > 0
42
- else:
43
- # Insertar nueva línea base
44
- result = collection.insert_one(document)
45
- success = result.inserted_id is not None
46
-
47
- logger.info(f"Línea base {'actualizada' if existing else 'creada'} para usuario: {username}")
48
- return success
49
-
50
- except Exception as e:
51
- logger.error(f"Error al guardar línea base: {str(e)}")
52
- return False
53
-
54
- def store_writing_progress(username, metrics, text):
55
- """
56
- Guarda una nueva iteración de progreso.
57
- """
58
- try:
59
- # Obtener último número de iteración
60
- collection = get_collection(COLLECTION_NAME)
61
- last_progress = collection.find_one(
62
- {'username': username},
63
- sort=[('iteration', -1)]
64
- )
65
-
66
- next_iteration = (last_progress['iteration'] + 1) if last_progress else 1
67
-
68
- document = {
69
- 'username': username,
70
- 'type': 'progress',
71
- 'metrics': metrics,
72
- 'text': text,
73
- 'timestamp': datetime.now(timezone.utc).isoformat(),
74
- 'iteration': next_iteration
75
- }
76
-
77
- result = collection.insert_one(document)
78
- success = result.inserted_id is not None
79
-
80
- if success:
81
- logger.info(f"Progreso guardado para {username}, iteración {next_iteration}")
82
-
83
- return success
84
-
85
- except Exception as e:
86
- logger.error(f"Error al guardar progreso: {str(e)}")
87
- return False
88
-
89
- def get_writing_baseline(username):
90
- """
91
- Obtiene la línea base de un usuario.
92
- """
93
- try:
94
- collection = get_collection(COLLECTION_NAME)
95
- return collection.find_one({
96
- 'username': username,
97
- 'type': 'baseline'
98
- })
99
- except Exception as e:
100
- logger.error(f"Error al obtener línea base: {str(e)}")
101
- return None
102
-
103
- def get_writing_progress(username, limit=None):
104
- """
105
- Obtiene el historial de progreso de un usuario.
106
- Args:
107
- username: ID del usuario
108
- limit: Número máximo de registros a retornar
109
- """
110
- try:
111
- collection = get_collection(COLLECTION_NAME)
112
- cursor = collection.find(
113
- {
114
- 'username': username,
115
- 'type': 'progress'
116
- },
117
- sort=[('iteration', -1)]
118
- )
119
-
120
- if limit:
121
- cursor = cursor.limit(limit)
122
-
123
- return list(cursor)
124
-
125
- except Exception as e:
126
- logger.error(f"Error al obtener progreso: {str(e)}")
127
- return []
128
-
129
- def get_latest_writing_metrics(username):
130
- """
131
- Obtiene las métricas más recientes (línea base o progreso).
132
- """
133
- try:
134
- collection = get_collection(COLLECTION_NAME)
135
- return collection.find_one(
136
- {'username': username},
137
- sort=[('timestamp', -1)]
138
- )
139
- except Exception as e:
140
- logger.error(f"Error al obtener métricas recientes: {str(e)}")
141
  return None
 
1
+ # modules/database/writing_progress_mongo_db.py
2
+
3
+ from .mongo_db import get_collection, insert_document
4
+ from datetime import datetime, timezone
5
+ import logging
6
+
7
+ logger = logging.getLogger(__name__)
8
+ COLLECTION_NAME = 'writing_progress'
9
+
10
+ def store_writing_baseline(username, metrics, text):
11
+ """
12
+ Guarda la línea base de escritura de un usuario.
13
+ Args:
14
+ username: ID del usuario
15
+ metrics: Diccionario con métricas iniciales
16
+ text: Texto analizado
17
+ """
18
+ try:
19
+ document = {
20
+ 'username': username,
21
+ 'type': 'baseline',
22
+ 'metrics': metrics,
23
+ 'text': text,
24
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
25
+ 'iteration': 0 # Línea base siempre es iteración 0
26
+ }
27
+
28
+ # Verificar si ya existe una línea base
29
+ collection = get_collection(COLLECTION_NAME)
30
+ existing = collection.find_one({
31
+ 'username': username,
32
+ 'type': 'baseline'
33
+ })
34
+
35
+ if existing:
36
+ # Actualizar línea base existente
37
+ result = collection.update_one(
38
+ {'_id': existing['_id']},
39
+ {'$set': document}
40
+ )
41
+ success = result.modified_count > 0
42
+ else:
43
+ # Insertar nueva línea base
44
+ result = collection.insert_one(document)
45
+ success = result.inserted_id is not None
46
+
47
+ logger.info(f"Línea base {'actualizada' if existing else 'creada'} para usuario: {username}")
48
+ return success
49
+
50
+ except Exception as e:
51
+ logger.error(f"Error al guardar línea base: {str(e)}")
52
+ return False
53
+
54
+ def store_writing_progress(username, metrics, text):
55
+ """
56
+ Guarda una nueva iteración de progreso.
57
+ """
58
+ try:
59
+ # Obtener último número de iteración
60
+ collection = get_collection(COLLECTION_NAME)
61
+ last_progress = collection.find_one(
62
+ {'username': username},
63
+ sort=[('iteration', -1)]
64
+ )
65
+
66
+ next_iteration = (last_progress['iteration'] + 1) if last_progress else 1
67
+
68
+ document = {
69
+ 'username': username,
70
+ 'type': 'progress',
71
+ 'metrics': metrics,
72
+ 'text': text,
73
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
74
+ 'iteration': next_iteration
75
+ }
76
+
77
+ result = collection.insert_one(document)
78
+ success = result.inserted_id is not None
79
+
80
+ if success:
81
+ logger.info(f"Progreso guardado para {username}, iteración {next_iteration}")
82
+
83
+ return success
84
+
85
+ except Exception as e:
86
+ logger.error(f"Error al guardar progreso: {str(e)}")
87
+ return False
88
+
89
+ def get_writing_baseline(username):
90
+ """
91
+ Obtiene la línea base de un usuario.
92
+ """
93
+ try:
94
+ collection = get_collection(COLLECTION_NAME)
95
+ return collection.find_one({
96
+ 'username': username,
97
+ 'type': 'baseline'
98
+ })
99
+ except Exception as e:
100
+ logger.error(f"Error al obtener línea base: {str(e)}")
101
+ return None
102
+
103
+ def get_writing_progress(username, limit=None):
104
+ """
105
+ Obtiene el historial de progreso de un usuario.
106
+ Args:
107
+ username: ID del usuario
108
+ limit: Número máximo de registros a retornar
109
+ """
110
+ try:
111
+ collection = get_collection(COLLECTION_NAME)
112
+ cursor = collection.find(
113
+ {
114
+ 'username': username,
115
+ 'type': 'progress'
116
+ },
117
+ sort=[('iteration', -1)]
118
+ )
119
+
120
+ if limit:
121
+ cursor = cursor.limit(limit)
122
+
123
+ return list(cursor)
124
+
125
+ except Exception as e:
126
+ logger.error(f"Error al obtener progreso: {str(e)}")
127
+ return []
128
+
129
+ def get_latest_writing_metrics(username):
130
+ """
131
+ Obtiene las métricas más recientes (línea base o progreso).
132
+ """
133
+ try:
134
+ collection = get_collection(COLLECTION_NAME)
135
+ return collection.find_one(
136
+ {'username': username},
137
+ sort=[('timestamp', -1)]
138
+ )
139
+ except Exception as e:
140
+ logger.error(f"Error al obtener métricas recientes: {str(e)}")
141
  return None