Initial commit (code), runtime ignoré, secrets exclus
This commit is contained in:
571
importer_evaluation.php
Normal file
571
importer_evaluation.php
Normal file
@ -0,0 +1,571 @@
|
||||
<?php
|
||||
/**
|
||||
* IMPORTATION D'ÉVALUATIONS - VERSION ADAPTÉE
|
||||
* Compatible avec le schéma existant de Nicolas (acces_evaluations, tentatives_eleves, etc.)
|
||||
*/
|
||||
|
||||
require_once 'config/database.php';
|
||||
require_once 'config/session.php';
|
||||
|
||||
// Session déjà démarrée dans session.php
|
||||
|
||||
// Vérification connexion enseignant
|
||||
if (!isset($_SESSION['user_id']) || $_SESSION['id_type'] != 1) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$db = Database::getInstance()->getConnection();
|
||||
$message = '';
|
||||
$message_type = '';
|
||||
|
||||
// Dossier transit
|
||||
$transit_dir = '/var/www/mathematiques/evaluations_transit/';
|
||||
|
||||
// === TRAITEMENT DE L'IMPORT ===
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['fichier'])) {
|
||||
$fichier_nom = basename($_POST['fichier']);
|
||||
$fichier_path = $transit_dir . $fichier_nom;
|
||||
|
||||
if (!file_exists($fichier_path)) {
|
||||
$message = "Fichier introuvable : $fichier_nom";
|
||||
$message_type = "error";
|
||||
} else {
|
||||
// Lecture du JSON
|
||||
$json_content = file_get_contents($fichier_path);
|
||||
$data = json_decode($json_content, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
$message = "Erreur JSON : " . json_last_error_msg();
|
||||
$message_type = "error";
|
||||
} else {
|
||||
// Validation structure
|
||||
$errors = [];
|
||||
|
||||
if (!isset($data['metadata']) || !isset($data['questions'])) {
|
||||
$errors[] = "Structure JSON invalide (metadata ou questions manquants)";
|
||||
}
|
||||
|
||||
$meta = $data['metadata'] ?? [];
|
||||
$required_meta = ['titre', 'niveau', 'duree_minutes', 'bareme_total'];
|
||||
foreach ($required_meta as $field) {
|
||||
if (!isset($meta[$field])) {
|
||||
$errors[] = "Champ metadata.$field manquant";
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($data['questions'])) {
|
||||
$errors[] = "Aucune question trouvée";
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$message = "Validation échouée :<br>• " . implode("<br>• ", $errors);
|
||||
$message_type = "error";
|
||||
} else {
|
||||
// Import dans la base de données
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
// 1. Insérer l'évaluation (ADAPTÉ AU SCHÉMA EXISTANT)
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO evaluations (
|
||||
titre, description, type, duree_minutes, note_totale,
|
||||
id_enseignant, actif, afficher_correction,
|
||||
melanger_questions, tentatives_max, date_creation
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
|
||||
");
|
||||
|
||||
$type_eval = ($meta['type_evaluation'] ?? 'formative') == 'sommative' ? 'evaluation' : 'exercice';
|
||||
|
||||
// Conversion des booléens en entiers
|
||||
$actif = isset($meta['est_active']) ? (int)(bool)$meta['est_active'] : 0;
|
||||
$afficher_correction = isset($meta['afficher_correction']) ? (int)(bool)$meta['afficher_correction'] : 1;
|
||||
$melanger_questions = isset($meta['melanger_questions']) ? (int)(bool)$meta['melanger_questions'] : 0;
|
||||
$tentatives_max = isset($meta['tentatives_max']) ? (int)$meta['tentatives_max'] : 1;
|
||||
|
||||
$stmt->execute([
|
||||
$meta['titre'],
|
||||
$meta['description'] ?? '',
|
||||
$type_eval,
|
||||
(int)$meta['duree_minutes'],
|
||||
(int)$meta['bareme_total'],
|
||||
$_SESSION['user_id'], // id_enseignant
|
||||
$actif,
|
||||
$afficher_correction,
|
||||
$melanger_questions,
|
||||
$tentatives_max
|
||||
]);
|
||||
|
||||
$id_evaluation = $db->lastInsertId();
|
||||
|
||||
// 2. Insérer les questions (ADAPTÉ AU SCHÉMA EXISTANT)
|
||||
$stmt_question = $db->prepare("
|
||||
INSERT INTO questions (
|
||||
id_evaluation, ordre, type_question, enonce,
|
||||
points, explication, options_json, reponse_correcte_json
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
foreach ($data['questions'] as $q) {
|
||||
// Validation question
|
||||
if (!isset($q['numero']) || !isset($q['type']) || !isset($q['enonce']) || !isset($q['points'])) {
|
||||
throw new Exception("Question invalide : champs manquants");
|
||||
}
|
||||
|
||||
// Préparer options_json et reponse_correcte_json selon le type
|
||||
$options_json = null;
|
||||
$reponse_correcte_json = null;
|
||||
|
||||
switch ($q['type']) {
|
||||
case 'qcm':
|
||||
case 'checkbox':
|
||||
if (!isset($q['reponses']) || !is_array($q['reponses'])) {
|
||||
throw new Exception("Question QCM/checkbox sans réponses");
|
||||
}
|
||||
$options_json = json_encode([
|
||||
'reponses' => $q['reponses']
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// Extraire les réponses correctes
|
||||
$correctes = [];
|
||||
foreach ($q['reponses'] as $rep) {
|
||||
if ($rep['est_correcte'] ?? false) {
|
||||
$correctes[] = $rep['id'];
|
||||
}
|
||||
}
|
||||
$reponse_correcte_json = json_encode($correctes, JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
case 'number':
|
||||
if (!isset($q['reponse_attendue'])) {
|
||||
throw new Exception("Question text/number sans reponse_attendue");
|
||||
}
|
||||
$options_json = json_encode([
|
||||
'sensible_casse' => $q['sensible_casse'] ?? false
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
$reponse_correcte_json = json_encode([
|
||||
'reponse' => $q['reponse_attendue']
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
if (!isset($q['options']) || !is_array($q['options'])) {
|
||||
throw new Exception("Question select sans options");
|
||||
}
|
||||
$options_json = json_encode([
|
||||
'options' => $q['options']
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// Trouver la réponse correcte
|
||||
foreach ($q['options'] as $opt) {
|
||||
if ($opt['est_correcte'] ?? false) {
|
||||
$reponse_correcte_json = json_encode([
|
||||
'id' => $opt['id']
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'text_trous':
|
||||
if (!isset($q['trous']) || !is_array($q['trous'])) {
|
||||
throw new Exception("Question text_trous sans trous");
|
||||
}
|
||||
$options_json = json_encode([
|
||||
'trous' => $q['trous']
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// Extraire les réponses attendues
|
||||
$reponses_trous = [];
|
||||
foreach ($q['trous'] as $trou) {
|
||||
$reponses_trous[$trou['id']] = $trou['reponse_attendue'];
|
||||
}
|
||||
$reponse_correcte_json = json_encode($reponses_trous, JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Pour types non supportés par l'enum (appariement, redaction)
|
||||
// On utilise 'text' comme fallback
|
||||
$options_json = json_encode($q, JSON_UNESCAPED_UNICODE);
|
||||
$reponse_correcte_json = json_encode(['type_original' => $q['type']], JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
}
|
||||
|
||||
$stmt_question->execute([
|
||||
$id_evaluation,
|
||||
$q['numero'],
|
||||
$q['type'],
|
||||
$q['enonce'],
|
||||
$q['points'],
|
||||
$q['explication'] ?? '',
|
||||
$options_json,
|
||||
$reponse_correcte_json
|
||||
]);
|
||||
}
|
||||
|
||||
// 3. Gérer les accès par classe (ADAPTÉ : acces_evaluations)
|
||||
if (isset($meta['classes_autorisees']) && is_array($meta['classes_autorisees'])) {
|
||||
$stmt_classe = $db->prepare("
|
||||
SELECT id_classe FROM classes WHERE nom_classe = ?
|
||||
");
|
||||
|
||||
$stmt_acces = $db->prepare("
|
||||
INSERT INTO acces_evaluations (
|
||||
id_evaluation, id_classe, date_debut, date_fin, actif
|
||||
) VALUES (?, ?, ?, ?, 1)
|
||||
");
|
||||
|
||||
$date_debut = $meta['date_debut'] ?? date('Y-m-d H:i:s');
|
||||
$date_fin = $meta['date_fin'] ?? date('Y-m-d H:i:s', strtotime('+1 year'));
|
||||
|
||||
foreach ($meta['classes_autorisees'] as $nom_classe) {
|
||||
$stmt_classe->execute([$nom_classe]);
|
||||
$id_classe = $stmt_classe->fetchColumn();
|
||||
|
||||
if ($id_classe) {
|
||||
$stmt_acces->execute([
|
||||
$id_evaluation,
|
||||
$id_classe,
|
||||
$date_debut,
|
||||
$date_fin
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
|
||||
// Déplacer le fichier vers un dossier "importé"
|
||||
$imported_dir = $transit_dir . 'imported/';
|
||||
if (!is_dir($imported_dir)) {
|
||||
mkdir($imported_dir, 0777, true);
|
||||
}
|
||||
rename($fichier_path, $imported_dir . $fichier_nom);
|
||||
|
||||
$message = "✅ Évaluation importée avec succès !<br>" .
|
||||
"ID : $id_evaluation | Titre : " . htmlspecialchars($meta['titre']) . "<br>" .
|
||||
"Questions : " . count($data['questions']);
|
||||
$message_type = "success";
|
||||
|
||||
// Redirection après 2 secondes
|
||||
header("Refresh: 2; url=gerer_evaluation.php?id=$id_evaluation");
|
||||
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
$message = "Erreur lors de l'import : " . $e->getMessage();
|
||||
$message_type = "error";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === LISTE DES FICHIERS DISPONIBLES ===
|
||||
$fichiers_disponibles = [];
|
||||
if (is_dir($transit_dir)) {
|
||||
$files = scandir($transit_dir);
|
||||
foreach ($files as $file) {
|
||||
if (pathinfo($file, PATHINFO_EXTENSION) === 'json') {
|
||||
$filepath = $transit_dir . $file;
|
||||
$fichiers_disponibles[] = [
|
||||
'nom' => $file,
|
||||
'taille' => filesize($filepath),
|
||||
'date' => filemtime($filepath)
|
||||
];
|
||||
}
|
||||
}
|
||||
// Trier par date décroissante
|
||||
usort($fichiers_disponibles, function($a, $b) {
|
||||
return $b['date'] - $a['date'];
|
||||
});
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Importer une évaluation</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;
|
||||
}
|
||||
|
||||
.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;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
background: rgba(255,255,255,0.3);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #667eea;
|
||||
font-size: 28px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 15px 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.message-success {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.message-error {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background: #e3f2fd;
|
||||
border-left: 4px solid #2196F3;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.info-box h3 {
|
||||
color: #1976D2;
|
||||
font-size: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.info-box ul {
|
||||
margin-left: 20px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.info-box li {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.files-list {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 15px;
|
||||
border: 2px solid #e0e0e0;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.file-item:hover {
|
||||
border-color: #667eea;
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.file-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.file-meta {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.empty-state-icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 20px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-state h3 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.code {
|
||||
background: #2d2d2d;
|
||||
color: #f8f8f2;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 13px;
|
||||
overflow-x: auto;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.file-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.file-meta {
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="enseignant/dashboard.php" class="back-link">← Retour au tableau de bord</a>
|
||||
|
||||
<div class="card">
|
||||
<h1>📥 Importer une évaluation</h1>
|
||||
<p class="subtitle">Importez vos évaluations JSON depuis le dossier transit</p>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="message message-<?php echo $message_type; ?>">
|
||||
<?php echo $message; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="info-box">
|
||||
<h3>📋 Workflow d'import</h3>
|
||||
<ul>
|
||||
<li><strong>Étape 1 :</strong> Créez votre évaluation au format JSON</li>
|
||||
<li><strong>Étape 2 :</strong> Uploadez le fichier via Cyberduck vers :
|
||||
<div class="code">/var/www/mathematiques/evaluations_transit/</div>
|
||||
</li>
|
||||
<li><strong>Étape 3 :</strong> Revenez sur cette page et cliquez sur "Importer"</li>
|
||||
<li><strong>Étape 4 :</strong> Gérez les paramètres et accès de l'évaluation</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="files-list">
|
||||
<h2 style="color: #667eea; font-size: 20px; margin-bottom: 20px;">
|
||||
📂 Fichiers disponibles (<?php echo count($fichiers_disponibles); ?>)
|
||||
</h2>
|
||||
|
||||
<?php if (empty($fichiers_disponibles)): ?>
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">📭</div>
|
||||
<h3>Aucun fichier à importer</h3>
|
||||
<p style="margin-top: 10px;">
|
||||
Uploadez des fichiers JSON dans le dossier transit pour les voir apparaître ici.
|
||||
</p>
|
||||
<div class="code" style="text-align: left; margin-top: 20px; max-width: 500px; margin-left: auto; margin-right: auto;">
|
||||
# Connexion Cyberduck<br>
|
||||
Serveur : nicolasboyer.com<br>
|
||||
Chemin : /var/www/mathematiques/evaluations_transit/
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($fichiers_disponibles as $fichier): ?>
|
||||
<div class="file-item">
|
||||
<div class="file-header">
|
||||
<div class="file-name">📄 <?php echo htmlspecialchars($fichier['nom']); ?></div>
|
||||
</div>
|
||||
<div class="file-meta">
|
||||
<span>📊 Taille : <?php echo number_format($fichier['taille'] / 1024, 2); ?> KB</span>
|
||||
<span>📅 Modifié : <?php echo date('d/m/Y H:i', $fichier['date']); ?></span>
|
||||
</div>
|
||||
<form method="POST" style="margin: 0;">
|
||||
<input type="hidden" name="fichier" value="<?php echo htmlspecialchars($fichier['nom']); ?>">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
⬇️ Importer cette évaluation
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user