<?php
$pageTitle = "Attendance Report";
include_once('includes/header.php');
// Check if student is logged in
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'student') {
header("Location: ../login.php");
exit();
}
$userId = $_SESSION['user_id'];
// Get course enrollments for filter dropdown
$courses_query = "
SELECT c.id, c.title
FROM courses c
INNER JOIN enrollments e ON c.id = e.course_id
WHERE e.user_id = ? AND e.status = 'active'
ORDER BY c.title
";
$stmt = $conn->prepare($courses_query);
$stmt->bind_param("i", $userId);
$stmt->execute();
$courses_result = $stmt->get_result();
$courses = [];
while ($row = $courses_result->fetch_assoc()) {
$courses[] = $row;
}
// Default filter for current month
$selected_month = date('m');
$selected_year = date('Y');
$selected_course = 0; // 0 means all courses
// Apply filters if submitted
if (isset($_GET['filter'])) {
$selected_month = isset($_GET['month']) ? $_GET['month'] : date('m');
$selected_year = isset($_GET['year']) ? $_GET['year'] : date('Y');
$selected_course = isset($_GET['course_id']) ? intval($_GET['course_id']) : 0;
}
// Fetch attendance data based on filters
$attendance_query = "
SELECT a.id, a.date, a.status, a.notes, c.title as course_title
FROM attendance a
INNER JOIN enrollments e ON a.enrollment_id = e.id
INNER JOIN courses c ON e.course_id = c.id
WHERE e.user_id = ?
";
// Add filter conditions
if ($selected_course > 0) {
$attendance_query .= " AND e.course_id = ?";
}
if ($selected_month > 0) {
$attendance_query .= " AND MONTH(a.date) = ?";
}
if ($selected_year > 0) {
$attendance_query .= " AND YEAR(a.date) = ?";
}
$attendance_query .= " ORDER BY a.date DESC";
// Prepare and bind parameters
$stmt = $conn->prepare($attendance_query);
$param_types = "i";
$params = [$userId];
if ($selected_course > 0) {
$param_types .= "i";
$params[] = $selected_course;
}
if ($selected_month > 0) {
$param_types .= "i";
$params[] = $selected_month;
}
if ($selected_year > 0) {
$param_types .= "i";
$params[] = $selected_year;
}
// Dynamic bind_param
$stmt->bind_param($param_types, ...$params);
$stmt->execute();
$attendance_result = $stmt->get_result();
$attendance_records = [];
while ($row = $attendance_result->fetch_assoc()) {
$attendance_records[] = $row;
}
// Calculate statistics
$total_records = count($attendance_records);
$present_count = 0;
$absent_count = 0;
$late_count = 0;
$excused_count = 0;
$daily_status = [];
$course_stats = [];
foreach ($attendance_records as $record) {
$date = date('Y-m-d', strtotime($record['date']));
$course = $record['course_title'];
// Count by status
if ($record['status'] === 'present') {
$present_count++;
} elseif ($record['status'] === 'absent') {
$absent_count++;
} elseif ($record['status'] === 'late') {
$late_count++;
} elseif ($record['status'] === 'excused') {
$excused_count++;
}
// Prepare data for daily chart
if (!isset($daily_status[$date])) {
$daily_status[$date] = ['present' => 0, 'absent' => 0, 'late' => 0, 'excused' => 0];
}
$daily_status[$date][$record['status']]++;
// Prepare data for course-wise chart
if (!isset($course_stats[$course])) {
$course_stats[$course] = ['present' => 0, 'absent' => 0, 'late' => 0, 'excused' => 0, 'total' => 0];
}
$course_stats[$course][$record['status']]++;
$course_stats[$course]['total']++;
}
// After the main attendance stats calculation section, add the course-wise percentage calculation
foreach ($course_stats as $course => &$stats) {
if ($stats['total'] > 0) {
$stats['present_percent'] = round(($stats['present'] / $stats['total']) * 100, 1);
$stats['absent_percent'] = round(($stats['absent'] / $stats['total']) * 100, 1);
$stats['late_percent'] = round(($stats['late'] / $stats['total']) * 100, 1);
$stats['excused_percent'] = round(($stats['excused'] / $stats['total']) * 100, 1);
} else {
$stats['present_percent'] = 0;
$stats['absent_percent'] = 0;
$stats['late_percent'] = 0;
$stats['excused_percent'] = 0;
}
}
// Prepare chart data
$dates = array_keys($daily_status);
$present_data = array_column(array_values($daily_status), 'present');
$absent_data = array_column(array_values($daily_status), 'absent');
$late_data = array_column(array_values($daily_status), 'late');
$excused_data = array_column(array_values($daily_status), 'excused');
$course_names = array_keys($course_stats);
$course_attendance_percent = [];
foreach ($course_names as $course_name) {
$total = $course_stats[$course_name]['total'];
if ($total > 0) {
$course_attendance_percent[$course_name] = round(($course_stats[$course_name]['present'] / $total) * 100, 1);
} else {
$course_attendance_percent[$course_name] = 0;
}
}
// Calculate attendance percentage
$attendance_percentage = $total_records > 0 ? round(($present_count / $total_records) * 100, 1) : 0;
$absent_percentage = $total_records > 0 ? round(($absent_count / $total_records) * 100, 1) : 0;
$late_percentage = $total_records > 0 ? round(($late_count / $total_records) * 100, 1) : 0;
$excused_percentage = $total_records > 0 ? round(($excused_count / $total_records) * 100, 1) : 0;
// Calculate monthly attendance trend
$months = [];
$monthly_percentage = [];
// Get data for last 6 months for trend analysis
for ($i = 5; $i >= 0; $i--) {
$month = date('m', strtotime("-$i months"));
$year = date('Y', strtotime("-$i months"));
$month_name = date('M Y', strtotime("$year-$month-01"));
$monthly_query = "
SELECT a.status
FROM attendance a
INNER JOIN enrollments e ON a.enrollment_id = e.id
WHERE e.user_id = ? AND MONTH(a.date) = ? AND YEAR(a.date) = ?
";
$stmt = $conn->prepare($monthly_query);
$stmt->bind_param("iii", $userId, $month, $year);
$stmt->execute();
$monthly_result = $stmt->get_result();
$month_present = 0;
$month_total = 0;
while ($row = $monthly_result->fetch_assoc()) {
$month_total++;
if ($row['status'] === 'present') {
$month_present++;
}
}
$months[] = $month_name;
$monthly_percentage[] = $month_total > 0 ? round(($month_present / $month_total) * 100, 1) : 0;
}
?>
<div class="container py-4">
<h2 class="mb-4">Attendance Report</h2>
<!-- Filter Form -->
<div class="card mb-4">
<div class="card-body">
<form method="GET" class="row g-3">
<div class="col-md-3">
<label for="course_id" class="form-label">Course</label>
<select class="form-select" id="course_id" name="course_id">
<option value="0" <?php echo $selected_course == 0 ? 'selected' : ''; ?>>All Courses</option>
<?php foreach ($courses as $course): ?>
<option value="<?php echo $course['id']; ?>" <?php echo $selected_course == $course['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($course['title']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3">
<label for="month" class="form-label">Month</label>
<select class="form-select" id="month" name="month">
<option value="0" <?php echo $selected_month == 0 ? 'selected' : ''; ?>>All Months</option>
<?php for ($i = 1; $i <= 12; $i++): ?>
<option value="<?php echo $i; ?>" <?php echo $selected_month == $i ? 'selected' : ''; ?>>
<?php echo date('F', mktime(0, 0, 0, $i, 1)); ?>
</option>
<?php endfor; ?>
</select>
</div>
<div class="col-md-3">
<label for="year" class="form-label">Year</label>
<select class="form-select" id="year" name="year">
<?php for ($i = date('Y'); $i >= date('Y') - 5; $i--): ?>
<option value="<?php echo $i; ?>" <?php echo $selected_year == $i ? 'selected' : ''; ?>>
<?php echo $i; ?>
</option>
<?php endfor; ?>
</select>
</div>
<div class="col-md-3">
<label class="form-label"> </label>
<button type="submit" name="filter" class="btn btn-primary w-100">
<i class="fas fa-filter me-2"></i>Apply Filter
</button>
</div>
</form>
</div>
</div>
<!-- Attendance Overview Cards -->
<div class="row mb-4">
<div class="col-md-3 mb-3">
<div class="card h-100 shadow-sm">
<div class="card-body text-center d-flex flex-column justify-content-center">
<div class="attendance-chart-container position-relative" style="width: 120px; height: 120px; margin: 0 auto;">
<canvas id="attendancePercentDoughnut"></canvas>
<div class="position-absolute top-50 start-50 translate-middle">
<h3 class="mb-0"><?php echo $attendance_percentage; ?>%</h3>
<div class="small text-muted">Present</div>
</div>
</div>
<h5 class="mt-3 mb-0">Overall Attendance</h5>
<div class="small text-muted"><?php echo $present_count; ?> out of <?php echo $total_records; ?> days</div>
</div>
</div>
</div>
<div class="col-md-3 mb-3">
<div class="card h-100 shadow-sm">
<div class="card-body p-3">
<h6 class="card-title text-muted mb-3">Attendance Breakdown</h6>
<div class="mb-2">
<div class="d-flex justify-content-between mb-1">
<span class="small">Present</span>
<span class="small text-success"><?php echo $present_count; ?> (<?php echo $attendance_percentage; ?>%)</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-success" role="progressbar" style="width: <?php echo $attendance_percentage; ?>%"
aria-valuenow="<?php echo $attendance_percentage; ?>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<div class="mb-2">
<div class="d-flex justify-content-between mb-1">
<span class="small">Absent</span>
<span class="small text-danger"><?php echo $absent_count; ?> (<?php echo $absent_percentage; ?>%)</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-danger" role="progressbar" style="width: <?php echo $absent_percentage; ?>%"
aria-valuenow="<?php echo $absent_percentage; ?>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<div class="mb-2">
<div class="d-flex justify-content-between mb-1">
<span class="small">Late</span>
<span class="small text-warning"><?php echo $late_count; ?> (<?php echo $late_percentage; ?>%)</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-warning" role="progressbar" style="width: <?php echo $late_percentage; ?>%"
aria-valuenow="<?php echo $late_percentage; ?>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<div>
<div class="d-flex justify-content-between mb-1">
<span class="small">Excused</span>
<span class="small text-info"><?php echo $excused_count; ?> (<?php echo $excused_percentage; ?>%)</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-info" role="progressbar" style="width: <?php echo $excused_percentage; ?>%"
aria-valuenow="<?php echo $excused_percentage; ?>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card h-100 shadow-sm">
<div class="card-body">
<h6 class="card-title text-muted mb-3">Attendance Trend (6 Months)</h6>
<div style="height: 170px;">
<canvas id="attendanceTrendChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Course-wise Attendance -->
<?php if (count($course_stats) > 0): ?>
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0 text-primary font-weight-bold">Course-wise Attendance</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div style="height: 300px;">
<canvas id="courseAttendanceChart"></canvas>
</div>
</div>
<div class="col-md-6">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Course</th>
<th>Present</th>
<th>Absent</th>
<th>Late</th>
<th>Attendance %</th>
</tr>
</thead>
<tbody>
<?php foreach ($course_stats as $course => $stats): ?>
<tr>
<td><?php echo htmlspecialchars($course); ?></td>
<td><?php echo $stats['present']; ?></td>
<td><?php echo $stats['absent']; ?></td>
<td><?php echo $stats['late']; ?></td>
<td>
<div class="d-flex align-items-center">
<div class="progress flex-grow-1 me-2" style="height: 8px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: <?php echo $stats['present_percent']; ?>%"
aria-valuenow="<?php echo $stats['present_percent']; ?>"
aria-valuemin="0" aria-valuemax="100"></div>
</div>
<span class="text-success"><?php echo $stats['present_percent']; ?>%</span>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<!-- Daily Attendance Chart -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0 text-primary font-weight-bold">Daily Attendance Report</h5>
</div>
<div class="card-body">
<?php if (count($attendance_records) > 0): ?>
<div class="mb-4" style="height: 300px;">
<canvas id="dailyAttendanceChart"></canvas>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Date</th>
<th>Course</th>
<th>Status</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<?php foreach ($attendance_records as $record): ?>
<tr>
<td><?php echo date('d M Y', strtotime($record['date'])); ?></td>
<td><?php echo htmlspecialchars($record['course_title']); ?></td>
<td>
<span class="badge bg-<?php
echo $record['status'] === 'present' ? 'success' :
($record['status'] === 'absent' ? 'danger' :
($record['status'] === 'late' ? 'warning' : 'info')); ?>">
<?php echo ucfirst($record['status']); ?>
</span>
</td>
<td><?php echo htmlspecialchars($record['notes'] ?? 'No notes'); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i> No attendance records found for the selected filters.
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Include Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Doughnut chart for overall attendance
new Chart(document.getElementById('attendancePercentDoughnut'), {
type: 'doughnut',
data: {
datasets: [{
data: [<?php echo $attendance_percentage; ?>, <?php echo 100 - $attendance_percentage; ?>],
backgroundColor: ['#28a745', '#f0f0f0'],
borderWidth: 0
}]
},
options: {
cutout: '75%',
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false
}
}
}
});
// Course attendance bar chart
<?php if (count($course_stats) > 0): ?>
new Chart(document.getElementById('courseAttendanceChart'), {
type: 'bar',
data: {
labels: <?php echo json_encode(array_keys($course_stats)); ?>,
datasets: [{
label: 'Attendance %',
data: <?php echo json_encode(array_column($course_stats, 'present_percent')); ?>,
backgroundColor: '#28a745',
borderColor: '#28a745',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
<?php endif; ?>
// Daily attendance stacked bar chart
<?php if (count($daily_status) > 0): ?>
new Chart(document.getElementById('dailyAttendanceChart'), {
type: 'bar',
data: {
labels: <?php echo json_encode(array_map(function($date) { return date('d M', strtotime($date)); }, $dates)); ?>,
datasets: [
{
label: 'Present',
data: <?php echo json_encode($present_data); ?>,
backgroundColor: '#28a745'
},
{
label: 'Absent',
data: <?php echo json_encode($absent_data); ?>,
backgroundColor: '#dc3545'
},
{
label: 'Late',
data: <?php echo json_encode($late_data); ?>,
backgroundColor: '#ffc107'
},
{
label: 'Excused',
data: <?php echo json_encode($excused_data); ?>,
backgroundColor: '#17a2b8'
}
]
},
options: {
responsive: true,
maintainAspectRatio: true,
scales: {
x: {
stacked: true
},
y: {
stacked: true,
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
<?php endif; ?>
// Attendance trend line chart
new Chart(document.getElementById('attendanceTrendChart'), {
type: 'line',
data: {
labels: <?php echo json_encode($months); ?>,
datasets: [{
label: 'Attendance %',
data: <?php echo json_encode($monthly_percentage); ?>,
backgroundColor: 'rgba(40, 167, 69, 0.2)',
borderColor: '#28a745',
borderWidth: 2,
tension: 0.3,
fill: true,
pointBackgroundColor: '#28a745',
pointRadius: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
});
</script>
<?php include_once 'includes/footer.php'; ?>