from django.http.response import JsonResponse from rest_framework.views import APIView from rest_framework import status from drf_yasg.utils import swagger_auto_schema 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 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__) @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') 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: student = Student.objects.get(id=student_id) except Student.DoesNotExist: return JsonResponse({'error': 'Élève introuvable'}, status=404) # 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', 'establishment_competency__competency__category__domain', 'establishment_competency__custom_category', 'establishment_competency__custom_category__domain', ) result = [] total_competencies = 0 domaines = Domain.objects.all() for domaine in domaines: domaine_dict = { "domaine_id": domaine.id, "domaine_nom": domaine.name, "categories": [] } categories = domaine.categories.all() for categorie in categories: categorie_dict = { "categorie_id": categorie.id, "categorie_nom": categorie.name, "competences": [] } # On ne boucle que sur les compétences du student pour cette catégorie for sc in student_competencies: ec = sc.establishment_competency # Cas compétence de référence if ec.competency and ec.competency.category_id == categorie.id: comp = ec.competency categorie_dict["competences"].append({ "competence_id": ec.id, # <-- retourne l'id de l'EstablishmentCompetency "nom": comp.name, "score": sc.score, "comment": sc.comment or "", "period":sc.period or "" }) total_competencies += 1 # Cas compétence custom elif ec.competency is None and ec.custom_category_id == categorie.id: categorie_dict["competences"].append({ "competence_id": ec.id, # <-- retourne l'id de l'EstablishmentCompetency "nom": ec.custom_name, "score": sc.score, "comment": sc.comment or "", "period":sc.period or "" }) total_competencies += 1 if categorie_dict["competences"]: domaine_dict["categories"].append(categorie_dict) if domaine_dict["categories"]: result.append(domaine_dict) return JsonResponse({ "count": total_competencies, "data": result }, safe=False, status=200) def put(self, request): """ Met à jour en masse les notes des compétences d'un élève. Attend une liste d'objets {"competenceId": ..., "grade": ...} """ data = request.data if not isinstance(data, list): return JsonResponse({"error": "Une liste est attendue."}, status=400) updated = [] errors = [] updated_competency_ids = set() for item in data: 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 try: sc = StudentCompetency.objects.get( establishment_competency_id=comp_id, student_id=student_id, period=period ) sc.score = grade sc.save() updated.append(comp_id) updated_competency_ids.add(sc.id) except StudentCompetency.DoesNotExist: errors.append({"competenceId": comp_id, "error": "not found"}) # Génération du PDF si au moins une compétence a été mise à jour if updated: student = Student.objects.get(id=student_id) # On ne prend que les StudentCompetency mis à jour pour la génération du PDF student_competencies = StudentCompetency.objects.filter( student=student, period=period, id__in=updated_competency_ids ).select_related( 'establishment_competency', 'establishment_competency__competency', 'establishment_competency__competency__category', 'establishment_competency__competency__category__domain', 'establishment_competency__custom_category', 'establishment_competency__custom_category__domain', ) result = [] domaines = Domain.objects.all() for domaine in domaines: domaine_dict = { "nom": domaine.name, "categories": [] } categories = domaine.categories.all() for categorie in categories: categorie_dict = { "nom": categorie.name, "competences": [] } for sc in student_competencies: ec = sc.establishment_competency if ec.competency and ec.competency.category_id == categorie.id: comp = ec.competency categorie_dict["competences"].append({ "nom": comp.name, "score": sc.score, "comment": sc.comment or "", }) elif ec.competency is None and ec.custom_category_id == categorie.id: categorie_dict["competences"].append({ "nom": ec.custom_name, "score": sc.score, "comment": sc.comment or "", }) if categorie_dict["competences"]: domaine_dict["categories"].append(categorie_dict) if domaine_dict["categories"]: result.append(domaine_dict) context = { "student": { "first_name": student.first_name, "last_name": student.last_name, "level": student.level, "class_name": student.associated_class.atmosphere_name, }, "period": period, "date": date.today().strftime("%d/%m/%Y"), "domaines": result, } pdf_result = render_to_pdf('pdfs/bilan_competences.html', context) try: filename = f"bilan_competences_{student.last_name}_{student.first_name}_{period}.pdf" for existing_bilan in BilanCompetence.objects.filter(student=student, period=period): 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 return JsonResponse({"updated": updated, "errors": errors}, status=200) @method_decorator(csrf_protect, name='dispatch') @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)