Spaces:
Running
Running
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
|