6 changed files with 138 additions and 196 deletions
@ -1,129 +0,0 @@
@@ -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 |
||||
@ -1,38 +0,0 @@
@@ -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'); |
||||
} |
||||
Loading…
Reference in new issue