Zeggai Abdellah
add status
8517947
raw
history blame
17.6 kB
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Générateur de Questions sur les Vaccins</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
background-color: #f5f7fa;
}
h1 {
color: #2c3e50;
text-align: center;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.container {
background-color: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 20px;
margin-bottom: 20px;
}
button {
background-color: #3498db;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}
button:hover {
background-color: #2980b9;
}
button:disabled {
background-color: #95a5a6;
cursor: not-allowed;
}
.download-btn {
background-color: #27ae60;
}
.download-btn:hover {
background-color: #219955;
}
#statusContainer {
margin-top: 20px;
padding: 15px;
border-left: 4px solid #3498db;
background-color: #e8f4fc;
}
.loader {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top-color: #3498db;
animation: spin 1s ease infinite;
margin-right: 10px;
vertical-align: middle;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.stats-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
margin-top: 20px;
}
.stat-card {
background-color: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
text-align: center;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #2c3e50;
margin: 10px 0;
}
.stat-label {
color: #7f8c8d;
font-size: 14px;
}
#questionsPreview {
margin-top: 20px;
max-height: 400px;
overflow-y: auto;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f8f9fa;
font-weight: 600;
}
tr:hover {
background-color: #f1f5f9;
}
.badge {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
color: white;
}
.badge-easy { background-color: #27ae60; }
.badge-medium { background-color: #f39c12; }
.badge-hard { background-color: #e74c3c; }
.badge-factual { background-color: #3498db; }
.badge-conceptual { background-color: #9b59b6; }
.badge-applied { background-color: #e67e22; }
.progress-container {
margin-top: 15px;
background-color: #ecf0f1;
border-radius: 4px;
height: 20px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background-color: #3498db;
transition: width 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
font-weight: bold;
}
.progress-bar.complete {
background-color: #27ae60;
}
.progress-bar.error {
background-color: #e74c3c;
}
</style>
</head>
<body>
<h1>Générateur de Questions sur les Vaccins</h1>
<div class="container">
<h2>Générer des Questions</h2>
<p>Cliquez sur le bouton ci-dessous pour commencer la génération de questions à partir du guide de vaccination.</p>
<button id="generateBtn">Générer des Questions</button>
<div id="statusContainer" style="display: none;">
<div id="statusHeader">
<div class="loader"></div>
<span id="statusText">Initialisation de la génération...</span>
</div>
<div class="progress-container">
<div id="progressBar" class="progress-bar" style="width: 0%">0%</div>
</div>
<div id="statusDetails" style="margin-top: 10px;">
<div>Chunks traités: <span id="processedChunks">0</span>/<span id="totalChunks">0</span></div>
<div>Questions générées: <span id="questionsGenerated">0</span></div>
</div>
</div>
</div>
<div class="container" id="resultsContainer" style="display: none;">
<h2>Résultats</h2>
<div class="stats-container" id="statsContainer">
<!-- Stats will be populated here -->
</div>
<div style="margin-top: 20px; text-align: center;">
<button id="downloadBtn" class="download-btn">Télécharger le Dataset</button>
</div>
<h3 style="margin-top: 30px;">Aperçu des Questions</h3>
<div id="questionsPreview">
<table>
<thead>
<tr>
<th>Question</th>
<th>Type</th>
<th>Difficulté</th>
<th>But d'Entraînement</th>
</tr>
</thead>
<tbody id="questionsTableBody">
<!-- Questions will be populated here -->
</tbody>
</table>
</div>
</div>
<script>
const apiBaseUrl = window.location.origin; // Use the same origin for API calls
let generatedDataset = null;
let downloadUrl = '';
let statusCheckInterval = null;
document.getElementById('generateBtn').addEventListener('click', startGeneration);
document.getElementById('downloadBtn').addEventListener('click', downloadDataset);
// Check for ongoing generation when the page loads
window.addEventListener('load', checkOngoingGeneration);
function checkOngoingGeneration() {
fetch(`${apiBaseUrl}/generation-status`)
.then(response => response.json())
.then(status => {
if (status.is_running || status.completed) {
setupStatusMonitoring();
updateStatusDisplay(status);
}
if (status.completed && status.result_file) {
downloadUrl = `/download/${status.result_file}`;
loadResults();
}
})
.catch(error => {
console.error('Error checking generation status:', error);
});
}
function startGeneration() {
const generateBtn = document.getElementById('generateBtn');
const statusContainer = document.getElementById('statusContainer');
const resultsContainer = document.getElementById('resultsContainer');
// Disable button and show status
generateBtn.disabled = true;
statusContainer.style.display = 'block';
resultsContainer.style.display = 'none';
// Call the API endpoint to start generation
fetch(`${apiBaseUrl}/generate-questions`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Generation started:', data);
// Setup status monitoring
setupStatusMonitoring();
})
.catch(error => {
console.error('Error starting generation:', error);
document.getElementById('statusText').textContent = `Erreur: ${error.message}`;
generateBtn.disabled = false;
});
}
function setupStatusMonitoring() {
// Clear any existing interval
if (statusCheckInterval) {
clearInterval(statusCheckInterval);
}
// Show the status container
document.getElementById('statusContainer').style.display = 'block';
// Start checking status regularly
statusCheckInterval = setInterval(checkGenerationStatus, 5000);
// Do an immediate check
checkGenerationStatus();
}
function checkGenerationStatus() {
fetch(`${apiBaseUrl}/generation-status`)
.then(response => response.json())
.then(status => {
updateStatusDisplay(status);
// If generation is completed, stop checking and load results
if (status.completed) {
clearInterval(statusCheckInterval);
downloadUrl = `/download/${status.result_file}`;
loadResults();
}
// If there was an error, stop checking
if (status.error) {
clearInterval(statusCheckInterval);
document.getElementById('generateBtn').disabled = false;
}
})
.catch(error => {
console.error('Error checking status:', error);
});
}
function updateStatusDisplay(status) {
const statusText = document.getElementById('statusText');
const progressBar = document.getElementById('progressBar');
const processedChunks = document.getElementById('processedChunks');
const totalChunks = document.getElementById('totalChunks');
const questionsGenerated = document.getElementById('questionsGenerated');
// Update text and counts
if (status.error) {
statusText.textContent = `Erreur: ${status.error}`;
progressBar.classList.add('error');
} else if (status.completed) {
statusText.textContent = 'Génération terminée avec succès !';
progressBar.classList.add('complete');
} else if (status.is_running) {
statusText.textContent = 'Génération des questions en cours...';
}
// Update progress data
processedChunks.textContent = status.processed_chunks;
totalChunks.textContent = status.total_chunks;
questionsGenerated.textContent = status.questions_generated;
// Calculate and update progress percentage
if (status.total_chunks > 0) {
const percentage = Math.round((status.processed_chunks / status.total_chunks) * 100);
progressBar.style.width = `${percentage}%`;
progressBar.textContent = `${percentage}%`;
}
}
function loadResults() {
if (!downloadUrl) return;
// Fetch the generated dataset
fetch(`${apiBaseUrl}${downloadUrl}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(dataset => {
generatedDataset = dataset;
displayResults(dataset);
// Update status
const statusText = document.getElementById('statusText');
statusText.textContent = 'Génération terminée avec succès !';
document.getElementById('generateBtn').disabled = false;
})
.catch(error => {
console.error('Error loading results:', error);
});
}
function displayResults(dataset) {
const resultsContainer = document.getElementById('resultsContainer');
const statsContainer = document.getElementById('statsContainer');
const questionsTableBody = document.getElementById('questionsTableBody');
// Display dataset info stats
statsContainer.innerHTML = '';
// Total questions
addStatCard(statsContainer, dataset.dataset_info.total_questions, 'Questions Totales');
// Count by type
const typeCount = countByProperty(dataset.questions, 'type');
for (const [type, count] of Object.entries(typeCount)) {
addStatCard(statsContainer, count, `Questions ${capitalizeFirstLetter(type)}`);
}
// Count by difficulty
const difficultyCount = countByProperty(dataset.questions, 'difficulty');
for (const [difficulty, count] of Object.entries(difficultyCount)) {
addStatCard(statsContainer, count, `Niveau ${capitalizeFirstLetter(difficulty)}`);
}
// Display questions preview
questionsTableBody.innerHTML = '';
dataset.questions.forEach(question => {
const row = document.createElement('tr');
const questionCell = document.createElement('td');
questionCell.textContent = question.question;
const typeCell = document.createElement('td');
const typeBadge = document.createElement('span');
typeBadge.className = `badge badge-${question.type}`;
typeBadge.textContent = question.type;
typeCell.appendChild(typeBadge);
const difficultyCell = document.createElement('td');
const difficultyBadge = document.createElement('span');
difficultyBadge.className = `badge badge-${question.difficulty}`;
difficultyBadge.textContent = question.difficulty;
difficultyCell.appendChild(difficultyBadge);
const purposeCell = document.createElement('td');
purposeCell.textContent = question.training_purpose;
row.appendChild(questionCell);
row.appendChild(typeCell);
row.appendChild(difficultyCell);
row.appendChild(purposeCell);
questionsTableBody.appendChild(row);
});
// Show the results container
resultsContainer.style.display = 'block';
}
function addStatCard(container, value, label) {
const card = document.createElement('div');
card.className = 'stat-card';
const valueElement = document.createElement('div');
valueElement.className = 'stat-value';
valueElement.textContent = value;
const labelElement = document.createElement('div');
labelElement.className = 'stat-label';
labelElement.textContent = label;
card.appendChild(valueElement);
card.appendChild(labelElement);
container.appendChild(card);
}
function countByProperty(array, property) {
const counts = {};
array.forEach(item => {
const value = item[property];
counts[value] = (counts[value] || 0) + 1;
});
return counts;
}
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function downloadDataset() {
if (downloadUrl) {
window.location.href = `${apiBaseUrl}${downloadUrl}`;
}
}
</script>
</body>
</html>