|
<!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 Données Synthétiques</title> |
|
<style> |
|
* { |
|
margin: 0; |
|
padding: 0; |
|
box-sizing: border-box; |
|
} |
|
|
|
body { |
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
min-height: 100vh; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
padding: 20px; |
|
} |
|
|
|
.container { |
|
background: white; |
|
border-radius: 20px; |
|
box-shadow: 0 20px 40px rgba(0,0,0,0.1); |
|
padding: 40px; |
|
max-width: 800px; |
|
width: 100%; |
|
} |
|
|
|
.header { |
|
text-align: center; |
|
margin-bottom: 40px; |
|
} |
|
|
|
.header h1 { |
|
color: #333; |
|
font-size: 2.5em; |
|
margin-bottom: 10px; |
|
background: linear-gradient(135deg, #667eea, #764ba2); |
|
-webkit-background-clip: text; |
|
-webkit-text-fill-color: transparent; |
|
background-clip: text; |
|
} |
|
|
|
.header p { |
|
color: #666; |
|
font-size: 1.1em; |
|
} |
|
|
|
.upload-section { |
|
border: 3px dashed #ddd; |
|
border-radius: 15px; |
|
padding: 40px; |
|
text-align: center; |
|
margin-bottom: 30px; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.upload-section:hover { |
|
border-color: #667eea; |
|
background: #f8f9ff; |
|
} |
|
|
|
.upload-section.dragover { |
|
border-color: #667eea; |
|
background: #f0f4ff; |
|
} |
|
|
|
input[type="file"] { |
|
display: none; |
|
} |
|
|
|
.upload-btn { |
|
background: linear-gradient(135deg, #667eea, #764ba2); |
|
color: white; |
|
padding: 15px 30px; |
|
border: none; |
|
border-radius: 50px; |
|
cursor: pointer; |
|
font-size: 1.1em; |
|
font-weight: 600; |
|
transition: all 0.3s ease; |
|
margin: 10px; |
|
} |
|
|
|
.upload-btn:hover { |
|
transform: translateY(-2px); |
|
box-shadow: 0 10px 20px rgba(0,0,0,0.2); |
|
} |
|
|
|
.progress-section { |
|
display: none; |
|
margin-top: 30px; |
|
} |
|
|
|
.progress-bar { |
|
width: 100%; |
|
height: 20px; |
|
background: #f0f0f0; |
|
border-radius: 10px; |
|
overflow: hidden; |
|
margin: 20px 0; |
|
} |
|
|
|
.progress-fill { |
|
height: 100%; |
|
background: linear-gradient(90deg, #667eea, #764ba2); |
|
width: 0%; |
|
transition: width 0.3s ease; |
|
position: relative; |
|
} |
|
|
|
.progress-fill::after { |
|
content: ''; |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
right: 0; |
|
bottom: 0; |
|
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); |
|
animation: shimmer 2s infinite; |
|
} |
|
|
|
@keyframes shimmer { |
|
0% { transform: translateX(-100%); } |
|
100% { transform: translateX(100%); } |
|
} |
|
|
|
.status-info { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
|
gap: 20px; |
|
margin: 20px 0; |
|
} |
|
|
|
.status-card { |
|
background: #f8f9ff; |
|
padding: 20px; |
|
border-radius: 10px; |
|
text-align: center; |
|
border-left: 4px solid #667eea; |
|
} |
|
|
|
.status-card h3 { |
|
color: #333; |
|
margin-bottom: 10px; |
|
} |
|
|
|
.status-card p { |
|
color: #666; |
|
font-size: 1.2em; |
|
font-weight: bold; |
|
} |
|
|
|
.download-btn { |
|
background: linear-gradient(135deg, #28a745, #20c997); |
|
color: white; |
|
padding: 15px 30px; |
|
border: none; |
|
border-radius: 50px; |
|
cursor: pointer; |
|
font-size: 1.1em; |
|
font-weight: 600; |
|
transition: all 0.3s ease; |
|
margin: 20px 10px; |
|
display: none; |
|
} |
|
|
|
.download-btn:hover { |
|
transform: translateY(-2px); |
|
box-shadow: 0 10px 20px rgba(0,0,0,0.2); |
|
} |
|
|
|
.tasks-section { |
|
margin-top: 30px; |
|
padding-top: 30px; |
|
border-top: 2px solid #eee; |
|
} |
|
|
|
.task-item { |
|
background: #f8f9ff; |
|
padding: 15px; |
|
border-radius: 10px; |
|
margin: 10px 0; |
|
border-left: 4px solid #667eea; |
|
} |
|
|
|
.task-item.completed { |
|
border-left-color: #28a745; |
|
} |
|
|
|
.notification { |
|
position: fixed; |
|
top: 20px; |
|
right: 20px; |
|
padding: 15px 25px; |
|
border-radius: 10px; |
|
color: white; |
|
font-weight: 600; |
|
transform: translateX(100%); |
|
transition: transform 0.3s ease; |
|
z-index: 1000; |
|
} |
|
|
|
.notification.success { |
|
background: linear-gradient(135deg, #28a745, #20c997); |
|
} |
|
|
|
.notification.error { |
|
background: linear-gradient(135deg, #dc3545, #c82333); |
|
} |
|
|
|
.notification.show { |
|
transform: translateX(0); |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="container"> |
|
<div class="header"> |
|
<h1>🤖 Générateur de Données Synthétiques</h1> |
|
<p>Uploadez votre fichier pour générer 470 jeux de données synthétiques</p> |
|
</div> |
|
|
|
<div class="upload-section" id="uploadSection"> |
|
<h3>📁 Sélectionnez votre fichier</h3> |
|
<p>Glissez-déposez votre fichier ici ou cliquez pour sélectionner</p> |
|
<input type="file" id="fileInput" accept=".txt"> |
|
<button class="upload-btn" onclick="document.getElementById('fileInput').click()"> |
|
Choisir le fichier |
|
</button> |
|
<button class="upload-btn" id="startBtn" onclick="startGeneration()" style="display: none;"> |
|
🚀 Démarrer la génération |
|
</button> |
|
</div> |
|
|
|
<div class="progress-section" id="progressSection"> |
|
<h3>⏳ Génération en cours...</h3> |
|
<div class="progress-bar"> |
|
<div class="progress-fill" id="progressFill"></div> |
|
</div> |
|
<div class="status-info"> |
|
<div class="status-card"> |
|
<h3>Progrès</h3> |
|
<p id="progressText">0 / 470</p> |
|
</div> |
|
<div class="status-card"> |
|
<h3>Pourcentage</h3> |
|
<p id="percentageText">0%</p> |
|
</div> |
|
<div class="status-card"> |
|
<h3>Statut</h3> |
|
<p id="statusText">En attente</p> |
|
</div> |
|
<div class="status-card"> |
|
<h3>Heure de début</h3> |
|
<p id="startTimeText">-</p> |
|
</div> |
|
</div> |
|
|
|
<button class="download-btn" id="downloadBtn" onclick="downloadResults()"> |
|
📥 Télécharger les résultats |
|
</button> |
|
</div> |
|
|
|
<div class="tasks-section"> |
|
<h3>📋 Tâches précédentes</h3> |
|
<div id="tasksList"></div> |
|
</div> |
|
</div> |
|
|
|
<div class="notification" id="notification"></div> |
|
|
|
<script> |
|
let currentTaskId = null; |
|
let statusInterval = null; |
|
|
|
|
|
const uploadSection = document.getElementById('uploadSection'); |
|
const fileInput = document.getElementById('fileInput'); |
|
|
|
uploadSection.addEventListener('dragover', (e) => { |
|
e.preventDefault(); |
|
uploadSection.classList.add('dragover'); |
|
}); |
|
|
|
uploadSection.addEventListener('dragleave', () => { |
|
uploadSection.classList.remove('dragover'); |
|
}); |
|
|
|
uploadSection.addEventListener('drop', (e) => { |
|
e.preventDefault(); |
|
uploadSection.classList.remove('dragover'); |
|
|
|
const files = e.dataTransfer.files; |
|
if (files.length > 0) { |
|
fileInput.files = files; |
|
handleFileSelect(); |
|
} |
|
}); |
|
|
|
fileInput.addEventListener('change', handleFileSelect); |
|
|
|
function handleFileSelect() { |
|
const file = fileInput.files[0]; |
|
if (file) { |
|
document.getElementById('startBtn').style.display = 'inline-block'; |
|
showNotification(`Fichier sélectionné: ${file.name}`, 'success'); |
|
} |
|
} |
|
|
|
function startGeneration() { |
|
const file = fileInput.files[0]; |
|
if (!file) { |
|
showNotification('Veuillez sélectionner un fichier', 'error'); |
|
return; |
|
} |
|
|
|
const formData = new FormData(); |
|
formData.append('file', file); |
|
|
|
fetch('/upload', { |
|
method: 'POST', |
|
body: formData |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
if (data.error) { |
|
showNotification(data.error, 'error'); |
|
} else { |
|
currentTaskId = data.task_id; |
|
document.getElementById('progressSection').style.display = 'block'; |
|
startStatusPolling(); |
|
showNotification('Génération démarrée!', 'success'); |
|
} |
|
}) |
|
.catch(error => { |
|
showNotification('Erreur lors du démarrage', 'error'); |
|
console.error('Error:', error); |
|
}); |
|
} |
|
|
|
function startStatusPolling() { |
|
if (statusInterval) clearInterval(statusInterval); |
|
|
|
statusInterval = setInterval(() => { |
|
if (currentTaskId) { |
|
fetch(`/status/${currentTaskId}`) |
|
.then(response => response.json()) |
|
.then(data => { |
|
updateProgress(data); |
|
if (data.status === 'completed') { |
|
clearInterval(statusInterval); |
|
document.getElementById('downloadBtn').style.display = 'inline-block'; |
|
showNotification('Génération terminée!', 'success'); |
|
} |
|
}) |
|
.catch(console.error); |
|
} |
|
}, 2000); |
|
} |
|
|
|
function updateProgress(data) { |
|
const progressFill = document.getElementById('progressFill'); |
|
const progressText = document.getElementById('progressText'); |
|
const percentageText = document.getElementById('percentageText'); |
|
const statusText = document.getElementById('statusText'); |
|
const startTimeText = document.getElementById('startTimeText'); |
|
|
|
progressFill.style.width = `${data.percentage}%`; |
|
progressText.textContent = `${data.progress} / ${data.total}`; |
|
percentageText.textContent = `${data.percentage}%`; |
|
statusText.textContent = data.status === 'running' ? 'En cours' : |
|
data.status === 'completed' ? 'Terminé' : data.status; |
|
startTimeText.textContent = data.start_time; |
|
} |
|
|
|
function downloadResults() { |
|
if (currentTaskId) { |
|
window.location.href = `/download/${currentTaskId}`; |
|
} |
|
} |
|
|
|
function showNotification(message, type) { |
|
const notification = document.getElementById('notification'); |
|
notification.textContent = message; |
|
notification.className = `notification ${type}`; |
|
notification.classList.add('show'); |
|
|
|
setTimeout(() => { |
|
notification.classList.remove('show'); |
|
}, 3000); |
|
} |
|
|
|
function loadTasks() { |
|
fetch('/tasks') |
|
.then(response => response.json()) |
|
.then(tasks => { |
|
const tasksList = document.getElementById('tasksList'); |
|
tasksList.innerHTML = ''; |
|
|
|
tasks.forEach(task => { |
|
const taskItem = document.createElement('div'); |
|
taskItem.className = `task-item ${task.status}`; |
|
taskItem.innerHTML = ` |
|
<div style="display: flex; justify-content: space-between; align-items: center;"> |
|
<div> |
|
<strong>Tâche ${task.id.substring(0, 8)}...</strong><br> |
|
<small>Démarré: ${task.start_time} | Progrès: ${task.progress}/${task.total} (${task.percentage}%)</small> |
|
</div> |
|
<div> |
|
${task.status === 'completed' ? |
|
`<button class="download-btn" style="display: inline-block; margin: 0;" onclick="window.location.href='/download/${task.id}'">Télécharger</button>` : |
|
`<span style="color: #667eea; font-weight: bold;">${task.status}</span>` |
|
} |
|
</div> |
|
</div> |
|
`; |
|
tasksList.appendChild(taskItem); |
|
}); |
|
}) |
|
.catch(console.error); |
|
} |
|
|
|
|
|
loadTasks(); |
|
|
|
|
|
setInterval(loadTasks, 30000); |
|
</script> |
|
</body> |
|
</html> |