Initial commit (code), runtime ignoré, secrets exclus
This commit is contained in:
303
soumettre_evaluation.php
Normal file
303
soumettre_evaluation.php
Normal file
@ -0,0 +1,303 @@
|
||||
<?php
|
||||
/**
|
||||
* SOUMISSION FINALE DE L'ÉVALUATION
|
||||
* Calcul de la note et sauvegarde définitive
|
||||
*/
|
||||
|
||||
define('APP_ROOT', dirname(__DIR__));
|
||||
require_once __DIR__ . '/config/config.php';
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
|
||||
// session_start(); // Géré par config/session.php
|
||||
/**
|
||||
* Normalise une réponse mathématique (entier, fraction, décimal)
|
||||
* @param string $reponse La réponse brute de l'élève
|
||||
* @return float|false Valeur normalisée ou false si invalide
|
||||
*/
|
||||
function normaliser_reponse_mathematique($reponse) {
|
||||
if (empty($reponse)) return false;
|
||||
|
||||
$reponse = trim($reponse);
|
||||
$reponse = str_replace(',', '.', $reponse); // Virgule française → point
|
||||
|
||||
// Cas fraction (ex: "2/3", "-5/3")
|
||||
if (preg_match('/^([+-]?\d+)\s*\/\s*([+-]?\d+)$/', $reponse, $matches)) {
|
||||
$numerateur = (float)$matches[1];
|
||||
$denominateur = (float)$matches[2];
|
||||
|
||||
if ($denominateur == 0) return false; // Division par zéro
|
||||
return $numerateur / $denominateur;
|
||||
}
|
||||
|
||||
// Cas nombre décimal ou entier
|
||||
if (is_numeric($reponse)) {
|
||||
return (float)$reponse;
|
||||
}
|
||||
|
||||
return false; // Format invalide
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare deux réponses mathématiques avec tolérance
|
||||
* @param float $attendue Réponse attendue (normalisée)
|
||||
* @param float $donnee Réponse de l'élève (normalisée)
|
||||
* @param float $epsilon Tolérance de comparaison
|
||||
* @return bool True si les réponses sont équivalentes
|
||||
*/
|
||||
function comparer_reponses_mathematiques($attendue, $donnee, $epsilon = 0.0001) {
|
||||
return abs($attendue - $donnee) < $epsilon;
|
||||
}
|
||||
// Vérification authentification
|
||||
if (!isLoggedIn() || !isEleve()) {
|
||||
header('Location: login.php?error=access_denied');
|
||||
exit();
|
||||
}
|
||||
|
||||
// $auth = new Auth(); // MIGRÉ vers SessionManager
|
||||
$user = currentUser();
|
||||
$db = Database::getInstance()->getConnection();
|
||||
|
||||
$id_evaluation = isset($_POST['id_evaluation']) ? (int)$_POST['id_evaluation'] :
|
||||
(isset($_GET['id_evaluation']) ? (int)$_GET['id_evaluation'] :
|
||||
(isset($_GET['id']) ? (int)$_GET['id'] : 0));
|
||||
$id_tentative = isset($_POST['id_tentative']) ? (int)$_POST['id_tentative'] :
|
||||
(isset($_GET['id_tentative']) ? (int)$_GET['id_tentative'] : 0);
|
||||
$auto_submit = isset($_GET['auto']) && $_GET['auto'] == 1;
|
||||
|
||||
if ($id_tentative <= 0 || $id_evaluation <= 0) {
|
||||
die("Données invalides");
|
||||
}
|
||||
|
||||
// Vérifier que la tentative appartient à l'élève
|
||||
$stmt = $db->prepare("
|
||||
SELECT * FROM tentatives_eleves
|
||||
WHERE id_tentative = ?
|
||||
AND id_eleve = ?
|
||||
AND statut = 'en_cours'
|
||||
");
|
||||
$stmt->execute([$id_tentative, $user['id_utilisateur']]);
|
||||
$tentative = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$tentative) {
|
||||
die("Tentative introuvable ou déjà terminée");
|
||||
}
|
||||
|
||||
// Récupérer les réponses finales si soumission manuelle
|
||||
if (!$auto_submit && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$reponses = [];
|
||||
foreach ($_POST as $key => $value) {
|
||||
if (strpos($key, 'reponse_') === 0) {
|
||||
$id_question = str_replace('reponse_', '', $key);
|
||||
$reponses[$id_question] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour les réponses finales
|
||||
$reponses_json = json_encode($reponses, JSON_UNESCAPED_UNICODE);
|
||||
$stmt = $db->prepare("
|
||||
UPDATE tentatives_eleves
|
||||
SET reponses_json = ?
|
||||
WHERE id_tentative = ?
|
||||
");
|
||||
$stmt->execute([$reponses_json, $id_tentative]);
|
||||
|
||||
// Recharger la tentative avec les nouvelles réponses
|
||||
$stmt = $db->prepare("SELECT * FROM tentatives_eleves WHERE id_tentative = ?");
|
||||
$stmt->execute([$id_tentative]);
|
||||
$tentative = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// Récupérer toutes les questions de l'évaluation
|
||||
$stmt = $db->prepare("
|
||||
SELECT * FROM questions
|
||||
WHERE id_evaluation = ?
|
||||
ORDER BY ordre ASC
|
||||
");
|
||||
$stmt->execute([$id_evaluation]);
|
||||
$questions = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Décoder les réponses de l'élève
|
||||
$reponses_eleve = json_decode($tentative['reponses_json'], true) ?: [];
|
||||
|
||||
// CALCUL DE LA NOTE
|
||||
$points_obtenus = 0;
|
||||
$points_totaux = 0;
|
||||
|
||||
foreach ($questions as $question) {
|
||||
$id_question = $question['id_question'];
|
||||
$points_question = (float)$question['points'];
|
||||
$points_totaux += $points_question;
|
||||
|
||||
// Récupérer la réponse de l'élève
|
||||
$reponse_eleve = $reponses_eleve[$id_question] ?? null;
|
||||
|
||||
// Récupérer les options et la réponse correcte de la question
|
||||
$options = json_decode($question['options_json'], true);
|
||||
$reponse_correcte_data = json_decode($question['reponse_correcte_json'], true);
|
||||
|
||||
// Vérifier la réponse selon le type
|
||||
switch ($question['type_question']) {
|
||||
|
||||
case 'checkbox':
|
||||
// Trouver toutes les réponses correctes
|
||||
$reponses_correctes = [];
|
||||
if (isset($options['reponses'])) {
|
||||
foreach ($options['reponses'] as $rep) {
|
||||
if (isset($rep['est_correcte']) && $rep['est_correcte'] === true) {
|
||||
$reponses_correctes[] = $rep['texte'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$reponses_eleve_array = is_array($reponse_eleve) ? $reponse_eleve : [];
|
||||
|
||||
// Toutes correctes = points complets
|
||||
sort($reponses_correctes);
|
||||
sort($reponses_eleve_array);
|
||||
|
||||
if ($reponses_correctes === $reponses_eleve_array) {
|
||||
$points_obtenus += $points_question;
|
||||
} else {
|
||||
// Points partiels selon le nombre de bonnes réponses
|
||||
$nb_correctes = count($reponses_correctes);
|
||||
if ($nb_correctes > 0) {
|
||||
$nb_bonnes = count(array_intersect($reponses_correctes, $reponses_eleve_array));
|
||||
$nb_mauvaises = count(array_diff($reponses_eleve_array, $reponses_correctes));
|
||||
|
||||
$points_partiels = max(0, ($nb_bonnes - $nb_mauvaises) / $nb_correctes * $points_question);
|
||||
$points_obtenus += $points_partiels;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'qcm':
|
||||
// Trouver la réponse correcte dans le tableau reponses
|
||||
$reponse_correcte = null;
|
||||
if (isset($options['reponses'])) {
|
||||
foreach ($options['reponses'] as $rep) {
|
||||
if (isset($rep['est_correcte']) && $rep['est_correcte'] === true) {
|
||||
$reponse_correcte = $rep['texte'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($reponse_eleve === $reponse_correcte) {
|
||||
$points_obtenus += $points_question;
|
||||
}
|
||||
break;
|
||||
case 'number':
|
||||
// La réponse correcte est dans reponse_correcte_json: {"reponse": "18"}
|
||||
$reponse_correcte = $reponse_correcte_data['reponse'] ?? null;
|
||||
|
||||
// Normaliser les deux réponses (gère entiers, fractions, décimaux)
|
||||
$valeur_eleve = normaliser_reponse_mathematique($reponse_eleve);
|
||||
$valeur_attendue = normaliser_reponse_mathematique($reponse_correcte);
|
||||
|
||||
// Comparer avec tolérance
|
||||
if ($valeur_eleve !== false && $valeur_attendue !== false) {
|
||||
if (comparer_reponses_mathematiques($valeur_attendue, $valeur_eleve)) {
|
||||
$points_obtenus += $points_question;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
// Pour les questions texte, nécessite correction manuelle
|
||||
// On compte 0 point automatiquement, à corriger manuellement
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
// La réponse correcte est dans reponse_correcte_json: {"id": "opt2"}
|
||||
// Les options sont dans options_json: {"options": [{"id": "opt1", "texte": "...", "est_correcte": false}, ...]}
|
||||
$id_reponse_correcte = $reponse_correcte_data['id'] ?? null;
|
||||
|
||||
// L'élève peut avoir répondu avec l'ID ou le texte, on vérifie les deux
|
||||
$est_correct = false;
|
||||
if ($id_reponse_correcte && isset($options['options'])) {
|
||||
// Trouver le texte correspondant à l'ID correct
|
||||
$texte_correct = null;
|
||||
foreach ($options['options'] as $opt) {
|
||||
if (($opt['id'] ?? '') === $id_reponse_correcte) {
|
||||
$texte_correct = $opt['texte'] ?? null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier si la réponse de l'élève correspond (ID ou texte)
|
||||
if ($reponse_eleve === $id_reponse_correcte || $reponse_eleve === $texte_correct) {
|
||||
$est_correct = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($est_correct) {
|
||||
$points_obtenus += $points_question;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'text_trous':
|
||||
$trous = $options['trous'] ?? [];
|
||||
$reponses_eleve_trous = is_array($reponse_eleve) ? $reponse_eleve : [];
|
||||
$nb_trous = count($trous);
|
||||
$nb_corrects = 0;
|
||||
|
||||
foreach ($trous as $trou) {
|
||||
$trou_id = $trou['id'];
|
||||
$reponse_correcte = $trou['reponse_correcte'];
|
||||
$reponse_eleve_trou = $reponses_eleve_trous[$trou_id] ?? '';
|
||||
|
||||
if (trim(strtolower($reponse_eleve_trou)) === trim(strtolower($reponse_correcte))) {
|
||||
$nb_corrects++;
|
||||
}
|
||||
}
|
||||
|
||||
$points_obtenus += ($nb_corrects / $nb_trous) * $points_question;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculer le pourcentage
|
||||
$pourcentage = $points_totaux > 0 ? ($points_obtenus / $points_totaux) * 100 : 0;
|
||||
|
||||
// Récupérer la note totale de l'évaluation
|
||||
$stmt = $db->prepare("SELECT note_totale FROM evaluations WHERE id_evaluation = ?");
|
||||
$stmt->execute([$id_evaluation]);
|
||||
$eval = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$note_sur = $eval['note_totale'] ?? 20;
|
||||
|
||||
// Calculer la note finale
|
||||
$note_finale = ($pourcentage / 100) * $note_sur;
|
||||
|
||||
// Calculer le temps passé
|
||||
$date_debut = new DateTime($tentative['date_debut']);
|
||||
$date_fin = new DateTime();
|
||||
$temps_passe = $date_fin->getTimestamp() - $date_debut->getTimestamp();
|
||||
|
||||
// Mettre à jour la tentative
|
||||
try {
|
||||
$stmt = $db->prepare("
|
||||
UPDATE tentatives_eleves
|
||||
SET statut = 'terminee',
|
||||
note = ?,
|
||||
note_sur = ?,
|
||||
pourcentage = ?,
|
||||
temps_passe = ?,
|
||||
date_fin = NOW()
|
||||
WHERE id_tentative = ?
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
round($note_finale, 2),
|
||||
$note_sur,
|
||||
round($pourcentage, 2),
|
||||
$temps_passe,
|
||||
$id_tentative
|
||||
]);
|
||||
|
||||
// Redirection vers la page de résultat
|
||||
header('Location: resultat_evaluation.php?id=' . $id_evaluation);
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Erreur soumission évaluation: " . $e->getMessage());
|
||||
die("Erreur lors de la soumission de l'évaluation");
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user