#modules/database/sql_db.py from .database_init import get_container from datetime import datetime, timezone import logging import bcrypt import uuid logger = logging.getLogger(__name__) def get_user(username, role=None): container = get_container("users") try: # Consulta básica por ID que es el partition key items = list(container.query_items( query=f"SELECT * FROM c WHERE c.id = @username", parameters=[{"name": "@username", "value": username}], partition_key=username )) # Si se especifica rol, filtrar por rol if role and items: items = [item for item in items if item['role'] == role] return items[0] if items else None except Exception as e: logger.error(f"Error al obtener usuario {username}: {str(e)}") return None def get_admin_user(username): return get_user(username, role='Administrador') def get_student_user(username): return get_user(username, role='Estudiante') def get_teacher_user(username): return get_user(username, role='Profesor') def create_user(username, password, role, additional_info=None): """Crea un nuevo usuario""" container = get_container("users") if not container: logger.error("No se pudo obtener el contenedor de usuarios") return False try: user_data = { 'id': username, # Este campo es el partition key 'password': password, 'role': role, 'timestamp': datetime.now(timezone.utc).isoformat(), 'additional_info': additional_info or {} } # Usar el id como partition key container.create_item( body=user_data, partition_key=username ) logger.info(f"Usuario {role} creado: {username}") return True except Exception as e: logger.error(f"Error al crear usuario {role}: {str(e)}") return False def create_student_user(username, password, additional_info=None): return create_user(username, password, 'Estudiante', additional_info) def create_teacher_user(username, password, additional_info=None): return create_user(username, password, 'Profesor', additional_info) def create_admin_user(username, password, additional_info=None): return create_user(username, password, 'Administrador', additional_info) def record_login(username): """Registra el inicio de sesión de un usuario""" try: container = get_container("users_sessions") if not container: logger.error("No se pudo obtener el contenedor users_sessions") return None session_id = str(uuid.uuid4()) session_doc = { "id": session_id, "type": "session", "username": username, "loginTime": datetime.now(timezone.utc).isoformat(), "additional_info": {} } result = container.create_item( body=session_doc, enable_cross_partition_query=True ) logger.info(f"Sesión {session_id} registrada para {username}") return session_id except Exception as e: logger.error(f"Error registrando login: {str(e)}") return None def record_logout(username, session_id): """Registra el cierre de sesión y calcula la duración""" try: container = get_container("users_sessions") if not container: logger.error("No se pudo obtener el contenedor users_sessions") return False items = list(container.query_items( query="SELECT * FROM c WHERE c.id = @id AND c.username = @username", parameters=[ {"name": "@id", "value": session_id}, {"name": "@username", "value": username} ], enable_cross_partition_query=True )) if not items: logger.warning(f"Sesión no encontrada: {session_id}") return False session = items[0] login_time = datetime.fromisoformat(session['loginTime'].rstrip('Z')) logout_time = datetime.now(timezone.utc) duration = int((logout_time - login_time).total_seconds()) session.update({ "logoutTime": logout_time.isoformat(), "sessionDuration": duration }) container.upsert_item( body=session, enable_cross_partition_query=True ) logger.info(f"Sesión {session_id} cerrada para {username}, duración: {duration}s") return True except Exception as e: logger.error(f"Error registrando logout: {str(e)}") return False def get_recent_sessions(limit=10): """Obtiene las sesiones más recientes""" try: container = get_container("users_sessions") if not container: logger.error("No se pudo obtener el contenedor users_sessions") return [] sessions = list(container.query_items( query=""" SELECT c.username, c.loginTime, c.logoutTime, c.sessionDuration FROM c WHERE c.type = 'session' ORDER BY c.loginTime DESC OFFSET 0 LIMIT @limit """, parameters=[{"name": "@limit", "value": limit}], enable_cross_partition_query=True )) clean_sessions = [] for session in sessions: try: clean_sessions.append({ "username": session["username"], "loginTime": session["loginTime"], "logoutTime": session.get("logoutTime", "Activo"), "sessionDuration": session.get("sessionDuration", 0) }) except KeyError as e: logger.warning(f"Sesión con datos incompletos: {e}") continue return clean_sessions except Exception as e: logger.error(f"Error obteniendo sesiones recientes: {str(e)}") return [] def get_user_total_time(username): """Obtiene el tiempo total que un usuario ha pasado en la plataforma""" try: container = get_container("users_sessions") if not container: return None result = list(container.query_items( query=""" SELECT VALUE SUM(c.sessionDuration) FROM c WHERE c.type = 'session' AND c.username = @username AND IS_DEFINED(c.sessionDuration) """, parameters=[{"name": "@username", "value": username}], enable_cross_partition_query=True )) return result[0] if result and result[0] is not None else 0 except Exception as e: logger.error(f"Error obteniendo tiempo total: {str(e)}") return 0 # Agregar estas funciones en sql_db.py def update_student_user(username, new_info): """Actualiza la información de un estudiante""" container = get_container("users") try: # Primero obtener el usuario existente user = get_student_user(username) if user: # Actualizar la información if 'additional_info' in user: user['additional_info'].update(new_info) else: user['additional_info'] = new_info # Actualizar el documento usando el partition key correcto container.upsert_item( body=user, partition_key=username ) logger.info(f"Información del estudiante actualizada: {username}") return True else: logger.warning(f"Intento de actualizar estudiante no existente: {username}") return False except Exception as e: logger.error(f"Error al actualizar información del estudiante {username}: {str(e)}") return False def delete_student_user(username): """Elimina un estudiante""" container = get_container("users") try: # Verificar que el usuario existe y es un estudiante user = get_student_user(username) if user: # Eliminar usando el partition key correcto container.delete_item( item=user['id'], partition_key=username ) logger.info(f"Estudiante eliminado: {username}") return True else: logger.warning(f"Intento de eliminar estudiante no existente: {username}") return False except Exception as e: logger.error(f"Error al eliminar estudiante {username}: {str(e)}") return False __all__ = [ 'get_user', 'get_admin_user', 'get_student_user', 'get_teacher_user', 'create_user', 'create_student_user', 'create_teacher_user', 'create_admin_user', 'update_student_user', # Agregada 'delete_student_user', # Agregada 'record_login', 'record_logout', 'get_recent_sessions', 'get_user_total_time' ]