Compare commits

..

No commits in common. 'main' and 'v1.0' have entirely different histories.
main ... v1.0

  1. 1
      .gitignore
  2. 168
      main/templates/resultat_appreciations.html
  3. 3
      main/urls.py
  4. 20
      main/views.py

1
.gitignore vendored

@ -3,4 +3,3 @@ db.sqlite3 @@ -3,4 +3,3 @@ db.sqlite3
__pycache__
migrations
.env
.vscode

168
main/templates/resultat_appreciations.html

@ -69,24 +69,6 @@ @@ -69,24 +69,6 @@
color: #23272b !important;
border: 1px solid #444c56 !important;
}
.btn-outline-primary, .btn-outline-primary:focus, .btn-outline-primary:active, .btn-outline-primary:hover {
color: #23272b !important;
background-color: #ffe082 !important;
border-color: #ffe082 !important;
box-shadow: none !important;
}
.btn-outline-primary:disabled, .btn-outline-primary.disabled {
color: #bdbdbd !important;
background-color: #444c56 !important;
border-color: #444c56 !important;
}
}
#bulletinModal .modal-content, #bulletinModal .modal-body, #bulletinModal .modal-header {
background-color: #fff !important;
color: #23272b !important;
}
#bulletinModal .modal-title {
color: #23272b !important;
}
</style>
</head>
@ -112,7 +94,7 @@ @@ -112,7 +94,7 @@
<div>
<label for="modele-select" class="form-label">Choisir un modèle</label>
<select id="modele-select" class="form-select">
{% for modele in modeles|dictsortreversed:'id' %}
{% for modele in modeles %}
<option value="{{ modele.code }}" {% if modele.code == 'gpt-4.1-mini' %}selected{% endif %}>{{ modele.nom }}</option>
{% endfor %}
{% if not modeles or not modeles|dictsort:'code'|length %}
@ -126,7 +108,7 @@ @@ -126,7 +108,7 @@
</div>
<div class="mb-3 d-flex gap-2">
<button id="generation-toggle" class="btn btn-primary mb-3">Lancer la génération</button>
<button id="export-pdf" class="btn btn-primary mb-3" type="button" onclick="exportTableToPDF()">Exporter en PDF</button>
<button id="export-pdf" class="btn btn-secondary mb-3" type="button" onclick="exportTableToPDF()">Exporter en PDF</button>
</div>
<div id="credit-alert" class="alert alert-danger d-none" role="alert">
Vous n'avez plus de crédits pour générer des appréciations.
@ -134,68 +116,29 @@ @@ -134,68 +116,29 @@
<table class="table table-bordered" id="appreciations-table">
<thead>
<tr>
<th style="width:1%; white-space:nowrap;">Élève</th>
<th>Élève</th>
<th>Appréciation</th>
<th style="width:1%; white-space:nowrap; text-align:center;">Action</th>
</tr>
</thead>
<tbody>
{% if appreciations_json and appreciations_json|length > 0 %}
{% for appreciation in appreciations_json %}
<tr>
<td class="eleve" style="width:1%; white-space:nowrap;">{{ appreciation.eleve }}</td>
<td class="eleve">{{ appreciation.eleve }}</td>
<td class="appreciation"></td>
<td style="width:1%; text-align:center; vertical-align:middle;">
<div class="d-flex justify-content-center gap-2">
<button class="btn btn-outline-primary btn-sm action-generate w-100" type="button">Générer</button>
<button class="btn btn-outline-primary btn-sm action-bulletin w-100" type="button" data-eleve="{{ appreciation.eleve }}">Bulletin</button>
</div>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="3"><div class="alert alert-warning mb-0">Aucune appréciation générée.</div></td>
<td colspan="2"><div class="alert alert-warning mb-0">Aucune appréciation générée.</div></td>
</tr>
{% endif %}
</tbody>
</table>
<!-- Injection des appréciations dans une variable JS globale pour accès rapide -->
<script id="appreciations-data" type="application/json">
{{ appreciations_json|safe }}
</script>
<script>
window.allAppreciations = {};
try {
const appreciationsList = JSON.parse(document.getElementById('appreciations-data').textContent);
appreciationsList.forEach(eleve => {
let appreciations = eleve.appreciations;
if (typeof appreciations === 'string') {
try { appreciations = JSON.parse(appreciations); } catch (e) { appreciations = []; }
}
window.allAppreciations[eleve.eleve] = appreciations;
});
} catch (e) { window.allAppreciations = {}; }
</script>
<script>
let stopGeneration = false;
let enCours = false;
function updateActionButtons() {
const rows = document.querySelectorAll('#appreciations-table tbody tr');
rows.forEach(row => {
const appreciationCell = row.querySelector('.appreciation');
const btn = row.querySelector('.action-generate');
if (!btn) return;
if (appreciationCell.textContent.trim()) {
btn.textContent = 'Régénérer';
} else {
btn.textContent = 'Générer';
}
btn.disabled = enCours;
});
}
function getRowsSansAppreciation() {
return Array.from(document.querySelectorAll('#appreciations-table tbody tr')).filter(row => !row.querySelector('.appreciation').textContent.trim());
}
@ -222,16 +165,13 @@ @@ -222,16 +165,13 @@
function traiterLignes() {
rows = getRowsSansAppreciation();
updateActionButtons();
if (rows.length === 0 || stopGeneration) {
enCours = false;
updateActionButtons();
if (stopGeneration) setButtonState('continuer');
else setButtonState('lancer');
return;
}
enCours = true;
updateActionButtons();
setButtonState('arreter');
const row = rows[0];
const eleve = row.querySelector('.eleve').textContent;
@ -248,7 +188,6 @@ @@ -248,7 +188,6 @@
.then(response => response.json())
.then(data => {
appreciationCell.textContent = data.appreciation;
updateActionButtons();
if (typeof data.credit !== 'undefined') {
creditRestant.textContent = 'Crédits restants : ' + data.credit;
}
@ -264,7 +203,6 @@ @@ -264,7 +203,6 @@
})
.catch(() => {
appreciationCell.textContent = 'Erreur lors de la génération';
updateActionButtons();
traiterLignes();
});
}
@ -287,81 +225,7 @@ @@ -287,81 +225,7 @@
}
});
// Action individuelle sur bouton Générer/Régénérer
document.querySelectorAll('.action-generate').forEach(btn => {
btn.addEventListener('click', function() {
if (enCours) return;
const row = btn.closest('tr');
const eleve = row.querySelector('.eleve').textContent;
const appreciationCell = row.querySelector('.appreciation');
appreciationCell.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Génération...';
btn.disabled = true;
fetch("{% url 'generer_appreciation_ajax' %}", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({eleve: eleve, modele: selectModele.value})
})
.then(response => response.json())
.then(data => {
appreciationCell.textContent = data.appreciation;
updateActionButtons();
if (typeof data.credit !== 'undefined') {
creditRestant.textContent = 'Crédits restants : ' + data.credit;
}
if (data.credit === 0) {
creditAlert.classList.remove('d-none');
}
})
.catch(() => {
appreciationCell.textContent = 'Erreur lors de la génération';
updateActionButtons();
});
});
});
// Action individuelle sur bouton Bulletin
document.querySelectorAll('.action-bulletin').forEach(btn => {
btn.addEventListener('click', function() {
const eleve = btn.getAttribute('data-eleve');
fetch("{% url 'get_bulletin_eleve' %}", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({eleve: eleve})
})
.then(response => response.json())
.then(data => {
// Mettre le titre dans le header du modal
const modalTitle = document.getElementById('bulletinModalLabel');
if (modalTitle) {
modalTitle.textContent = `Bulletin de l'élève ${eleve}`;
}
// Mettre uniquement les appréciations dans le body
let message = '';
if (Array.isArray(data.appreciations) && data.appreciations.length > 0) {
message += data.appreciations.map(app => `<div style='margin-bottom:8px;'><strong>${app.matiere} :</strong><br>${app.appreciation}</div>`).join('');
} else {
message += '<em>Aucune appréciation trouvée dans le bulletin.</em>';
}
showBulletinModal(message);
})
.catch(() => {
const modalTitle = document.getElementById('bulletinModalLabel');
if (modalTitle) {
modalTitle.textContent = `Bulletin`;
}
showBulletinModal('<em>Erreur lors de la récupération du bulletin.</em>');
});
});
});
setButtonState('lancer');
updateActionButtons();
});
</script>
<script>
@ -413,28 +277,6 @@ @@ -413,28 +277,6 @@
doc.save('appreciations.pdf');
}
</script>
<script>
function showBulletinModal(html) {
const modalBody = document.getElementById('bulletinModalBody');
if (modalBody) {
modalBody.innerHTML = html;
const modal = new bootstrap.Modal(document.getElementById('bulletinModal'));
modal.show();
}
}
</script>
<!-- Modal Bulletin -->
<div class="modal fade" id="bulletinModal" tabindex="-1" aria-labelledby="bulletinModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" style="max-width:80vw; width:80vw;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="bulletinModalLabel">Bulletin</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fermer"></button>
</div>
<div class="modal-body" id="bulletinModalBody" style="font-size:1.1rem; line-height:1.5; word-break:break-word;"></div>
</div>
</div>
</div>
</div>
</div>
</div>

3
main/urls.py

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
from django.urls import path
from .views import login_view, generation, resultat_appreciations, logout_view, generer_appreciation_ajax, get_bulletin_eleve
from .views import login_view, generation, resultat_appreciations, logout_view, generer_appreciation_ajax
urlpatterns = [
path('login/', login_view, name='login'),
@ -7,5 +7,4 @@ urlpatterns = [ @@ -7,5 +7,4 @@ urlpatterns = [
path('resultat/', resultat_appreciations, name='resultat_appreciations'),
path('logout/', logout_view, name='logout'),
path('generer_appreciation_ajax/', generer_appreciation_ajax, name='generer_appreciation_ajax'),
path('get_bulletin_eleve/', get_bulletin_eleve, name='get_bulletin_eleve'),
]

20
main/views.py

@ -10,7 +10,6 @@ from main.models import UserCredit, Modele @@ -10,7 +10,6 @@ from main.models import UserCredit, Modele
import os
import uuid
import json
from django.views.decorators.csrf import csrf_exempt
def login_view(request):
if request.method == 'POST':
@ -77,25 +76,6 @@ def generer_appreciation_ajax(request): @@ -77,25 +76,6 @@ def generer_appreciation_ajax(request):
return JsonResponse({'appreciation': 'Crédit épuisé', 'credit': 0, 'stop': True})
return JsonResponse({'error': 'Méthode non autorisée'}, status=405)
@login_required(login_url='/login/')
def get_bulletin_eleve(request):
if request.method == 'POST':
data = json.loads(request.body)
eleve_nom = data.get('eleve')
appreciations_json = request.session.get('appreciations_json', [])
appreciations = []
for eleve in appreciations_json:
if eleve.get('eleve') == eleve_nom:
appreciations = eleve.get('appreciations', [])
if isinstance(appreciations, str):
try:
appreciations = json.loads(appreciations)
except Exception:
appreciations = []
break
return JsonResponse({'appreciations': appreciations})
return JsonResponse({'error': 'Méthode non autorisée'}, status=405)
def logout_view(request):
logout(request)
return redirect('login')

Loading…
Cancel
Save