import os from typing import Dict, Any from modules.logger_tool import initialise_logger import modules.database.tools.neo4j_driver_tools as driver_tools import modules.database.tools.neo4j_session_tools as session_tools class Neo4jService: """Service for managing Neo4j database operations""" def __init__(self): self.logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True) self.driver = driver_tools.get_driver() def check_database_exists(self, database_name: str) -> Dict[str, Any]: """Check if a Neo4j database exists Args: database_name (str): Name of the database to check Returns: Dict[str, Any]: Result containing existence status and operation status """ try: with self.driver.session() as session: result = session.run( "SHOW DATABASES YIELD name WHERE name = $name", name=database_name ) exists = bool(result.single()) return { "exists": exists, "status": "success" } except Exception as e: self.logger.error(f"Error checking database {database_name}: {str(e)}") return { "exists": False, "status": "error", "message": str(e) } def create_database(self, db_name: str) -> Dict[str, Any]: """Creates a Neo4j database with the given name Args: db_name (str): Name of the database to create Returns: Dict[str, Any]: Result containing operation status and message """ try: # First check if database exists exists_result = self.check_database_exists(db_name) if exists_result["status"] == "error": return exists_result if not exists_result["exists"]: with self.driver.session() as session: session_tools.create_database(session, db_name) self.logger.info(f"Created database {db_name}") return { "status": "success", "message": f"Database {db_name} created successfully" } else: self.logger.info(f"Database {db_name} already exists") return { "status": "success", "message": f"Database {db_name} already exists" } except Exception as e: self.logger.error(f"Error creating database {db_name}: {str(e)}") return {"status": "error", "message": str(e)} def initialize_schema(self, database_name: str) -> Dict[str, Any]: """Initialize Neo4j schema (constraints and indexes) for a database Args: database_name (str): Name of the database to initialize schema for Returns: Dict[str, Any]: Result containing operation status and message """ try: with self.driver.session(database=database_name) as session: # Create constraints constraints = [ "CREATE CONSTRAINT IF NOT EXISTS FOR (n:School) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:Department) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:Subject) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:YearGroup) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:Class) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:Teacher) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:Student) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:Calendar) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:Term) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:Week) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:Day) REQUIRE n.unique_id IS UNIQUE", "CREATE CONSTRAINT IF NOT EXISTS FOR (n:Period) REQUIRE n.unique_id IS UNIQUE" ] # Create indexes indexes = [ "CREATE INDEX IF NOT EXISTS FOR (n:School) ON (n.urn)", "CREATE INDEX IF NOT EXISTS FOR (n:Department) ON (n.department_name)", "CREATE INDEX IF NOT EXISTS FOR (n:Subject) ON (n.subject_name)", "CREATE INDEX IF NOT EXISTS FOR (n:YearGroup) ON (n.year_group)", "CREATE INDEX IF NOT EXISTS FOR (n:Class) ON (n.class_name)", "CREATE INDEX IF NOT EXISTS FOR (n:Teacher) ON (n.email)", "CREATE INDEX IF NOT EXISTS FOR (n:Student) ON (n.email)", "CREATE INDEX IF NOT EXISTS FOR (n:Calendar) ON (n.calendar_name)", "CREATE INDEX IF NOT EXISTS FOR (n:Term) ON (n.term_name)", "CREATE INDEX IF NOT EXISTS FOR (n:Week) ON (n.week_number)", "CREATE INDEX IF NOT EXISTS FOR (n:Day) ON (n.date)", "CREATE INDEX IF NOT EXISTS FOR (n:Period) ON (n.period_name)" ] # Execute all constraints for constraint in constraints: session.run(constraint) # Execute all indexes for index in indexes: session.run(index) self.logger.info(f"Successfully initialized schema for database {database_name}") return { "status": "success", "message": f"Schema initialized successfully for database {database_name}" } except Exception as e: self.logger.error(f"Error initializing schema for database {database_name}: {str(e)}") return { "status": "error", "message": str(e) } def delete_database(self, db_name: str) -> Dict[str, Any]: """Deletes a Neo4j database Args: db_name (str): Name of the database to delete Returns: Dict[str, Any]: Result containing operation status and message """ try: exists_result = self.check_database_exists(db_name) if exists_result["status"] == "error": return exists_result if exists_result["exists"]: with self.driver.session() as session: session_tools.reset_database_in_session(session) self.logger.info(f"Deleted database {db_name}") return { "status": "success", "message": f"Database {db_name} deleted successfully" } else: return { "status": "success", "message": f"Database {db_name} does not exist" } except Exception as e: self.logger.error(f"Error deleting database {db_name}: {str(e)}") return {"status": "error", "message": str(e)} def check_node_exists(self, database_name: str, node_label: str) -> Dict[str, Any]: """Check if any nodes with the given label exist in the specified database Args: database_name (str): Name of the database to check node_label (str): Label of the node type to check for Returns: Dict[str, Any]: Result containing count and operation status """ try: with self.driver.session(database=database_name) as session: nodes = session_tools.find_nodes_by_label(session, node_label) count = len(nodes) return { "exists": count > 0, "count": count, "status": "success" } except Exception as e: self.logger.error(f"Error checking for {node_label} nodes in database {database_name}: {str(e)}") return { "exists": False, "count": 0, "status": "error", "message": str(e) }