Upload 16 files
Browse files- modules/database/chat_mongo_db.py +115 -115
- modules/database/claude_recommendations_mongo_db.py +137 -0
- modules/database/current_situation_mongo_db.py +136 -111
- modules/database/database_init.py +187 -187
- modules/database/discourse_mongo_db.py +172 -172
- modules/database/mongo_db.py +61 -62
- modules/database/morphosyntax_iterative_mongo_db.py +171 -171
- modules/database/semantic_export.py +77 -77
- modules/database/semantic_mongo_db.py +159 -159
- modules/database/sql_db.py +322 -322
- modules/database/writing_progress_mongo_db.py +140 -140
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
|
96 |
-
"""
|
97 |
-
Obtiene los análisis
|
98 |
-
"""
|
99 |
-
try:
|
100 |
-
collection = get_collection(COLLECTION_NAME)
|
101 |
-
if collection is None:
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
db
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
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
|