338 lines
8.4 KiB
Python
338 lines
8.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Student Management System
|
|
==========================
|
|
Terminal CRUD application for STUDENT / GUARDIAN / ATTENDANCE, backed by
|
|
MySQL on 10.10.0.160:3306 (student_db).
|
|
|
|
On startup this:
|
|
1. Connects to the MySQL server.
|
|
2. Verifies (or creates) the student_db database.
|
|
3. Verifies (or creates) the STUDENT, GUARDIAN and ATTENDANCE tables.
|
|
4. Launches an interactive terminal menu for CRUD operations.
|
|
|
|
Run:
|
|
python3 main.py
|
|
"""
|
|
|
|
import sys
|
|
|
|
import mysql.connector
|
|
|
|
import crud
|
|
import db
|
|
from ui_helpers import (
|
|
clear_hint,
|
|
pause,
|
|
print_table,
|
|
prompt_attendance_status,
|
|
prompt_date,
|
|
prompt_guid,
|
|
prompt_int,
|
|
prompt_phone,
|
|
prompt_str,
|
|
)
|
|
|
|
|
|
# ============================================================ GUARDIAN UI ==
|
|
|
|
def menu_guardians(conn):
|
|
while True:
|
|
clear_hint()
|
|
print("GUARDIAN MANAGEMENT")
|
|
print(" 1. List all guardians")
|
|
print(" 2. View a guardian")
|
|
print(" 3. Add a guardian")
|
|
print(" 4. Update a guardian")
|
|
print(" 5. Delete a guardian")
|
|
print(" 0. Back to main menu")
|
|
choice = input("\nChoice: ").strip()
|
|
|
|
try:
|
|
if choice == "1":
|
|
print()
|
|
print_table(crud.list_guardians(conn))
|
|
pause()
|
|
elif choice == "2":
|
|
guid = prompt_guid()
|
|
row = crud.get_guardian(conn, guid)
|
|
print()
|
|
print_table([row] if row else [])
|
|
pause()
|
|
elif choice == "3":
|
|
add_guardian(conn)
|
|
elif choice == "4":
|
|
update_guardian(conn)
|
|
elif choice == "5":
|
|
delete_guardian(conn)
|
|
elif choice == "0":
|
|
return
|
|
else:
|
|
print(" Invalid choice.")
|
|
except crud.CrudError as e:
|
|
print(f"\n [ERROR] {e}")
|
|
pause()
|
|
|
|
|
|
def add_guardian(conn):
|
|
print("\n-- Add Guardian --")
|
|
guid = prompt_guid()
|
|
gname = prompt_str("Guardian name", max_len=20)
|
|
gphone = prompt_phone()
|
|
gaddress = prompt_str("Guardian address", max_len=30)
|
|
crud.create_guardian(conn, guid, gname, gphone, gaddress)
|
|
print(f" [OK] Guardian {guid} added.")
|
|
pause()
|
|
|
|
|
|
def update_guardian(conn):
|
|
print("\n-- Update Guardian --")
|
|
guid = prompt_guid()
|
|
existing = crud.get_guardian(conn, guid)
|
|
if not existing:
|
|
print(" No guardian found with that GUID.")
|
|
pause()
|
|
return
|
|
print(f" Current: {existing}")
|
|
print(" (Press Enter to leave a field unchanged)")
|
|
gname = prompt_str("New name", max_len=20, allow_blank_skip=True)
|
|
gphone_raw = input("New phone (10 digits, blank = unchanged, 'clear' = remove): ").strip()
|
|
gphone = existing["GPhone"]
|
|
if gphone_raw.lower() == "clear":
|
|
gphone = None
|
|
elif gphone_raw:
|
|
gphone = prompt_phone_validate(gphone_raw)
|
|
if gphone is None:
|
|
print(" Invalid phone — update aborted for that field.")
|
|
gphone = existing["GPhone"]
|
|
gaddress = prompt_str("New address", max_len=30, allow_blank_skip=True)
|
|
|
|
crud.update_guardian(conn, guid, gname=gname, gphone=gphone, gaddress=gaddress)
|
|
print(" [OK] Guardian updated.")
|
|
pause()
|
|
|
|
|
|
def prompt_phone_validate(raw):
|
|
import re
|
|
return raw if re.match(r"^\d{10}$", raw) else None
|
|
|
|
|
|
def delete_guardian(conn):
|
|
print("\n-- Delete Guardian --")
|
|
guid = prompt_guid()
|
|
confirm = input(f" Type 'yes' to confirm deleting guardian {guid}: ").strip().lower()
|
|
if confirm != "yes":
|
|
print(" Cancelled.")
|
|
pause()
|
|
return
|
|
rows = crud.delete_guardian(conn, guid)
|
|
if rows:
|
|
print(" [OK] Guardian deleted.")
|
|
else:
|
|
print(" No guardian found with that GUID.")
|
|
pause()
|
|
|
|
|
|
# ============================================================= STUDENT UI ==
|
|
|
|
def menu_students(conn):
|
|
while True:
|
|
clear_hint()
|
|
print("STUDENT MANAGEMENT")
|
|
print(" 1. List all students")
|
|
print(" 2. View a student")
|
|
print(" 3. Add a student")
|
|
print(" 4. Update a student")
|
|
print(" 5. Delete a student")
|
|
print(" 0. Back to main menu")
|
|
choice = input("\nChoice: ").strip()
|
|
|
|
try:
|
|
if choice == "1":
|
|
print()
|
|
print_table(crud.list_students(conn))
|
|
pause()
|
|
elif choice == "2":
|
|
roll = prompt_int("Roll number", max_digits=3)
|
|
row = crud.get_student(conn, roll)
|
|
print()
|
|
print_table([row] if row else [])
|
|
pause()
|
|
elif choice == "3":
|
|
add_student(conn)
|
|
elif choice == "4":
|
|
update_student(conn)
|
|
elif choice == "5":
|
|
delete_student(conn)
|
|
elif choice == "0":
|
|
return
|
|
else:
|
|
print(" Invalid choice.")
|
|
except crud.CrudError as e:
|
|
print(f"\n [ERROR] {e}")
|
|
pause()
|
|
|
|
|
|
def add_student(conn):
|
|
print("\n-- Add Student --")
|
|
roll = prompt_int("Roll number (max 3 digits)", max_digits=3)
|
|
sname = prompt_str("Student name", max_len=20)
|
|
dob = prompt_date("Date of birth")
|
|
print(" (Guardian must already exist — add them first via Guardian Management if needed)")
|
|
guid = prompt_guid()
|
|
crud.create_student(conn, roll, sname, dob, guid)
|
|
print(f" [OK] Student {roll} added.")
|
|
pause()
|
|
|
|
|
|
def update_student(conn):
|
|
print("\n-- Update Student --")
|
|
roll = prompt_int("Roll number", max_digits=3)
|
|
existing = crud.get_student(conn, roll)
|
|
if not existing:
|
|
print(" No student found with that roll number.")
|
|
pause()
|
|
return
|
|
print(f" Current: {existing}")
|
|
print(" (Press Enter to leave a field unchanged)")
|
|
sname = prompt_str("New name", max_len=20, allow_blank_skip=True)
|
|
dob = prompt_date("New date of birth", allow_blank_skip=True)
|
|
guid = prompt_guid("New guardian GUID", allow_blank_skip=True)
|
|
|
|
crud.update_student(conn, roll, sname=sname, sdateofbirth=dob, guid=guid)
|
|
print(" [OK] Student updated.")
|
|
pause()
|
|
|
|
|
|
def delete_student(conn):
|
|
print("\n-- Delete Student --")
|
|
roll = prompt_int("Roll number", max_digits=3)
|
|
confirm = input(f" Type 'yes' to confirm deleting student {roll} "
|
|
f"(also deletes their attendance records): ").strip().lower()
|
|
if confirm != "yes":
|
|
print(" Cancelled.")
|
|
pause()
|
|
return
|
|
rows = crud.delete_student(conn, roll)
|
|
if rows:
|
|
print(" [OK] Student deleted.")
|
|
else:
|
|
print(" No student found with that roll number.")
|
|
pause()
|
|
|
|
|
|
# =========================================================== ATTENDANCE UI ==
|
|
|
|
def menu_attendance(conn):
|
|
while True:
|
|
clear_hint()
|
|
print("ATTENDANCE MANAGEMENT")
|
|
print(" 1. Mark / update attendance for a student")
|
|
print(" 2. View attendance by date")
|
|
print(" 3. View attendance history for a student")
|
|
print(" 4. Delete an attendance record")
|
|
print(" 0. Back to main menu")
|
|
choice = input("\nChoice: ").strip()
|
|
|
|
try:
|
|
if choice == "1":
|
|
mark_attendance(conn)
|
|
elif choice == "2":
|
|
d = prompt_date("Date")
|
|
print()
|
|
print_table(crud.list_attendance_by_date(conn, d))
|
|
pause()
|
|
elif choice == "3":
|
|
roll = prompt_int("Roll number", max_digits=3)
|
|
print()
|
|
print_table(crud.list_attendance_by_student(conn, roll))
|
|
pause()
|
|
elif choice == "4":
|
|
delete_attendance(conn)
|
|
elif choice == "0":
|
|
return
|
|
else:
|
|
print(" Invalid choice.")
|
|
except crud.CrudError as e:
|
|
print(f"\n [ERROR] {e}")
|
|
pause()
|
|
|
|
|
|
def mark_attendance(conn):
|
|
print("\n-- Mark Attendance --")
|
|
roll = prompt_int("Roll number", max_digits=3)
|
|
if not crud.get_student(conn, roll):
|
|
print(" No student found with that roll number.")
|
|
pause()
|
|
return
|
|
d = prompt_date("Date")
|
|
status = prompt_attendance_status()
|
|
result = crud.mark_attendance(conn, d, roll, status)
|
|
print(f" [OK] Attendance {result} for student {roll} on {d}: {status}")
|
|
pause()
|
|
|
|
|
|
def delete_attendance(conn):
|
|
print("\n-- Delete Attendance Record --")
|
|
roll = prompt_int("Roll number", max_digits=3)
|
|
d = prompt_date("Date")
|
|
confirm = input(" Type 'yes' to confirm: ").strip().lower()
|
|
if confirm != "yes":
|
|
print(" Cancelled.")
|
|
pause()
|
|
return
|
|
rows = crud.delete_attendance(conn, d, roll)
|
|
if rows:
|
|
print(" [OK] Attendance record deleted.")
|
|
else:
|
|
print(" No matching attendance record found.")
|
|
pause()
|
|
|
|
|
|
# ================================================================ MAIN ====
|
|
|
|
def main_menu(conn):
|
|
while True:
|
|
clear_hint()
|
|
print("STUDENT MANAGEMENT SYSTEM")
|
|
print(" 1. Guardian management")
|
|
print(" 2. Student management")
|
|
print(" 3. Attendance management")
|
|
print(" 0. Exit")
|
|
choice = input("\nChoice: ").strip()
|
|
|
|
if choice == "1":
|
|
menu_guardians(conn)
|
|
elif choice == "2":
|
|
menu_students(conn)
|
|
elif choice == "3":
|
|
menu_attendance(conn)
|
|
elif choice == "0":
|
|
print("\nGoodbye!")
|
|
return
|
|
else:
|
|
print(" Invalid choice.")
|
|
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print(" STUDENT MANAGEMENT SYSTEM - Startup")
|
|
print("=" * 60)
|
|
|
|
conn = db.get_connection()
|
|
|
|
print("\nAll checks passed. Starting application...\n")
|
|
try:
|
|
main_menu(conn)
|
|
except KeyboardInterrupt:
|
|
print("\n\nInterrupted. Exiting.")
|
|
except mysql.connector.Error as e:
|
|
print(f"\n[ERROR] Lost database connection: {e}")
|
|
sys.exit(1)
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|