feat: Rattachement d'un dossier de compétences à une période scolaire

(configuration dans l'établissement) [#16]
This commit is contained in:
N3WT DE COMPET
2025-05-22 01:25:34 +02:00
parent 0fe6c76189
commit 7de839ee5c
18 changed files with 450 additions and 274 deletions

View File

@ -6,13 +6,14 @@ from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
from django.utils.decorators import method_decorator
from Subscriptions.models import StudentCompetency, Student
from Common.models import Domain
from django.conf import settings
from Subscriptions.models import BilanCompetence
from datetime import date
from N3wtSchool.renderers import render_to_pdf
from django.core.files import File
from io import BytesIO
import os
import logging
from django.conf import settings
logger = logging.getLogger(__name__)
@ -21,6 +22,7 @@ logger = logging.getLogger(__name__)
class StudentCompetencyListCreateView(APIView):
def get(self, request):
student_id = request.GET.get('student_id')
period = request.GET.get('period')
if not student_id:
return JsonResponse({'error': 'student_id requis'}, status=400)
try:
@ -28,7 +30,12 @@ class StudentCompetencyListCreateView(APIView):
except Student.DoesNotExist:
return JsonResponse({'error': 'Élève introuvable'}, status=404)
student_competencies = StudentCompetency.objects.filter(student=student).select_related(
# Filtrer par student ET period si period est fourni
filter_kwargs = {'student': student}
if period:
filter_kwargs['period'] = period
student_competencies = StudentCompetency.objects.filter(**filter_kwargs).select_related(
'establishment_competency',
'establishment_competency__competency',
'establishment_competency__competency__category',
@ -64,6 +71,7 @@ class StudentCompetencyListCreateView(APIView):
"nom": comp.name,
"score": sc.score,
"comment": sc.comment or "",
"period":sc.period or ""
})
total_competencies += 1
# Cas compétence custom
@ -73,6 +81,7 @@ class StudentCompetencyListCreateView(APIView):
"nom": ec.custom_name,
"score": sc.score,
"comment": sc.comment or "",
"period":sc.period or ""
})
total_competencies += 1
if categorie_dict["competences"]:
@ -99,6 +108,7 @@ class StudentCompetencyListCreateView(APIView):
comp_id = item.get("competenceId")
grade = item.get("grade")
student_id = item.get('studentId')
period = item.get('period')
if comp_id is None or grade is None:
errors.append({"competenceId": comp_id, "error": "champ manquant"})
continue
@ -106,7 +116,8 @@ class StudentCompetencyListCreateView(APIView):
# Ajoute le filtre student_id
sc = StudentCompetency.objects.get(
establishment_competency_id=comp_id,
student_id=student_id
student_id=student_id,
period=period
)
sc.score = grade
sc.save()
@ -117,8 +128,8 @@ class StudentCompetencyListCreateView(APIView):
# Génération du PDF si au moins une compétence a été mise à jour
if updated:
student = Student.objects.get(id=student_id)
# Reconstituer la structure "domaines" comme dans le GET
student_competencies = StudentCompetency.objects.filter(student=student).select_related(
# Reconstituer la structure "domaines" pour la période concernée uniquement
student_competencies = StudentCompetency.objects.filter(student=student, period=period).select_related(
'establishment_competency',
'establishment_competency__competency',
'establishment_competency__competency__category',
@ -158,7 +169,7 @@ class StudentCompetencyListCreateView(APIView):
domaine_dict["categories"].append(categorie_dict)
if domaine_dict["categories"]:
result.append(domaine_dict)
context = {
"student": {
"first_name": student.first_name,
@ -166,37 +177,38 @@ class StudentCompetencyListCreateView(APIView):
"level": student.level,
"class_name": student.associated_class.atmosphere_name,
},
"period": period,
"date": date.today().strftime("%d/%m/%Y"),
"domaines": result,
}
print('génération du PDF...')
pdf_result = render_to_pdf('pdfs/bilan_competences.html', context)
# Vérifier si un fichier bilan_form existe déjà et le supprimer
if student.bilan_form and student.bilan_form.name:
if os.path.isabs(student.bilan_form.path):
existing_file_path = student.bilan_form.path
else:
existing_file_path = os.path.join(settings.MEDIA_ROOT, student.bilan_form.name.lstrip('/'))
if os.path.exists(existing_file_path):
os.remove(existing_file_path)
student.bilan_form.delete(save=False)
logger.info(f"Ancien PDF supprimé : {existing_file_path}")
else:
logger.info(f"File does not exist: {existing_file_path}")
try:
filename = f"bilan_competences_{student.last_name}_{student.first_name}.pdf"
student.bilan_form.save(
os.path.basename(filename), # Utiliser uniquement le nom de fichier
filename = f"bilan_competences_{student.last_name}_{student.first_name}_{period}.pdf"
# Vérifier si un bilan existe déjà pour cet élève et cette période
existing_bilan = BilanCompetence.objects.filter(student=student, period=period).first()
if existing_bilan:
# Supprimer le fichier physique si présent
if existing_bilan.file and existing_bilan.file.name:
file_path = existing_bilan.file.path
if os.path.exists(file_path):
os.remove(file_path)
existing_bilan.delete()
bilan = BilanCompetence.objects.create(
student=student,
period=period
)
bilan.file.save(
os.path.basename(filename),
File(BytesIO(pdf_result.content)),
save=True
)
except Exception as e:
logger.error(f"Erreur lors de la sauvegarde du fichier PDF : {e}")
raise
# Exemple : retour du PDF dans la réponse HTTP (pour test)
return JsonResponse({"updated": updated, "errors": errors}, status=200)
@ -204,18 +216,4 @@ class StudentCompetencyListCreateView(APIView):
@method_decorator(ensure_csrf_cookie, name='dispatch')
class StudentCompetencySimpleView(APIView):
def get(self, request, id):
return JsonResponse("ok", safe=False, status=status.HTTP_200_OK)
# def put(self, request, id):
# try:
# absence = AbsenceManagement.objects.get(id=id)
# serializer = AbsenceManagementSerializer(absence, data=request.data)
# if serializer.is_valid():
# serializer.save()
# return JsonResponse(serializer.data, safe=False, status=status.HTTP_200_OK)
# return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
# except AbsenceManagement.DoesNotExist:
# return JsonResponse({"error": "Absence not found"}, safe=False, status=status.HTTP_404_NOT_FOUND)
# def delete(self, request, id):
# return delete_object(AbsenceManagement, id)
return JsonResponse("ok", safe=False, status=status.HTTP_200_OK)