37970-vm/leaderboard.php
Flatlogic Bot 33ad70235b v5
2026-01-30 15:36:51 +00:00

207 lines
9.2 KiB
PHP

<?php
session_start();
require_once 'db/config.php';
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$db = db();
$school_id = $_SESSION['school_id'];
// Award badges automatically based on performance (Logic)
// Academic Star: Average >= 80%
// Consistent Performer: Average >= 60%
try {
// This is a simple logic to auto-award badges for demonstration
$stmt = $db->prepare("
SELECT l.id, AVG((m.marks_obtained / a.total_marks) * 100) as avg_score
FROM learners l
JOIN marks m ON l.id = m.learner_id
JOIN assessments a ON m.assessment_id = a.id
WHERE l.school_id = ?
GROUP BY l.id
");
$stmt->execute([$school_id]);
$performances = $stmt->fetchAll();
foreach ($performances as $perf) {
if ($perf['avg_score'] >= 80) {
// Award Academic Star (Badge ID 1)
$db->prepare("INSERT IGNORE INTO learner_badges (learner_id, badge_id) VALUES (?, 1)")->execute([$perf['id']]);
} elseif ($perf['avg_score'] >= 60) {
// Award Consistent Performer (Badge ID 2)
$db->prepare("INSERT IGNORE INTO learner_badges (learner_id, badge_id) VALUES (?, 2)")->execute([$perf['id']]);
}
}
} catch (Exception $e) {
// Silently fail if something goes wrong with auto-awarding
}
// Fetch Leaderboard (Top learners by average score)
$query = "
SELECT
l.id, l.full_name, l.grade, l.student_id,
AVG((m.marks_obtained / a.total_marks) * 100) as average_score,
COUNT(m.id) as assessments_count
FROM learners l
JOIN marks m ON l.id = m.learner_id
JOIN assessments a ON m.assessment_id = a.id
WHERE l.school_id = :school_id
GROUP BY l.id
ORDER BY average_score DESC
LIMIT 20
";
$stmt = $db->prepare($query);
$stmt->execute(['school_id' => $school_id]);
$leaderboard = $stmt->fetchAll();
// Fetch Badges for these learners
$badges_query = "
SELECT lb.learner_id, b.name, b.icon, b.description
FROM learner_badges lb
JOIN badges b ON lb.badge_id = b.id
WHERE lb.learner_id IN (SELECT id FROM learners WHERE school_id = ?)
";
$stmt = $db->prepare($badges_query);
$stmt->execute([$school_id]);
$all_learner_badges = $stmt->fetchAll(PDO::FETCH_GROUP | PDO::FETCH_ASSOC);
$pageTitle = "Learner Leaderboard | SOMS";
include 'includes/header.php';
?>
<div class="container py-5">
<div class="text-center mb-5">
<h1 class="display-5 fw-bold mb-2"><i class="bi bi-trophy text-warning me-2"></i>Academic Leaderboard</h1>
<p class="lead text-muted">Celebrating excellence and consistent growth in our school.</p>
</div>
<div class="row justify-content-center">
<div class="col-lg-10">
<div class="card shadow border-0 overflow-hidden">
<div class="card-header bg-primary text-white py-3">
<div class="row align-items-center">
<div class="col-auto">
<span class="badge bg-white text-primary">Top 20</span>
</div>
<div class="col">
<h5 class="mb-0">Current Rankings</h5>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="bg-light">
<tr>
<th class="ps-4" style="width: 80px;">Rank</th>
<th>Learner</th>
<th>Grade</th>
<th>Avg. Score</th>
<th>Achievements</th>
</tr>
</thead>
<tbody>
<?php if (empty($leaderboard)): ?>
<tr>
<td colspan="5" class="text-center py-5 text-muted">
No assessment data available yet to generate rankings.
</td>
</tr>
<?php endif; ?>
<?php foreach ($leaderboard as $index => $row): ?>
<?php
$rank = $index + 1;
$score = round($row['average_score'], 1);
$row_badges = $all_learner_badges[$row['id']] ?? [];
$rankClass = '';
if ($rank === 1) $rankClass = 'rank-1';
elseif ($rank === 2) $rankClass = 'rank-2';
elseif ($rank === 3) $rankClass = 'rank-3';
?>
<tr class="<?= $rank <= 3 ? 'table-light' : '' ?>">
<td class="ps-4">
<?php if ($rank === 1): ?>
<div class="rank-circle bg-warning text-dark"><i class="bi bi-award-fill"></i></div>
<?php elseif ($rank === 2): ?>
<div class="rank-circle bg-secondary text-white">2</div>
<?php elseif ($rank === 3): ?>
<div class="rank-circle bg-bronze text-white">3</div>
<?php else: ?>
<span class="text-muted fw-bold"><?= $rank ?></span>
<?php endif; ?>
</td>
<td>
<div class="d-flex align-items-center">
<div class="avatar-sm bg-primary text-white rounded-circle me-3 d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<?= strtoupper(substr($row['full_name'], 0, 1)) ?>
</div>
<div>
<div class="fw-bold"><?= htmlspecialchars($row['full_name']) ?></div>
<div class="text-muted smaller"><?= htmlspecialchars($row['student_id']) ?></div>
</div>
</div>
</td>
<td><span class="badge bg-light text-dark border">Grade <?= htmlspecialchars($row['grade']) ?></span></td>
<td>
<div class="d-flex align-items-center">
<div class="progress flex-grow-1 me-3" style="height: 6px; min-width: 60px;">
<div class="progress-bar bg-primary" role="progressbar" style="width: <?= $score ?>%"></div>
</div>
<span class="fw-bold"><?= $score ?>%</span>
</div>
</td>
<td>
<div class="d-flex flex-wrap gap-2">
<?php if (empty($row_badges)): ?>
<span class="text-muted smaller">No badges yet</span>
<?php else: ?>
<?php foreach ($row_badges as $badge): ?>
<span class="badge bg-soft-warning text-warning border border-warning" data-bs-toggle="tooltip" title="<?= htmlspecialchars($badge['description']) ?>">
<i class="bi <?= htmlspecialchars($badge['icon']) ?> me-1"></i>
<?= htmlspecialchars($badge['name']) ?>
</span>
<?php endforeach; ?>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<style>
.rank-circle {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.9rem;
}
.bg-bronze { background-color: #cd7f32; }
.bg-soft-warning { background-color: rgba(255, 193, 7, 0.1); }
.smaller { font-size: 0.75rem; }
.rank-1 { background-color: rgba(255, 193, 7, 0.05); }
</style>
<script>
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
</script>
<?php include 'includes/footer.php'; ?>