Spaces:
Sleeping
Sleeping
| <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> |