572 lines
22 KiB
PHP
572 lines
22 KiB
PHP
<?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>
|