Initial commit (code), runtime ignoré, secrets exclus

This commit is contained in:
nicoboy
2026-01-02 15:05:25 +01:00
commit 2fdcf50bc1
87 changed files with 11525 additions and 0 deletions

562
voir_resultat.php Normal file
View File

@ -0,0 +1,562 @@
<?php
/**
* VISUALISATION RÉSULTATS ÉLÈVE
* Affiche les résultats d'une tentative avec correction si activée
*/
require_once 'config/database.php';
require_once 'config/session.php';
// Session déjà démarrée dans session.php
// Vérification élève connecté
if (!SessionManager::isEleve()) {
header('Location: login.php?error=access_denied');
exit();
}
$user = SessionManager::getUser();
$db = Database::getInstance()->getConnection();
$id_evaluation = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$id_tentative = isset($_GET['tentative']) ? (int)$_GET['tentative'] : 0;
$nouvelle_soumission = isset($_GET['new']) && $_GET['new'] == 1;
if ($id_evaluation <= 0) {
die("Évaluation invalide");
}
// Récupérer l'évaluation
$stmt = $db->prepare("SELECT * FROM evaluations WHERE id_evaluation = ?");
$stmt->execute([$id_evaluation]);
$evaluation = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$evaluation) {
die("Évaluation introuvable");
}
// Récupérer les tentatives de l'élève
$stmt = $db->prepare("
SELECT * FROM tentatives_eleves
WHERE id_evaluation = ?
AND id_eleve = ?
ORDER BY numero_tentative DESC
");
$stmt->execute([$id_evaluation, $user['id']]);
$tentatives = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($tentatives)) {
die("Aucune tentative trouvée");
}
// Si tentative spécifique demandée
if ($id_tentative > 0) {
$tentative_affichee = null;
foreach ($tentatives as $t) {
if ($t['id_tentative'] == $id_tentative) {
$tentative_affichee = $t;
break;
}
}
if (!$tentative_affichee) {
die("Tentative introuvable");
}
} else {
// Afficher la dernière tentative terminée
$tentative_affichee = null;
foreach ($tentatives as $t) {
if ($t['statut'] == 'terminee') {
$tentative_affichee = $t;
break;
}
}
if (!$tentative_affichee) {
$tentative_affichee = $tentatives[0];
}
}
// Récupérer les questions
$stmt = $db->prepare("
SELECT * FROM questions
WHERE id_evaluation = ?
ORDER BY ordre
");
$stmt->execute([$id_evaluation]);
$questions = $stmt->fetchAll(PDO::FETCH_ASSOC);
$reponses_eleve = json_decode($tentative_affichee['reponses_json'], true) ?: [];
// Calculer statistiques
$nb_questions_repondues = count($reponses_eleve);
$note_sur_20 = ($tentative_affichee['note'] / $evaluation['note_totale']) * 20;
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Résultats - <?= htmlspecialchars($evaluation['titre']) ?></title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
}
.header {
background: white;
padding: 25px;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
margin-bottom: 20px;
text-align: center;
}
.header h1 {
color: #667eea;
font-size: 24px;
margin-bottom: 10px;
}
.header p {
color: #666;
font-size: 14px;
}
<?php if ($nouvelle_soumission): ?>
.success-banner {
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
color: white;
padding: 20px;
border-radius: 15px;
text-align: center;
margin-bottom: 20px;
animation: slideIn 0.5s;
}
@keyframes slideIn {
from { transform: translateY(-20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.success-banner h2 {
font-size: 20px;
margin-bottom: 5px;
}
<?php endif; ?>
.score-card {
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
margin-bottom: 20px;
text-align: center;
}
.score-big {
font-size: 64px;
font-weight: 800;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin: 20px 0;
}
.score-details {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin-top: 20px;
}
.score-item {
padding: 15px;
background: #f8f9fa;
border-radius: 10px;
}
.score-label {
font-size: 12px;
color: #666;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.score-value {
font-size: 20px;
font-weight: 700;
color: #333;
margin-top: 5px;
}
.tentatives-list {
background: white;
padding: 20px;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
margin-bottom: 20px;
}
.tentatives-list h3 {
color: #667eea;
margin-bottom: 15px;
}
.tentative-item {
padding: 12px;
border-radius: 8px;
background: #f8f9fa;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.tentative-item.active {
background: #e8eeff;
border: 2px solid #667eea;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
text-decoration: none;
display: inline-block;
transition: all 0.3s;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-secondary {
background: #f5f5f5;
color: #333;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.correction-section {
background: white;
padding: 25px;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
margin-bottom: 20px;
}
.correction-section h3 {
color: #667eea;
margin-bottom: 20px;
}
.question-result {
padding: 20px;
border: 2px solid #e0e0e0;
border-radius: 12px;
margin-bottom: 15px;
}
.question-result.correct {
border-color: #4CAF50;
background: #f1f8f4;
}
.question-result.incorrect {
border-color: #f44336;
background: #fef3f2;
}
.question-result.partial {
border-color: #ff9800;
background: #fff8f0;
}
.question-header {
display: flex;
justify-content: space-between;
align-items: start;
margin-bottom: 15px;
}
.question-number {
font-weight: 700;
color: #667eea;
}
.points-badge {
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.points-badge.correct {
background: #4CAF50;
color: white;
}
.points-badge.incorrect {
background: #f44336;
color: white;
}
.points-badge.partial {
background: #ff9800;
color: white;
}
.question-enonce {
font-size: 15px;
color: #333;
margin-bottom: 15px;
font-weight: 500;
}
.reponse-block {
background: white;
padding: 12px;
border-radius: 8px;
margin-bottom: 10px;
}
.reponse-label {
font-size: 13px;
color: #666;
margin-bottom: 5px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.reponse-value {
font-size: 15px;
color: #333;
}
.explication {
background: #f0f7ff;
padding: 12px;
border-left: 4px solid #667eea;
border-radius: 8px;
margin-top: 10px;
font-size: 14px;
color: #555;
}
.back-link {
display: inline-block;
margin-bottom: 20px;
color: white;
text-decoration: none;
font-weight: 600;
padding: 10px 20px;
background: rgba(255,255,255,0.2);
border-radius: 8px;
}
@media (max-width: 768px) {
body {
padding: 10px;
}
.score-big {
font-size: 48px;
}
.score-details {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<a href="eleve/dashboard.php" class="back-link">← Retour au tableau de bord</a>
<?php if ($nouvelle_soumission): ?>
<div class="success-banner">
<h2>✓ Évaluation soumise avec succès !</h2>
<p>Voici vos résultats</p>
</div>
<?php endif; ?>
<div class="header">
<h1><?= htmlspecialchars($evaluation['titre']) ?></h1>
<p>Tentative n°<?= $tentative_affichee['numero_tentative'] ?> -
<?= ($tentative_affichee['statut'] == 'terminee') ? 'Terminée' : 'En cours' ?></p>
</div>
<div class="score-card">
<h2>Votre note</h2>
<div class="score-big"><?= number_format($note_sur_20, 1) ?>/20</div>
<div class="score-details">
<div class="score-item">
<div class="score-label">Points obtenus</div>
<div class="score-value"><?= $tentative_affichee['note'] ?> / <?= $evaluation['note_totale'] ?></div>
</div>
<div class="score-item">
<div class="score-label">Questions répondues</div>
<div class="score-value"><?= $nb_questions_repondues ?> / <?= count($questions) ?></div>
</div>
<div class="score-item">
<div class="score-label">Durée</div>
<div class="score-value">
<?php
if ($tentative_affichee['date_fin']) {
$debut = new DateTime($tentative_affichee['date_debut']);
$fin = new DateTime($tentative_affichee['date_fin']);
$duree = $debut->diff($fin);
echo $duree->i . ' min ' . $duree->s . ' s';
} else {
echo 'En cours';
}
?>
</div>
</div>
</div>
</div>
<?php if (count($tentatives) > 1): ?>
<div class="tentatives-list">
<h3>Historique de vos tentatives</h3>
<?php foreach ($tentatives as $t): ?>
<div class="tentative-item <?= ($t['id_tentative'] == $tentative_affichee['id_tentative']) ? 'active' : '' ?>">
<div>
<strong>Tentative n°<?= $t['numero_tentative'] ?></strong>
<span style="margin-left: 15px; color: #666;">
<?= ($t['statut'] == 'terminee') ? 'Note: ' . $t['note'] . '/' . $evaluation['note_totale'] : 'En cours' ?>
</span>
</div>
<?php if ($t['id_tentative'] != $tentative_affichee['id_tentative'] && $t['statut'] == 'terminee'): ?>
<a href="voir_resultat.php?id=<?= $id_evaluation ?>&tentative=<?= $t['id_tentative'] ?>"
class="btn btn-secondary" style="padding: 6px 12px; font-size: 12px;">
Voir
</a>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($evaluation['afficher_correction'] && $tentative_affichee['statut'] == 'terminee'): ?>
<div class="correction-section">
<h3>📝 Correction détaillée</h3>
<?php foreach ($questions as $index => $q): ?>
<?php
$id_question = $q['id_question'];
$reponse_eleve = $reponses_eleve[$id_question] ?? null;
$reponse_correcte = json_decode($q['reponse_correcte_json'], true);
// Déterminer si correct
$est_correct = false;
$classe_css = 'incorrect';
switch ($q['type_question']) {
case 'qcm':
$correct = $reponse_correcte[0] ?? null;
$est_correct = ($reponse_eleve === $correct);
break;
case 'checkbox':
if (is_array($reponse_eleve) && is_array($reponse_correcte)) {
sort($reponse_eleve);
sort($reponse_correcte);
$est_correct = ($reponse_eleve === $reponse_correcte);
}
break;
case 'select':
$correct = $reponse_correcte['reponse'] ?? null;
$est_correct = ($reponse_eleve === $correct);
break;
}
$classe_css = $est_correct ? 'correct' : 'incorrect';
$badge_classe = $est_correct ? 'correct' : 'incorrect';
$badge_texte = $est_correct ? '✓ ' . $q['points'] . ' pts' : '✗ 0 pt';
?>
<div class="question-result <?= $classe_css ?>">
<div class="question-header">
<div class="question-number">Question <?= $index + 1 ?></div>
<div class="points-badge <?= $badge_classe ?>"><?= $badge_texte ?></div>
</div>
<div class="question-enonce"><?= nl2br(strip_tags($q['enonce'], '<img><a><br><strong><em><u><b><i><span>')) ?></div>
<div class="reponse-block">
<div class="reponse-label">Votre réponse</div>
<div class="reponse-value">
<?php
if ($reponse_eleve === null) {
echo '<em style="color: #999;">Non répondu</em>';
} elseif (is_array($reponse_eleve)) {
echo htmlspecialchars(implode(', ', $reponse_eleve));
} else {
echo htmlspecialchars($reponse_eleve);
}
?>
</div>
</div>
<?php if (!$est_correct): ?>
<div class="reponse-block">
<div class="reponse-label">Réponse correcte</div>
<div class="reponse-value" style="color: #4CAF50; font-weight: 600;">
<?php
if (is_array($reponse_correcte)) {
echo htmlspecialchars(implode(', ', $reponse_correcte));
} else {
$correct_display = $reponse_correcte['reponse'] ?? $reponse_correcte[0] ?? 'N/A';
echo htmlspecialchars($correct_display);
}
?>
</div>
</div>
<?php endif; ?>
<?php if (!empty($q['explication'])): ?>
<div class="explication">
<strong>💡 Explication :</strong><br>
<?= nl2br(htmlspecialchars($q['explication'])) ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div style="text-align: center; margin-top: 30px;">
<?php if (count($tentatives) < $evaluation['tentatives_max']): ?>
<a href="passer_evaluation.php?id=<?= $id_evaluation ?>" class="btn btn-primary">
🔄 Refaire l'évaluation (Tentative <?= count($tentatives) + 1 ?>/<?= $evaluation['tentatives_max'] ?>)
</a>
<?php else: ?>
<p style="color: white; font-size: 14px;">
Vous avez utilisé toutes vos tentatives (<?= $evaluation['tentatives_max'] ?>)
</p>
<?php endif; ?>
</div>
</div>
</body>
</html>