π Project Introduction: Attendance Management System
This project is a comprehensive Attendance Management System designed for educational institutions (schools, colleges, or training centers). It allows administrators to manage users and courses, teachers to mark and view attendance, and students to check their attendance records.
The Problem it Solves:
Manual attendance tracking using paper registers is time-consuming, error-prone, and difficult to analyze. This digital system automates the process, provides real-time attendance data, generates reports, and ensures data integrity with role-based access control.
Key Features:
- Multi-Role System:
- Admin: Full control - manage teachers, students, courses, classes, and view all attendance reports.
- Teacher: Mark daily attendance, view/edit attendance for their assigned classes, generate reports.
- Student: View personal attendance records, check attendance percentage, view course schedules.
- Core Functionality:
- User Authentication: Secure login for all three roles.
- Course Management: Create and manage courses/subjects.
- Class Management: Assign teachers to classes and courses.
- Student Management: Enroll students in classes.
- Attendance Marking: Mark present/absent with date selection.
- Reports: Generate attendance reports with percentages.
- Dashboard: Role-specific dashboards with statistics.
Technology Stack:
- Frontend: HTML5, CSS3, JavaScript (Chart.js for graphs, AJAX for dynamic updates)
- Backend: PHP (Object-Oriented PHP with PDO)
- Database: MySQL
- Server: Apache (XAMPP/WAMP/LAMP)
π Project File Structure
attendance-system/ β βββ index.php # Landing page / Login redirect βββ login.php # Main login page βββ logout.php # Logout script β βββ admin/ # Admin Panel β βββ dashboard.php # Admin dashboard β βββ manage_teachers.php # CRUD for teachers β βββ manage_students.php # CRUD for students β βββ manage_courses.php # CRUD for courses β βββ manage_classes.php # CRUD for classes β βββ view_attendance.php # View all attendance β βββ reports.php # Generate reports β βββ profile.php # Admin profile β βββ teacher/ # Teacher Panel β βββ dashboard.php # Teacher dashboard β βββ mark_attendance.php # Mark attendance for a class β βββ view_attendance.php # View attendance records β βββ my_classes.php # View assigned classes β βββ profile.php # Teacher profile β βββ student/ # Student Panel β βββ dashboard.php # Student dashboard β βββ view_attendance.php # View personal attendance β βββ my_courses.php # View enrolled courses β βββ profile.php # Student profile β βββ includes/ # Backend logic β βββ config.php # Database connection β βββ functions.php # Helper functions β βββ session.php # Session management β βββ auth.php # Role-based authentication β βββ assets/ # Static assets β βββ css/ β β βββ style.css # Main styles β β βββ admin.css # Admin panel styles β βββ js/ β β βββ main.js # Main JavaScript β β βββ chart.js # Chart configurations β βββ images/ # Images and icons β βββ api/ # AJAX endpoints β βββ get_students.php # Get students by class β βββ save_attendance.php # Save attendance via AJAX β βββ get_attendance.php # Get attendance records β βββ database/ βββ attendance_system.sql # Database dump
ποΈ Database Setup (database/attendance_system.sql)
Create a database named attendance_system and run this SQL.
-- phpMyAdmin SQL Dump
-- Database: `attendance_system`
CREATE DATABASE IF NOT EXISTS `attendance_system`;
USE `attendance_system`;
-- --------------------------------------------------------
-- Table structure for table `users`
-- --------------------------------------------------------
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(255) NOT NULL,
`email` varchar(100) NOT NULL,
`full_name` varchar(100) NOT NULL,
`role` enum('admin','teacher','student') NOT NULL DEFAULT 'student',
`status` tinyint(1) DEFAULT 1,
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Default admin account (password: admin123)
INSERT INTO `users` (`username`, `password`, `email`, `full_name`, `role`) VALUES
('admin', '$2y$10$YourHashedPasswordHere', '[email protected]', 'System Administrator', 'admin');
-- Sample teacher (password: teacher123)
INSERT INTO `users` (`username`, `password`, `email`, `full_name`, `role`) VALUES
('john.doe', '$2y$10$YourHashedPasswordHere', '[email protected]', 'John Doe', 'teacher');
-- Sample student (password: student123)
INSERT INTO `users` (`username`, `password`, `email`, `full_name`, `role`) VALUES
('jane.smith', '$2y$10$YourHashedPasswordHere', '[email protected]', 'Jane Smith', 'student');
-- --------------------------------------------------------
-- Table structure for table `courses`
-- --------------------------------------------------------
CREATE TABLE `courses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`course_code` varchar(20) NOT NULL,
`course_name` varchar(100) NOT NULL,
`credits` int(11) DEFAULT NULL,
`description` text DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `course_code` (`course_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `courses` (`course_code`, `course_name`, `credits`, `description`) VALUES
('CS101', 'Introduction to Computer Science', 3, 'Fundamentals of programming and computer science'),
('MATH201', 'Calculus I', 4, 'Differential and integral calculus'),
('PHY101', 'Physics Fundamentals', 3, 'Basic physics concepts and laboratory work');
-- --------------------------------------------------------
-- Table structure for table `classes`
-- --------------------------------------------------------
CREATE TABLE `classes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`class_name` varchar(50) NOT NULL,
`section` varchar(10) DEFAULT NULL,
`academic_year` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `classes` (`class_name`, `section`, `academic_year`) VALUES
('10th Grade', 'A', '2024-2025'),
('10th Grade', 'B', '2024-2025'),
('12th Grade', 'Science', '2024-2025');
-- --------------------------------------------------------
-- Table structure for table `teacher_courses`
-- --------------------------------------------------------
CREATE TABLE `teacher_courses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`teacher_id` int(11) NOT NULL,
`course_id` int(11) NOT NULL,
`class_id` int(11) NOT NULL,
`academic_year` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `teacher_id` (`teacher_id`),
KEY `course_id` (`course_id`),
KEY `class_id` (`class_id`),
CONSTRAINT `teacher_courses_ibfk_1` FOREIGN KEY (`teacher_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `teacher_courses_ibfk_2` FOREIGN KEY (`course_id`) REFERENCES `courses` (`id`) ON DELETE CASCADE,
CONSTRAINT `teacher_courses_ibfk_3` FOREIGN KEY (`class_id`) REFERENCES `classes` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Assign teacher (id=2) to courses
INSERT INTO `teacher_courses` (`teacher_id`, `course_id`, `class_id`, `academic_year`) VALUES
(2, 1, 1, '2024-2025'),
(2, 2, 3, '2024-2025');
-- --------------------------------------------------------
-- Table structure for table `students`
-- --------------------------------------------------------
CREATE TABLE `students` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`class_id` int(11) NOT NULL,
`roll_number` varchar(20) DEFAULT NULL,
`admission_date` date DEFAULT NULL,
`parent_phone` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `class_id` (`class_id`),
CONSTRAINT `students_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `students_ibfk_2` FOREIGN KEY (`class_id`) REFERENCES `classes` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Enroll student (id=3) in class
INSERT INTO `students` (`user_id`, `class_id`, `roll_number`, `admission_date`, `parent_phone`) VALUES
(3, 1, '1001', '2024-01-15', '+1234567890');
-- --------------------------------------------------------
-- Table structure for table `attendance`
-- --------------------------------------------------------
CREATE TABLE `attendance` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_id` int(11) NOT NULL,
`class_id` int(11) NOT NULL,
`course_id` int(11) NOT NULL,
`teacher_id` int(11) NOT NULL,
`attendance_date` date NOT NULL,
`status` enum('present','absent','late','holiday') NOT NULL DEFAULT 'present',
`remarks` text DEFAULT NULL,
`marked_at` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `unique_attendance` (`student_id`, `course_id`, `attendance_date`),
KEY `class_id` (`class_id`),
KEY `course_id` (`course_id`),
KEY `teacher_id` (`teacher_id`),
CONSTRAINT `attendance_ibfk_1` FOREIGN KEY (`student_id`) REFERENCES `students` (`id`) ON DELETE CASCADE,
CONSTRAINT `attendance_ibfk_2` FOREIGN KEY (`class_id`) REFERENCES `classes` (`id`) ON DELETE CASCADE,
CONSTRAINT `attendance_ibfk_3` FOREIGN KEY (`course_id`) REFERENCES `courses` (`id`) ON DELETE CASCADE,
CONSTRAINT `attendance_ibfk_4` FOREIGN KEY (`teacher_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Sample attendance records
INSERT INTO `attendance` (`student_id`, `class_id`, `course_id`, `teacher_id`, `attendance_date`, `status`) VALUES
(1, 1, 1, 2, CURDATE(), 'present'),
(1, 1, 1, 2, DATE_SUB(CURDATE(), INTERVAL 1 DAY), 'present'),
(1, 1, 1, 2, DATE_SUB(CURDATE(), INTERVAL 2 DAY), 'absent');
-- --------------------------------------------------------
-- Table structure for table `holidays`
-- --------------------------------------------------------
CREATE TABLE `holidays` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`holiday_date` date NOT NULL,
`description` varchar(200) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `holiday_date` (`holiday_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `holidays` (`holiday_date`, `description`) VALUES
('2024-12-25', 'Christmas Day'),
('2024-01-01', 'New Year\'s Day');
COMMIT;
π» Core PHP Files
1. Database Configuration (includes/config.php)
<?php
// Database configuration
define('DB_HOST', 'localhost');
define('DB_NAME', 'attendance_system');
define('DB_USER', 'root');
define('DB_PASS', '');
try {
$pdo = new PDO(
"mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4",
DB_USER,
DB_PASS,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]
);
} catch (PDOException $e) {
die("Connection failed: " . $e->getMessage());
}
// Start session if not started
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
// Site configuration
define('SITE_NAME', 'Attendance Management System');
define('SITE_URL', 'http://localhost/attendance-system/');
// Helper functions
function sanitize($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
function redirect($url) {
header("Location: $url");
exit;
}
function isLoggedIn() {
return isset($_SESSION['user_id']);
}
function getUserRole() {
return $_SESSION['user_role'] ?? null;
}
function formatDate($date, $format = 'd M Y') {
return date($format, strtotime($date));
}
function calculateAttendancePercentage($present, $total) {
if ($total == 0) return 0;
return round(($present / $total) * 100, 2);
}
?>
2. Authentication Middleware (includes/auth.php)
<?php
require_once 'config.php';
class Auth {
public static function checkLogin() {
if (!isset($_SESSION['user_id'])) {
redirect('login.php');
}
}
public static function checkRole($allowed_roles = []) {
if (!isset($_SESSION['user_role'])) {
redirect('login.php');
}
if (!empty($allowed_roles) && !in_array($_SESSION['user_role'], $allowed_roles)) {
// Redirect to appropriate dashboard based on role
switch ($_SESSION['user_role']) {
case 'admin':
redirect('admin/dashboard.php');
break;
case 'teacher':
redirect('teacher/dashboard.php');
break;
case 'student':
redirect('student/dashboard.php');
break;
default:
redirect('login.php');
}
}
}
public static function login($username, $password) {
global $pdo;
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND status = 1");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_role'] = $user['role'];
$_SESSION['user_name'] = $user['full_name'];
$_SESSION['user_username'] = $user['username'];
// Update last login
$pdo->prepare("UPDATE users SET last_login = NOW() WHERE id = ?")->execute([$user['id']]);
return true;
}
return false;
}
public static function logout() {
session_destroy();
redirect('login.php');
}
public static function getCurrentUser() {
global $pdo;
if (!isset($_SESSION['user_id'])) {
return null;
}
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
return $stmt->fetch();
}
}
?>
3. Login Page (login.php)
<?php
require_once 'includes/config.php';
require_once 'includes/auth.php';
// If already logged in, redirect to appropriate dashboard
if (isLoggedIn()) {
$role = getUserRole();
switch ($role) {
case 'admin':
redirect('admin/dashboard.php');
break;
case 'teacher':
redirect('teacher/dashboard.php');
break;
case 'student':
redirect('student/dashboard.php');
break;
}
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if (Auth::login($username, $password)) {
// Redirect based on role
$role = $_SESSION['user_role'];
switch ($role) {
case 'admin':
redirect('admin/dashboard.php');
break;
case 'teacher':
redirect('teacher/dashboard.php');
break;
case 'student':
redirect('student/dashboard.php');
break;
}
} else {
$error = 'Invalid username or password';
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - <?php echo SITE_NAME; ?></title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body class="login-page">
<div class="login-container">
<div class="login-header">
<h1><?php echo SITE_NAME; ?></h1>
<p>Please login to continue</p>
</div>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<form method="POST" action="" class="login-form">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" required
placeholder="Enter your username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required
placeholder="Enter your password">
</div>
<button type="submit" class="btn btn-primary btn-block">Login</button>
</form>
<div class="login-footer">
<p>Demo Credentials:</p>
<ul>
<li>Admin: admin / admin123</li>
<li>Teacher: john.doe / teacher123</li>
<li>Student: jane.smith / student123</li>
</ul>
</div>
</div>
</body>
</html>
4. Admin Dashboard (admin/dashboard.php)
<?php
require_once '../includes/config.php';
require_once '../includes/auth.php';
// Check if user is logged in and is admin
Auth::checkLogin();
Auth::checkRole(['admin']);
// Get statistics
$totalTeachers = $pdo->query("SELECT COUNT(*) FROM users WHERE role = 'teacher'")->fetchColumn();
$totalStudents = $pdo->query("SELECT COUNT(*) FROM users WHERE role = 'student'")->fetchColumn();
$totalCourses = $pdo->query("SELECT COUNT(*) FROM courses")->fetchColumn();
$totalClasses = $pdo->query("SELECT COUNT(*) FROM classes")->fetchColumn();
// Today's attendance
$today = date('Y-m-d');
$todayAttendance = $pdo->prepare("
SELECT COUNT(*) as total,
SUM(CASE WHEN status = 'present' THEN 1 ELSE 0 END) as present
FROM attendance
WHERE attendance_date = ?
");
$todayAttendance->execute([$today]);
$attendanceData = $todayAttendance->fetch();
// Recent attendance records
$recentAttendance = $pdo->query("
SELECT a.*, u.full_name as student_name, c.course_name, cl.class_name
FROM attendance a
JOIN students s ON a.student_id = s.id
JOIN users u ON s.user_id = u.id
JOIN courses c ON a.course_id = c.id
JOIN classes cl ON a.class_id = cl.id
ORDER BY a.attendance_date DESC, a.marked_at DESC
LIMIT 10
")->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Dashboard - <?php echo SITE_NAME; ?></title>
<link rel="stylesheet" href="../assets/css/admin.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div class="admin-wrapper">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<h2>Admin Panel</h2>
<p>Welcome, <?php echo $_SESSION['user_name']; ?></p>
</div>
<nav class="sidebar-nav">
<ul>
<li><a href="dashboard.php" class="active">Dashboard</a></li>
<li><a href="manage_teachers.php">Manage Teachers</a></li>
<li><a href="manage_students.php">Manage Students</a></li>
<li><a href="manage_courses.php">Manage Courses</a></li>
<li><a href="manage_classes.php">Manage Classes</a></li>
<li><a href="view_attendance.php">View Attendance</a></li>
<li><a href="reports.php">Reports</a></li>
<li><a href="profile.php">Profile</a></li>
<li><a href="../logout.php">Logout</a></li>
</ul>
</nav>
</aside>
<!-- Main Content -->
<main class="admin-main">
<div class="container">
<h1>Dashboard</h1>
<!-- Statistics Cards -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon">π¨βπ«</div>
<div class="stat-details">
<h3><?php echo $totalTeachers; ?></h3>
<p>Total Teachers</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">π¨βπ</div>
<div class="stat-details">
<h3><?php echo $totalStudents; ?></h3>
<p>Total Students</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">π</div>
<div class="stat-details">
<h3><?php echo $totalCourses; ?></h3>
<p>Total Courses</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">π«</div>
<div class="stat-details">
<h3><?php echo $totalClasses; ?></h3>
<p>Total Classes</p>
</div>
</div>
</div>
<!-- Today's Attendance Summary -->
<div class="attendance-summary">
<h2>Today's Attendance (<?php echo formatDate($today); ?>)</h2>
<div class="progress-circle">
<?php
$presentCount = $attendanceData['present'] ?? 0;
$totalCount = $attendanceData['total'] ?? 0;
$percentage = $totalCount > 0 ? round(($presentCount / $totalCount) * 100) : 0;
?>
<div class="circle" data-percent="<?php echo $percentage; ?>">
<span><?php echo $percentage; ?>%</span>
</div>
<p><?php echo $presentCount; ?> out of <?php echo $totalCount; ?> students present</p>
</div>
</div>
<!-- Recent Attendance Records -->
<div class="recent-section">
<h2>Recent Attendance Records</h2>
<table class="data-table">
<thead>
<tr>
<th>Date</th>
<th>Student</th>
<th>Course</th>
<th>Class</th>
<th>Status</th>
<th>Marked At</th>
</tr>
</thead>
<tbody>
<?php foreach ($recentAttendance as $record): ?>
<tr>
<td><?php echo formatDate($record['attendance_date']); ?></td>
<td><?php echo htmlspecialchars($record['student_name']); ?></td>
<td><?php echo htmlspecialchars($record['course_name']); ?></td>
<td><?php echo htmlspecialchars($record['class_name']); ?></td>
<td>
<span class="badge badge-<?php echo $record['status']; ?>">
<?php echo ucfirst($record['status']); ?>
</span>
</td>
<td><?php echo date('h:i A', strtotime($record['marked_at'])); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</main>
</div>
<script src="../assets/js/admin.js"></script>
</body>
</html>
5. Mark Attendance (Teacher) - teacher/mark_attendance.php
<?php
require_once '../includes/config.php';
require_once '../includes/auth.php';
Auth::checkLogin();
Auth::checkRole(['teacher']);
$teacher_id = $_SESSION['user_id'];
$message = '';
$error = '';
// Get teacher's assigned classes/courses
$classes = $pdo->prepare("
SELECT tc.*, c.course_name, cl.class_name, cl.section
FROM teacher_courses tc
JOIN courses c ON tc.course_id = c.id
JOIN classes cl ON tc.class_id = cl.id
WHERE tc.teacher_id = ? AND tc.academic_year = ?
");
$classes->execute([$teacher_id, '2024-2025']);
$assignedClasses = $classes->fetchAll();
// Handle class selection
$selected_class = $_GET['class_id'] ?? null;
$selected_course = $_GET['course_id'] ?? null;
$attendance_date = $_GET['date'] ?? date('Y-m-d');
$students = [];
if ($selected_class && $selected_course) {
// Get students in this class
$stmt = $pdo->prepare("
SELECT s.id, s.roll_number, u.full_name, u.email
FROM students s
JOIN users u ON s.user_id = u.id
WHERE s.class_id = ?
ORDER BY s.roll_number
");
$stmt->execute([$selected_class]);
$students = $stmt->fetchAll();
// Get existing attendance for this date
$existing = $pdo->prepare("
SELECT student_id, status, remarks
FROM attendance
WHERE class_id = ? AND course_id = ? AND attendance_date = ?
");
$existing->execute([$selected_class, $selected_course, $attendance_date]);
$attendanceMap = [];
foreach ($existing->fetchAll() as $att) {
$attendanceMap[$att['student_id']] = $att;
}
}
// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$class_id = $_POST['class_id'];
$course_id = $_POST['course_id'];
$date = $_POST['attendance_date'];
try {
$pdo->beginTransaction();
// Delete existing attendance for this date/class/course
$pdo->prepare("
DELETE FROM attendance
WHERE class_id = ? AND course_id = ? AND attendance_date = ?
")->execute([$class_id, $course_id, $date]);
// Insert new attendance records
$stmt = $pdo->prepare("
INSERT INTO attendance (student_id, class_id, course_id, teacher_id, attendance_date, status, remarks)
VALUES (?, ?, ?, ?, ?, ?, ?)
");
foreach ($_POST['attendance'] as $student_id => $status) {
$remarks = $_POST['remarks'][$student_id] ?? '';
$stmt->execute([
$student_id,
$class_id,
$course_id,
$teacher_id,
$date,
$status,
$remarks
]);
}
$pdo->commit();
$message = 'Attendance saved successfully!';
} catch (Exception $e) {
$pdo->rollBack();
$error = 'Error saving attendance: ' . $e->getMessage();
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mark Attendance - Teacher Panel</title>
<link rel="stylesheet" href="../assets/css/admin.css">
</head>
<body>
<div class="admin-wrapper">
<!-- Teacher Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<h2>Teacher Panel</h2>
<p>Welcome, <?php echo $_SESSION['user_name']; ?></p>
</div>
<nav class="sidebar-nav">
<ul>
<li><a href="dashboard.php">Dashboard</a></li>
<li><a href="mark_attendance.php" class="active">Mark Attendance</a></li>
<li><a href="view_attendance.php">View Attendance</a></li>
<li><a href="my_classes.php">My Classes</a></li>
<li><a href="profile.php">Profile</a></li>
<li><a href="../logout.php">Logout</a></li>
</ul>
</nav>
</aside>
<main class="admin-main">
<div class="container">
<h1>Mark Attendance</h1>
<?php if ($message): ?>
<div class="alert alert-success"><?php echo $message; ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<!-- Class Selection Form -->
<div class="card">
<div class="card-header">
<h3>Select Class and Date</h3>
</div>
<div class="card-body">
<form method="GET" action="" class="filter-form">
<div class="form-row">
<div class="form-group">
<label for="class_id">Class</label>
<select name="class_id" id="class_id" required>
<option value="">Select Class</option>
<?php foreach ($assignedClasses as $class): ?>
<option value="<?php echo $class['class_id']; ?>"
data-course="<?php echo $class['course_id']; ?>"
<?php echo $selected_class == $class['class_id'] ? 'selected' : ''; ?>>
<?php echo $class['class_name'] . ' ' . $class['section']; ?> -
<?php echo $class['course_name']; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="course_id">Course</label>
<select name="course_id" id="course_id" required>
<option value="">Select Course</option>
<?php foreach ($assignedClasses as $class): ?>
<option value="<?php echo $class['course_id']; ?>"
data-class="<?php echo $class['class_id']; ?>"
<?php echo $selected_course == $class['course_id'] ? 'selected' : ''; ?>>
<?php echo $class['course_name']; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="date">Date</label>
<input type="date" name="date" id="date"
value="<?php echo $attendance_date; ?>" required>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Load Students</button>
</div>
</div>
</form>
</div>
</div>
<!-- Attendance Marking Form -->
<?php if (!empty($students)): ?>
<div class="card mt-4">
<div class="card-header">
<h3>Mark Attendance for <?php echo $attendance_date; ?></h3>
</div>
<div class="card-body">
<form method="POST" action="" id="attendanceForm">
<input type="hidden" name="class_id" value="<?php echo $selected_class; ?>">
<input type="hidden" name="course_id" value="<?php echo $selected_course; ?>">
<input type="hidden" name="attendance_date" value="<?php echo $attendance_date; ?>">
<table class="data-table">
<thead>
<tr>
<th>Roll No</th>
<th>Student Name</th>
<th>Status</th>
<th>Remarks</th>
</tr>
</thead>
<tbody>
<?php foreach ($students as $student): ?>
<?php
$existingStatus = $attendanceMap[$student['id']]['status'] ?? 'present';
$existingRemarks = $attendanceMap[$student['id']]['remarks'] ?? '';
?>
<tr>
<td><?php echo $student['roll_number']; ?></td>
<td><?php echo htmlspecialchars($student['full_name']); ?></td>
<td>
<select name="attendance[<?php echo $student['id']; ?>]"
class="status-select">
<option value="present" <?php echo $existingStatus == 'present' ? 'selected' : ''; ?>>
Present
</option>
<option value="absent" <?php echo $existingStatus == 'absent' ? 'selected' : ''; ?>>
Absent
</option>
<option value="late" <?php echo $existingStatus == 'late' ? 'selected' : ''; ?>>
Late
</option>
</select>
</td>
<td>
<input type="text" name="remarks[<?php echo $student['id']; ?>]"
value="<?php echo htmlspecialchars($existingRemarks); ?>"
placeholder="Optional remarks">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="form-actions">
<button type="submit" class="btn btn-success">Save Attendance</button>
<button type="button" class="btn btn-secondary" onclick="markAll('present')">
Mark All Present
</button>
<button type="button" class="btn btn-secondary" onclick="markAll('absent')">
Mark All Absent
</button>
</div>
</form>
</div>
</div>
<?php endif; ?>
</div>
</main>
</div>
<script>
function markAll(status) {
const selects = document.querySelectorAll('.status-select');
selects.forEach(select => {
select.value = status;
});
}
// Auto-select course based on class
document.getElementById('class_id').addEventListener('change', function() {
const selectedOption = this.options[this.selectedIndex];
const courseId = selectedOption.dataset.course;
if (courseId) {
document.getElementById('course_id').value = courseId;
}
});
</script>
</body>
</html>
6. Student View Attendance (student/view_attendance.php)
<?php
require_once '../includes/config.php';
require_once '../includes/auth.php';
Auth::checkLogin();
Auth::checkRole(['student']);
$user_id = $_SESSION['user_id'];
// Get student details
$student = $pdo->prepare("
SELECT s.*, c.class_name, c.section
FROM students s
JOIN classes c ON s.class_id = c.id
WHERE s.user_id = ?
");
$student->execute([$user_id]);
$studentData = $student->fetch();
if (!$studentData) {
die('Student record not found');
}
// Get attendance summary by course
$attendance = $pdo->prepare("
SELECT
c.course_name,
c.course_code,
COUNT(a.id) as total_classes,
SUM(CASE WHEN a.status = 'present' OR a.status = 'late' THEN 1 ELSE 0 END) as attended,
SUM(CASE WHEN a.status = 'absent' THEN 1 ELSE 0 END) as absent,
SUM(CASE WHEN a.status = 'late' THEN 1 ELSE 0 END) as late
FROM attendance a
JOIN courses c ON a.course_id = c.id
WHERE a.student_id = ?
GROUP BY a.course_id, c.course_name, c.course_code
");
$attendance->execute([$studentData['id']]);
$attendanceData = $attendance->fetchAll();
// Get detailed attendance records
$records = $pdo->prepare("
SELECT a.*, c.course_name, u.full_name as teacher_name
FROM attendance a
JOIN courses c ON a.course_id = c.id
JOIN users u ON a.teacher_id = u.id
WHERE a.student_id = ?
ORDER BY a.attendance_date DESC
LIMIT 50
");
$records->execute([$studentData['id']]);
$attendanceRecords = $records->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Attendance - Student Panel</title>
<link rel="stylesheet" href="../assets/css/admin.css">
</head>
<body>
<div class="admin-wrapper">
<!-- Student Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<h2>Student Panel</h2>
<p>Welcome, <?php echo $_SESSION['user_name']; ?></p>
</div>
<nav class="sidebar-nav">
<ul>
<li><a href="dashboard.php">Dashboard</a></li>
<li><a href="view_attendance.php" class="active">My Attendance</a></li>
<li><a href="my_courses.php">My Courses</a></li>
<li><a href="profile.php">Profile</a></li>
<li><a href="../logout.php">Logout</a></li>
</ul>
</nav>
</aside>
<main class="admin-main">
<div class="container">
<h1>My Attendance Records</h1>
<!-- Student Info -->
<div class="student-info card">
<div class="card-body">
<p><strong>Name:</strong> <?php echo $_SESSION['user_name']; ?></p>
<p><strong>Class:</strong> <?php echo $studentData['class_name'] . ' ' . $studentData['section']; ?></p>
<p><strong>Roll Number:</strong> <?php echo $studentData['roll_number']; ?></p>
</div>
</div>
<!-- Attendance Summary by Course -->
<h2>Attendance Summary</h2>
<div class="summary-grid">
<?php foreach ($attendanceData as $course):
$percentage = calculateAttendancePercentage($course['attended'], $course['total_classes']);
$colorClass = $percentage >= 75 ? 'good' : ($percentage >= 60 ? 'warning' : 'danger');
?>
<div class="summary-card <?php echo $colorClass; ?>">
<h3><?php echo $course['course_code']; ?></h3>
<p class="course-name"><?php echo $course['course_name']; ?></p>
<div class="progress-circle">
<div class="circle" data-percent="<?php echo $percentage; ?>">
<span><?php echo $percentage; ?>%</span>
</div>
</div>
<div class="stats">
<div>Present: <?php echo $course['attended']; ?></div>
<div>Absent: <?php echo $course['absent']; ?></div>
<div>Late: <?php echo $course['late']; ?></div>
<div>Total: <?php echo $course['total_classes']; ?></div>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- Detailed Attendance Records -->
<h2>Recent Attendance Records</h2>
<table class="data-table">
<thead>
<tr>
<th>Date</th>
<th>Course</th>
<th>Teacher</th>
<th>Status</th>
<th>Remarks</th>
<th>Marked At</th>
</tr>
</thead>
<tbody>
<?php foreach ($attendanceRecords as $record): ?>
<tr>
<td><?php echo formatDate($record['attendance_date']); ?></td>
<td><?php echo htmlspecialchars($record['course_name']); ?></td>
<td><?php echo htmlspecialchars($record['teacher_name']); ?></td>
<td>
<span class="badge badge-<?php echo $record['status']; ?>">
<?php echo ucfirst($record['status']); ?>
</span>
</td>
<td><?php echo htmlspecialchars($record['remarks'] ?: '-'); ?></td>
<td><?php echo date('h:i A', strtotime($record['marked_at'])); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</main>
</div>
</body>
</html>
7. CSS Styling (assets/css/admin.css)
/* Reset and Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background: #f4f6f9;
}
/* Login Page */
.login-page {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.login-container {
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
width: 100%;
max-width: 400px;
}
.login-header {
text-align: center;
margin-bottom: 30px;
}
.login-header h1 {
color: #333;
font-size: 24px;
margin-bottom: 10px;
}
.login-header p {
color: #666;
}
.login-footer {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #eee;
font-size: 14px;
color: #666;
}
.login-footer ul {
list-style: none;
margin-top: 10px;
}
.login-footer li {
margin-bottom: 5px;
}
/* Admin Layout */
.admin-wrapper {
display: flex;
min-height: 100vh;
}
/* Sidebar */
.sidebar {
width: 250px;
background: #2c3e50;
color: white;
position: fixed;
height: 100vh;
overflow-y: auto;
}
.sidebar-header {
padding: 20px;
background: #243342;
text-align: center;
}
.sidebar-header h2 {
font-size: 20px;
margin-bottom: 5px;
}
.sidebar-header p {
font-size: 14px;
opacity: 0.8;
}
.sidebar-nav ul {
list-style: none;
padding: 20px 0;
}
.sidebar-nav li a {
display: block;
padding: 12px 20px;
color: white;
text-decoration: none;
transition: all 0.3s;
}
.sidebar-nav li a:hover,
.sidebar-nav li a.active {
background: #34495e;
border-left: 4px solid #3498db;
}
/* Admin Main Content */
.admin-main {
flex: 1;
margin-left: 250px;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
/* Cards */
.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-header {
padding: 15px 20px;
border-bottom: 1px solid #eee;
}
.card-header h3 {
margin: 0;
color: #333;
}
.card-body {
padding: 20px;
}
/* Statistics Grid */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
display: flex;
align-items: center;
transition: transform 0.3s;
}
.stat-card:hover {
transform: translateY(-5px);
}
.stat-icon {
font-size: 40px;
margin-right: 15px;
}
.stat-details h3 {
font-size: 28px;
margin-bottom: 5px;
color: #333;
}
.stat-details p {
color: #666;
margin: 0;
}
/* Forms */
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #333;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: #3498db;
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
/* Buttons */
.btn {
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s;
text-decoration: none;
}
.btn-primary {
background: #3498db;
color: white;
}
.btn-primary:hover {
background: #2980b9;
}
.btn-success {
background: #27ae60;
color: white;
}
.btn-success:hover {
background: #229954;
}
.btn-danger {
background: #e74c3c;
color: white;
}
.btn-danger:hover {
background: #c0392b;
}
.btn-secondary {
background: #95a5a6;
color: white;
}
.btn-secondary:hover {
background: #7f8c8d;
}
.btn-block {
display: block;
width: 100%;
}
/* Tables */
.data-table {
width: 100%;
background: white;
border-collapse: collapse;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.data-table thead {
background: #34495e;
color: white;
}
.data-table th,
.data-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.data-table tbody tr:hover {
background: #f5f5f5;
}
/* Badges */
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: 500;
}
.badge-present {
background: #d4edda;
color: #155724;
}
.badge-absent {
background: #f8d7da;
color: #721c24;
}
.badge-late {
background: #fff3cd;
color: #856404;
}
.badge-holiday {
background: #e2e3e5;
color: #383d41;
}
/* Alerts */
.alert {
padding: 12px 20px;
border-radius: 4px;
margin-bottom: 20px;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-danger {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
/* Progress Circle */
.progress-circle {
text-align: center;
margin: 20px 0;
}
.circle {
width: 120px;
height: 120px;
border-radius: 50%;
background: #f4f6f9;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 10px;
position: relative;
}
.circle::before {
content: '';
position: absolute;
width: 100px;
height: 100px;
border-radius: 50%;
background: white;
}
.circle span {
position: relative;
font-size: 24px;
font-weight: bold;
color: #333;
}
/* Summary Cards */
.summary-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin: 20px 0;
}
.summary-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.summary-card.good {
border-top: 4px solid #27ae60;
}
.summary-card.warning {
border-top: 4px solid #f39c12;
}
.summary-card.danger {
border-top: 4px solid #e74c3c;
}
.summary-card h3 {
margin-bottom: 5px;
color: #333;
}
.summary-card .course-name {
color: #666;
font-size: 14px;
margin-bottom: 15px;
}
.summary-card .stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-top: 15px;
}
/* Responsive */
@media (max-width: 768px) {
.sidebar {
width: 100%;
height: auto;
position: relative;
}
.admin-main {
margin-left: 0;
}
.admin-wrapper {
flex-direction: column;
}
.form-row {
grid-template-columns: 1fr;
}
.data-table {
display: block;
overflow-x: auto;
}
}
π How to Use This Project (Step-by-Step Guide)
Step 1: Install Local Server
- Download and install XAMPP from apachefriends.org
- Launch XAMPP Control Panel
- Start Apache and MySQL services
Step 2: Create Project Folder
- Navigate to
C:\xampp\htdocs\(Windows) or/Applications/XAMPP/htdocs/(Mac) - Create a new folder named
attendance-system
Step 3: Set Up Database
- Open browser and go to
http://localhost/phpmyadmin - Click on "SQL" tab
- Copy the entire SQL from
database/attendance_system.sql - Paste and click "Go" to create database and tables
Step 4: Create Password Hashes
Create a file named hash.php in project root:
<?php
echo "admin123 hash: " . password_hash('admin123', PASSWORD_DEFAULT) . "\n";
echo "teacher123 hash: " . password_hash('teacher123', PASSWORD_DEFAULT) . "\n";
echo "student123 hash: " . password_hash('student123', PASSWORD_DEFAULT);
?>
Run it: http://localhost/attendance-system/hash.php
Copy the hashes and update them in the SQL insert statements or directly in phpMyAdmin.
Step 5: Configure Database Connection
Open includes/config.php and verify credentials:
define('DB_HOST', 'localhost');
define('DB_NAME', 'attendance_system');
define('DB_USER', 'root');
define('DB_PASS', ''); // Change if you have MySQL password
Step 6: Test the System
Login Page:
- Open:
http://localhost/attendance-system/login.php
Test Accounts:
| Role | Username | Password |
|---|---|---|
| Admin | admin | admin123 |
| Teacher | john.doe | teacher123 |
| Student | jane.smith | student123 |
Step 7: Admin Setup
- Login as admin
- Go to Manage Teachers β Add more teachers
- Go to Manage Students β Enroll students
- Go to Manage Courses β Add courses
- Go to Manage Classes β Create classes
- Assign teachers to courses and classes
Step 8: Teacher Tasks
- Login as teacher
- Go to Mark Attendance
- Select class, course, and date
- Mark attendance for all students
- View attendance records
Step 9: Student Access
- Login as student
- View attendance summary by course
- Check detailed attendance records
- View enrolled courses
Step 10: Generate Reports (Admin)
- Go to Reports
- Select date range
- Choose class or course
- Generate and export attendance reports
π― Features Summary
Admin Features:
- β Dashboard with statistics
- β Manage teachers (add/edit/delete)
- β Manage students (add/edit/delete)
- β Manage courses (add/edit/delete)
- β Manage classes (add/edit/delete)
- β View all attendance records
- β Generate reports
- β Filter by date/class/course
- β Export data
Teacher Features:
- β View assigned classes
- β Mark daily attendance
- β Edit previous attendance
- β View attendance records
- β Filter by date/class
- β Quick actions (mark all present/absent)
- β Add remarks
Student Features:
- β View personal attendance
- β Attendance percentage by course
- β Detailed attendance history
- β View enrolled courses
- β Color-coded status indicators
System Features:
- β Role-based access control
- β Secure password hashing
- β Session management
- β Responsive design
- β Database relationships
- β Input validation
- β Error handling
π Future Enhancements
- Email Notifications: Send attendance reports to parents
- SMS Integration: Alert parents for absent students
- QR Code Attendance: Students scan QR code to mark attendance
- Biometric Integration: Fingerprint/face recognition
- Mobile App: Native mobile apps for teachers
- Analytics: Advanced charts and trends
- Export Options: PDF, Excel reports
- Calendar View: Visual attendance calendar
- Notifications: Push notifications for updates
- Multiple Branches: Support for multiple campus locations
- Academic Years: Automatic year progression
- Bulk Operations: Import students via Excel
- Parent Portal: Parents can view child's attendance
- Attendance Alerts: Automatic alerts for low attendance
- API Integration: REST API for third-party apps
This comprehensive Attendance Management System provides a solid foundation that can be customized for any educational institution's needs!