0% found this document useful (0 votes)
21 views117 pages

EduPlan Pro: Automated Timetable Software

EduPlan Pro is an automated timetable generator designed to streamline the scheduling process in educational institutions using Python and MySQL. The software features a user-friendly GUI, conflict-free timetable generation, teacher management, and attendance tracking, while allowing for customization and secure data handling. It aims to enhance efficiency in school administration by minimizing manual effort and errors associated with traditional scheduling methods.

Uploaded by

meenu.pgeps
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
21 views117 pages

EduPlan Pro: Automated Timetable Software

EduPlan Pro is an automated timetable generator designed to streamline the scheduling process in educational institutions using Python and MySQL. The software features a user-friendly GUI, conflict-free timetable generation, teacher management, and attendance tracking, while allowing for customization and secure data handling. It aims to enhance efficiency in school administration by minimizing manual effort and errors associated with traditional scheduling methods.

Uploaded by

meenu.pgeps
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

ABSTRACT

The EduPlan Pro – Automatic Timetable Generator is a Python-based software


application developed to automate and optimize the creation and management of school
timetables. It efficiently schedules classes, teachers, and subjects while eliminating
conflicts and reducing manual effort. The system integrates an intelligent database-
driven architecture using MySQL, allowing secure and persistent data storage for
multiple users and sessions.
EduPlan Pro provides an intuitive Graphical User Interface (GUI) built with Tkinter
and ttk themes, enabling administrators to generate, view, and modify timetables easily.
It offers several advanced features, including teacher management, attendance tracking,
automatic substitution for absentees, and CSV-based timetable export and import. The
system also supports role-based user authentication, theme customization, and school
configuration settings, ensuring both usability and personalization.
The project aims to make timetable management seamless, accurate, and scalable. It
leverages modern programming techniques and database integration to handle complex
scheduling constraints effectively. EduPlan Pro ultimately enhances efficiency in school
administration by minimizing errors, saving time, and improving overall resource
utilization.

PROBLEM STATEMENT:
Creating and managing school timetables manually is a highly complex
and time-consuming task that involves coordinating multiple constraints such as teacher
availability, class schedules, subject requirements, and room assignments. Manual
scheduling often leads to conflicts like overlapping periods, teacher overload, or
unbalanced workloads, making it inefficient and prone to human error. Additionally,
handling changes such as teacher absences or special events requires significant manual
adjustment, further increasing administrative workload. Therefore, there is a need for
an automated timetable management system that can intelligently generate conflict-free
schedules, manage teacher attendance and substitutions, and allow easy modification
and data storage through a user-friendly graphical interface. This system should ensure
accuracy, save time, and enhance the overall efficiency of school operations.

1|Page
CUSTOMIZABLE INPUTS
EduPlan Pro offers flexibility by allowing administrators to customize various settings
and parameters according to the institution’s structure and needs. The key customizable
inputs include:
 Number of Working Days: Specify the working days (e.g., Monday to
Saturday).
 Number of Periods per Day: Define the total number of class periods available
each day.
 Class and Section Names: Add or manage multiple classes and their respective
sections.
 Teacher Details: Enter teacher names, employee IDs, subjects, assigned classes,
and maximum number of periods per day.
 Teacher Availability: Manage attendance and mark absences to automatically
handle substitutions.
 Subjects and Categories: Define subjects under categories such as Science,
Language, Social Studies, etc.
 School Configuration: Customize details like school name, academic year, and
visual theme using configuration files.
 Export/Import Options: Export timetables to CSV files for backup and import
them back when needed.
These dynamic inputs ensure that the system remains adaptable and can efficiently serve
different school environments.
TECHNOLOGIES USED
>Programming Language
 Python 3.10+ — The core programming language used for logic implementation
and GUI development.
>Frontend (Graphical User Interface)
 Tkinter and ttk Themes: For building an interactive and visually appealing user
interface.
 Pillow (PIL): Used for image handling, such as displaying school logos and
icons.

2|Page
>Backend (Database & Configuration)
 MySQL: For managing all application data, including teachers, classes,
attendance, and timetables.
 ConfigParser: For maintaining application configurations such as theme,
academic year, and school details.
 CSV and JSON: For exporting/importing timetables and maintaining backups.
>Security & Validation
 hashlib: For secure password hashing (SHA-256 encryption).
 re (Regular Expressions): For validating inputs like email formats.
 logging: To record activities and maintain error logs for debugging.
>Python Libraries & Modules
 [Link], tkinter, configparser, PIL, csv, json, os, random, datetime,
hashlib, re, logging
Together, these technologies form a reliable, secure, and user-friendly scheduling
system capable of handling real-time school operations efficiently.
EXPECTED OUTCOMES
The primary objective of EduPlan Pro is to simplify timetable generation while ensuring
accuracy and efficiency. The expected outcomes include:
 Automatic Timetable Generation: A complete and conflict-free timetable for
all classes and teachers.
 Teacher Management: Easy addition, editing, and removal of teachers with
subject and workload tracking.
 Data Storage and Security: Secure data handling using MySQL and encrypted
passwords.
 User-Friendly Interface: Simplified navigation with responsive design and
theme customization.
 Export/Import Support: Save or load timetables via CSV files for easy sharing
or backup.
 Scalable Design: Adaptable for different institutions, class sizes, and academic
years.

3|Page
CHALLENGES FACED
 Developing algorithms to prevent scheduling conflicts and balance teacher
workloads.
 Integrating MySQL with Python for dynamic and persistent data handling.
 Designing a modern and interactive GUI using Tkinter with ttk styling.
 Managing real-time updates, such as handling teacher absences and generating
replacements.
 Ensuring security and authentication using password hashing and access roles.
 Implementing export/import functionality while maintaining data consistency.
LEARNING OPPORTUNITIES
 Gained experience in database design and management with MySQL.
 Learned about GUI programming and UX design principles using Tkinter.
 Improved understanding of data encryption and software security techniques.
 Enhanced knowledge of file handling, configuration management, and
modular coding.
 Strengthened skills in software lifecycle management and documentation
writing.
 Learned to apply problem-solving and logical thinking for real-world software
systems.
This project provided deep insight into both technical and practical aspects of
software development, preparing the developers for advanced programming and real-
world application design.

4|Page
INTRODUCTION

Timetable management is one of the most essential and challenging


administrative tasks in any educational institution. It involves carefully allocating
teachers, subjects, and classrooms across multiple classes and periods while ensuring
that no conflicts occur. Traditional manual methods of creating timetables using paper
or spreadsheets are not only time-consuming but also highly prone to human error.
Managing last-minute changes, such as a teacher’s absence or rescheduling classes, adds
further complexity to the process.
EduPlan Pro – Automatic Timetable Generator is a
Python-based application designed to automate and simplify this entire process. The
system uses MySQL as its database backend for efficient data storage and retrieval,
ensuring that all records—such as teacher details, subjects, and timetables—are securely
maintained. With an interactive Graphical User Interface (GUI) built using Tkinter and
ttk themes, the application allows users to generate, view, and edit timetables
dynamically with just a few clicks.
EduPlan Pro introduces several advanced features, including
teacher attendance tracking, automatic substitution management, and CSV-based export
and import of timetables. It also supports role-based authentication, allowing separate
logins for administrators and staff, ensuring controlled access to data. Additionally,
customization options such as theme selection, school name editing, and academic year
configuration make the application adaptable for different educational environments.
By automating the scheduling process, EduPlan Pro
minimizes administrative effort, reduces the chances of scheduling conflicts, and
ensures fair workload distribution among teachers. It provides a structured, efficient,
and scalable solution to modernize how schools manage their academic timetables,
ultimately improving productivity and transparency within the institution.

5|Page
LITERATURE SURVEY

INTRODUCTION
Timetable management is a vital process in every educational institution.
It involves assigning subjects, teachers, and classrooms in a way that ensures there are
no overlaps or conflicts. Traditionally, schools have relied on manual scheduling
methods, which require a lot of time, effort, and coordination. These manual systems
often lead to errors such as double-booked teachers or uneven workloads. With the
advancement of technology, automatic timetable generators have emerged as a solution
to overcome these problems by using programming and algorithms to automate
scheduling efficiently.

EXISTING SYSTEM
In most schools, timetable creation is still done manually or through simple spreadsheet
tools like Microsoft Excel. While this method is straightforward, it has several
limitations:
 Time-Consuming: Preparing a timetable manually takes several hours or even
days, especially for schools with many classes and teachers.
 Error-Prone: Mistakes such as overlapping classes, assigning one teacher to
multiple periods at the same time, or missing periods are common.
 Lack of Flexibility: If any teacher is absent or changes occur, administrators need
to redo large portions of the timetable.
 Difficult to Maintain: Manual records are harder to update, back up, or share
across departments.
 No Data Analysis: Manual systems do not provide insights into teacher
workloads, subject distribution, or period usage.
Overall, the existing manual system is inefficient, difficult to manage, and unsuitable
for schools that require frequent updates or handle a large number of classes.

6|Page
PROPOSED SYSTEM
The proposed system, EduPlan Pro – Automatic Timetable Generator, overcomes
the challenges of manual scheduling by using automation, database integration, and
intelligent algorithms. It is developed using Python for the application logic and
MySQL for database management. The system uses a Graphical User Interface (GUI)
built with Tkinter and ttk themes to provide a simple and interactive experience for
users.
Key features of the proposed system include:
 Automatic Timetable Generation: Creates conflict-free schedules by assigning
subjects and teachers automatically.
 Teacher Management: Allows administrators to add, edit, and manage teacher
records, including subjects handled and maximum teaching hours.
 Attendance Tracking: Enables marking teacher absences and tracking.
 Data Export and Import: Supports CSV-based export and import of timetables
for easy sharing and backups.
 Secure User Authentication: Includes admin and user login systems with
encrypted passwords for data protection.
 Customization Options: Lets users configure school names, themes, and
academic years using configuration files.
 Database Integration: Stores all data securely in MySQL, ensuring reliability
and ease of access.
The proposed system significantly reduces administrative effort, prevents scheduling
conflicts, and allows real-time updates whenever changes occur. It ensures fair
distribution of teacher workloads, provides permanent digital records, and enhances
overall efficiency in school management.

7|Page
SYSTEM REQUIREMENTS

The EduPlan Pro – Automatic Timetable Generator is a Python-based desktop


application that integrates with a MySQL database. It requires moderate hardware and
software resources to function smoothly. The following are the system requirements for
installing and running the application effectively.
Hardware Requirements
 A computer with Intel Core i3 or AMD Ryzen 3 processor (minimum).
 Intel Core i5 or AMD Ryzen 5 processor recommended for faster performance.
 Minimum 4 GB RAM, recommended 8 GB or more for efficient multitasking.
 At least 500 MB of free disk space for program files and database storage.
 1366×768 pixel display resolution minimum; 1920×1080 or higher
recommended for better clarity.
 Standard keyboard and mouse or touchpad for input.
 Internet connection optional, required only if using a remote database.

Software Requirements
 Operating System:
o Windows 10 or higher
o macOS Monterey or later
o Linux (Ubuntu 20.04 LTS or newer)
 Programming Language:
o Python 3.8 or above
 Required Python Libraries:
o Tkinter and ttk – For graphical user interface design
o Pillow (PIL) – For image handling (school logo, icons)
o [Link] – For MySQL database connection
o ConfigParser – For managing configuration files

8|Page
o CSV and JSON – For data import/export and backups
o hashlib – For password encryption using SHA-256
o logging – For system activity and error tracking
o os, random, datetime, re – For scheduling logic, file handling, and input
validation
 Installation Command (for required packages):
 pip install mysql-connector-python pillow

Database Requirements
 Database System: MySQL
 Database Name: eduplan_pro
 Tables Included:
o users – Stores user login details and roles
o teachers – Stores teacher information and subject assignments
o classes – Stores class and section details
o timetables – Stores generated timetable data
o absences – Tracks teacher attendance
Additional Tools
 MySQL Workbench: For managing and visualizing database tables.
 Text Editor or IDE: PyCharm, Visual Studio Code, or IDLE for code editing.
 Spreadsheet Software: Microsoft Excel or Google Sheets for viewing exported
CSV timetables.
System Compatibility
EduPlan Pro is lightweight and designed to work on most standard computers used in
schools and offices. It runs efficiently without requiring high-end hardware and does
not depend on an active internet connection unless remote access is configured.

9|Page
SOFTWARE DEVELOPMENT CYCLE

The Software Development Life Cycle (SDLC) is the process used to


design, develop, test, and deploy software systematically. For the EduPlan Pro –
Automatic Timetable Generator, the SDLC model followed is the Waterfall Model,
where each phase is completed before moving to the next. This approach ensures proper
planning, organization, and documentation throughout the development process.

1. Requirement Analysis
In this initial phase, the objectives and needs of the project were clearly defined.
Discussions were held to understand the main problems in manual timetable creation,
such as scheduling conflicts, uneven teacher workloads, and difficulty in managing
substitutions. Based on this, the following requirements were finalized:
 Automatic timetable generation without conflicts.
 Teacher management and attendance tracking.
 Export and import of timetables using CSV files.
 Secure login system with user authentication.
 User-friendly graphical interface for ease of use.
2. System Design
In the design phase, the system’s structure and layout were planned. The database
schema was created using MySQL, defining tables for teachers, classes, users, and
timetables. The GUI design was planned using Tkinter, focusing on simplicity and user
accessibility.
The design phase included:
 Designing the database structure for storing information.
 Creating UI mockups to visualize the application’s layout.
 Planning the flow of operations, such as data input, timetable generation, and
output export.
 Implementing configuration management using ConfigParser for custom themes
and school details.

10 | P a g e
3. Implementation (Coding)
Once the design was finalized, the actual coding of the project began using Python.
Each module was developed separately and tested for functionality. The main
components included:
 GUI Development: Created using Tkinter and ttk themes for a professional look.
 Database Connection: Established using [Link] to connect Python
with the MySQL database.
 User Authentication: Implemented with SHA-256 encryption for secure login.
 Timetable Logic: Developed to assign teachers, subjects, and classes
automatically without conflicts.
 Attendance and Substitution: Added to manage absences and automatically
assign replacements.
 Data Export/Import: Enabled through CSV files for easy data sharing and
backup.

4. Testing
After development, the application underwent thorough testing to ensure that all
modules worked correctly and met the specified requirements. The following types of
testing were performed:
 Unit Testing: Individual modules, such as login, attendance, and timetable
generation, were tested separately.
 Integration Testing: Verified that the database, GUI, and logic components
worked smoothly together.
 System Testing: Checked the entire application for functionality and
performance.
 User Testing: The system was tested by users to ensure ease of use and accurate
results.

11 | P a g e
5. Deployment
After successful testing, the system was deployed on local computers for school use.
The database was set up in MySQL, and the application files were installed. A user
manual was prepared to guide administrators and teachers on how to use the software.
Deployment ensured that the software was fully functional and accessible to users in a
real working environment.
6. Maintenance
Once deployed, the system required occasional updates and improvements based on
user feedback. Maintenance activities included:
 Fixing minor bugs or errors identified during usage.
 Updating the database or interface to add new features.
 Enhancing security or improving system performance.
Regular maintenance ensures that the application continues to function efficiently and
remains compatible with newer versions of Python or MySQL.

12 | P a g e
SYSTEM ARCHITECTURE
EduPlan Pro is an automated timetable and school management system that integrates a
Python-based application layer with a MySQL relational database. The system
ensures efficient scheduling, substitution management, and administrative control
through a structured, modular, and secure architecture.
Its architecture follows a three-tier model — consisting of the Presentation Layer,
Application Layer, and Database Layer — each responsible for specific
functionalities that collectively enable seamless operation.

ARCHITECTURAL LAYERS
A. Presentation Layer (Frontend Interface)
>This layer represents the user interaction point of the system.
>Users: Administrators, Teachers, and potentially Students.
>Functions:
 Login authentication (via username and password).
 Dashboard for timetable visualization and management.
 Teacher management (add, view, activate/deactivate).
 Class and section overview.
 Substitution and absence management interface.
>Technology Used:
 Python GUI (Tkinter )
 Collect user inputs and display system outputs.
 Ensure validation before passing data to the application layer.
 Provide a user-friendly experience for both administrators and faculty members.
B. Application Layer (Logic Controller)
The Application Layer acts as the intermediary between the user interface and the
database. It processes inputs, applies business logic, and manages communication with
the database server.
>Core Modules:
 User Authentication Module – Validates credentials, manages sessions, and
enforces role-based access (admin, teacher, etc.).

13 | P a g e
 Timetable Management Module – Generates, updates, and maintains daily and
weekly schedules based on class sections, teachers, and available periods.
 Teacher Management Module – Handles teacher records, subject allocations,
and availability tracking.
 Absence and Substitution Module – Logs teacher absences and automatically
assigns substitute teachers.
 Class Management Module – Organizes class details such as strength, section,
room allocation, and academic year.
>Responsibilities:
 Execute all business rules (e.g., preventing duplicate periods or over-
assignments).
 Manage communication with the database using MySQL connectors.
 Generate alerts or logs for administrative actions.
 Ensure data consistency and handle concurrency when multiple updates occur
simultaneously.
C. Database Layer (Data Storage and Management)
The Database Layer forms the foundation of EduPlan Pro, responsible for data
persistence and relational integrity.
>Database Used: MySQL
>Key Entities:
 Users Table – Stores login credentials, roles, and timestamps.
 Teachers Table – Manages teacher profiles, subjects, and workloads.
 Classes Table – Holds data on class names, sections, and academic years.
 Timetables Table – Stores generated schedules linking classes, teachers, and
periods.
 Absences Table – Tracks teacher absences and assigned substitutes.
>Data Integrity:
 Enforced through primary keys, foreign keys, and unique constraints.
 Referential integrity ensures relationships between classes, teachers, and
timetables are consistent.

14 | P a g e
>Security Measures:
 Hashed password storage.
 Controlled access for different roles.
 Use of default and unique constraints to prevent data duplication.

DATA FLOW AND INTERACTION


 User Interaction:
o Admins and teachers log in through the application interface.
 Request Processing:
o The application layer authenticates the user and fetches or modifies
required data.
 Database Communication:
o SQL queries are executed through the MySQL connector to retrieve or
store information.
 Response Generation:
o Processed data (e.g., timetable or teacher list) is formatted and sent back to
the interface for display.
 Automation:
o In the case of absences, the system automatically searches for available
substitutes and updates the timetable accordingly.

SYSTEM MODULES AND THEIR INTERACTIONS


Module Input Process Output
User
Username, Password Authenticate, verify role Access granted/denied
Management
Class
Class details Add/Edit/Delete classes Updated class database
Management
Teacher Teacher info, Record, update, track Teacher list, status
Management subjects workload report
Timetable Class, period, Allocate based on
Finalized schedule
Generation subject, teacher constraints
Absence Absent teacher, date, Updated timetable
Substitute assignment
Handling reason entry

15 | P a g e
SECURITY AND RELIABILITY
 Authentication & Authorization: Role-based access (admin vs. teacher).
 Data Encryption: Passwords stored as SHA-256 hashes.
 Backup & Recovery: Regular database backups to prevent data loss.
 Error Handling: Controlled exceptions for connection failures or invalid
operations.
 Data Validation: Input sanitation at both application and database levels.

SCALABILITY AND EXTENSIBILITY


 Scalable Design: New classes, teachers, and academic years can be added
without modifying the schema.
 Extensible Architecture: Future modules (e.g., student attendance, exam
scheduling, performance analytics) can integrate smoothly.

16 | P a g e
System architecture flowchart

17 | P a g e
CODE
import tkinter as tk
from tkinter import messagebox, simpledialog, ttk, filedialog
from datetime import datetime, timedelta
import [Link]
import random
import hashlib
import csv
import logging
import configparser
import json
import os
from PIL import Image, ImageTk
import re

# Configure logging
[Link](
filename='[Link]',
level=[Link],
format='%(asctime)s - %(levelname)s - %(message)s'
)

# Constants
DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
PERIODS = ["Period 1", "Period 2", "Period 3", "Period 4", "Period 5", "Period
6","Period 7","Period 8","Period 9"]
CLASSES = ["LKG", "UKG"] + [f"{i}th" for i in range(1, 13)]

18 | P a g e
SECTIONS = ["A", "B", "C", "D"]
SUBJECTS = ["Mathematics", "Physics", "Chemistry", "Biology", "English",
"History",
"Geography", "Computer Science", "Physical Education", "Art", "Music"]
SUBJECT_CATEGORIES = {
"Mathematics": "math",
"Physics": "science",
"Chemistry": "science",
"Biology": "science",
"English": "language",
"History": "social",
"Geography": "social",
"Computer Science": "tech",
"Physical Education": "sports",
"Art": "arts",
"Music": "arts"
}
ADMIN_USERNAME = "admin"
ADMIN_PASSWORD = "admin123"
DB_NAME = "eduplan_pro"
CONFIG_FILE = "eduplan_config.ini"

class ConfigManager:
def __init__(self, config_file=CONFIG_FILE):
[Link] = [Link]()
self.config_file = config_file
if not [Link](config_file):
self.create_default_config()

19 | P a g e
[Link](config_file)

def create_default_config(self):
[Link]['DEFAULT'] = {
'zoom_meeting_id': '',
'zoom_password': '',
'zoom_api_key': '',
'school_name': 'Peace On Green Earth Public School',
'academic_year': '2025-26',
'theme': 'light'
}
with open(self.config_file, 'w') as configfile:
[Link](configfile)

def get_setting(self, section, key):


return [Link](section, key, fallback='')

def set_setting(self, section, key, value):


if section not in [Link]:
[Link][section] = {}
[Link][section][key] = value
with open(self.config_file, 'w') as configfile:
[Link](configfile)

class DatabaseManager:
def __init__(self, db_name=DB_NAME):
self.db_name = db_name

20 | P a g e
try:
[Link] = [Link](host='localhost', user='root',
password='root')
[Link] = [Link]()
[Link]("CREATE DATABASE IF NOT EXISTS eduplan_pro")
[Link]()
[Link]()
[Link] = [Link](host='localhost', user='root',
password='root', database='eduplan_pro')
[Link] = [Link]()
self.create_tables()
self.insert_default_data()
except [Link] as err:
[Link](f"Database connection error: {err}")
raise

def create_tables(self):
tables = {
'users': '''CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
role VARCHAR(50) DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP,
is_active BOOLEAN DEFAULT 1
)''',
'teachers': '''CREATE TABLE IF NOT EXISTS teachers (

21 | P a g e
id INTEGER PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) UNIQUE NOT NULL,
employee_id VARCHAR(100) UNIQUE,
subject VARCHAR(100) NOT NULL,
classes TEXT,
max_periods INTEGER DEFAULT 6,
assigned_periods INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT 1
)''',
'classes': '''CREATE TABLE IF NOT EXISTS classes (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
class_name VARCHAR(50) NOT NULL,
section VARCHAR(10) NOT NULL,
room_number VARCHAR(20),
strength INTEGER DEFAULT 0,
academic_year VARCHAR(20),
is_active BOOLEAN DEFAULT 1,
UNIQUE(class_name, section)
)''',
'timetables': '''CREATE TABLE IF NOT EXISTS timetables (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
class_name VARCHAR(50) NOT NULL,
section VARCHAR(10) NOT NULL,
day VARCHAR(20) NOT NULL,
period VARCHAR(20) NOT NULL,
teacher_id INTEGER,
subject_id INTEGER,

22 | P a g e
room_number VARCHAR(20),
is_substitution BOOLEAN DEFAULT 0,
FOREIGN KEY (teacher_id) REFERENCES teachers (id),
UNIQUE(class_name, section, day, period)
)''',
'absences': '''CREATE TABLE IF NOT EXISTS absences (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
teacher_id INTEGER,
absent_date DATE,
absent_day VARCHAR(20),
absent_period VARCHAR(20),
reason TEXT,
substitute_teacher_id INTEGER,
is_approved BOOLEAN DEFAULT 0,
FOREIGN KEY (teacher_id) REFERENCES teachers (id),
FOREIGN KEY (substitute_teacher_id) REFERENCES teachers (id)
)'''
}

for table_name, create_sql in [Link]():


try:
[Link](create_sql)
[Link]()
except [Link] as e:
[Link](f"Error creating table {table_name}: {e}")
raise

23 | P a g e
[Link]("SHOW COLUMNS FROM teachers")
columns = [column[0] for column in [Link]()]
if 'classes' not in columns:
try:
[Link]("ALTER TABLE teachers ADD COLUMN classes
TEXT")
[Link]()
[Link]("Added 'classes' column to teachers table")
except [Link] as e:
[Link](f"Error adding classes column: {e}")

[Link]("SHOW COLUMNS FROM absences")


columns = [column[0] for column in [Link]()]
if 'absent_date' not in columns:
[Link]("ALTER TABLE absences ADD COLUMN absent_date
DATE")
[Link]()
[Link]("Added 'absent_date' column to absences table")
if 'absent_day' not in columns:
[Link]("ALTER TABLE absences ADD COLUMN absent_day
VARCHAR(20)")
[Link]()
[Link]("Added 'absent_day' column to absences table")
if 'absent_period' not in columns:
[Link]("ALTER TABLE absences ADD COLUMN absent_period
VARCHAR(20)")
[Link]()
[Link]("Added 'absent_period' column to absences table")
if 'reason' not in columns:
24 | P a g e
[Link]("ALTER TABLE absences ADD COLUMN reason
TEXT")
[Link]()
[Link]("Added 'reason' column to absences table")
if 'substitute_teacher_id' not in columns:
[Link]("ALTER TABLE absences ADD COLUMN
substitute_teacher_id INTEGER")
[Link]()
[Link]("Added 'substitute_teacher_id' column to absences table")
if 'is_approved' not in columns:
[Link]("ALTER TABLE absences ADD COLUMN is_approved
BOOLEAN DEFAULT 0")
[Link]()
[Link]("Added 'is_approved' column to absences table")

def insert_default_data(self):
try:
[Link]("SELECT COUNT(*) FROM users WHERE username =
%s", (ADMIN_USERNAME,))
if [Link]()[0] == 0:
admin_hash = hashlib.sha256(ADMIN_PASSWORD.encode()).hexdigest()
[Link]("INSERT INTO users (username, password_hash, role)
VALUES (%s, %s, %s)",
(ADMIN_USERNAME, admin_hash, 'admin'))

for class_name in CLASSES:


for section in SECTIONS:
[Link]("INSERT IGNORE INTO classes (class_name,
section, academic_year) VALUES (%s, %s, %s)",

25 | P a g e
(class_name, section, "2025-26"))

[Link]()
except [Link] as e:
[Link](f"Error inserting default data: {e}")
[Link]()
raise

def execute_query(self, query, params=None, fetch=False):


try:
if params:
[Link](query, params)
else:
[Link](query)
if fetch:
return [Link]()
[Link]()
return [Link]
except [Link] as e:
[Link](f"SQL Error: {e}, Query: {query}, Params: {params}")
[Link]()
raise

def close(self):
[Link]()
[Link]()

26 | P a g e
class SecurityManager:
@staticmethod
def hash_password(password):
return hashlib.sha256([Link]()).hexdigest()

@staticmethod
def verify_password(password, hash_value):
return SecurityManager.hash_password(password) == hash_value

@staticmethod
def validate_email(email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return [Link](pattern, email) is not None

class EduPlanPro:
def __init__(self, root):
[Link] = root
[Link]("EduPlan Pro: Automatic Timetable Generator")
[Link]("1200x700")
[Link] = DatabaseManager()
[Link] = SecurityManager()
[Link] = ConfigManager()
self.current_user = None
self.current_user_id = None
self.current_user_role = None
self.logo_img = None
[Link] = [Link].get_setting('DEFAULT', 'theme')

27 | P a g e
self.bg_color = "#e3f2fd" if [Link] == 'light' else "#263238"
self.fg_color = "#1565c0" if [Link] == 'light' else "#90caf9"
[Link](bg=self.bg_color)
self.setup_styles()
self.create_login_screen()
self.update_clock()

def setup_styles(self):
[Link] = [Link]()
[Link].theme_use('clam')
[Link]('[Link]', font=('Segoe UI', 16, 'bold'),
background=self.bg_color, foreground=self.fg_color)
[Link]('[Link]', padding=8, font=('Segoe UI', 10),
background='#42a5f5', foreground='white')
[Link]('[Link]', background=[('active', '#1976d2')])
[Link]('TCombobox', font=('Segoe UI', 10), padding=5)
[Link]('TEntry', font=('Segoe UI', 10), padding=5)
[Link]('Treeview', font=('Segoe UI', 10), rowheight=25,
background='#ffffff' if [Link] == 'light' else '#37474f')
[Link]('[Link]', font=('Segoe UI', 10, 'bold'),
background='#bbdefb' if [Link] == 'light' else '#455a64')

def update_clock(self):
now = [Link]().strftime("%Y-%m-%d %H:%M:%S")
if hasattr(self, "clock_label"):
self.clock_label.config(text=now)
[Link](1000, self.update_clock)

def create_login_screen(self):
28 | P a g e
for widget in [Link].winfo_children():
[Link]()

main_frame = [Link]([Link], bg=self.bg_color)


main_frame.pack(expand=True, fill=[Link])

self.clock_label = [Link](main_frame, text="", font=("Segoe UI", 12, 'bold'),


bg=self.bg_color, fg=self.fg_color)
self.clock_label.pack(side=[Link], anchor=tk.E, padx=10, pady=5)

logo_container = [Link](main_frame, bg=self.bg_color)


logo_container.pack(pady=10, fill=tk.X)

logo_frame = [Link](logo_container, bg=self.bg_color)


logo_frame.pack(expand=True)

if self.logo_img:
[Link](logo_frame, image=self.logo_img,
bg=self.bg_color).pack(side=[Link])

school_name = [Link].get_setting('DEFAULT', 'school_name')


[Link](logo_frame, text=school_name, font=("Segoe UI", 18, "bold"),
bg=self.bg_color, fg=self.fg_color).pack(side=[Link], padx=10)

login_frame = [Link](main_frame, bg="#ffffff" if [Link] == 'light' else


"#37474f", relief=[Link], bd=2, padx=20, pady=20)
login_frame.place(relx=0.5, rely=0.5, anchor=[Link])

29 | P a g e
[Link](login_frame, text="EduPlan Pro", font=("Segoe UI", 24, "bold"),
bg="#ffffff" if [Link] == 'light' else "#37474f", fg=self.fg_color).pack(pady=10)
[Link](login_frame, text="Automatic Timetable Generator", font=("Segoe UI",
12), bg="#ffffff" if [Link] == 'light' else "#37474f", fg="#455a64" if [Link] ==
'light' else "#b0bec5").pack(pady=5)

form_frame = [Link](login_frame, bg="#ffffff" if [Link] == 'light' else


"#37474f")
form_frame.pack(pady=20)

[Link](form_frame, text="Username:", bg="#ffffff" if [Link] == 'light' else


"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=0,
column=0, pady=5, sticky="e")
self.username_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
self.username_entry.grid(row=0, column=1, pady=5, padx=10)

[Link](form_frame, text="Password:", bg="#ffffff" if [Link] == 'light' else


"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=1,
column=0, pady=5, sticky="e")
self.password_entry = [Link](form_frame, width=25, show="*", font=("Segoe
UI", 10))
self.password_entry.grid(row=1, column=1, pady=5, padx=10)

self.username_entry.bind('<Return>', lambda event: [Link]())


self.password_entry.bind('<Return>', lambda event: [Link]())

button_frame = [Link](login_frame, bg="#ffffff" if [Link] == 'light' else


"#37474f")
button_frame.pack(pady=20)

30 | P a g e
[Link](button_frame, text="Login", command=[Link],
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Register",
command=self.show_registration_dialog,
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Upload Logo", command=self.choose_logo,
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="About Software",
command=self.show_about_dialog, style='[Link]').pack(side=[Link],
padx=5)

def authenticate(self):
username = self.username_entry.get().strip()
password = self.password_entry.get().strip()

if not username or not password:


[Link]("Error", "Please enter both username and password.")
return

try:
query = "SELECT id, username, password_hash, role FROM users WHERE
username = %s AND is_active = 1"
result = [Link].execute_query(query, (username,), fetch=True)

if result and [Link].verify_password(password, result[0][2]):


self.current_user = username
self.current_user_id = result[0][0]
self.current_user_role = result[0][3]
[Link].execute_query("UPDATE users SET last_login =
CURRENT_TIMESTAMP WHERE id = %s", (self.current_user_id,))

31 | P a g e
self.create_main_dashboard()
else:
[Link]("Error", "Invalid username or password.")
except [Link] as e:
[Link]("Error", f"Database error: {e}")
[Link](f"Authentication error: {e}")

def show_registration_dialog(self):
dialog = [Link]([Link])
[Link]("Register")
[Link]("400x300")
[Link](bg="#ffffff" if [Link] == 'light' else "#37474f")
[Link]([Link])
dialog.grab_set()

[Link](dialog, text="Register New User", font=("Segoe UI", 16, "bold"),


bg="#ffffff" if [Link] == 'light' else "#37474f", fg=self.fg_color).pack(pady=10)

form_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
form_frame.pack(pady=20)

[Link](form_frame, text="Username:", bg="#ffffff" if [Link] == 'light' else


"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=0,
column=0, pady=5, sticky="e")
username_entry = [Link](form_frame, width=20, font=("Segoe UI", 10))
username_entry.grid(row=0, column=1, pady=5, padx=10)

32 | P a g e
[Link](form_frame, text="Password:", bg="#ffffff" if [Link] == 'light' else
"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=1,
column=0, pady=5, sticky="e")
password_entry = [Link](form_frame, show="*", width=20, font=("Segoe UI",
10))
password_entry.grid(row=1, column=1, pady=5, padx=10)

def register():
username = username_entry.get().strip()
password = password_entry.get().strip()

if not all([username, password]):


[Link]("Error", "All fields are required.")
return

if len(password) < 6:
[Link]("Error", "Password must be at least 6 characters.")
return

try:
check_query = "SELECT COUNT(*) FROM users WHERE username =
%s"
if [Link].execute_query(check_query, (username,), fetch=True)[0][0] > 0:
[Link]("Error", "Username already exists.")
return

password_hash = [Link].hash_password(password)
[Link].execute_query("INSERT INTO users (username, password_hash,
role) VALUES (%s, %s, %s)",

33 | P a g e
(username, password_hash, 'user'))
[Link]("Success", "User registered successfully!")
[Link]()
except [Link] as e:
[Link]("Error", f"Registration failed: {e}")
[Link](f"Registration error: {e}")

button_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
button_frame.pack(pady=20)
[Link](button_frame, text="Register", command=register,
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Cancel", command=[Link],
style='[Link]').pack(side=[Link], padx=5)

def show_about_dialog(self):
dialog = [Link]([Link])
[Link]("About EduPlan Pro")
[Link]("500x400")
[Link](bg="#ffffff" if [Link] == 'light' else "#37474f")
[Link]([Link])
dialog.grab_set()

[Link](dialog, text="About EduPlan Pro", font=("Segoe UI", 16, "bold"),


bg="#ffffff" if [Link] == 'light' else "#37474f", fg=self.fg_color).pack(pady=10)

info_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
info_frame.pack(pady=10, padx=10, fill=[Link])

34 | P a g e
info_text = """
EduPlan Pro is an advanced timetable management system designed for schools to
streamline scheduling and teacher management. Key features include:

- Automatic timetable generation


- Teacher absence management with automatic substitution
- Export/import timetables in CSV format
- User-friendly interface with customizable themes

Version: 1.0.0
Developed by: Kishore, Roopa, and Mohan

Story: Kishore, Roopa, and Mohan, three innovative students, created this app for their
Board project. Inspired by their own experiences with school scheduling chaos, they
combined Kishore's database expertise, Roopa's UI design flair, and Mohan's
algorithm prowess to build EduPlan Pro. Their goal was to make timetable
management easy and efficient for schools worldwide.
"""
[Link](info_frame, text=info_text, font=("Segoe UI", 10), bg="#ffffff" if
[Link] == 'light' else "#37474f", fg="#455a64" if [Link] == 'light' else
"#b0bec5", justify=[Link], wraplength=460).pack(pady=5)

terms_frame = [Link](dialog, text="Terms and Conditions",


font=("Segoe UI", 12, "bold"), bg="#ffffff" if [Link] == 'light' else "#37474f",
fg=self.fg_color)
terms_frame.pack(pady=10, padx=10, fill=[Link])

terms_text = """
1. Use of this software is restricted to authorized users only.

35 | P a g e
2. Data entered into the system remains the property of the school.
3. The software is provided as-is, with no warranty for data loss.
4. Users are responsible for maintaining secure login credentials.
5. For support, contact support@[Link]
"""
[Link](terms_frame, text=terms_text, font=("Segoe UI", 10), bg="#ffffff" if
[Link] == 'light' else "#37474f", fg="#455a64" if [Link] == 'light' else
"#b0bec5", justify=[Link], wraplength=460).pack(pady=5)

button_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
button_frame.pack(pady=20)
[Link](button_frame, text="Close", command=[Link],
style='[Link]').pack()

def choose_logo(self):
file_path = [Link](filetypes=[("Image files", "*.png *.jpg
*.jpeg")])
if file_path:
try:
image = [Link](file_path)
image = [Link]((100, 100), [Link])
self.logo_img = [Link](image)
self.create_login_screen()
except Exception as e:
[Link]("Error", f"Failed to load logo: {e}")
[Link](f"Logo loading error: {e}")

def toggle_theme(self):

36 | P a g e
current_theme = [Link].get_setting('DEFAULT', 'theme')
new_theme = 'dark' if current_theme == 'light' else 'light'
[Link].set_setting('DEFAULT', 'theme', new_theme)
[Link] = new_theme
self.bg_color = "#e3f2fd" if [Link] == 'light' else "#263238"
self.fg_color = "#1565c0" if [Link] == 'light' else "#90caf9"
self.create_main_dashboard() if self.current_user else self.create_login_screen()

def create_main_dashboard(self):
for widget in [Link].winfo_children():
[Link]()

main_frame = [Link]([Link], bg=self.bg_color)


main_frame.pack(fill=[Link], expand=True)

top_frame = [Link](main_frame, bg="#bbdefb" if [Link] == 'light' else


"#455a64")
top_frame.pack(fill=tk.X, padx=10, pady=5)

self.clock_label = [Link](top_frame, text="", font=("Segoe UI", 12, 'bold'),


bg="#bbdefb" if [Link] == 'light' else "#455a64", fg=self.fg_color)
self.clock_label.pack(side=[Link])

logo_container = [Link](top_frame, bg="#bbdefb" if [Link] == 'light' else


"#455a64")
logo_container.pack(fill=tk.X)

logo_frame = [Link](logo_container, bg="#bbdefb" if [Link] == 'light' else


"#455a64")
37 | P a g e
logo_frame.pack(expand=True)

if self.logo_img:
[Link](logo_frame, image=self.logo_img, bg="#bbdefb" if [Link] ==
'light' else "#455a64").pack(side=[Link])

school_name = [Link].get_setting('DEFAULT', 'school_name')


[Link](logo_frame, text=school_name, font=("Segoe UI", 18, "bold"),
bg="#bbdefb" if [Link] == 'light' else "#455a64",
fg=self.fg_color).pack(side=[Link], padx=10)

welcome_frame = [Link](main_frame, bg=self.bg_color)


welcome_frame.pack(pady=10)

[Link](welcome_frame, text=f"Welcome to {school_name}", font=("Segoe


UI", 18, "bold"),
bg=self.bg_color, fg=self.fg_color).pack()
[Link](welcome_frame, text=f"User: {self.current_user} | Role:
{self.current_user_role}",
font=("Segoe UI", 12), bg=self.bg_color, fg="#455a64" if [Link] ==
'light' else "#b0bec5").pack()

class_frame = [Link](main_frame, text="Select Class", bg="#ffffff" if


[Link] == 'light' else "#37474f", font=("Segoe UI", 12, "bold"), fg=self.fg_color)
class_frame.pack(fill=tk.X, padx=10, pady=10)

class_grid_frame = [Link](class_frame, bg="#ffffff" if [Link] == 'light' else


"#37474f")
class_grid_frame.pack(pady=10)

38 | P a g e
for i, class_name in enumerate(CLASSES):
btn = [Link](class_grid_frame, text=class_name, command=lambda
c=class_name: self.show_sections(c),
style='[Link]', width=8)
[Link](row=i//7, column=i%7, padx=2, pady=2)

action_frame = [Link](main_frame, text="Actions", bg="#ffffff" if


[Link] == 'light' else "#37474f", font=("Segoe UI", 12, "bold"), fg=self.fg_color)
action_frame.pack(fill=tk.X, padx=10, pady=10)

action_grid_frame = [Link](action_frame, bg="#ffffff" if [Link] == 'light'


else "#37474f")
action_grid_frame.pack(pady=10)

actions = [
("Add Teacher", self.add_teacher),
("Manage Teachers", self.manage_teachers),
("Teacher Attendance", self.teacher_attendance),
("Auto Generate Timetable", self.auto_generate_timetable),
("Export Timetable", self.export_timetable),
("Import Timetable", self.import_timetable),
("About Software", self.show_about_dialog),
("Toggle Theme", self.toggle_theme),
("Logout", [Link])
]

for i, (text, cmd) in enumerate(actions):


btn = [Link](action_grid_frame, text=text, command=cmd,
style='[Link]', width=18)
39 | P a g e
[Link](row=i//4, column=i%4, padx=5, pady=5)

def show_sections(self, class_name):


dialog = [Link]([Link])
[Link](f"Sections for {class_name}")
[Link]("400x300")
[Link](bg="#ffffff" if [Link] == 'light' else "#37474f")
[Link]([Link])
dialog.grab_set()

[Link](dialog, text=f"Sections for {class_name}", font=("Segoe UI", 16,


"bold"), bg="#ffffff" if [Link] == 'light' else "#37474f",
fg=self.fg_color).pack(pady=10)

try:
sections = [Link].execute_query("SELECT section FROM classes WHERE
class_name = %s AND is_active = 1",
(class_name,), fetch=True)
sections = [s[0] for s in sections]

sections_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
sections_frame.pack(pady=10)

for i, section in enumerate(sections):


btn = [Link](sections_frame, text=f"{class_name} {section}",
command=lambda s=section: [[Link](),
self.open_timetable_editor(class_name, s)],
style='[Link]', width=15)

40 | P a g e
[Link](row=i//2, column=i%2, padx=5, pady=5)

button_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
button_frame.pack(pady=20)
[Link](button_frame, text="Add Section", command=lambda:
self.add_section(class_name, dialog),
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Close", command=[Link],
style='[Link]').pack(side=[Link], padx=5)
except [Link] as e:
[Link]("Error", f"Failed to load sections: {e}")
[Link](f"Sections loading error: {e}")

def add_section(self, class_name, parent_dialog=None):


section = [Link]("Add Section", "Enter new section (e.g., A, B,
C):")
if section:
section = [Link]().strip()
try:
check_query = "SELECT COUNT(*) FROM classes WHERE class_name =
%s AND section = %s"
if [Link].execute_query(check_query, (class_name, section),
fetch=True)[0][0] > 0:
[Link]("Error", "Section already exists.")
return
[Link].execute_query("INSERT INTO classes (class_name, section,
academic_year) VALUES (%s, %s, %s)",
(class_name, section, [Link].get_setting('DEFAULT',
'academic_year')))
41 | P a g e
[Link]("Success", f"Section {section} added for
{class_name}.")
if parent_dialog:
parent_dialog.destroy()
self.show_sections(class_name)
except [Link] as e:
[Link]("Error", f"Failed to add section: {e}")
[Link](f"Add section error: {e}")

def add_teacher(self):
dialog = [Link]([Link])
[Link]("Add Teacher")
[Link]("450x500")
[Link](bg="#ffffff" if [Link] == 'light' else "#37474f")
[Link]([Link])
dialog.grab_set()

[Link](dialog, text="Add New Teacher", font=("Segoe UI", 16, "bold"),


bg="#ffffff" if [Link] == 'light' else "#37474f", fg=self.fg_color).pack(pady=10)

form_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
form_frame.pack(pady=20)

[Link](form_frame, text="Name:", bg="#ffffff" if [Link] == 'light' else


"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=0,
column=0, pady=5, sticky="e")
name_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
name_entry.grid(row=0, column=1, pady=5, padx=10)

42 | P a g e
[Link](form_frame, text="Employee ID:", bg="#ffffff" if [Link] == 'light'
else "#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=1,
column=0, pady=5, sticky="e")
emp_id_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
emp_id_entry.grid(row=1, column=1, pady=5, padx=10)

[Link](form_frame, text="Subject:", bg="#ffffff" if [Link] == 'light' else


"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=2,
column=0, pady=5, sticky="e")
subject_combo = [Link](form_frame, values=SUBJECTS, width=22,
font=("Segoe UI", 10))
subject_combo.grid(row=2, column=1, pady=5, padx=10)

[Link](form_frame, text="Classes (comma-separated):", bg="#ffffff" if


[Link] == 'light' else "#37474f", fg="#455a64" if [Link] == 'light' else
"#b0bec5").grid(row=3, column=0, pady=5, sticky="e")
classes_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
classes_entry.grid(row=3, column=1, pady=5, padx=10)

[Link](form_frame, text="Max Periods per Day:", bg="#ffffff" if [Link] ==


'light' else "#37474f", fg="#455a64" if [Link] == 'light' else
"#b0bec5").grid(row=4, column=0, pady=5, sticky="e")
max_periods_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
max_periods_entry.insert(0, "6")
max_periods_entry.grid(row=4, column=1, pady=5, padx=10)

def save_teacher():
name = name_entry.get().strip()
emp_id = emp_id_entry.get().strip()

43 | P a g e
subject = subject_combo.get().strip()
classes = classes_entry.get().strip()
max_periods = max_periods_entry.get().strip()

if not all([name, emp_id, subject, max_periods]):


[Link]("Error", "Name, Employee ID, Subject, and Max
Periods are required.")
return

try:
max_periods = int(max_periods)
if max_periods <= 0 or max_periods > len(PERIODS):
[Link]("Error", f"Max periods must be between 1 and
{len(PERIODS)}.")
return

if classes:
class_list = [[Link]() for c in [Link](",")]
for c in class_list:
if c not in CLASSES:
[Link]("Error", f"Invalid class: {c}")
return

check_query = "SELECT COUNT(*) FROM teachers WHERE


employee_id = %s OR name = %s"
if [Link].execute_query(check_query, (emp_id, name), fetch=True)[0][0] >
0:
[Link]("Error", "Teacher name or Employee ID already
exists.")

44 | P a g e
return

[Link].execute_query(
"INSERT INTO teachers (name, employee_id, subject, classes,
max_periods) VALUES (%s, %s, %s, %s, %s)",
(name, emp_id, subject, classes, max_periods)
)
[Link]("Success", "Teacher added successfully!")
[Link]()
except ValueError:
[Link]("Error", "Max periods must be a number.")
except [Link] as e:
[Link]("Error", f"Failed to add teacher: {e}")
[Link](f"Add teacher error: {e}")

button_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
button_frame.pack(pady=20)
[Link](button_frame, text="Save", command=save_teacher,
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Cancel", command=[Link],
style='[Link]').pack(side=[Link], padx=5)

def manage_teachers(self):
dialog = [Link]([Link])
[Link]("Manage Teachers")
[Link]("800x500")
[Link](bg="#ffffff" if [Link] == 'light' else "#37474f")
[Link]([Link])
45 | P a g e
dialog.grab_set()

[Link](dialog, text="Manage Teachers", font=("Segoe UI", 16, "bold"),


bg="#ffffff" if [Link] == 'light' else "#37474f", fg=self.fg_color).pack(pady=10)

tree_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else "#37474f")


tree_frame.pack(fill=[Link], expand=True, padx=10, pady=10)

columns = ("ID", "Name", "Employee ID", "Subject", "Classes", "Max Periods")


tree = [Link](tree_frame, columns=columns, show="headings")
[Link](fill=[Link], expand=True)

for col in columns:


[Link](col, text=col)
[Link](col, width=100)

try:
teachers = [Link].execute_query("SELECT id, name, employee_id, subject,
classes, max_periods FROM teachers WHERE is_active = 1", fetch=True)
for teacher in teachers:
[Link]("", [Link], values=teacher)

def delete_teacher():
selected = [Link]()
if not selected:
[Link]("Error", "Select a teacher to delete.")
return

46 | P a g e
if [Link]("Confirm", "Are you sure you want to delete this
teacher?"):
teacher_id = [Link](selected[0])['values'][0]
[Link].execute_query("UPDATE teachers SET is_active = 0 WHERE id
= %s", (teacher_id,))
[Link](selected[0])
[Link]("Success", "Teacher deleted successfully.")

def edit_teacher():
selected = [Link]()
if not selected:
[Link]("Error", "Select a teacher to edit.")
return
teacher_data = [Link](selected[0])['values']
self.edit_teacher_dialog(teacher_data, dialog)

button_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
button_frame.pack(pady=20)
[Link](button_frame, text="Edit", command=edit_teacher,
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Delete", command=delete_teacher,
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Close", command=[Link],
style='[Link]').pack(side=[Link], padx=5)
except [Link] as e:
[Link]("Error", f"Failed to load teachers: {e}")
[Link](f"Manage teachers error: {e}")

47 | P a g e
def edit_teacher_dialog(self, teacher_data, parent_dialog):
dialog = [Link]([Link])
[Link]("Edit Teacher")
[Link]("450x500")
[Link](bg="#ffffff" if [Link] == 'light' else "#37474f")
[Link]([Link])
dialog.grab_set()

[Link](dialog, text="Edit Teacher", font=("Segoe UI", 16, "bold"), bg="#ffffff"


if [Link] == 'light' else "#37474f", fg=self.fg_color).pack(pady=10)

form_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
form_frame.pack(pady=20)

[Link](form_frame, text="Name:", bg="#ffffff" if [Link] == 'light' else


"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=0,
column=0, pady=5, sticky="e")
name_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
name_entry.insert(0, teacher_data[1])
name_entry.grid(row=0, column=1, pady=5, padx=10)

[Link](form_frame, text="Employee ID:", bg="#ffffff" if [Link] == 'light'


else "#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=1,
column=0, pady=5, sticky="e")
emp_id_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
emp_id_entry.insert(0, teacher_data[2])
emp_id_entry.grid(row=1, column=1, pady=5, padx=10)

48 | P a g e
[Link](form_frame, text="Subject:", bg="#ffffff" if [Link] == 'light' else
"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=2,
column=0, pady=5, sticky="e")
subject_combo = [Link](form_frame, values=SUBJECTS, width=22,
font=("Segoe UI", 10))
subject_combo.set(teacher_data[3])
subject_combo.grid(row=2, column=1, pady=5, padx=10)

[Link](form_frame, text="Classes (comma-separated):", bg="#ffffff" if


[Link] == 'light' else "#37474f", fg="#455a64" if [Link] == 'light' else
"#b0bec5").grid(row=3, column=0, pady=5, sticky="e")
classes_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
classes_entry.insert(0, teacher_data[4] or "")
classes_entry.grid(row=3, column=1, pady=5, padx=10)

[Link](form_frame, text="Max Periods per Day:", bg="#ffffff" if [Link] ==


'light' else "#37474f", fg="#455a64" if [Link] == 'light' else
"#b0bec5").grid(row=4, column=0, pady=5, sticky="e")
max_periods_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
max_periods_entry.insert(0, str(teacher_data[5]))
max_periods_entry.grid(row=4, column=1, pady=5, padx=10)

def save_teacher():
name = name_entry.get().strip()
emp_id = emp_id_entry.get().strip()
subject = subject_combo.get().strip()
classes = classes_entry.get().strip()
max_periods = max_periods_entry.get().strip()

if not all([name, emp_id, subject, max_periods]):


49 | P a g e
[Link]("Error", "Name, Employee ID, Subject, and Max
Periods are required.")
return

try:
max_periods = int(max_periods)
if max_periods <= 0 or max_periods > len(PERIODS):
[Link]("Error", f"Max periods must be between 1 and
{len(PERIODS)}.")
return

if classes:
class_list = [[Link]() for c in [Link](",")]
for c in class_list:
if c not in CLASSES:
[Link]("Error", f"Invalid class: {c}")
return

check_query = "SELECT COUNT(*) FROM teachers WHERE


(employee_id = %s OR name = %s) AND id != %s"
if [Link].execute_query(check_query, (emp_id, name, teacher_data[0]),
fetch=True)[0][0] > 0:
[Link]("Error", "Teacher name or Employee ID already
exists.")
return

[Link].execute_query(
"UPDATE teachers SET name = %s, employee_id = %s, subject = %s,
classes = %s, max_periods = %s WHERE id = %s",

50 | P a g e
(name, emp_id, subject, classes, max_periods, teacher_data[0])
)
[Link]("Success", "Teacher updated successfully!")
[Link]()
parent_dialog.destroy()
self.manage_teachers()
except ValueError:
[Link]("Error", "Max periods must be a number.")
except [Link] as e:
[Link]("Error", f"Failed to update teacher: {e}")
[Link](f"Edit teacher error: {e}")

button_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
button_frame.pack(pady=20)
[Link](button_frame, text="Save", command=save_teacher,
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Cancel", command=[Link],
style='[Link]').pack(side=[Link], padx=5)

def teacher_attendance(self):
dialog = [Link]([Link])
[Link]("Teacher Attendance")
[Link]("800x500")
[Link](bg="#ffffff" if [Link] == 'light' else "#37474f")
[Link]([Link])
dialog.grab_set()

51 | P a g e
[Link](dialog, text="Teacher Attendance", font=("Segoe UI", 16, "bold"),
bg="#ffffff" if [Link] == 'light' else "#37474f", fg=self.fg_color).pack(pady=10)

tree_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else "#37474f")


tree_frame.pack(fill=[Link], expand=True, padx=10, pady=10)

columns = ("ID", "Name", "Subject", "Date", "Day", "Period", "Status",


"Reason")
tree = [Link](tree_frame, columns=columns, show="headings")
[Link](fill=[Link], expand=True)

for col in columns:


[Link](col, text=col)
[Link](col, width=100)

try:
query = """
SELECT [Link], [Link], [Link], a.absent_date, a.absent_day,
a.absent_period,
CASE WHEN a.is_approved THEN 'Absent' ELSE 'Pending' END,
[Link]
FROM absences a
JOIN teachers t ON a.teacher_id = [Link]
WHERE t.is_active = 1
"""
records = [Link].execute_query(query, fetch=True)
for record in records:
[Link]("", [Link], values=record)

52 | P a g e
def mark_absence():
teacher_dialog = [Link](dialog)
teacher_dialog.title("Mark Absence")
teacher_dialog.geometry("400x500")
teacher_dialog.configure(bg="#ffffff" if [Link] == 'light' else "#37474f")
teacher_dialog.transient(dialog)
teacher_dialog.grab_set()

[Link](teacher_dialog, text="Mark Teacher Absence", font=("Segoe UI",


14, "bold"), bg="#ffffff" if [Link] == 'light' else "#37474f",
fg=self.fg_color).pack(pady=10)

form_frame = [Link](teacher_dialog, bg="#ffffff" if [Link] == 'light'


else "#37474f")
form_frame.pack(pady=10)

[Link](form_frame, text="Teacher:", bg="#ffffff" if [Link] == 'light'


else "#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=0,
column=0, pady=5, sticky="e")
teachers = [Link].execute_query("SELECT name FROM teachers WHERE
is_active = 1", fetch=True)
teacher_names = [t[0] for t in teachers]
teacher_combo = [Link](form_frame, values=teacher_names,
width=22, font=("Segoe UI", 10))
teacher_combo.grid(row=0, column=1, pady=5, padx=10)

[Link](form_frame, text="Date:", bg="#ffffff" if [Link] == 'light' else


"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=1,
column=0, pady=5, sticky="e")
date_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))

53 | P a g e
date_entry.insert(0, [Link]().strftime("%Y-%m-%d"))
date_entry.grid(row=1, column=1, pady=5, padx=10)

[Link](form_frame, text="Day:", bg="#ffffff" if [Link] == 'light' else


"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=2,
column=0, pady=5, sticky="e")
day_combo = [Link](form_frame, values=DAYS, width=22,
font=("Segoe UI", 10))
day_combo.grid(row=2, column=1, pady=5, padx=10)

[Link](form_frame, text="Period:", bg="#ffffff" if [Link] == 'light'


else "#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=3,
column=0, pady=5, sticky="e")
period_combo = [Link](form_frame, values=PERIODS, width=22,
font=("Segoe UI", 10))
period_combo.grid(row=3, column=1, pady=5, padx=10)

[Link](form_frame, text="Reason:", bg="#ffffff" if [Link] == 'light'


else "#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=4,
column=0, pady=5, sticky="e")
reason_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
reason_entry.grid(row=4, column=1, pady=5, padx=10)

whole_day_var = [Link](value=False)
def toggle_period_combo():
if whole_day_var.get():
period_combo.config(state='disabled')
else:
period_combo.config(state='normal')

54 | P a g e
check = [Link](form_frame, text="Absent for whole day",
variable=whole_day_var,
command=toggle_period_combo, bg="#ffffff" if [Link]
== 'light' else "#37474f",
fg="#455a64" if [Link] == 'light' else "#b0bec5",
selectcolor="#ffffff" if [Link] == 'light' else "#37474f")
[Link](row=5, column=0, columnspan=2, pady=5, sticky="w")

def save_absence():
teacher_name = teacher_combo.get().strip()
absent_date = date_entry.get().strip()
absent_day = day_combo.get().strip()
absent_period = period_combo.get().strip()
reason = reason_entry.get().strip()
whole_day = whole_day_var.get()

if not all([teacher_name, absent_date, absent_day, reason]):


[Link]("Error", "Teacher, Date, Day, and Reason are
required.")
return
if not whole_day and not absent_period:
[Link]("Error", "Period is required if not absent for
whole day.")
return

try:
dt = [Link](absent_date, "%Y-%m-%d")
calculated_day = [Link]("%A")
if calculated_day != absent_day:

55 | P a g e
[Link]("Error", f"Selected day {absent_day} does
not match the date's day {calculated_day}.")
return

if absent_day not in DAYS:


[Link]("Error", "Invalid day selected.")
return

teacher_result = [Link].execute_query("SELECT id FROM teachers


WHERE name = %s AND is_active = 1", (teacher_name,), fetch=True)
if not teacher_result:
[Link]("Error", "Invalid teacher selected.")
return
teacher_id = teacher_result[0][0]

if whole_day:
periods = PERIODS
else:
periods = [absent_period]
if absent_period not in PERIODS:
[Link]("Error", "Invalid period selected.")
return

substituted = 0
for p in periods:
check_query = "SELECT COUNT(*) FROM absences WHERE
teacher_id = %s AND absent_date = %s AND absent_day = %s AND absent_period =
%s"

56 | P a g e
if [Link].execute_query(check_query, (teacher_id, absent_date,
absent_day, p), fetch=True)[0][0] > 0:
continue # Skip if already recorded

[Link].execute_query(
"INSERT INTO absences (teacher_id, absent_date, absent_day,
absent_period, reason, substitute_teacher_id, is_approved) VALUES (%s, %s, %s, %s,
%s, %s, %s)",
(teacher_id, absent_date, absent_day, p, reason, None, 1)
)
self.handle_substitution(teacher_id, absent_date, absent_day, p)
substituted += 1

message = f"Absence(s) recorded for {teacher_name} on


{absent_day}"
if whole_day:
message += " for the whole day."
else:
message += f" for {absent_period}."
[Link]("Success", message)
teacher_dialog.destroy()
[Link]()
self.teacher_attendance()
except ValueError:
[Link]("Error", "Invalid date format. Use YYYY-MM-
DD.")
except [Link] as e:
[Link]("Error", f"Failed to record absence: {e}")
[Link](f"Mark absence error: {e}")

57 | P a g e
button_frame = [Link](teacher_dialog, bg="#ffffff" if [Link] == 'light'
else "#37474f")
button_frame.pack(pady=20)
[Link](button_frame, text="Save", command=save_absence,
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Cancel", command=teacher_dialog.destroy,
style='[Link]').pack(side=[Link], padx=5)

button_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
button_frame.pack(pady=20)
[Link](button_frame, text="Mark Absence", command=mark_absence,
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Close", command=[Link],
style='[Link]').pack(side=[Link], padx=5)
except [Link] as e:
[Link]("Error", f"Failed to load attendance: {e}")
[Link](f"Teacher attendance error: {e}")

def get_class_level(self, class_name):


if class_name in ["LKG", "UKG"]:
return 0
try:
return int(class_name[:-2])
except:
return 0

def handle_substitution(self, teacher_id, absent_date, absent_day, absent_period):

58 | P a g e
try:
# Find classes affected by the teacher's absence on the specific day and period
query = """
SELECT class_name, section, subject_id
FROM timetables
WHERE teacher_id = %s AND day = %s AND period = %s
"""
affected_classes = [Link].execute_query(query, (teacher_id, absent_day,
absent_period), fetch=True)
if not affected_classes:
[Link](f"No classes assigned to teacher {teacher_id} on {absent_day},
{absent_period}.")
return

# Get all teachers except the absent one


available_teachers = [Link].execute_query(
"SELECT id, name, subject, classes, max_periods FROM teachers WHERE
is_active = 1 AND id != %s",
(teacher_id,), fetch=True
)

for class_name, section, subject_id in affected_classes:


subject = SUBJECTS[subject_id - 1]
category = SUBJECT_CATEGORIES.get(subject, "other")

# Get busy teachers for this period


period_teachers = [Link].execute_query(
"SELECT teacher_id FROM timetables WHERE day = %s AND period =
%s",
59 | P a g e
(absent_day, absent_period), fetch=True
)
busy_teacher_ids = {t[0] for t in period_teachers if t[0] is not None}

# Level 1: same subject and class, free, load < max


suitable_teachers = [
t for t in available_teachers
if t[0] not in busy_teacher_ids and
t[2] == subject and
class_name in (t[3] or "").split(",") and
self.get_teacher_load(t[0], absent_day) < t[4] and
not self.is_teacher_absent(t[0], absent_date, absent_period)
]
level = 1

if not suitable_teachers:
# Level 2: same class, any subject, free, load < max
suitable_teachers = [
t for t in available_teachers
if t[0] not in busy_teacher_ids and
class_name in (t[3] or "").split(",") and
self.get_teacher_load(t[0], absent_day) < t[4] and
not self.is_teacher_absent(t[0], absent_date, absent_period)
]
level = 2
if suitable_teachers:
[Link](f"No same-subject substitute found for {class_name}
{section}, {absent_day}, {absent_period}. Using same-class any-subject teacher.")

60 | P a g e
if not suitable_teachers:
# Level 3: any, free, load < max
suitable_teachers = [
t for t in available_teachers
if t[0] not in busy_teacher_ids and
self.get_teacher_load(t[0], absent_day) < t[4] and
not self.is_teacher_absent(t[0], absent_date, absent_period)
]
level = 3
if suitable_teachers:
[Link](f"No class-qualified substitute found for {class_name}
{section}, {absent_day}, {absent_period}. Using any available teacher.")

if not suitable_teachers:
# Level 4: any, free, load <= max (allow at max)
suitable_teachers = [
t for t in available_teachers
if t[0] not in busy_teacher_ids and
self.get_teacher_load(t[0], absent_day) <= t[4] and
not self.is_teacher_absent(t[0], absent_date, absent_period)
]
level = 4
if suitable_teachers:
[Link](f"Assigning substitute at level 4 (allowing max load) for
{class_name} {section}, {absent_day}, {absent_period}.")

if not suitable_teachers:

61 | P a g e
# Level 5: reassign from lower priority class
level = 5
potential_teachers = [
t for t in available_teachers
if self.get_teacher_load(t[0], absent_day) < t[4] and
not self.is_teacher_absent(t[0], absent_date, absent_period)
] # no busy check

candidates = []
for t in potential_teachers:
query = "SELECT class_name, section FROM timetables WHERE
teacher_id = %s AND day = %s AND period = %s"
result = [Link].execute_query(query, (t[0], absent_day, absent_period),
fetch=True)
if result:
old_class, old_section = result[0]
old_level = self.get_class_level(old_class)
load_ratio = self.get_teacher_load(t[0], absent_day) / t[4] if t[4] > 0
else 0
same_category = 0 if SUBJECT_CATEGORIES.get(t[2], "other")
== category else 1
[Link]((t, old_class, old_section, old_level, load_ratio,
same_category))

if candidates:
# Select the one with min old_level, then min load_ratio, then min
same_category
[Link](key=lambda x: (x[3], x[4], x[5]))
selected_teacher, old_class, old_section, _, _, _ = candidates[0]

62 | P a g e
# Unassign the old slot
[Link].execute_query(
"UPDATE timetables SET teacher_id = NULL, is_substitution = 1
WHERE class_name = %s AND section = %s AND day = %s AND period = %s",
(old_class, old_section, absent_day, absent_period)
)

# Assign to new slot


[Link].execute_query(
"UPDATE timetables SET teacher_id = %s, is_substitution = 1
WHERE class_name = %s AND section = %s AND day = %s AND period = %s",
(selected_teacher[0], class_name, section, absent_day,
absent_period)
)

# Update absence
[Link].execute_query(
"UPDATE absences SET substitute_teacher_id = %s WHERE
teacher_id = %s AND absent_date = %s AND absent_day = %s AND absent_period =
%s",
(selected_teacher[0], teacher_id, absent_date, absent_day,
absent_period)
)

[Link](f"Reassigned {selected_teacher[1]} from {old_class}


{old_section} to {class_name} {section} for substitution on {absent_day},
{absent_period}. {old_class} {old_section} is now unassigned.")
continue

63 | P a g e
if not suitable_teachers and level != 5:
# No suitable substitute found
[Link].execute_query(
"UPDATE timetables SET teacher_id = NULL, is_substitution = 1
WHERE class_name = %s AND section = %s AND day = %s AND period = %s",
(class_name, section, absent_day, absent_period)
)
[Link].execute_query(
"UPDATE absences SET substitute_teacher_id = NULL WHERE
teacher_id = %s AND absent_date = %s AND absent_day = %s AND absent_period =
%s",
(teacher_id, absent_date, absent_day, absent_period)
)
[Link](f"No suitable substitute for {class_name} {section},
{absent_day}, {absent_period}. Slot marked as Unassigned.")
continue

if level != 5:
# Select the teacher
def select_key(x):
load_ratio = self.get_teacher_load(x[0], absent_day) / x[4] if x[4] > 0
else 0
same_category = 0 if SUBJECT_CATEGORIES.get(x[2], "other") ==
category else 1
return (load_ratio, same_category)

selected_teacher = min(suitable_teachers, key=select_key)

# Update timetable

64 | P a g e
[Link].execute_query(
"UPDATE timetables SET teacher_id = %s, is_substitution = 1
WHERE class_name = %s AND section = %s AND day = %s AND period = %s",
(selected_teacher[0], class_name, section, absent_day, absent_period)
)

# Update absence
[Link].execute_query(
"UPDATE absences SET substitute_teacher_id = %s WHERE
teacher_id = %s AND absent_date = %s AND absent_day = %s AND absent_period =
%s",
(selected_teacher[0], teacher_id, absent_date, absent_day,
absent_period)
)

# Increment assigned if new assignment (not for level 5, as it's


reassignment)
[Link].execute_query(
"UPDATE teachers SET assigned_periods = assigned_periods + 1
WHERE id = %s",
(selected_teacher[0],)
)

[Link](f"Assigned substitute {selected_teacher[1]} (subject:


{selected_teacher[2]}) at level {level} for {class_name} {section}, {absent_day},
{absent_period}")
except [Link] as e:
[Link](f"Substitution error: {e}")
[Link]("Error", f"Failed to handle substitution: {e}")

65 | P a g e
def open_timetable_editor(self, class_name, section):
dialog = [Link]([Link])
[Link](f"Timetable Editor: {class_name} {section}")
[Link]("1000x600")
[Link](bg="#ffffff" if [Link] == 'light' else "#37474f")
[Link]([Link])
dialog.grab_set()

[Link](dialog, text=f"Timetable for {class_name} {section}", font=("Segoe


UI", 16, "bold"), bg="#ffffff" if [Link] == 'light' else "#37474f",
fg=self.fg_color).pack(pady=10)

tree_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else "#37474f")


tree_frame.pack(fill=[Link], expand=True, padx=10, pady=10)

columns = ["Period"] + DAYS


tree = [Link](tree_frame, columns=columns, show="headings")
[Link](fill=[Link], expand=True)

[Link]("Period", text="Period")
[Link]("Period", width=100)
for day in DAYS:
[Link](day, text=day)
[Link](day, width=150)

try:
timetable_data = {}
for period in PERIODS:

66 | P a g e
timetable_data[period] = {day: "" for day in DAYS}
for day in DAYS:
query = """
SELECT t.teacher_id, t.subject_id, t.room_number
FROM timetables t
WHERE t.class_name = %s AND [Link] = %s AND [Link] = %s AND
[Link] = %s
"""
results = [Link].execute_query(query, (class_name, section, day, period),
fetch=True)
if results:
teacher_id, subject_id, room = results[0]
teacher_name = [Link].execute_query("SELECT name FROM teachers
WHERE id = %s", (teacher_id,), fetch=True)[0][0] if teacher_id else "Unassigned"
subject = SUBJECTS[subject_id - 1]
timetable_data[period][day] = f"{teacher_name} ({subject})"

for period in PERIODS:


[Link]("", [Link], values=[period] + [timetable_data[period][day] for
day in DAYS])

def edit_period(event):
item = [Link]()
if not item:
return
period = [Link](item)["values"][0]
col = tree.identify_column(event.x)
day = columns[int(col[1:]) - 1] if col != "#0" else None
if day == "Period":
67 | P a g e
return
self.edit_timetable_entry(class_name, section, day, period, dialog)

[Link]("<Double-1>", edit_period)

button_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
button_frame.pack(pady=20)
[Link](button_frame, text="Close", command=[Link],
style='[Link]').pack(side=[Link], padx=5)
except [Link] as e:
[Link]("Error", f"Failed to load timetable: {e}")
[Link](f"Timetable editor error: {e}")

def edit_timetable_entry(self, class_name, section, day, period, parent_dialog):


dialog = [Link]([Link])
[Link](f"Edit Timetable: {class_name} {section} - {day} {period}")
[Link]("400x300")
[Link](bg="#ffffff" if [Link] == 'light' else "#37474f")
[Link]([Link])
dialog.grab_set()

[Link](dialog, text=f"Edit {day} {period}", font=("Segoe UI", 14, "bold"),


bg="#ffffff" if [Link] == 'light' else "#37474f", fg=self.fg_color).pack(pady=10)

form_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
form_frame.pack(pady=10)

68 | P a g e
[Link](form_frame, text="Teacher:", bg="#ffffff" if [Link] == 'light' else
"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=0,
column=0, pady=5, sticky="e")
teachers = [Link].execute_query("SELECT name FROM teachers WHERE
is_active = 1", fetch=True)
teacher_names = ["Unassigned"] + [t[0] for t in teachers]
teacher_combo = [Link](form_frame, values=teacher_names, width=22,
font=("Segoe UI", 10))
teacher_combo.grid(row=0, column=1, pady=5, padx=10)

[Link](form_frame, text="Subject:", bg="#ffffff" if [Link] == 'light' else


"#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=1,
column=0, pady=5, sticky="e")
subject_combo = [Link](form_frame, values=SUBJECTS, width=22,
font=("Segoe UI", 10))
subject_combo.grid(row=1, column=1, pady=5, padx=10)

[Link](form_frame, text="Room Number:", bg="#ffffff" if [Link] == 'light'


else "#37474f", fg="#455a64" if [Link] == 'light' else "#b0bec5").grid(row=2,
column=0, pady=5, sticky="e")
room_entry = [Link](form_frame, width=25, font=("Segoe UI", 10))
room_entry.grid(row=2, column=1, pady=5, padx=10)

def save_entry():
teacher_name = teacher_combo.get().strip()
subject = subject_combo.get().strip()
room = room_entry.get().strip()

if not subject:
[Link]("Error", "Subject is required.")

69 | P a g e
return

try:
teacher_id = None if teacher_name == "Unassigned" else
[Link].execute_query("SELECT id FROM teachers WHERE name = %s",
(teacher_name,), fetch=True)[0][0]
subject_id = [Link](subject) + 1

if teacher_id:
assigned_periods = [Link].execute_query(
"SELECT COUNT(*) FROM timetables WHERE teacher_id = %s
AND day = %s",
(teacher_id, day), fetch=True
)[0][0]
max_periods = [Link].execute_query(
"SELECT max_periods FROM teachers WHERE id = %s",
(teacher_id,), fetch=True
)[0][0]
if assigned_periods >= max_periods:
[Link]("Error", f"{teacher_name} has reached max
periods for {day}.")
return

absent_date = [Link]().strftime("%Y-%m-%d")
is_absent = [Link].execute_query(
"SELECT COUNT(*) FROM absences WHERE teacher_id = %s AND
absent_date = %s AND absent_period = %s AND is_approved = 1",
(teacher_id, absent_date, period), fetch=True
)[0][0]

70 | P a g e
if is_absent:
[Link]("Error", f"{teacher_name} is absent on
{absent_date} for {period}.")
return

[Link].execute_query(
"INSERT INTO timetables (class_name, section, day, period, teacher_id,
subject_id, room_number) VALUES (%s, %s, %s, %s, %s, %s, %s) ON DUPLICATE
KEY UPDATE teacher_id = VALUES(teacher_id), subject_id = VALUES(subject_id),
room_number = VALUES(room_number)",
(class_name, section, day, period, teacher_id, subject_id, room)
)
[Link]("Success", "Timetable entry updated.")
[Link]()
parent_dialog.destroy()
self.open_timetable_editor(class_name, section)
except [Link] as e:
[Link]("Error", f"Failed to update timetable: {e}")
[Link](f"Edit timetable error: {e}")

button_frame = [Link](dialog, bg="#ffffff" if [Link] == 'light' else


"#37474f")
button_frame.pack(pady=20)
[Link](button_frame, text="Save", command=save_entry,
style='[Link]').pack(side=[Link], padx=5)
[Link](button_frame, text="Cancel", command=[Link],
style='[Link]').pack(side=[Link], padx=5)

def get_teacher_load(self, teacher_id, day):

71 | P a g e
query = "SELECT COUNT(*) FROM timetables WHERE teacher_id = %s AND
day = %s"
return [Link].execute_query(query, (teacher_id, day), fetch=True)[0][0]

def is_teacher_absent(self, teacher_id, date, period=None):


if period:
query = "SELECT COUNT(*) FROM absences WHERE teacher_id = %s
AND absent_date = %s AND absent_period = %s AND is_approved = 1"
return [Link].execute_query(query, (teacher_id, date, period),
fetch=True)[0][0] > 0
query = "SELECT COUNT(*) FROM absences WHERE teacher_id = %s AND
absent_date = %s AND is_approved = 1"
return [Link].execute_query(query, (teacher_id, date), fetch=True)[0][0] > 0

def is_teacher_assigned(self, teacher_id, day, period):


query = "SELECT COUNT(*) FROM timetables WHERE teacher_id = %s AND
day = %s AND period = %s"
return [Link].execute_query(query, (teacher_id, day, period), fetch=True)[0][0] >
0

def get_previous_period_teacher(self, class_name, section, day, period_index):


if period_index == 0:
return None
prev_period = PERIODS[period_index - 1]
query = "SELECT teacher_id FROM timetables WHERE class_name = %s AND
section = %s AND day = %s AND period = %s"
result = [Link].execute_query(query, (class_name, section, day, prev_period),
fetch=True)
return result[0][0] if result else None

72 | P a g e
def auto_generate_timetable(self):
if not [Link]("Confirm", "This will overwrite existing timetables.
Proceed?"):
return

try:
[Link].execute_query("DELETE FROM timetables")
[Link].execute_query("UPDATE teachers SET assigned_periods = 0")
[Link]("Cleared existing timetables and reset teacher assignments.")

classes = [Link].execute_query(
"SELECT class_name, section FROM classes WHERE is_active = 1",
fetch=True
)
teachers = [Link].execute_query(
"SELECT id, name, subject, classes, max_periods, assigned_periods FROM
teachers WHERE is_active = 1",
fetch=True
)

teacher_data = [
{
'id': t[0],
'name': t[1],
'subject': t[2],
'classes': t[3].split(",") if t[3] else [],
'max_periods': t[4],
'total_assigned': 0

73 | P a g e
}
for t in teachers
]

for class_name, section in classes:


for day in DAYS:
timetable = {period: None for period in PERIODS}
period_indices = list(range(len(PERIODS)))
[Link](period_indices)

for period_index in period_indices:


period = PERIODS[period_index]
available_teachers = [
t for t in teacher_data
if class_name in t['classes'] and
t['subject'] in SUBJECTS and
self.get_teacher_load(t['id'], day) < t['max_periods'] and
not self.is_teacher_assigned(t['id'], day, period)
]

if not available_teachers:
[Link](f"No available teachers for {class_name} {section},
{day}, {period}")
continue

available_teachers.sort(key=lambda x: x['total_assigned'])
prev_teacher_id = self.get_previous_period_teacher(class_name,
section, day, period_index)

74 | P a g e
preferred_teachers = [
t for t in available_teachers
if prev_teacher_id is None or t['id'] != prev_teacher_id
] or available_teachers

if not preferred_teachers:
[Link](f"No non-consecutive teachers available for
{class_name} {section}, {day}, {period}")
continue

selected_teacher = preferred_teachers[0]
subject_id = [Link](selected_teacher['subject']) + 1

[Link].execute_query(
"INSERT INTO timetables (class_name, section, day, period,
teacher_id, subject_id, room_number) "
"VALUES (%s, %s, %s, %s, %s, %s, %s)",
(class_name, section, day, period, selected_teacher['id'], subject_id,
f"Room {class_name}-{section}")
)
selected_teacher['total_assigned'] += 1
[Link].execute_query(
"UPDATE teachers SET assigned_periods = assigned_periods + 1
WHERE id = %s",
(selected_teacher['id'],)
)
[Link](f"Assigned {selected_teacher['name']}
({selected_teacher['subject']}) to "
f"{class_name} {section}, {day}, {period}")
75 | P a g e
# Re-apply approved absences substitutions
absences = [Link].execute_query(
"SELECT teacher_id, absent_date, absent_day, absent_period FROM
absences WHERE is_approved = 1", fetch=True
)
for teacher_id, absent_date, absent_day, absent_period in absences:
self.handle_substitution(teacher_id, absent_date, absent_day, absent_period)

for class_name, section in classes:


for day in DAYS:
for period in PERIODS:
query = "SELECT COUNT(*) FROM timetables WHERE class_name
= %s AND section = %s AND day = %s AND period = %s"
count = [Link].execute_query(query, (class_name, section, day,
period), fetch=True)[0][0]
if count == 0:
[Link](f"Timetable incomplete for {class_name}
{section}, {day}, {period}")

[Link]("Success", "Timetable generated successfully!")


[Link]("Timetable generation completed successfully.")
except [Link] as e:
[Link]("Error", f"Failed to generate timetable: {e}")
[Link](f"Auto generate timetable error: {e}")

def export_timetable(self):
file_path = [Link](defaultextension=".csv",
filetypes=[("CSV files", "*.csv")])

76 | P a g e
if not file_path:
return

try:
with open(file_path, mode='w', newline='') as file:
writer = [Link](file)
[Link](["Class", "Section", "Day", "Period", "Teacher", "Subject",
"Room"])
query = """
SELECT t.class_name, [Link], [Link], [Link], [Link], t.subject_id,
t.room_number
FROM timetables t
LEFT JOIN teachers te ON t.teacher_id = [Link]
"""
rows = [Link].execute_query(query, fetch=True)
[Link]([(row[0], row[1], row[2], row[3], row[4] or "Unassigned",
SUBJECTS[int(row[5])-1], row[6]) for row in rows])
[Link]("Success", "Timetable exported successfully!")
except Exception as e:
[Link]("Error", f"Failed to export timetable: {e}")
[Link](f"Export timetable error: {e}")

def import_timetable(self):
file_path = [Link](filetypes=[("CSV files", "*.csv")])
if not file_path:
return

try:

77 | P a g e
with open(file_path, mode='r') as file:
reader = [Link](file)
header = next(reader)
if header != ["Class", "Section", "Day", "Period", "Teacher", "Subject",
"Room"]:
[Link]("Error", "Invalid CSV format.")
return

[Link].execute_query("DELETE FROM timetables")


[Link].execute_query("UPDATE teachers SET assigned_periods = 0")

for row in reader:


class_name, section, day, period, teacher_name, subject, room = row
if day not in DAYS or period not in PERIODS or class_name not in
CLASSES or section not in SECTIONS:
continue
if subject not in SUBJECTS:
continue

teacher_id = None
if teacher_name != "Unassigned":
teacher_id_result = [Link].execute_query(
"SELECT id FROM teachers WHERE name = %s AND is_active =
1", (teacher_name,), fetch=True
)
if not teacher_id_result:
continue
teacher_id = teacher_id_result[0][0]

78 | P a g e
assigned_periods = [Link].execute_query(
"SELECT COUNT(*) FROM timetables WHERE teacher_id = %s
AND day = %s",
(teacher_id, day), fetch=True
)[0][0]
max_periods = [Link].execute_query(
"SELECT max_periods FROM teachers WHERE id = %s",
(teacher_id,), fetch=True
)[0][0]
if assigned_periods >= max_periods:
continue

subject_id = [Link](subject) + 1
[Link].execute_query(
"INSERT INTO timetables (class_name, section, day, period,
teacher_id, subject_id, room_number) VALUES (%s, %s, %s, %s, %s, %s, %s) ON
DUPLICATE KEY UPDATE teacher_id = VALUES(teacher_id), subject_id =
VALUES(subject_id), room_number = VALUES(room_number)",
(class_name, section, day, period, teacher_id, subject_id, room)
)
if teacher_id:
[Link].execute_query(
"UPDATE teachers SET assigned_periods = assigned_periods + 1
WHERE id = %s",
(teacher_id,)
)
[Link]("Success", "Timetable imported successfully!")
except Exception as e:
[Link]("Error", f"Failed to import timetable: {e}")

79 | P a g e
[Link](f"Import timetable error: {e}")

def logout(self):
self.current_user = None
self.current_user_id = None
self.current_user_role = None
self.create_login_screen()

def main():
root = [Link]()
app = EduPlanPro(root)
[Link]()

if __name__ == "__main__":
main()

80 | P a g e
OUTPUT SCREENSHOTS

81 | P a g e
82 | P a g e
83 | P a g e
84 | P a g e
85 | P a g e
86 | P a g e
87 | P a g e
88 | P a g e
89 | P a g e
90 | P a g e
91 | P a g e
92 | P a g e
93 | P a g e
94 | P a g e
95 | P a g e
96 | P a g e
EXPORTED TIMETABLE:

97 | P a g e
MYSQL-DATABASE

98 | P a g e
99 | P a g e
100 | P a g e
101 | P a g e
102 | P a g e
103 | P a g e
TESTING
 Software testing is the process of evaluating software to detect errors and ensure
quality.
 It checks whether the software meets specified requirements and functions
correctly.
 Can be done manually or using automated tools.

Importance

 Helps identify bugs, security vulnerabilities, and performance issues.


 Ensures software reliability and user satisfaction.
 Reduces costs by finding defects early in development.

Types of Software Testing

 Functional Testing: Ensures software functions as expected (e.g., unit,


integration, system, and acceptance testing).
 Non-Functional Testing: Checks performance, security, usability, and reliability.
 Maintenance Testing: Done after deployment to fix bugs or improve
performance.

Testing Strategies

 Manual Testing: Testers execute test cases without automation.


 Automated Testing: Uses scripts and tools for faster and repetitive testing.
 Black-Box Testing: Focuses on outputs without knowing internal code.
 White-Box Testing: Examines internal code structure and logic.
 Gray-Box Testing: A mix of black-box and white-box testing techniques.

Testing Levels

 Unit Testing: Tests individual components/modules.


 Integration Testing: Checks interactions between modules.
 System Testing: Evaluates the entire application.
 Acceptance Testing: Ensures the software meets user requirements.

Test Automation Tools

 Selenium, Junit, TestNG, Appium, etc., help automate testing processes.

Software Development Models & Testing

 Waterfall Model: Testing occurs after development.


104 | P a g e
 Agile Model: Testing is continuous and iterative.
 DevOps: Testing is integrated into CI/CD pipelines.

Challenges in Software Testing

 Limited time and resources.


 Complexity of modern applications.
 Rapidly evolving technologies.

Future of Software Testing

 Increased use of AI and machine learning in test automation.


 Shift-left testing (testing earlier in development).
 More emphasis on security and performance testing.

105 | P a g e
WHITEBOX TESTING
Definition: White-box testing is a software testing technique where the internal
structure, code, and logic of a program are tested.

Also Known As: Clear-box testing, glass-box testing, transparent-box testing, or


structural testing.

Purpose:

 Ensures internal operations perform as expected.


 Checks code structure, logic, and paths.
 Identifies security vulnerabilities, broken paths, and improper coding practices.

Techniques Used:

 Control Flow Testing – Examines program execution paths.


 Data Flow Testing – Analyses data usage in different parts of the code.
 Branch Testing – Tests all possible branches (if-else, loops).
 Path Testing – Ensures all execution paths are covered.
 Loop Testing – Focuses on different loop conditions.

Advantages:

 Finds hidden errors in code.


 Optimizes code by eliminating redundant or inefficient logic.
 Thorough testing due to direct access to the code.

Disadvantages:

 Requires knowledge of programming and code structure.


 Time-consuming and complex for large software systems.
 Doesn't test the software's functionality from a user perspective.

Comparison with Black-Box Testing:

 White-box testing examines internal code, while black-box testing focuses on


external functionality.
 White-box testing is done by developers/testers with coding knowledge; black-
box testing is done by testers without needing code access.

106 | P a g e
White box testing for the EduPlan Pro – Automatic Timetable Generator was
carried out during the development phase to verify the internal logic and
functioning of each module. The testing focused on checking the correctness of
code execution, decision-making conditions, and data flow within the system.

The main areas tested included:

 Login and Authentication Module: Verified that user credentials were


validated correctly and password encryption using SHA-256 worked as
intended.
 Database Connection Module: Ensured successful connection and
disconnection from the MySQL database without errors.
 Timetable Generation Logic: Checked the accuracy of subject and teacher
allocation, ensuring no overlapping classes or duplicate entries occurred.
 Attendance and Substitution Module: Tested internal functions to confirm
that teacher absence detection and substitution assignments worked correctly.
 File Operations: Verified export and import functionality for CSV files to
ensure data integrity during read/write operations.
 Configuration Management: Checked that changes in configuration files
(school name, theme, academic year) were read and applied correctly.
 Error Handling and Logging: Ensured proper exception handling and that all
system activities were correctly logged in the log file.

During white box testing, print statements and log entries were used to trace
code execution paths, identify logic errors, and confirm expected outputs. Any
discovered bugs were corrected immediately to ensure reliable performance
before integration testing.

107 | P a g e
BLACK BOX TESTING
Definition: Black-box testing is a software testing technique that evaluates a
system's functionality without knowing its internal code, structure, or
implementation.

Also Known As: Behavioral testing, opaque-box testing, closed-box testing, or


functional testing.

Purpose:

 Tests software based on inputs and expected outputs.


 Ensures the system meets functional requirements.
 Identifies issues in user interface, performance, and security.

Techniques Used:

 Equivalence Partitioning – Divides input data into valid and invalid groups for
testing.
 Boundary Value Analysis – Tests values at the boundary limits (e.g., minimum
and maximum inputs).
 Decision Table Testing – Uses tables to represent complex business logic and
test different input combinations.
 State Transition Testing – Evaluates system behavior based on state changes.
 Error Guessing – Relies on tester experience to predict and find potential
errors.

Advantages:

 No need for knowledge of internal code.


 Tests the system from an end-user perspective.
 Suitable for functional and usability testing.

Disadvantages:

 Cannot test internal code quality, security flaws, or hidden errors.


 May require extensive test cases to cover all scenarios.
 Debugging failures can be difficult since the tester lacks code access.

Comparison with White-Box Testing: Black-box testing focuses on external


functionality, while white-box testing examines internal code.

108 | P a g e
Black box testing for the EduPlan Pro – Automatic Timetable Generator was
conducted after the completion of the coding phase to ensure that the system met
all functional requirements and behaved correctly from a user’s perspective. The
testing focused on verifying inputs, outputs, and overall system functionality
without considering the internal code structure.

The following areas were tested:

 Login Module: Checked whether valid user credentials granted access and
invalid credentials displayed proper error messages.
 Teacher Management: Tested adding, editing, and deleting teacher details to
ensure correct data entry and updates in the database.
 Timetable Generation: Verified that the system produced a complete, conflict-
free timetable and handled invalid or incomplete inputs appropriately.
 Attendance: Tested marking teacher absences
 Export and Import Functions: Ensured timetables were correctly exported to
CSV format and re-imported without data loss or formatting issues.
 Configuration Settings: Checked that changes in school name, academic year,
and theme were reflected accurately in the application interface.
 Error Messages and Notifications: Verified that the system displayed proper
alerts and handled invalid actions gracefully without crashing.

All modules were tested with both valid and invalid input data to confirm the
system’s reliability, stability, and user-friendliness. The testing confirmed that the
software performed as expected under different user scenarios, ensuring accuracy
and usability before deployment.

109 | P a g e
SUSTAINABLE DEVELOPMENT GOALS
BEING ATTAINED IN OUR PROJECT

SDG 4: Quality Education


 Efficient Scheduling ensures that students and teachers get well-organized
learning hours without conflicts.
 Reduced Administrative Burden allows educators to focus more on teaching
rather than spending time on manual scheduling.
 Optimized Resource Allocation ensures that classrooms, teachers, and subjects
are assigned efficiently, maximizing learning opportunities.

SDG 9: Industry, Innovation, and Infrastructure


 Technology Integration: The use of Python, Tkinter, and JSON for automation
modernizes traditional scheduling methods.
 Digital Transformation: Shifting from manual scheduling (paper/spreadsheet-
based) to an automated system enhances efficiency and reduces errors.

SDG 8: Decent Work and Economic Growth


 Workload Reduction: Reducing the manual workload of teachers and
administrative staff improves their productivity.
 Conflict Resolution: Automated scheduling minimizes issues like double-
booking, making school management smoother and stress-free.

SDG 11: Sustainable Cities and Communities


 Paperless System: Using JSON for data storage reduces dependency on printed
timetables, promoting environmental sustainability.
 Scalability: The system can be implemented across multiple schools,
supporting the development of smarter educational infrastructure.

110 | P a g e
INTEGRATION OF STE(A)M

The EduPlan Pro – Automatic Timetable Generator project effectively integrates


the principles of STEAM education by combining scientific thinking, technological
innovation, engineering design, artistic presentation, and mathematical logic into a
single cohesive software solution. Each component of STEAM plays an important role
in shaping the functionality and design of the project.

SCIENCE
The project applies scientific thinking through logical reasoning, observation, and
problem-solving. The process of analysing how timetables are created manually and
converting that process into a structured automated system demonstrates the
application of scientific methods. Hypothesis testing, experimentation, and iterative
improvements were used during development to ensure that the system produced
accurate, conflict-free results.

TECHNOLOGY
Technology forms the core of the project. The entire system is built using Python
programming, MySQL database management, and Tkinter for the graphical
interface. These technologies are combined to create a practical and efficient digital
tool that automates a real-world administrative process. The use of encryption for
security, database integration, and CSV handling reflects the application of modern
technological solutions to everyday school management problems.

ENGINEERING
Engineering principles are reflected in the system’s design, structure, and workflow.
The project follows the Software Development Life Cycle (SDLC), which is an
engineering-based approach to developing reliable software. Each module, from data
handling to timetable generation, was designed with precision to ensure efficiency and
reliability. The integration between different system components, such as GUI,
database, and logic modules, showcases engineering design thinking and structured
implementation.

ARTS
Artistic elements are integrated through the design and presentation of the graphical
user interface. The Tkinter-based GUI with customizable themes and colour schemes
adds an aesthetic appeal, making the software visually pleasing and easy to navigate.

111 | P a g e
Attention was given to layout arrangement, colour balance, and readability to enhance
the user experience. The inclusion of school logos and a modern interface design
combine creativity with functionality.

MATHEMATICS
Mathematics plays a critical role in timetable generation and optimization. Logical and
arithmetic operations are used to allocate teachers, subjects, and periods without
overlaps. Algorithms rely on mathematical logic to handle constraints such as teacher
workload, class capacity, and number of periods per day. Randomization and
validation techniques further ensure balanced and accurate scheduling. The project
thus applies mathematical reasoning to solve real scheduling challenges efficiently.

112 | P a g e
CONCLUSION
The EduPlan Pro – Automatic Timetable Generator project successfully
demonstrates the use of computer technology to automate and simplify the process of
timetable creation in schools. It eliminates the difficulties of manual scheduling by
introducing a system that automatically allocates teachers, subjects, and classes without
conflicts. This project aims to make timetable generation more efficient, accurate, and
convenient for school administrators, saving both time and effort.
The project was developed using Python for the main programming logic and MySQL
for database management, ensuring both reliability and scalability. A user-friendly
graphical interface was built using Tkinter, allowing easy interaction with the system.
Features such as teacher attendance tracking, automatic substitution, and CSV
export/import add flexibility and make the application suitable for real-world use in
schools and institutions.
The entire development process followed the Software Development Life Cycle
(SDLC) model, which helped ensure proper planning, design, implementation, testing,
and maintenance. Different modules of the project were thoroughly tested using white
box and black box testing to confirm functionality, accuracy, and system stability. These
testing methods helped identify and fix any issues before deployment, resulting in a
reliable and user-friendly software product.
The project also reflects a strong integration of STEAM (Science, Technology,
Engineering, Arts, and Mathematics) principles. Logical reasoning and problem-solving
represent the scientific aspect, while programming and database connectivity
demonstrate technology. Engineering principles were applied through systematic
software design, artistic elements were reflected in the interface layout and color
scheme, and mathematical reasoning was used in timetable generation and scheduling
algorithms.
In conclusion, EduPlan Pro successfully achieves its goal of automating timetable
generation while promoting effective learning and application of technical skills. It
provides a scalable and efficient solution that can be adapted for future improvements
such as online data synchronization or student attendance tracking. The project not only
showcases practical problem-solving through programming but also highlights how
technology can bring real improvements to educational administration.

113 | P a g e
FUTURE ENHANCEMENTS

 Web-Based and Mobile Application – Expanding the system to a web or


mobile platform for remote access and better usability.

 AI-Based Optimization – Implementing machine learning algorithms to


optimize timetables based on teacher preferences, student workloads, and room
availability.

 Automated Conflict Resolution – Enhancing the system to detect and


automatically resolve scheduling conflicts in real time.

 Notification System – Adding email or SMS notifications to inform teachers


and students about timetable updates or changes.

 Integration with School Management Systems – Connecting the timetable


generator with existing school management software for seamless data
synchronization.

 Customizable User Roles and Permissions – Implementing role-based access


control for administrators, teachers, and students with different levels of
permissions.

 Classes integration-Generation of time table for all classes even when one
teacher handles for more than one class

 Section-Wise Allocation-Proper generation of timetable according to each


section

 Timetable of each teacher-Separate timetable of each teacher can be taken.

114 | P a g e
BIBILIOGRAPHY
o NCERT Computer Science Textbook for Class XI – National Council of
Educational Research and Training (NCERT), New Delhi.
Provided foundational knowledge on Python programming, algorithms,
flowcharts, and database concepts essential for developing the logic of the
project.
o NCERT Computer Science Textbook for Class XII – National Council of
Educational Research and Training (NCERT), New Delhi.
Offered deeper insights into database connectivity, file handling, MySQL
integration with Python, and software development methodologies, which were
applied throughout the project.
o Sumita Arora – Computer Science with Python, Class XI – Dhanpat Rai &
Co. Publications.
Helped in understanding Python fundamentals, control structures, data types,
and modular programming concepts used in the project’s core logic.
o Sumita Arora – Computer Science with Python, Class XII – Dhanpat Rai &
Co. Publications.
Provided detailed explanations on advanced Python topics such as file
handling, database connectivity, and project development practices that guided
the overall design of the system.
o Python Official Documentation – [Link]
Used as a reference for Python syntax, libraries, and functions implemented in
the project.
o MySQL Documentation – [Link]
Referred for understanding SQL commands, database creation, and integration
with Python through the MySQL connector.
o Tkinter and ttk Reference Guide –
[Link]
Used for designing and customizing the graphical user interface of the project.
o Online Programming Resources and Tutorials – W3Schools, GeeksforGeeks,
and Programiz.
Consulted for syntax clarification, GUI examples, and logic optimization during
the development and debugging phases.

115 | P a g e
116 | P a g e
117 | P a g e

You might also like