File size: 8,651 Bytes
319fe09 17de8c9 319fe09 17de8c9 dcdbcfb 17de8c9 319fe09 17de8c9 dcdbcfb 17de8c9 dcdbcfb 17de8c9 dcdbcfb 17de8c9 dcdbcfb 17de8c9 dcdbcfb 17de8c9 dcdbcfb 17de8c9 96d5539 dcdbcfb 17de8c9 dcdbcfb 17de8c9 dcdbcfb 17de8c9 dcdbcfb 17de8c9 dcdbcfb 17de8c9 4d2b77b 17de8c9 20aec23 17de8c9 20aec23 a3a54d6 20aec23 a3a54d6 20aec23 17de8c9 20aec23 17de8c9 20aec23 17de8c9 20aec23 17de8c9 | 1 2 3 4 5 6 7 8 9 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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# Importaciones est谩ndar
import logging
import io
import base64
from datetime import datetime, timezone
from pymongo.errors import PyMongoError
from PIL import Image
# Importaciones de terceros
import matplotlib.pyplot as plt
# Importaciones locales
from .mongo_db import (
get_collection,
insert_document,
find_documents,
update_document,
delete_document
)
# Configuraci贸n del logger
logger = logging.getLogger(__name__)
COLLECTION_NAME = 'student_semantic_live_analysis'
##########################################
##########################################
def store_student_semantic_live_result(username, text, analysis_result, lang_code='en'):
"""
Versi贸n optimizada:
- Elimina redundancias en el procesamiento de bytes.
- Soluciona el error 413 (RequestEntityTooLarge) mediante compresi贸n JPEG.
- Manejo de fechas nativo para MongoDB.
"""
try:
# 1. Validaci贸n inicial y obtenci贸n de colecci贸n
if not all([username, text, analysis_result]):
logger.error("Par谩metros incompletos para guardar an谩lisis")
return False
collection = get_collection(COLLECTION_NAME)
if collection is None:
logger.error(f"No se pudo obtener la colecci贸n {COLLECTION_NAME}")
return False
# 2. Procesamiento y Optimizaci贸n del Gr谩fico (Sin redundancias)
graph_data = analysis_result.get('concept_graph')
final_graph_bytes = None
if graph_data:
try:
# Convertir a bytes si viene en base64 (string)
if isinstance(graph_data, str):
final_graph_bytes = base64.b64decode(graph_data)
else:
final_graph_bytes = graph_data
# Optimizaci贸n de tama帽o para evitar error 413 en Azure/Mongo
# Solo comprimimos si detectamos que existe la imagen
img = Image.open(io.BytesIO(final_graph_bytes))
if img.mode != 'RGB':
img = img.convert('RGB')
output = io.BytesIO()
# JPEG al 75% mantiene legibilidad y reduce el peso dr谩sticamente
img.save(output, format="JPEG", quality=75, optimize=True)
final_graph_bytes = output.getvalue()
logger.info(f"Grafo optimizado para {username} ({len(final_graph_bytes)} bytes)")
except Exception as e:
logger.warning(f"Error optimizando imagen, se usar谩 formato original: {e}")
# Si falla la optimizaci贸n, mantenemos lo que ten铆amos (si eran bytes)
final_graph_bytes = final_graph_bytes if isinstance(final_graph_bytes, bytes) else None
# 3. Preparaci贸n del documento (Directo y limpio)
analysis_document = {
'username': username,
'timestamp': datetime.now(timezone.utc),
'text': text[:50000], # L铆mite de seguridad
'analysis_type': 'semantic_live',
'language': lang_code,
'key_concepts': analysis_result.get('key_concepts', []),
'concept_centrality': analysis_result.get('concept_centrality', {}),
'concept_graph': final_graph_bytes # Insertamos los bytes ya procesados
}
# 4. Inserci贸n en base de datos
try:
result = collection.insert_one(analysis_document)
if result.inserted_id:
logger.info(f"An谩lisis guardado exitosamente. ID: {result.inserted_id}")
return True
return False
except PyMongoError as e:
logger.error(f"Error de inserci贸n en MongoDB: {str(e)}")
return False
except Exception as e:
logger.error(f"Error inesperado en store_student_semantic_live_result: {str(e)}", exc_info=True)
return False
##########################################
##########################################
def get_student_semantic_live_analysis(username, limit=10):
"""
Versi贸n optimizada: Elimina redundancia y garantiza orden cronol贸gico
mediante normalizaci贸n de fechas al vuelo.
"""
try:
collection = get_collection(COLLECTION_NAME)
if collection is None:
logger.error("No se pudo obtener la colecci贸n")
return []
# Criterio de b煤squeda: usuario espec铆fico y que tenga el grafo generado
query = {
"username": username,
"concept_graph": {"$exists": True, "$ne": None}
}
# Pipeline de Agregaci贸n para resolver el desorden de fechas (2025 vs 2026)
pipeline = [
{"$match": query},
# Convertimos el timestamp a objeto Date real para que el sort sea exacto
{"$addFields": {
"sort_date": {
"$convert": {
"input": "$timestamp",
"to": "date",
"onError": "$timestamp", # Si falla, mantiene el valor original
"onNull": "$timestamp"
}
}
}},
# Ordenamos por la fecha normalizada de forma descendente (m谩s reciente primero)
{"$sort": {"sort_date": -1}},
{"$limit": limit},
# Proyectamos solo los campos necesarios para no sobrecargar la memoria
{"$project": {
"timestamp": 1,
"text": 1,
"key_concepts": 1,
"concept_graph": 1,
"analysis_type": 1,
"_id": 1
}}
]
# Ejecutamos una 煤nica vez la consulta
results = list(collection.aggregate(pipeline))
logger.info(f"Recuperados {len(results)} an谩lisis 'live' para {username}")
return results
except PyMongoError as e:
logger.error(f"Error de MongoDB en live analysis: {str(e)}")
return []
except Exception as e:
logger.error(f"Error inesperado en live analysis: {str(e)}")
return []
#######################################################
#######################################################
def update_student_semantic_live_analysis(analysis_id, update_data):
"""Actualiza un an谩lisis existente con manejo de errores"""
try:
query = {"_id": analysis_id}
update = {"$set": update_data}
return update_document(COLLECTION_NAME, query, update) > 0
except PyMongoError as e:
logger.error(f"Error al actualizar: {str(e)}")
return False
#######################################################
#######################################################
def delete_student_semantic_live_analysis(analysis_id):
"""Elimina un an谩lisis con manejo de errores"""
try:
query = {"_id": analysis_id}
return delete_document(COLLECTION_NAME, query) > 0
except PyMongoError as e:
logger.error(f"Error al eliminar: {str(e)}")
return False
#######################################################
#######################################################
def get_student_semantic_live_data(username):
"""
Obtiene todos los an谩lisis sem谩nticos en vivo de un estudiante.
Versi贸n corregida que usa la funci贸n _live.
"""
try:
analyses = get_student_semantic_live_analysis(username, limit=None)
formatted_analyses = []
for analysis in analyses:
formatted_analysis = {
'timestamp': analysis.get('timestamp'),
'text': analysis.get('text', ''),
'key_concepts': analysis.get('key_concepts', []),
'concept_graph': analysis.get('concept_graph')
}
formatted_analyses.append(formatted_analysis)
return {
'username': username,
'entries': formatted_analyses,
'count': len(formatted_analyses),
'status': 'success'
}
except Exception as e:
logger.error(f"Error al obtener datos: {str(e)}")
return {
'username': username,
'entries': [],
'count': 0,
'status': 'error',
'error': str(e)
}
#######################################################
#######################################################
__all__ = [
'store_student_semantic_live_result',
'get_student_semantic_live_analysis',
'update_student_semantic_live_analysis',
'delete_student_semantic_live_analysis',
'get_student_semantic_live_data'
] |