""" Database layer: connect to MySQL, verify/create the student_db database, and verify/create the STUDENT, GUARDIAN and ATTENDANCE tables per the schema (Tables 9.3, 9.4, 9.5). Schema notes: - GUARDIAN.GUID is the primary key (12-digit Aadhaar number, stored as CHAR(12)). - STUDENT.GUID is a foreign key referencing GUARDIAN.GUID. - ATTENDANCE has a composite primary key (AttendanceDate, RollNumber), and RollNumber is also a foreign key referencing STUDENT.RollNumber. - Because STUDENT references GUARDIAN, GUARDIAN must be created first. """ import sys import mysql.connector from mysql.connector import errorcode import config # Table creation statements, in dependency order (GUARDIAN -> STUDENT -> ATTENDANCE) TABLE_DEFINITIONS = { "GUARDIAN": """ CREATE TABLE GUARDIAN ( GUID CHAR(12) NOT NULL, GName VARCHAR(20) NOT NULL, GPhone CHAR(10) NULL, GAddress VARCHAR(30) NOT NULL, PRIMARY KEY (GUID), UNIQUE (GPhone) ) ENGINE=InnoDB """, "STUDENT": """ CREATE TABLE STUDENT ( RollNumber INT NOT NULL, SName VARCHAR(20) NOT NULL, SDateofBirth DATE NOT NULL, GUID CHAR(12) NOT NULL, PRIMARY KEY (RollNumber), FOREIGN KEY (GUID) REFERENCES GUARDIAN(GUID) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINE=InnoDB """, "ATTENDANCE": """ CREATE TABLE ATTENDANCE ( AttendanceDate DATE NOT NULL, RollNumber INT NOT NULL, AttendanceStatus CHAR(1) NOT NULL, PRIMARY KEY (AttendanceDate, RollNumber), FOREIGN KEY (RollNumber) REFERENCES STUDENT(RollNumber) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT chk_attendance_status CHECK (AttendanceStatus IN ('P', 'A')) ) ENGINE=InnoDB """, } # Creation order matters because of foreign keys. TABLE_ORDER = ["GUARDIAN", "STUDENT", "ATTENDANCE"] def connect_server(database=None): """Connect to the MySQL server (optionally selecting a database).""" try: conn = mysql.connector.connect( host=config.DB_HOST, port=config.DB_PORT, user=config.DB_USER, password=config.DB_PASSWORD, database=database, connection_timeout=config.DB_CONNECT_TIMEOUT, autocommit=False, ) return conn except mysql.connector.Error as e: if e.errno == errorcode.ER_ACCESS_DENIED_ERROR: print(f"\n[ERROR] Access denied for user '{config.DB_USER}' — check " f"DB_USER/DB_PASSWORD.") elif database and e.errno == errorcode.ER_BAD_DB_ERROR: print(f"\n[ERROR] Database '{database}' does not exist.") else: print(f"\n[ERROR] Could not connect to MySQL server at " f"{config.DB_HOST}:{config.DB_PORT} as user '{config.DB_USER}'.") print(f" Details: {e}") sys.exit(1) def ensure_database(): """Verify the student_db database exists; create it if it does not.""" conn = connect_server(database=None) try: cur = conn.cursor() try: cur.execute("SHOW DATABASES LIKE %s", (config.DB_NAME,)) exists = cur.fetchone() is not None if exists: print(f"[OK] Database '{config.DB_NAME}' found.") else: print(f"[..] Database '{config.DB_NAME}' not found. Creating it...") cur.execute( f"CREATE DATABASE `{config.DB_NAME}` " "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" ) conn.commit() print(f"[OK] Database '{config.DB_NAME}' created.") finally: cur.close() finally: conn.close() def ensure_tables(conn): """Verify each required table exists; create any that are missing.""" cur = conn.cursor() cur.execute("SHOW TABLES") existing = {row[0] for row in cur.fetchall()} cur.close() for table in TABLE_ORDER: if table in existing: print(f"[OK] Table '{table}' found.") else: print(f"[..] Table '{table}' not found. Creating it...") cur = conn.cursor() try: cur.execute(TABLE_DEFINITIONS[table]) finally: cur.close() conn.commit() print(f"[OK] Table '{table}' created.") def get_connection(): """ Full startup sequence: 1. Ensure the student_db database exists (creating it if needed). 2. Connect to student_db. 3. Ensure all required tables exist (creating any that are missing). Returns an open connection to student_db, ready for use. """ print(f"Connecting to MySQL server at {config.DB_HOST}:{config.DB_PORT} ...") ensure_database() conn = connect_server(database=config.DB_NAME) print(f"[OK] Connected to database '{config.DB_NAME}'.") ensure_tables(conn) return conn