File size: 6,776 Bytes
3657e14
 
 
 
c9030f8
 
3657e14
c9030f8
3657e14
 
 
 
 
 
c9030f8
3657e14
 
c9030f8
 
 
 
 
 
 
3657e14
 
c9030f8
 
 
 
 
3657e14
 
 
c9030f8
 
 
 
 
 
 
 
 
 
 
3657e14
c9030f8
3657e14
c9030f8
 
3657e14
c9030f8
3657e14
 
 
c9030f8
3657e14
c9030f8
 
 
 
 
3657e14
 
c9030f8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3657e14
 
c9030f8
3657e14
 
 
 
 
c9030f8
3657e14
 
c9030f8
 
 
 
 
 
 
 
 
3657e14
 
 
 
 
 
 
c9030f8
 
3657e14
c9030f8
 
3657e14
 
c9030f8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3657e14
c9030f8
3657e14
 
 
 
 
 
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
# modules/database/semantic_mongo_live_db.py
import logging
from datetime import datetime, timezone
import base64
from bson import Binary
from pymongo.errors import PyMongoError

# 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'):
    """
    Guarda el resultado del análisis semántico en vivo en MongoDB.
    Versión mejorada con manejo robusto de errores y verificación de datos.
    """
    try:
        # 1. Validación exhaustiva de los parámetros de entrada
        if not username or not isinstance(username, str):
            logger.error("Nombre de usuario inválido o vacío")
            return False

        if not text or not isinstance(text, str):
            logger.error("Texto de análisis inválido o vacío")
            return False

        if not analysis_result or not isinstance(analysis_result, dict):
            logger.error("Resultado de análisis inválido o vacío")
            return False

        # 2. Preparación del gráfico conceptual con múltiples formatos soportados
        concept_graph_data = None
        if 'concept_graph' in analysis_result and analysis_result['concept_graph'] is not None:
            try:
                graph_data = analysis_result['concept_graph']
                
                if isinstance(graph_data, bytes):
                    # Codificar a base64 para almacenamiento eficiente
                    concept_graph_data = base64.b64encode(graph_data).decode('utf-8')
                elif isinstance(graph_data, str):
                    # Si ya es string (base64), usarlo directamente
                    concept_graph_data = graph_data
                elif isinstance(graph_data, Binary):
                    # Si es Binary de pymongo, convertirlo
                    concept_graph_data = base64.b64encode(graph_data).decode('utf-8')
                else:
                    logger.warning(f"Formato de gráfico no soportado: {type(graph_data)}")
            except Exception as e:
                logger.error(f"Error al procesar gráfico conceptual: {str(e)}", exc_info=True)
                # Continuar sin gráfico en lugar de fallar completamente

        # 3. Preparación del documento con validación de campos
        analysis_document = {
            'username': username,
            'timestamp': datetime.now(timezone.utc),
            'text': text[:10000],  # Limitar tamaño para prevenir documentos muy grandes
            'analysis_type': 'semantic_live',
            'language': lang_code,
            'metadata': {
                'version': '1.0',
                'source': 'live_interface'
            }
        }

        # Campos opcionales con validación
        if 'key_concepts' in analysis_result and isinstance(analysis_result['key_concepts'], list):
            analysis_document['key_concepts'] = analysis_result['key_concepts'][:50]  # Limitar a 50 conceptos

        if 'concept_centrality' in analysis_result and isinstance(analysis_result['concept_centrality'], dict):
            analysis_document['concept_centrality'] = analysis_result['concept_centrality']

        if concept_graph_data:
            analysis_document['concept_graph'] = concept_graph_data

        # 4. Operación de base de datos con manejo de errores específico
        try:
            collection = get_collection(COLLECTION_NAME)
            if not collection:
                logger.error("No se pudo obtener la colección MongoDB")
                return False

            result = collection.insert_one(analysis_document)
            
            if result.inserted_id:
                logger.info(f"Análisis guardado exitosamente para {username}. ID: {result.inserted_id}")
                return True
            
            logger.error("La operación de inserción no devolvió un ID")
            return False

        except PyMongoError as mongo_error:
            logger.error(f"Error de MongoDB al guardar análisis: {str(mongo_error)}", exc_info=True)
            return False

    except Exception as e:
        logger.error(f"Error inesperado al guardar análisis: {str(e)}", exc_info=True)
        return False

def get_student_semantic_live_analysis(username, limit=10):
    """
    Recupera los análisis semánticos en vivo de un estudiante.
    Versión mejorada con paginación y manejo de errores.
    """
    try:
        # Validación de parámetros
        if not username or not isinstance(username, str):
            logger.error("Nombre de usuario inválido para recuperación")
            return []

        if not isinstance(limit, int) or limit <= 0:
            limit = 10  # Valor por defecto si el límite es inválido

        # Consulta con proyección para optimizar transferencia
        query = {
            "username": username,
            "analysis_type": "semantic_live"
        }
        
        projection = {
            "timestamp": 1,
            "text": {"$substr": ["$text", 0, 200]},  # Solo primeros 200 caracteres
            "key_concepts": {"$slice": ["$key_concepts", 10]},  # Solo primeros 10 conceptos
            "concept_graph": 1,
            "_id": 1,
            "metadata": 1
        }
        
        # Operación de base de datos con manejo de errores
        try:
            collection = get_collection(COLLECTION_NAME)
            if not collection:
                logger.error("No se pudo obtener la colección MongoDB")
                return []

            cursor = collection.find(query, projection).sort("timestamp", -1).limit(limit)
            results = list(cursor)
            
            # Post-procesamiento para asegurar formato consistente
            for doc in results:
                if 'concept_graph' in doc and isinstance(doc['concept_graph'], str):
                    try:
                        # Convertir base64 string a bytes para compatibilidad
                        doc['concept_graph'] = base64.b64decode(doc['concept_graph'])
                    except Exception as e:
                        logger.warning(f"Error al decodificar gráfico: {str(e)}")
                        doc.pop('concept_graph', None)
            
            logger.info(f"Recuperados {len(results)} análisis para {username}")
            return results

        except PyMongoError as mongo_error:
            logger.error(f"Error de MongoDB al recuperar análisis: {str(mongo_error)}")
            return []

    except Exception as e:
        logger.error(f"Error inesperado al recuperar análisis: {str(e)}", exc_info=True)
        return []

__all__ = [
    'store_student_semantic_live_result',
    'get_student_semantic_live_analysis'
]