Create database.py
Browse files- modules/database/database.py +392 -0
modules/database/database.py
ADDED
@@ -0,0 +1,392 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# database.py
|
2 |
+
import logging
|
3 |
+
import os
|
4 |
+
from azure.cosmos import CosmosClient
|
5 |
+
from azure.cosmos.exceptions import CosmosHttpResponseError
|
6 |
+
from pymongo import MongoClient
|
7 |
+
import certifi
|
8 |
+
from datetime import datetime
|
9 |
+
import io
|
10 |
+
import base64
|
11 |
+
import matplotlib.pyplot as plt
|
12 |
+
from matplotlib.figure import Figure
|
13 |
+
import bcrypt
|
14 |
+
print(f"Bcrypt version: {bcrypt.__version__}")
|
15 |
+
import uuid
|
16 |
+
|
17 |
+
logging.basicConfig(level=logging.DEBUG)
|
18 |
+
logger = logging.getLogger(__name__)
|
19 |
+
|
20 |
+
# Variables globales para Cosmos DB SQL API
|
21 |
+
application_requests_container = None
|
22 |
+
cosmos_client = None
|
23 |
+
user_database = None
|
24 |
+
user_container = None
|
25 |
+
|
26 |
+
# Variables globales para Cosmos DB MongoDB API
|
27 |
+
mongo_client = None
|
28 |
+
mongo_db = None
|
29 |
+
analysis_collection = None
|
30 |
+
chat_collection = None # Nueva variable global
|
31 |
+
|
32 |
+
|
33 |
+
#####################################################################################33
|
34 |
+
def initialize_database_connections():
|
35 |
+
try:
|
36 |
+
print("Iniciando conexión a MongoDB")
|
37 |
+
mongodb_success = initialize_mongodb_connection()
|
38 |
+
print(f"Conexión a MongoDB: {'exitosa' if mongodb_success else 'fallida'}")
|
39 |
+
except Exception as e:
|
40 |
+
print(f"Error al conectar con MongoDB: {str(e)}")
|
41 |
+
mongodb_success = False
|
42 |
+
|
43 |
+
try:
|
44 |
+
print("Iniciando conexión a Cosmos DB SQL API")
|
45 |
+
sql_success = initialize_cosmos_sql_connection()
|
46 |
+
print(f"Conexión a Cosmos DB SQL API: {'exitosa' if sql_success else 'fallida'}")
|
47 |
+
except Exception as e:
|
48 |
+
print(f"Error al conectar con Cosmos DB SQL API: {str(e)}")
|
49 |
+
sql_success = False
|
50 |
+
|
51 |
+
return {
|
52 |
+
"mongodb": mongodb_success,
|
53 |
+
"cosmos_sql": sql_success
|
54 |
+
}
|
55 |
+
|
56 |
+
#####################################################################################33
|
57 |
+
def initialize_cosmos_sql_connection():
|
58 |
+
global cosmos_client, user_database, user_container, application_requests_container
|
59 |
+
logger.info("Initializing Cosmos DB SQL API connection")
|
60 |
+
try:
|
61 |
+
cosmos_endpoint = os.environ.get("COSMOS_ENDPOINT")
|
62 |
+
cosmos_key = os.environ.get("COSMOS_KEY")
|
63 |
+
logger.info(f"Cosmos Endpoint: {cosmos_endpoint}")
|
64 |
+
logger.info(f"Cosmos Key: {'*' * len(cosmos_key) if cosmos_key else 'Not set'}")
|
65 |
+
|
66 |
+
if not cosmos_endpoint or not cosmos_key:
|
67 |
+
logger.error("COSMOS_ENDPOINT or COSMOS_KEY environment variables are not set")
|
68 |
+
raise ValueError("Las variables de entorno COSMOS_ENDPOINT y COSMOS_KEY deben estar configuradas")
|
69 |
+
|
70 |
+
cosmos_client = CosmosClient(cosmos_endpoint, cosmos_key)
|
71 |
+
user_database = cosmos_client.get_database_client("user_database")
|
72 |
+
user_container = user_database.get_container_client("users")
|
73 |
+
application_requests_container = user_database.get_container_client("application_requests")
|
74 |
+
|
75 |
+
logger.info(f"user_container initialized: {user_container is not None}")
|
76 |
+
logger.info(f"application_requests_container initialized: {application_requests_container is not None}")
|
77 |
+
|
78 |
+
logger.info("Conexión a Cosmos DB SQL API exitosa")
|
79 |
+
return True
|
80 |
+
except Exception as e:
|
81 |
+
logger.error(f"Error al conectar con Cosmos DB SQL API: {str(e)}", exc_info=True)
|
82 |
+
return False
|
83 |
+
|
84 |
+
############################################################################################3
|
85 |
+
def initialize_mongodb_connection():
|
86 |
+
global mongo_client, mongo_db, analysis_collection, chat_collection
|
87 |
+
try:
|
88 |
+
cosmos_mongodb_connection_string = os.getenv("MONGODB_CONNECTION_STRING")
|
89 |
+
if not cosmos_mongodb_connection_string:
|
90 |
+
logger.error("La variable de entorno MONGODB_CONNECTION_STRING no está configurada")
|
91 |
+
return False
|
92 |
+
|
93 |
+
mongo_client = MongoClient(cosmos_mongodb_connection_string,
|
94 |
+
tls=True,
|
95 |
+
tlsCAFile=certifi.where(),
|
96 |
+
retryWrites=False,
|
97 |
+
serverSelectionTimeoutMS=5000,
|
98 |
+
connectTimeoutMS=10000,
|
99 |
+
socketTimeoutMS=10000)
|
100 |
+
|
101 |
+
mongo_client.admin.command('ping')
|
102 |
+
|
103 |
+
mongo_db = mongo_client['aideatext_db']
|
104 |
+
analysis_collection = mongo_db['text_analysis']
|
105 |
+
chat_collection = mongo_db['chat_history'] # Inicializar la nueva colección
|
106 |
+
|
107 |
+
# Verificar la conexión
|
108 |
+
mongo_client.admin.command('ping')
|
109 |
+
|
110 |
+
logger.info("Conexión a Cosmos DB MongoDB API exitosa")
|
111 |
+
return True
|
112 |
+
except Exception as e:
|
113 |
+
logger.error(f"Error al conectar con Cosmos DB MongoDB API: {str(e)}", exc_info=True)
|
114 |
+
return False
|
115 |
+
|
116 |
+
#######################################################################################################
|
117 |
+
def create_user(username, password, role):
|
118 |
+
global user_container
|
119 |
+
try:
|
120 |
+
print(f"Attempting to create user: {username} with role: {role}")
|
121 |
+
if user_container is None:
|
122 |
+
print("Error: user_container is None. Attempting to reinitialize connection.")
|
123 |
+
if not initialize_cosmos_sql_connection():
|
124 |
+
raise Exception("Failed to initialize SQL connection")
|
125 |
+
|
126 |
+
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
|
127 |
+
print(f"Password hashed successfully for user: {username}")
|
128 |
+
user_data = {
|
129 |
+
'id': username,
|
130 |
+
'password': hashed_password,
|
131 |
+
'role': role,
|
132 |
+
'created_at': datetime.utcnow().isoformat()
|
133 |
+
}
|
134 |
+
user_container.create_item(body=user_data)
|
135 |
+
print(f"Usuario {role} creado: {username}") # Log para depuración
|
136 |
+
return True
|
137 |
+
except Exception as e:
|
138 |
+
print(f"Detailed error in create_user: {str(e)}")
|
139 |
+
return False
|
140 |
+
|
141 |
+
#######################################################################################################
|
142 |
+
def create_admin_user(username, password):
|
143 |
+
return create_user(username, password, 'Administrador')
|
144 |
+
|
145 |
+
#######################################################################################################
|
146 |
+
def create_student_user(username, password):
|
147 |
+
return create_user(username, password, 'Estudiante')
|
148 |
+
|
149 |
+
#######################################################################################################
|
150 |
+
# Funciones para Cosmos DB SQL API (manejo de usuarios)
|
151 |
+
def get_user(username):
|
152 |
+
try:
|
153 |
+
query = f"SELECT * FROM c WHERE c.id = '{username}'"
|
154 |
+
items = list(user_container.query_items(query=query, enable_cross_partition_query=True))
|
155 |
+
user = items[0] if items else None
|
156 |
+
if user:
|
157 |
+
print(f"Usuario encontrado: {username}, Rol: {user.get('role')}") # Log añadido
|
158 |
+
else:
|
159 |
+
print(f"Usuario no encontrado: {username}") # Log añadido
|
160 |
+
return user
|
161 |
+
except Exception as e:
|
162 |
+
print(f"Error al obtener usuario {username}: {str(e)}")
|
163 |
+
return None
|
164 |
+
|
165 |
+
#######################################################################################################
|
166 |
+
def store_application_request(name, email, institution, role, reason):
|
167 |
+
global application_requests_container
|
168 |
+
logger.info("Entering store_application_request function")
|
169 |
+
try:
|
170 |
+
logger.info("Checking application_requests_container")
|
171 |
+
if application_requests_container is None:
|
172 |
+
logger.error("application_requests_container is not initialized")
|
173 |
+
return False
|
174 |
+
|
175 |
+
logger.info("Creating application request document")
|
176 |
+
application_request = {
|
177 |
+
"id": str(uuid.uuid4()),
|
178 |
+
"name": name,
|
179 |
+
"email": email,
|
180 |
+
"institution": institution,
|
181 |
+
"role": role,
|
182 |
+
"reason": reason,
|
183 |
+
"requestDate": datetime.utcnow().isoformat()
|
184 |
+
}
|
185 |
+
|
186 |
+
logger.info(f"Attempting to store document: {application_request}")
|
187 |
+
application_requests_container.create_item(body=application_request)
|
188 |
+
logger.info(f"Application request stored for email: {email}")
|
189 |
+
return True
|
190 |
+
except Exception as e:
|
191 |
+
logger.error(f"Error storing application request: {str(e)}")
|
192 |
+
return False
|
193 |
+
|
194 |
+
#######################################################################################################
|
195 |
+
def store_morphosyntax_result(username, text, repeated_words, arc_diagrams, pos_analysis, morphological_analysis, sentence_structure):
|
196 |
+
if analysis_collection is None:
|
197 |
+
logger.error("La conexión a MongoDB no está inicializada")
|
198 |
+
return False
|
199 |
+
|
200 |
+
try:
|
201 |
+
word_count = {}
|
202 |
+
for word, color in repeated_words.items():
|
203 |
+
category = color # Asumiendo que 'color' es la categoría gramatical
|
204 |
+
word_count[category] = word_count.get(category, 0) + 1
|
205 |
+
|
206 |
+
analysis_document = {
|
207 |
+
'username': username,
|
208 |
+
'timestamp': datetime.utcnow(),
|
209 |
+
'text': text,
|
210 |
+
'word_count': word_count,
|
211 |
+
'arc_diagrams': arc_diagrams,
|
212 |
+
'pos_analysis': pos_analysis,
|
213 |
+
'morphological_analysis': morphological_analysis,
|
214 |
+
'sentence_structure': sentence_structure
|
215 |
+
}
|
216 |
+
|
217 |
+
result = analysis_collection.insert_one(analysis_document)
|
218 |
+
logger.info(f"Análisis guardado con ID: {result.inserted_id} para el usuario: {username}")
|
219 |
+
return True
|
220 |
+
except Exception as e:
|
221 |
+
logger.error(f"Error al guardar el análisis para el usuario {username}: {str(e)}")
|
222 |
+
return False
|
223 |
+
|
224 |
+
################################################################################################################
|
225 |
+
def store_semantic_result(username, text, analysis_result):
|
226 |
+
if analysis_collection is None:
|
227 |
+
logger.error("La conexión a MongoDB no está inicializada")
|
228 |
+
return False
|
229 |
+
try:
|
230 |
+
buf = io.BytesIO()
|
231 |
+
analysis_result['relations_graph'].savefig(buf, format='png')
|
232 |
+
buf.seek(0)
|
233 |
+
img_str = base64.b64encode(buf.getvalue()).decode('utf-8')
|
234 |
+
analysis_document = {
|
235 |
+
'username': username,
|
236 |
+
'timestamp': datetime.utcnow(),
|
237 |
+
'text': text,
|
238 |
+
'entities': analysis_result['entities'],
|
239 |
+
'key_concepts': analysis_result['key_concepts'],
|
240 |
+
'network_diagram': img_str, # Cambiado de 'relations_graph' a 'network_diagram'
|
241 |
+
'analysis_type': 'semantic'
|
242 |
+
}
|
243 |
+
result = analysis_collection.insert_one(analysis_document)
|
244 |
+
logger.info(f"Análisis semántico guardado con ID: {result.inserted_id} para el usuario: {username}")
|
245 |
+
logger.info(f"Longitud de la imagen guardada: {len(img_str)}")
|
246 |
+
return True
|
247 |
+
except Exception as e:
|
248 |
+
logger.error(f"Error al guardar el análisis semántico para el usuario {username}: {str(e)}")
|
249 |
+
return False
|
250 |
+
|
251 |
+
###############################################################################################################
|
252 |
+
|
253 |
+
def store_discourse_analysis_result(username, text1, text2, graph1, graph2):
|
254 |
+
try:
|
255 |
+
# Crear una nueva figura combinada
|
256 |
+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
|
257 |
+
|
258 |
+
# Añadir la primera imagen con título
|
259 |
+
ax1.imshow(graph1.get_figure().canvas.renderer.buffer_rgba())
|
260 |
+
ax1.set_title("Documento Patrón: Relaciones semánticas relevantes")
|
261 |
+
ax1.axis('off')
|
262 |
+
|
263 |
+
# Añadir la segunda imagen con título
|
264 |
+
ax2.imshow(graph2.get_figure().canvas.renderer.buffer_rgba())
|
265 |
+
ax2.set_title("Documento Comparado con el documento patrón: Relaciones semánticas relevantes")
|
266 |
+
ax2.axis('off')
|
267 |
+
|
268 |
+
# Ajustar el diseño
|
269 |
+
plt.tight_layout()
|
270 |
+
|
271 |
+
# Convertir la figura combinada a una imagen base64
|
272 |
+
buf = io.BytesIO()
|
273 |
+
fig.savefig(buf, format='png')
|
274 |
+
buf.seek(0)
|
275 |
+
img_str = base64.b64encode(buf.getvalue()).decode('utf-8')
|
276 |
+
|
277 |
+
# Cerrar las figuras para liberar memoria
|
278 |
+
plt.close(fig)
|
279 |
+
plt.close(graph1.get_figure())
|
280 |
+
plt.close(graph2.get_figure())
|
281 |
+
|
282 |
+
analysis_document = {
|
283 |
+
'username': username,
|
284 |
+
'timestamp': datetime.utcnow(),
|
285 |
+
'text1': text1,
|
286 |
+
'text2': text2,
|
287 |
+
'combined_graph': img_str,
|
288 |
+
'analysis_type': 'discourse'
|
289 |
+
}
|
290 |
+
|
291 |
+
result = analysis_collection.insert_one(analysis_document)
|
292 |
+
|
293 |
+
logger.info(f"Análisis discursivo guardado con ID: {result.inserted_id} para el usuario: {username}")
|
294 |
+
return True
|
295 |
+
except Exception as e:
|
296 |
+
logger.error(f"Error al guardar el análisis discursivo para el usuario {username}: {str(e)}")
|
297 |
+
return False
|
298 |
+
|
299 |
+
###############################################################################################################
|
300 |
+
def store_chat_history(username, messages):
|
301 |
+
try:
|
302 |
+
logger.info(f"Attempting to save chat history for user: {username}")
|
303 |
+
logger.debug(f"Messages to save: {messages}")
|
304 |
+
|
305 |
+
chat_document = {
|
306 |
+
'username': username,
|
307 |
+
'timestamp': datetime.utcnow(),
|
308 |
+
'messages': messages
|
309 |
+
}
|
310 |
+
result = chat_collection.insert_one(chat_document)
|
311 |
+
logger.info(f"Chat history saved with ID: {result.inserted_id} for user: {username}")
|
312 |
+
logger.debug(f"Chat content: {messages}")
|
313 |
+
return True
|
314 |
+
except Exception as e:
|
315 |
+
logger.error(f"Error saving chat history for user {username}: {str(e)}")
|
316 |
+
return False
|
317 |
+
|
318 |
+
#######################################################################################################
|
319 |
+
def get_student_data(username):
|
320 |
+
if analysis_collection is None or chat_collection is None:
|
321 |
+
logger.error("La conexión a MongoDB no está inicializada")
|
322 |
+
return None
|
323 |
+
|
324 |
+
formatted_data = {
|
325 |
+
"username": username,
|
326 |
+
"entries": [],
|
327 |
+
"entries_count": 0,
|
328 |
+
"word_count": {},
|
329 |
+
"semantic_analyses": [],
|
330 |
+
"discourse_analyses": [],
|
331 |
+
"chat_history": []
|
332 |
+
}
|
333 |
+
|
334 |
+
try:
|
335 |
+
logger.info(f"Buscando datos de análisis para el usuario: {username}")
|
336 |
+
cursor = analysis_collection.find({"username": username})
|
337 |
+
|
338 |
+
for entry in cursor:
|
339 |
+
formatted_entry = {
|
340 |
+
"timestamp": entry.get("timestamp", datetime.utcnow()),
|
341 |
+
"text": entry.get("text", ""),
|
342 |
+
"analysis_type": entry.get("analysis_type", "morphosyntax")
|
343 |
+
}
|
344 |
+
|
345 |
+
if formatted_entry["analysis_type"] == "morphosyntax":
|
346 |
+
formatted_entry.update({
|
347 |
+
"word_count": entry.get("word_count", {}),
|
348 |
+
"arc_diagrams": entry.get("arc_diagrams", [])
|
349 |
+
})
|
350 |
+
for category, count in formatted_entry["word_count"].items():
|
351 |
+
formatted_data["word_count"][category] = formatted_data["word_count"].get(category, 0) + count
|
352 |
+
|
353 |
+
elif formatted_entry["analysis_type"] == "semantic":
|
354 |
+
formatted_entry["network_diagram"] = entry.get("network_diagram", "")
|
355 |
+
formatted_data["semantic_analyses"].append(formatted_entry)
|
356 |
+
|
357 |
+
elif formatted_entry["analysis_type"] == "discourse":
|
358 |
+
formatted_entry.update({
|
359 |
+
"text1": entry.get("text1", ""),
|
360 |
+
"text2": entry.get("text2", ""),
|
361 |
+
"combined_graph": entry.get("combined_graph", "")
|
362 |
+
})
|
363 |
+
formatted_data["discourse_analyses"].append(formatted_entry)
|
364 |
+
|
365 |
+
formatted_data["entries"].append(formatted_entry)
|
366 |
+
|
367 |
+
formatted_data["entries_count"] = len(formatted_data["entries"])
|
368 |
+
formatted_data["entries"].sort(key=lambda x: x["timestamp"], reverse=True)
|
369 |
+
|
370 |
+
for entry in formatted_data["entries"]:
|
371 |
+
entry["timestamp"] = entry["timestamp"].isoformat()
|
372 |
+
|
373 |
+
except Exception as e:
|
374 |
+
logger.error(f"Error al obtener datos de análisis del estudiante {username}: {str(e)}")
|
375 |
+
|
376 |
+
try:
|
377 |
+
logger.info(f"Buscando historial de chat para el usuario: {username}")
|
378 |
+
chat_cursor = chat_collection.find({"username": username})
|
379 |
+
for chat in chat_cursor:
|
380 |
+
formatted_chat = {
|
381 |
+
"timestamp": chat["timestamp"].isoformat(),
|
382 |
+
"messages": chat["messages"]
|
383 |
+
}
|
384 |
+
formatted_data["chat_history"].append(formatted_chat)
|
385 |
+
|
386 |
+
formatted_data["chat_history"].sort(key=lambda x: x["timestamp"], reverse=True)
|
387 |
+
|
388 |
+
except Exception as e:
|
389 |
+
logger.error(f"Error al obtener historial de chat del estudiante {username}: {str(e)}")
|
390 |
+
|
391 |
+
logger.info(f"Datos formateados para {username}: {formatted_data}")
|
392 |
+
return formatted_data
|