feat: add operation exercises generation using gen_op.py
Moved gen-op.py to app/gen_op.py and integrated it with the FastAPI application Added a new section in the UI for generating operation exercises Updated the PDF generation to work in memory and upload to S3 Bumped version to 1.0.9
This commit is contained in:
+133
-6
@@ -33,14 +33,15 @@
|
||||
<div class="col-lg-8">
|
||||
<div class="text-center mb-5">
|
||||
<h1 class="display-4 fw-bold">Générateur d'Exercices de Mathématiques</h1>
|
||||
<p class="lead">Créez des exercices de multiplication et division personnalisés en PDF</p>
|
||||
<p class="lead">Créez des exercices de multiplication, division et opérations complexes personnalisés en PDF</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Multiplication/Division Exercises Section -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Paramètres des Exercices</h5>
|
||||
<h5 class="card-title">Paramètres des Exercices (Multiplications/Divisions)</h5>
|
||||
<form id="exerciseForm">
|
||||
<div class="mb-3">
|
||||
<label for="minTable" class="form-label">Table minimale</label>
|
||||
@@ -62,7 +63,7 @@
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-lg" id="generateBtn">
|
||||
<span id="buttonText">Générer le PDF</span>
|
||||
<span id="buttonText">Générer le PDF (Mult/Div)</span>
|
||||
<span id="spinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -70,6 +71,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Operation Exercises Section -->
|
||||
<div class="card mt-4">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Paramètres des Exercices d'Opérations</h5>
|
||||
<form id="operationExerciseForm">
|
||||
<div class="mb-3">
|
||||
<label for="numOperationExercises" class="form-label">Nombre d'exercices</label>
|
||||
<input type="number" class="form-control" id="numOperationExercises" min="1" max="100" value="20" required>
|
||||
<div class="form-text">Nombre total d'exercices d'opérations à générer (entre 1 et 100)</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-success btn-lg" id="generateOperationBtn">
|
||||
<span id="operationButtonText">Générer le PDF (Opérations)</span>
|
||||
<span id="operationSpinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Existing PDFs Section -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
@@ -161,8 +183,40 @@
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="fw-bold">Trois Colonnes</h5>
|
||||
<p>Les exercices sont présentés en trois colonnes pour économiser du papier.</p>
|
||||
<h5 class="fw-bold">Colonnes Variables</h5>
|
||||
<p>Les exercices de multiplication/division sont présentés en trois colonnes, les opérations complexes en deux colonnes.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="feature-icon bg-danger bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-calculator text-danger" viewBox="0 0 16 16">
|
||||
<path d="M12 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h8zM4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H4z"/>
|
||||
<path d="M4 2.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5v-2zm0 4a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5V6.5zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5V6.5zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5V6.5zm-6 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5V9.5zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5V9.5zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5V9.5zm-6 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="fw-bold">Opérations Complexes</h5>
|
||||
<p>Générez des exercices avec des expressions mathématiques complexes incluant parenthèses, additions, soustractions et multiplications.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="feature-icon bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-cloud-arrow-down text-success" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M7.646 10.854a.5.5 0 0 0 .708 0l2-2a.5.5 0 0 0-.708-.708L8.5 9.293V5.5a.5.5 0 0 0-1 0v3.793L6.354 8.146a.5.5 0 1 0-.708.708l2 2z"/>
|
||||
<path d="M4.406 3.342A5.53 5.53 0 0 1 8 2c2.69 0 4.923 2 5.166 4.579C14.758 6.804 16 8.137 16 9.773 16 11.569 14.502 13 12.687 13H3.781C1.708 13 0 11.366 0 9.318c0-1.763 1.266-3.223 2.942-3.593.143-.863.698-1.723 1.464-2.383zm.653.757c-.757.653-1.153 1.44-1.153 2.056v.448l-.445.049C2.064 6.805 1 7.952 1 9.318 1 10.785 2.23 12 3.781 12h8.906C13.98 12 15 10.988 15 9.773c0-1.216-1.02-2.228-2.313-2.228h-.5v-.5C12.188 4.825 10.328 3 8 3a4.53 4.53 0 0 0-2.941 1.1z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="fw-bold">Stockage Cloud</h5>
|
||||
<p>Tous les fichiers générés sont automatiquement stockés dans le cloud et peuvent être téléchargés ultérieurement.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -522,7 +576,80 @@
|
||||
resultContainer.classList.remove('d-none');
|
||||
} finally {
|
||||
// Reset button state
|
||||
buttonText.textContent = 'Générer le PDF';
|
||||
buttonText.textContent = 'Générer le PDF (Mult/Div)';
|
||||
spinner.classList.add('d-none');
|
||||
generateBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Handle operation exercises form submission
|
||||
document.getElementById('operationExerciseForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const numExercises = parseInt(document.getElementById('numOperationExercises').value);
|
||||
|
||||
const generateBtn = document.getElementById('generateOperationBtn');
|
||||
const buttonText = document.getElementById('operationButtonText');
|
||||
const spinner = document.getElementById('operationSpinner');
|
||||
|
||||
// Show loading state
|
||||
buttonText.textContent = 'Génération en cours...';
|
||||
spinner.classList.remove('d-none');
|
||||
generateBtn.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('/generate-operations', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
num_exercises: numExercises
|
||||
})
|
||||
});
|
||||
|
||||
// Check if it's a redirect response (for automatic download)
|
||||
if (response.redirected) {
|
||||
// Automatically trigger download
|
||||
window.location.href = response.url;
|
||||
|
||||
// Refresh the PDF list (reset to first page)
|
||||
setTimeout(() => loadPdfList(1), 1000);
|
||||
|
||||
// Show success message
|
||||
const resultContainer = document.getElementById('resultContainer');
|
||||
const resultMessage = document.getElementById('resultMessage');
|
||||
resultMessage.innerHTML = `
|
||||
<div class="alert alert-success">
|
||||
<h6>PDF d'opérations généré avec succès!</h6>
|
||||
<p>Le téléchargement devrait commencer automatiquement.</p>
|
||||
<ul class="mb-0">
|
||||
<li>${numExercises} exercices d'opérations générés</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
resultContainer.classList.remove('d-none');
|
||||
} else {
|
||||
// Handle JSON response (error case)
|
||||
const result = await response.json();
|
||||
const resultContainer = document.getElementById('resultContainer');
|
||||
const resultMessage = document.getElementById('resultMessage');
|
||||
|
||||
if (result.error) {
|
||||
resultMessage.innerHTML = `<div class="alert alert-danger">${result.error}</div>`;
|
||||
}
|
||||
|
||||
resultContainer.classList.remove('d-none');
|
||||
}
|
||||
} catch (error) {
|
||||
const resultContainer = document.getElementById('resultContainer');
|
||||
const resultMessage = document.getElementById('resultMessage');
|
||||
|
||||
resultMessage.innerHTML = `<div class="alert alert-danger">Erreur: ${error.message}</div>`;
|
||||
resultContainer.classList.remove('d-none');
|
||||
} finally {
|
||||
// Reset button state
|
||||
buttonText.textContent = 'Générer le PDF (Opérations)';
|
||||
spinner.classList.add('d-none');
|
||||
generateBtn.disabled = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user