""" CRUD operations for GUARDIAN, STUDENT and ATTENDANCE. Every function takes an open mysql-connector-python connection as its first argument and manages its own commit/rollback. All functions return plain data (dicts/lists/bool) so this module has no dependency on the UI layer. """ import mysql.connector class CrudError(Exception): """Raised for expected, user-facing data errors (bad FK, duplicate key, etc).""" pass def _run(conn, sql, params=None, fetch=None): """ Helper: execute a statement, commit, and optionally fetch results. fetch: None (no fetch), 'one', or 'all' Translates common MySQL errors into CrudError with a friendly message. """ cur = conn.cursor(dictionary=True) try: cur.execute(sql, params or ()) result = None if fetch == "one": result = cur.fetchone() elif fetch == "all": result = cur.fetchall() conn.commit() return result if fetch else cur.rowcount except mysql.connector.errors.IntegrityError as e: conn.rollback() if e.errno == 1062: raise CrudError("A record with that primary/unique key already exists.") if e.errno in (1451, 1452): raise CrudError( "That operation violates a foreign key constraint " "(referenced record missing, or still referenced by other rows)." ) raise CrudError(f"Database integrity error: {e}") except mysql.connector.errors.DataError as e: conn.rollback() raise CrudError(f"Invalid data for one or more fields: {e}") except mysql.connector.Error as e: conn.rollback() raise CrudError(f"Database error: {e}") finally: cur.close() # ---------------------------------------------------------------- GUARDIAN -- def create_guardian(conn, guid, gname, gphone, gaddress): _run( conn, "INSERT INTO GUARDIAN (GUID, GName, GPhone, GAddress) VALUES (%s, %s, %s, %s)", (guid, gname, gphone, gaddress), ) def get_guardian(conn, guid): return _run(conn, "SELECT * FROM GUARDIAN WHERE GUID = %s", (guid,), fetch="one") def list_guardians(conn): return _run(conn, "SELECT * FROM GUARDIAN ORDER BY GUID", fetch="all") def update_guardian(conn, guid, gname=None, gphone=None, gaddress=None): fields, params = [], [] if gname is not None: fields.append("GName = %s") params.append(gname) if gphone is not None: fields.append("GPhone = %s") params.append(gphone) if gaddress is not None: fields.append("GAddress = %s") params.append(gaddress) if not fields: return 0 params.append(guid) return _run(conn, f"UPDATE GUARDIAN SET {', '.join(fields)} WHERE GUID = %s", params) def delete_guardian(conn, guid): return _run(conn, "DELETE FROM GUARDIAN WHERE GUID = %s", (guid,)) # ----------------------------------------------------------------- STUDENT -- def create_student(conn, roll_number, sname, sdateofbirth, guid): _run( conn, "INSERT INTO STUDENT (RollNumber, SName, SDateofBirth, GUID) VALUES (%s, %s, %s, %s)", (roll_number, sname, sdateofbirth, guid), ) def get_student(conn, roll_number): return _run(conn, "SELECT * FROM STUDENT WHERE RollNumber = %s", (roll_number,), fetch="one") def list_students(conn): return _run( conn, """ SELECT s.RollNumber, s.SName, s.SDateofBirth, s.GUID, g.GName AS GuardianName FROM STUDENT s LEFT JOIN GUARDIAN g ON s.GUID = g.GUID ORDER BY s.RollNumber """, fetch="all", ) def update_student(conn, roll_number, sname=None, sdateofbirth=None, guid=None): fields, params = [], [] if sname is not None: fields.append("SName = %s") params.append(sname) if sdateofbirth is not None: fields.append("SDateofBirth = %s") params.append(sdateofbirth) if guid is not None: fields.append("GUID = %s") params.append(guid) if not fields: return 0 params.append(roll_number) return _run(conn, f"UPDATE STUDENT SET {', '.join(fields)} WHERE RollNumber = %s", params) def delete_student(conn, roll_number): return _run(conn, "DELETE FROM STUDENT WHERE RollNumber = %s", (roll_number,)) # --------------------------------------------------------------- ATTENDANCE -- def mark_attendance(conn, attendance_date, roll_number, status): """Insert attendance; if a record for that date+student exists, update it instead.""" existing = _run( conn, "SELECT 1 FROM ATTENDANCE WHERE AttendanceDate = %s AND RollNumber = %s", (attendance_date, roll_number), fetch="one", ) if existing: _run( conn, "UPDATE ATTENDANCE SET AttendanceStatus = %s " "WHERE AttendanceDate = %s AND RollNumber = %s", (status, attendance_date, roll_number), ) return "updated" else: _run( conn, "INSERT INTO ATTENDANCE (AttendanceDate, RollNumber, AttendanceStatus) " "VALUES (%s, %s, %s)", (attendance_date, roll_number, status), ) return "created" def get_attendance(conn, attendance_date, roll_number): return _run( conn, "SELECT * FROM ATTENDANCE WHERE AttendanceDate = %s AND RollNumber = %s", (attendance_date, roll_number), fetch="one", ) def list_attendance_by_date(conn, attendance_date): return _run( conn, """ SELECT a.AttendanceDate, a.RollNumber, s.SName, a.AttendanceStatus FROM ATTENDANCE a JOIN STUDENT s ON a.RollNumber = s.RollNumber WHERE a.AttendanceDate = %s ORDER BY a.RollNumber """, (attendance_date,), fetch="all", ) def list_attendance_by_student(conn, roll_number): return _run( conn, "SELECT * FROM ATTENDANCE WHERE RollNumber = %s ORDER BY AttendanceDate", (roll_number,), fetch="all", ) def delete_attendance(conn, attendance_date, roll_number): return _run( conn, "DELETE FROM ATTENDANCE WHERE AttendanceDate = %s AND RollNumber = %s", (attendance_date, roll_number), )