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> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| </head> | |
| <body class="bg-gray-100 font-sans"> | |
| <div class="container mx-auto p-6"> | |
| <!-- Header --> | |
| <h1 class="text-3xl font-bold text-center mb-8">🧬 Générateur de Questions sur les Vaccins</h1> | |
| <!-- API Keys Status Section --> | |
| <section class="bg-white p-6 rounded-lg shadow-md mb-8"> | |
| <h2 class="text-2xl font-semibold mb-4">🔑 État des Clés API</h2> | |
| <div id="api-keys-status" class="text-gray-700"> | |
| Vérification des clés API... | |
| </div> | |
| <button id="check-api-keys" class="mt-4 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"> | |
| Vérifier les Clés | |
| </button> | |
| </section> | |
| <!-- Question Generation Section --> | |
| <section class="bg-white p-6 rounded-lg shadow-md mb-8"> | |
| <h2 class="text-2xl font-semibold mb-4">🚀 Génération de Questions</h2> | |
| <p class="mb-4">Générez des questions à partir du guide de vaccination avec gestion multi-clés et suivi en temps réel.</p> | |
| <div class="flex space-x-4"> | |
| <button id="generate-questions" class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600"> | |
| Générer des Questions | |
| </button> | |
| <a id="download-progress" class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600" href="#" style="display: none;"> | |
| Télécharger Progrès | |
| </a> | |
| <button id="view-failed-chunks" class="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600"> | |
| Voir Échecs | |
| </button> | |
| </div> | |
| <!-- Generation Status --> | |
| <div id="generation-status" class="mt-4 text-gray-700"> | |
| Initialisation de la génération... | |
| </div> | |
| <!-- Progress Bar --> | |
| <div class="mt-4"> | |
| <div class="w-full bg-gray-200 rounded-full h-4"> | |
| <div id="progress-bar" class="bg-blue-500 h-4 rounded-full" style="width: 0%;"></div> | |
| </div> | |
| <p id="progress-text" class="text-center mt-2">0%</p> | |
| </div> | |
| <!-- Generation Details --> | |
| <div id="generation-details" class="mt-4 text-gray-700"> | |
| <p>Chunks traités: <span id="processed-chunks">0</span>/<span id="total-chunks">0</span></p> | |
| <p>Questions générées: <span id="questions-generated">0</span></p> | |
| <p>Clé API actuelle: <span id="current-api-key">-</span></p> | |
| <p>Échecs: <span id="failed-chunks-count">0</span></p> | |
| </div> | |
| <!-- Estimated Time --> | |
| <div id="estimated-time" class="mt-4 text-gray-700"> | |
| ⏱️ Temps estimé restant: Calcul en cours... | |
| </div> | |
| <!-- Failed Chunks --> | |
| <div id="failed-chunks" class="mt-4 hidden"> | |
| <h3 class="text-lg font-semibold">❌ Chunks Échoués</h3> | |
| <ul id="failed-chunks-list" class="list-disc pl-5"></ul> | |
| <button id="retry-failed" class="mt-4 bg-yellow-500 text-white px-4 py-2 rounded hover:bg-yellow-600"> | |
| Réessayer les Chunks Échoués | |
| </button> | |
| </div> | |
| </section> | |
| <!-- Results Section --> | |
| <section class="bg-white p-6 rounded-lg shadow-md"> | |
| <h2 class="text-2xl font-semibold mb-4">📊 Résultats de Génération</h2> | |
| <div id="results-status" class="text-gray-700 mb-4"> | |
| Aucun résultat disponible pour le moment. | |
| </div> | |
| <a id="download-results" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600" href="#" style="display: none;"> | |
| 📥 Télécharger le Dataset Complet | |
| </a> | |
| <!-- Questions Preview --> | |
| <h3 class="text-lg font-semibold mt-6">👀 Aperçu des Questions</h3> | |
| <table class="w-full mt-4 border-collapse"> | |
| <thead> | |
| <tr class="bg-gray-200"> | |
| <th class="border p-2">Question</th> | |
| <th class="border p-2">Type</th> | |
| <th class="border p-2">Difficulté</th> | |
| <th class="border p-2">But d'Entraînement</th> | |
| <th class="border p-2">Chunk ID</th> | |
| <th class="border p-2">Clé API</th> | |
| </tr> | |
| </thead> | |
| <tbody id="questions-table"></tbody> | |
| </table> | |
| </section> | |
| </div> | |
| <script> | |
| // Utility function to fetch data from API | |
| async function fetchData(url) { | |
| try { | |
| const response = await fetch(url); | |
| if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); | |
| return await response.json(); | |
| } catch (error) { | |
| console.error('Error fetching data:', error); | |
| return null; | |
| } | |
| } | |
| // Update API Keys Status | |
| async function updateApiKeysStatus() { | |
| const statusDiv = document.getElementById('api-keys-status'); | |
| const data = await fetchData('/api-keys-status'); | |
| if (data && data.status === 'success') { | |
| statusDiv.innerHTML = `${data.total_keys} clés API configurées pour rotation. Clé actuelle: ${data.current_key_index}`; | |
| } else { | |
| statusDiv.innerHTML = `Erreur: ${data?.message || 'Impossible de vérifier les clés API'}`; | |
| } | |
| } | |
| // Update Generation Status | |
| async function updateGenerationStatus() { | |
| const data = await fetchData('/generation-status'); | |
| if (!data) return; | |
| const statusDiv = document.getElementById('generation-status'); | |
| const progressBar = document.getElementById('progress-bar'); | |
| const progressText = document.getElementById('progress-text'); | |
| const processedChunks = document.getElementById('processed-chunks'); | |
| const totalChunks = document.getElementById('total-chunks'); | |
| const questionsGenerated = document.getElementById('questions-generated'); | |
| const currentApiKey = document.getElementById('current-api-key'); | |
| const failedChunksCount = document.getElementById('failed-chunks-count'); | |
| const estimatedTime = document.getElementById('estimated-time'); | |
| const downloadProgress = document.getElementById('download-progress'); | |
| const failedChunksDiv = document.getElementById('failed-chunks'); | |
| const failedChunksList = document.getElementById('failed-chunks-list'); | |
| if (data.is_running) { | |
| statusDiv.innerHTML = 'Génération en cours...'; | |
| } else if (data.completed) { | |
| statusDiv.innerHTML = 'Génération terminée!'; | |
| } else if (data.error) { | |
| statusDiv.innerHTML = `Erreur: ${data.error}`; | |
| } else { | |
| statusDiv.innerHTML = 'Prêt à générer des questions.'; | |
| } | |
| progressBar.style.width = `${data.progress_percentage || 0}%`; | |
| progressText.innerText = `${data.progress_percentage || 0}%`; | |
| processedChunks.innerText = data.processed_chunks || 0; | |
| totalChunks.innerText = data.total_chunks || 0; | |
| questionsGenerated.innerText = data.questions_generated || 0; | |
| currentApiKey.innerText = data.current_api_key_index || '-'; | |
| failedChunksCount.innerText = data.failed_chunks?.length || 0; | |
| if (data.estimated_remaining_minutes) { | |
| estimatedTime.innerHTML = `⏱️ Temps estimé restant: ${data.estimated_remaining_minutes} minutes`; | |
| } else { | |
| estimatedTime.innerHTML = '⏱️ Temps estimé restant: Calcul en cours...'; | |
| } | |
| // Update download progress link | |
| if (data.progress_file) { | |
| downloadProgress.href = `/download/${data.progress_file}`; | |
| downloadProgress.style.display = 'inline-block'; | |
| } else { | |
| downloadProgress.style.display = 'none'; | |
| } | |
| // Update failed chunks | |
| if (data.failed_chunks?.length > 0) { | |
| failedChunksDiv.classList.remove('hidden'); | |
| failedChunksList.innerHTML = data.failed_chunks.map(chunk => | |
| `<li>Chunk ${chunk.chunk_id}: ${chunk.error} (Tentatives: ${chunk.attempts})</li>` | |
| ).join(''); | |
| } else { | |
| failedChunksDiv.classList.add('hidden'); | |
| } | |
| // Update results | |
| updateResults(data); | |
| } | |
| // Update Results and Questions Preview | |
| async function updateResults(statusData) { | |
| const resultsStatus = document.getElementById('results-status'); | |
| const downloadResults = document.getElementById('download-results'); | |
| const questionsTable = document.getElementById('questions-table'); | |
| if (statusData.completed && statusData.result_file) { | |
| resultsStatus.innerHTML = `Dataset généré avec ${statusData.questions_generated} questions.`; | |
| downloadResults.href = `/download/${statusData.result_file}`; | |
| downloadResults.style.display = 'inline-block'; | |
| } else if (statusData.partial_results?.length > 0) { | |
| resultsStatus.innerHTML = `Résultats partiels disponibles (${statusData.partial_results.length} questions).`; | |
| downloadResults.style.display = 'none'; | |
| } else { | |
| resultsStatus.innerHTML = 'Aucun résultat disponible pour le moment.'; | |
| downloadResults.style.display = 'none'; | |
| } | |
| // Update questions preview | |
| questionsTable.innerHTML = statusData.partial_results?.slice(0, 10).map(q => ` | |
| <tr> | |
| <td class="border p-2">${q.question}</td> | |
| <td class="border p-2">${q.type}</td> | |
| <td class="border p-2">${q.difficulty}</td> | |
| <td class="border p-2">${q.training_purpose}</td> | |
| <td class="border p-2">${q.chunk_id}</td> | |
| <td class="border p-2">${q.api_key_used}</td> | |
| </tr> | |
| `).join('') || ''; | |
| } | |
| // Start Generation | |
| async function startGeneration() { | |
| const generateButton = document.getElementById('generate-questions'); | |
| generateButton.disabled = true; | |
| generateButton.innerText = 'Génération en cours...'; | |
| const response = await fetchData('/generate-questions'); | |
| if (response && response.status === 'started') { | |
| alert(response.message); | |
| // Start polling for status | |
| const interval = setInterval(() => { | |
| updateGenerationStatus().then(() => { | |
| if (!response.is_running && response.completed) { | |
| clearInterval(interval); | |
| generateButton.disabled = false; | |
| generateButton.innerText = 'Générer des Questions'; | |
| } | |
| }); | |
| }, 5000); | |
| } else { | |
| alert(response?.message || 'Erreur lors du démarrage de la génération.'); | |
| generateButton.disabled = false; | |
| generateButton.innerText = 'Générer des Questions'; | |
| } | |
| } | |
| // Retry Failed Chunks | |
| async function retryFailedChunks() { | |
| const retryButton = document.getElementById('retry-failed'); | |
| retryButton.disabled = true; | |
| const response = await fetchData('/retry-failed'); | |
| alert(response.message); | |
| retryButton.disabled = false; | |
| updateGenerationStatus(); | |
| } | |
| // Event Listeners | |
| document.getElementById('check-api-keys').addEventListener('click', updateApiKeysStatus); | |
| document.getElementById('generate-questions').addEventListener('click', startGeneration); | |
| document.getElementById('view-failed-chunks').addEventListener('click', () => { | |
| document.getElementById('failed-chunks').classList.toggle('hidden'); | |
| }); | |
| document.getElementById('retry-failed').addEventListener('click', retryFailedChunks); | |
| // Initial Load | |
| updateApiKeysStatus(); | |
| updateGenerationStatus(); | |
| </script> | |
| </body> | |
| </html> |