Browse Source

Modif CSS PDF

main
scayac 6 months ago
parent
commit
0c9b466d42
  1. 1
      README.md
  2. 0
      docker-compose.yml
  3. 129
      main/openaiApprecioations copy.py
  4. 38
      main/templates/export_pdf.js
  5. 90
      main/templates/generation.html
  6. 66
      main/templates/resultat_appreciations.html

1
README.md

@ -69,6 +69,7 @@ IAProf
5. Appliquez les migrations : 5. Appliquez les migrations :
``` ```
python manage.py makemigrations main
python manage.py migrate python manage.py migrate
``` ```

0
docker-compose.yml

129
main/openaiApprecioations copy.py

@ -1,129 +0,0 @@
from openai import OpenAI
from PyPDF2 import PdfReader
from django.conf import settings
liste_eleves = {}
client = OpenAI(api_key=settings.OPENAI_API_KEY)
def split_by_eleve(text):
"""
Découpe le texte en une liste de chaînes, chaque élément commençant par 'ELEVE'.
"""
import re
# On split sur chaque occurrence de 'ELEVE' suivie d'un ou plusieurs chiffres
parts = re.split(r'(ELEVE\d+)', text)
result = []
for i in range(1, len(parts), 2):
# parts[i] est 'ELEVEid', parts[i+1] est le texte associé
bloc = parts[i] + parts[i+1] if i+1 < len(parts) else parts[i]
result.append(bloc.strip())
return result
def anonymiserPdf(file, eleves):
eleve_id = 1
pages_content = ""
for page in file.pages:
lines = page.extract_text().split('\n')
curent_page = []
write = False
eleve_found = False
for i, line in enumerate(lines):
# Attendre la ligne qui se termine par " Trimestre"
if not write:
if line.strip().endswith("Trimestre"):
write = True
continue
# Enregistrer la ligne suivante (nom de l'élève)
if write and not eleve_found:
eleve_nom = line.strip()
if eleve_nom not in eleves:
eleves[eleve_nom] = f"ELEVE{eleve_id}"
eleve_id += 1
curent_page.append(eleves[eleve_nom] + "\n")
eleve_found = True
continue
# Attendre la ligne qui commence par "Appréciations"
if eleve_found:
if line.strip().startswith("Appréciations"):
# Commencer à écrire les lignes suivantes
for l in lines[i+1:]:
if l.startswith("M.") or l.startswith("Mme"):
continue
curent_page.append(l + "\n")
break # Fin du traitement de cette page
pages_content += "".join(curent_page)
return pages_content
def get_eleve_name(text, eleves):
"""
Extrait le nom de l'élève à partir du texte.
"""
for line in text.split('\n'):
line = line.strip()
if line in eleves:
return eleves[line]
return None
def replace_eleve_ids_with_names(text, eleves):
# Inverse the eleves dict to map ELEVEid -> nom prénom
id_to_name = {v: k for k, v in eleves.items()}
# Trier les IDs par longueur décroissante pour éviter les remplacements partiels
for eleve_id in sorted(id_to_name.keys(), key=len, reverse=True):
nom = id_to_name[eleve_id]
if eleve_id in text:
text = text.replace(eleve_id, nom)
return text
def generatationAppreciations(appreciations_path, modele_path=None):
if (modele_path):
modele = PdfReader(modele_path)
modele_anonyme = anonymiserPdf(modele, liste_eleves)
appreciations = PdfReader(appreciations_path)
appreciation_anonyme = anonymiserPdf(appreciations, liste_eleves)
tableau_appreciations = split_by_eleve(appreciation_anonyme)
responses = []
#debug
tableau_appreciations = tableau_appreciations[:3]
for eleve in tableau_appreciations:
if modele_path:
# Si un modèle est fourni, on utilise le modèle pour chaque élève
data = [
{
"role": "developer",
"content": "Tu dois générer des appréciations générales de bulletins avec le style de l'exemple suivant (500 caractères max)"
},{
"role": "assistant",
"content": modele_anonyme
},
{
"role": "user",
"content": eleve
}]
else:
data = [
{
"role": "developer",
"content": "Tu dois générer des appréciations générales de bulletins (500 caractères max)"
},
{
"role": "user",
"content": eleve
}]
completion = client.chat.completions.create(
model="gpt-4.1-mini",
messages=data,
temperature=1,
top_p=1)
resultat = "<b><u><i>" + get_eleve_name(completion.choices[0].message.content, liste_eleves) + "</i></u></b>\n"
resultat += replace_eleve_ids_with_names(completion.choices[0].message.content, liste_eleves)
responses.append(resultat)
return responses

38
main/templates/export_pdf.js

@ -1,38 +0,0 @@
// CDN jsPDF + autoTable pour export PDF
document.addEventListener('DOMContentLoaded', function() {
if (!window.jspdfLoaded) {
const script1 = document.createElement('script');
script1.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
script1.onload = function() {
const script2 = document.createElement('script');
script2.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js';
script2.onload = function() { window.jspdfLoaded = true; };
document.body.appendChild(script2);
};
document.body.appendChild(script1);
}
});
function exportTableToPDF() {
if (!window.jspdfLoaded) {
alert('Les librairies PDF ne sont pas encore chargées. Veuillez réessayer dans quelques secondes.');
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
doc.text('Tableau des appréciations', 14, 14);
const table = document.getElementById('appreciations-table');
const rows = Array.from(table.querySelectorAll('tbody tr'))
.map(tr => [
tr.querySelector('.eleve')?.textContent.trim() || '',
tr.querySelector('.appreciation')?.textContent.trim() || ''
]);
doc.autoTable({
head: [['Élève', 'Appréciation']],
body: rows,
startY: 20,
styles: { fontSize: 10, cellWidth: 'wrap' },
headStyles: { fillColor: [55, 90, 127] }
});
doc.save('appreciations.pdf');
}

90
main/templates/generation.html

@ -60,12 +60,78 @@
[class*="bg-"] { [class*="bg-"] {
background-color: inherit !important; background-color: inherit !important;
} }
/* Forcer le texte noir sur fond blanc pour le modal */ #processingModal .modal-content, #processingModal .modal-header, #processingModal .modal-body, #processingModal p, #processingModal .modal-body * {
#processingModal .modal-content, #processingModal .modal-header, #processingModal .modal-body, #processingModal p { background-color: #fff !important;
color: #212529 !important;
}
@media (prefers-color-scheme: dark) {
#processingModal .modal-content, #processingModal .modal-header, #processingModal .modal-body, #processingModal p, #processingModal .modal-body * {
background-color: #fff !important;
color: #212529 !important;
}
}
#btn-aide {
background-color: #ffe082 !important;
color: #23272b !important;
border: 1px solid #444c56 !important;
font-weight: 600;
}
@media (prefers-color-scheme: dark) {
#btn-aide {
background-color: #ffe082 !important;
color: #23272b !important;
border: 1px solid #444c56 !important;
font-weight: 600;
}
}
}
#aideModal .modal-content, #aideModal .modal-header, #aideModal .modal-body, #aideModal p, #aideModal .modal-body * {
background-color: #fff !important;
color: #212529 !important;
}
@media (prefers-color-scheme: dark) {
#aideModal .modal-content, #aideModal .modal-header, #aideModal .modal-body, #aideModal p, #aideModal .modal-body * {
background-color: #fff !important; background-color: #fff !important;
color: #212529 !important; color: #212529 !important;
} }
} }
html, body {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
body {
min-height: 100vh;
overflow: hidden;
}
#layoutSidenav {
min-height: 0;
height: 100%;
overflow: hidden;
}
#layoutSidenav_content {
min-height: 0;
height: 100%;
overflow: hidden;
}
main {
min-height: 0;
height: 100%;
overflow: hidden;
}
.container-fluid {
min-height: 0;
overflow: hidden;
}
.card.shadow.mb-4 {
min-height: 0;
overflow: hidden;
}
.card-body {
min-height: 0;
overflow: hidden;
}
</style> </style>
</head> </head>
@ -83,8 +149,25 @@
<main> <main>
<div class="container-fluid px-4 mt-4"> <div class="container-fluid px-4 mt-4">
<div class="card shadow mb-4"> <div class="card shadow mb-4">
<div class="card-header py-3"> <div class="card-header py-3 d-flex justify-content-between align-items-center">
<h2 class="m-0 font-weight-bold text-primary">Choix du fichier de bulletins à traiter :</h2> <h2 class="m-0 font-weight-bold text-primary">Choix du fichier de bulletins à traiter :</h2>
<button id="btn-aide" type="button" class="btn btn-info btn-sm" data-bs-toggle="modal" data-bs-target="#aideModal">Aide</button>
</div>
<div class="modal fade" id="aideModal" tabindex="-1" aria-labelledby="aideModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="aideModalLabel">Aide : Exporter un PDF Pronote</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fermer"></button>
</div>
<div class="modal-body">
Dans Pronote, aller dans <b>Bulletins &gt; Bulletin</b>, choisir la classe et appuyer en haut à droite sur <b>PDF</b>, puis sur l'engrenage en sélectionnant <b>"Bulletins élèves de toute la classe"</b>.<br>Valider puis appuyer sur <b>Voir le PDF</b>.<br>Enregistrer ce PDF pour le mettre dans l'application IAProf.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
</div>
</div>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
{% if success %} {% if success %}
@ -132,5 +215,4 @@
}); });
</script> </script>
</body> </body>
</html> </html>

66
main/templates/resultat_appreciations.html

@ -70,7 +70,7 @@ body, .bg-light {
border: 1px solid #444c56 !important; border: 1px solid #444c56 !important;
} }
} }
</style> </style>
</head> </head>
<body class="bg-light"> <body class="bg-light">
<nav class="navbar navbar-expand navbar-dark bg-dark"> <nav class="navbar navbar-expand navbar-dark bg-dark">
@ -108,7 +108,7 @@ body, .bg-light {
</div> </div>
<div class="mb-3 d-flex gap-2"> <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="generation-toggle" class="btn btn-primary mb-3">Lancer la génération</button>
<button id="export-pdf" class="btn btn-secondary mb-3" type="button">Exporter en PDF</button> <button id="export-pdf" class="btn btn-secondary mb-3" type="button" onclick="exportTableToPDF()">Exporter en PDF</button>
</div> </div>
<div id="credit-alert" class="alert alert-danger d-none" role="alert"> <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. Vous n'avez plus de crédits pour générer des appréciations.
@ -228,19 +228,30 @@ document.addEventListener('DOMContentLoaded', function() {
setButtonState('lancer'); setButtonState('lancer');
}); });
</script> </script>
</div>
</div>
</div>
</main>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js"></script>
<script> <script>
function exportTableToPDF() { // CDN jsPDF + autoTable pour export PDF
const doc = new window.jspdf.jsPDF(); document.addEventListener('DOMContentLoaded', function() {
doc.text('Tableau des appréciations', 14, 14); if (!window.jspdfLoaded) {
const script1 = document.createElement('script');
script1.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
script1.onload = function() {
const script2 = document.createElement('script');
script2.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js';
script2.onload = function() { window.jspdfLoaded = true; };
document.body.appendChild(script2);
};
document.body.appendChild(script1);
}
});
function exportTableToPDF() {
const doc = new window.jspdf.jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' });
doc.text('Tableau des appréciations', 40, 40);
// Couleurs fixes mode clair
const headColor = [55, 90, 127];
const textColor = [33, 37, 41];
const bgColor = [255, 255, 255];
const borderColor = [200, 200, 200];
const table = document.getElementById('appreciations-table'); const table = document.getElementById('appreciations-table');
const rows = Array.from(table.querySelectorAll('tbody tr')) const rows = Array.from(table.querySelectorAll('tbody tr'))
.map(tr => [ .map(tr => [
@ -250,13 +261,28 @@ document.addEventListener('DOMContentLoaded', function() {
doc.autoTable({ doc.autoTable({
head: [['Élève', 'Appréciation']], head: [['Élève', 'Appréciation']],
body: rows, body: rows,
startY: 20, startY: 60,
styles: { fontSize: 10, cellWidth: 'wrap' }, margin: { left: 30, right: 30 },
headStyles: { fillColor: [55, 90, 127] } styles: { fontSize: 11, overflow: 'linebreak', cellWidth: 'auto', textColor: textColor, fillColor: bgColor, lineColor: borderColor },
headStyles: { fillColor: headColor, textColor: textColor, lineColor: borderColor },
bodyStyles: { fillColor: bgColor, textColor: textColor, lineColor: borderColor },
alternateRowStyles: { fillColor: [245, 245, 245] },
columnStyles: {
0: { cellWidth: 'auto'},
1: { cellWidth: 'auto' }
},
tableWidth: 'auto',
pageBreak: 'auto',
}); });
doc.save('appreciations.pdf'); doc.save('appreciations.pdf');
} }
document.getElementById('export-pdf').addEventListener('click', exportTableToPDF); </script>
</script> </div>
</div>
</div>
</main>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body> </body>
</html> </html>

Loading…
Cancel
Save