refactor: Utilisation d'une application "Common" pour tous les modèles

de référence
This commit is contained in:
N3WT DE COMPET
2025-05-18 15:42:21 +02:00
parent 7fe53465ac
commit e65e31014d
24 changed files with 491 additions and 277 deletions

View File

@ -4,3 +4,6 @@ from django.apps import AppConfig
class CommonConfig(AppConfig): class CommonConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'Common' name = 'Common'
def ready(self):
import Common.signals

View File

@ -1,13 +1,59 @@
from django.db import models from django.db import models
class StudentCompetency(models.Model): class Domain(models.Model):
student = models.ForeignKey('Subscriptions.Student', on_delete=models.CASCADE, related_name='competency_scores') name = models.CharField(max_length=255)
competency = models.ForeignKey('School.Competency', on_delete=models.CASCADE, related_name='student_scores') cycle = models.IntegerField(choices=[(1, 'Cycle 1'), (2, 'Cycle 2'), (3, 'Cycle 3'), (4, 'Cycle 4')])
score = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
comment = models.TextField(blank=True, null=True)
class Meta:
unique_together = ('student', 'competency')
def __str__(self): def __str__(self):
return f"{self.student} - {self.competency.name} - Score: {self.score}" return f"{self.name} (Cycle {self.cycle})"
class Category(models.Model):
name = models.CharField(max_length=255)
domain = models.ForeignKey(Domain, on_delete=models.CASCADE, related_name='categories')
def __str__(self):
return self.name
class Competency(models.Model):
name = models.TextField()
end_of_cycle = models.BooleanField(default=False, null=True, blank=True)
level = models.CharField(max_length=50, null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='competencies')
establishments = models.ManyToManyField(
'Establishment.Establishment',
through='School.EstablishmentCompetency',
related_name='competencies',
blank=True
)
def __str__(self):
return self.name
class PaymentPlanType(models.Model):
code = models.CharField(max_length=50, unique=True)
label = models.CharField(max_length=255)
def __str__(self):
return self.label
class PaymentModeType(models.Model):
code = models.CharField(max_length=50, unique=True)
label = models.CharField(max_length=255)
def __str__(self):
return self.label
class Cycle(models.Model):
number = models.IntegerField(unique=True)
label = models.CharField(max_length=50)
def __str__(self):
return f"Cycle {self.number} - {self.label}"
class Level(models.Model):
name = models.CharField(max_length=50)
cycle = models.ForeignKey(Cycle, on_delete=models.CASCADE, related_name='levels')
def __str__(self):
return self.name

View File

@ -0,0 +1,21 @@
from rest_framework import serializers
from Common.models import (
Domain,
Category,
Competency
)
class DomainSerializer(serializers.ModelSerializer):
class Meta:
model = Domain
fields = '__all__'
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
class CompetencySerializer(serializers.ModelSerializer):
class Meta:
model = Competency
fields = '__all__'

View File

@ -2,10 +2,10 @@ import json
import os import os
from django.db.models.signals import post_migrate from django.db.models.signals import post_migrate
from django.dispatch import receiver from django.dispatch import receiver
from School.models import Domain, Category, Competency, PaymentModeType, PaymentPlanType from Common.models import Domain, Category, Competency, PaymentModeType, PaymentPlanType, Cycle, Level
@receiver(post_migrate) @receiver(post_migrate)
def school_post_migrate(sender, **kwargs): def common_post_migrate(sender, **kwargs):
if sender.name != "School": if sender.name != "School":
return return
@ -57,7 +57,6 @@ def school_post_migrate(sender, **kwargs):
for mode in payment_mode_types: for mode in payment_mode_types:
PaymentModeType.objects.get_or_create(code=mode["code"], defaults={"label": mode["label"]}) PaymentModeType.objects.get_or_create(code=mode["code"], defaults={"label": mode["label"]})
# ... après la création des PaymentModeType ...
payment_plan_types = [ payment_plan_types = [
{"code": "ONE_TIME", "label": "1 fois"}, {"code": "ONE_TIME", "label": "1 fois"},
{"code": "THREE_TIMES", "label": "3 fois"}, {"code": "THREE_TIMES", "label": "3 fois"},
@ -65,4 +64,34 @@ def school_post_migrate(sender, **kwargs):
{"code": "TWELVE_TIMES", "label": "12 fois"}, {"code": "TWELVE_TIMES", "label": "12 fois"},
] ]
for plan in payment_plan_types: for plan in payment_plan_types:
PaymentPlanType.objects.get_or_create(code=plan["code"], defaults={"label": plan["label"]}) PaymentPlanType.objects.get_or_create(code=plan["code"], defaults={"label": plan["label"]})
# Création des cycles
cycles_data = [
{"number": 1, "label": "Cycle 1"},
{"number": 2, "label": "Cycle 2"},
{"number": 3, "label": "Cycle 3"},
{"number": 4, "label": "Cycle 4"},
]
cycle_objs = {}
for cycle in cycles_data:
obj, _ = Cycle.objects.get_or_create(number=cycle["number"], defaults={"label": cycle["label"]})
cycle_objs[cycle["number"]] = obj
# Création des niveaux et association au cycle
levels_data = [
{"name": "TPS", "cycle": 1},
{"name": "PS", "cycle": 1},
{"name": "MS", "cycle": 1},
{"name": "GS", "cycle": 1},
{"name": "CP", "cycle": 2},
{"name": "CE1", "cycle": 2},
{"name": "CE2", "cycle": 2},
{"name": "CM1", "cycle": 3},
{"name": "CM2", "cycle": 3},
]
for level in levels_data:
Level.objects.get_or_create(
name=level["name"],
defaults={"cycle": cycle_objs[level["cycle"]]}
)

18
Back-End/Common/urls.py Normal file
View File

@ -0,0 +1,18 @@
from django.urls import path, re_path
from .views import (
DomainListCreateView, DomainDetailView,
CategoryListCreateView, CategoryDetailView,
CompetencyListCreateView, CompetencyDetailView,
)
urlpatterns = [
re_path(r'^domains$', DomainListCreateView.as_view(), name="domain_list_create"),
re_path(r'^domains/(?P<id>[0-9]+)$', DomainDetailView.as_view(), name="domain_detail"),
re_path(r'^categories$', CategoryListCreateView.as_view(), name="category_list_create"),
re_path(r'^categories/(?P<id>[0-9]+)$', CategoryDetailView.as_view(), name="category_detail"),
re_path(r'^competencies$', CompetencyListCreateView.as_view(), name="competency_list_create"),
re_path(r'^competencies/(?P<id>[0-9]+)$', CompetencyDetailView.as_view(), name="competency_detail"),
]

View File

@ -1,3 +1,165 @@
from django.shortcuts import render from django.http.response import JsonResponse
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
from django.utils.decorators import method_decorator
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from .models import (
Domain,
Category,
Competency
)
from .serializers import (
DomainSerializer,
CategorySerializer,
CompetencySerializer
)
# Create your views here. @method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class DomainListCreateView(APIView):
def get(self, request):
domains = Domain.objects.all()
serializer = DomainSerializer(domains, many=True)
return JsonResponse(serializer.data, safe=False)
def post(self, request):
data = JSONParser().parse(request)
serializer = DomainSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class DomainDetailView(APIView):
def get(self, request, id):
try:
domain = Domain.objects.get(id=id)
serializer = DomainSerializer(domain)
return JsonResponse(serializer.data, safe=False)
except Domain.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
def put(self, request, id):
try:
domain = Domain.objects.get(id=id)
except Domain.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
data = JSONParser().parse(request)
serializer = DomainSerializer(domain, data=data, partial=True)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id):
try:
domain = Domain.objects.get(id=id)
domain.delete()
return JsonResponse({'message': 'Deleted'}, safe=False)
except Domain.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
# Répète la même logique pour Category, Competency, EstablishmentCompetency
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class CategoryListCreateView(APIView):
def get(self, request):
categories = Category.objects.all()
serializer = CategorySerializer(categories, many=True)
return JsonResponse(serializer.data, safe=False)
def post(self, request):
data = JSONParser().parse(request)
serializer = CategorySerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class CategoryDetailView(APIView):
def get(self, request, id):
try:
category = Category.objects.get(id=id)
serializer = CategorySerializer(category)
return JsonResponse(serializer.data, safe=False)
except Category.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
def put(self, request, id):
try:
category = Category.objects.get(id=id)
except Category.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
data = JSONParser().parse(request)
serializer = CategorySerializer(category, data=data, partial=True)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id):
try:
category = Category.objects.get(id=id)
category.delete()
return JsonResponse({'message': 'Deleted'}, safe=False)
except Category.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class CompetencyListCreateView(APIView):
def get(self, request):
cycle = request.GET.get('cycle')
if cycle is None:
return JsonResponse({'error': 'cycle est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
competencies_list = getAllObjects(Competency)
competencies_list = competencies_list.filter(
category__domain__cycle=cycle
).distinct()
serializer = CompetencySerializer(competencies_list, many=True)
return JsonResponse(serializer.data, safe=False)
def post(self, request):
data = JSONParser().parse(request)
serializer = CompetencySerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class CompetencyDetailView(APIView):
def get(self, request, id):
try:
competency = Competency.objects.get(id=id)
serializer = CompetencySerializer(competency)
return JsonResponse(serializer.data, safe=False)
except Competency.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
def put(self, request, id):
try:
competency = Competency.objects.get(id=id)
except Competency.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
data = JSONParser().parse(request)
serializer = CompetencySerializer(competency, data=data, partial=True)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id):
try:
competency = Competency.objects.get(id=id)
competency.delete()
return JsonResponse({'message': 'Deleted'}, safe=False)
except Competency.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)

View File

@ -7,7 +7,8 @@ from rest_framework import status
from .models import Establishment from .models import Establishment
from .serializers import EstablishmentSerializer from .serializers import EstablishmentSerializer
from N3wtSchool.bdd import delete_object, getAllObjects from N3wtSchool.bdd import delete_object, getAllObjects
from School.models import Competency, EstablishmentCompetency from School.models import EstablishmentCompetency
from Common.models import Competency
from django.db.models import Q from django.db.models import Q
@method_decorator(csrf_protect, name='dispatch') @method_decorator(csrf_protect, name='dispatch')

View File

@ -42,6 +42,7 @@ ALLOWED_HOSTS = ['*']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'Common.apps.CommonConfig',
'Subscriptions.apps.GestioninscriptionsConfig', 'Subscriptions.apps.GestioninscriptionsConfig',
'Auth.apps.GestionloginConfig', 'Auth.apps.GestionloginConfig',
'GestionMessagerie.apps.GestionMessagerieConfig', 'GestionMessagerie.apps.GestionMessagerieConfig',

View File

@ -39,6 +39,7 @@ schema_view = get_schema_view(
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path("Common/", include(("Common.urls", 'Common'), namespace='Common')),
path("Subscriptions/", include(("Subscriptions.urls", 'Subscriptions'), namespace='Subscriptions')), path("Subscriptions/", include(("Subscriptions.urls", 'Subscriptions'), namespace='Subscriptions')),
path("Auth/", include(("Auth.urls", 'Auth'), namespace='Auth')), path("Auth/", include(("Auth.urls", 'Auth'), namespace='Auth')),
path("GestionMessagerie/", include(("GestionMessagerie.urls", 'GestionMessagerie'), namespace='GestionMessagerie')), path("GestionMessagerie/", include(("GestionMessagerie.urls", 'GestionMessagerie'), namespace='GestionMessagerie')),

View File

@ -2,7 +2,4 @@ from django.apps import AppConfig
class SchoolConfig(AppConfig): class SchoolConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'School' name = 'School'
def ready(self):
import School.signals

View File

@ -4,12 +4,15 @@ from Subscriptions.models import (
RegistrationFileGroup RegistrationFileGroup
) )
from Auth.models import Profile, ProfileRole from Auth.models import Profile, ProfileRole
from Common.models import (
PaymentModeType,
PaymentPlanType,
Level
)
from School.models import ( from School.models import (
FeeType, FeeType,
Speciality, Speciality,
Teacher, Teacher,
PaymentModeType,
PaymentPlanType,
DiscountType, DiscountType,
Fee, Fee,
Discount, Discount,
@ -280,6 +283,7 @@ class Command(BaseCommand):
def init_school_classes(self): def init_school_classes(self):
school_classes_data = self.load_data('school_classes.json') school_classes_data = self.load_data('school_classes.json')
levels = list(Level.objects.all())
for index, class_data in enumerate(school_classes_data, start=1): for index, class_data in enumerate(school_classes_data, start=1):
# Randomize establishment # Randomize establishment
@ -288,7 +292,7 @@ class Command(BaseCommand):
class_data["establishment"] = establishment.id class_data["establishment"] = establishment.id
# Randomize levels # Randomize levels
class_data["levels"] = random.sample(range(1, 10), random.randint(1, 5)) class_data["levels"] = [level.id for level in random.sample(levels, random.randint(1, min(5, len(levels))))]
# Randomize teachers # Randomize teachers
establishment_teachers = list(Teacher.objects.filter(profile_role__establishment=establishment)) establishment_teachers = list(Teacher.objects.filter(profile_role__establishment=establishment))
@ -331,6 +335,7 @@ class Command(BaseCommand):
fake = Faker('fr_FR') # Utiliser le locale français pour Faker fake = Faker('fr_FR') # Utiliser le locale français pour Faker
file_group_count = RegistrationFileGroup.objects.count() file_group_count = RegistrationFileGroup.objects.count()
levels = list(Level.objects.all())
# Récupérer tous les profils existants avec un ProfileRole Parent # Récupérer tous les profils existants avec un ProfileRole Parent
profiles_with_parent_role = Profile.objects.filter(roles__role_type=ProfileRole.RoleType.PROFIL_PARENT).distinct() profiles_with_parent_role = Profile.objects.filter(roles__role_type=ProfileRole.RoleType.PROFIL_PARENT).distinct()
@ -376,7 +381,7 @@ class Command(BaseCommand):
"birth_postal_code": fake.postcode(), "birth_postal_code": fake.postcode(),
"nationality": fake.country(), "nationality": fake.country(),
"attending_physician": fake.name(), "attending_physician": fake.name(),
"level": fake.random_int(min=1, max=6), "level": random.choice(levels).id,
"guardians": [guardian_data], "guardians": [guardian_data],
"sibling": [] "sibling": []
} }

View File

@ -50,7 +50,7 @@ class SchoolClass(models.Model):
school_year = models.CharField(max_length=9, blank=True) school_year = models.CharField(max_length=9, blank=True)
updated_date = models.DateTimeField(auto_now=True) updated_date = models.DateTimeField(auto_now=True)
teachers = models.ManyToManyField(Teacher, blank=True) teachers = models.ManyToManyField(Teacher, blank=True)
levels = ArrayField(models.IntegerField(choices=LEVEL_CHOICES), default=list) levels = models.ManyToManyField('Common.Level', blank=True, related_name='school_classes')
type = models.IntegerField(choices=PLANNING_TYPE_CHOICES, default=1) type = models.IntegerField(choices=PLANNING_TYPE_CHOICES, default=1)
time_range = models.JSONField(default=list) time_range = models.JSONField(default=list)
opening_days = ArrayField(models.IntegerField(), default=list) opening_days = ArrayField(models.IntegerField(), default=list)
@ -75,21 +75,6 @@ class FeeType(models.IntegerChoices):
REGISTRATION_FEE = 0, 'Registration Fee' REGISTRATION_FEE = 0, 'Registration Fee'
TUITION_FEE = 1, 'Tuition Fee' TUITION_FEE = 1, 'Tuition Fee'
class PaymentPlanType(models.Model):
code = models.CharField(max_length=50, unique=True)
label = models.CharField(max_length=255)
def __str__(self):
return self.label
class PaymentModeType(models.Model):
code = models.CharField(max_length=50, unique=True)
label = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.label
class Discount(models.Model): class Discount(models.Model):
name = models.CharField(max_length=255, null=True, blank=True) name = models.CharField(max_length=255, null=True, blank=True)
amount = models.DecimalField(max_digits=10, decimal_places=2, default=0) amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
@ -115,7 +100,7 @@ class Fee(models.Model):
return self.name return self.name
class PaymentPlan(models.Model): class PaymentPlan(models.Model):
plan_type = models.ForeignKey(PaymentPlanType, on_delete=models.PROTECT, related_name='payment_plans') plan_type = models.ForeignKey('Common.PaymentPlanType', on_delete=models.PROTECT, related_name='payment_plans')
due_dates = ArrayField(models.DateField(), null=True, blank=True) due_dates = ArrayField(models.DateField(), null=True, blank=True)
type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE)
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_plans') establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_plans')
@ -124,45 +109,13 @@ class PaymentPlan(models.Model):
return f"{self.plan_type.label} - {self.get_type_display()}" return f"{self.plan_type.label} - {self.get_type_display()}"
class PaymentMode(models.Model): class PaymentMode(models.Model):
mode = models.ForeignKey(PaymentModeType, on_delete=models.PROTECT, related_name='payment_modes') mode = models.ForeignKey('Common.PaymentModeType', on_delete=models.PROTECT, related_name='payment_modes')
type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE)
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_modes') establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_modes')
def __str__(self): def __str__(self):
return f"{self.mode.label} - {self.get_type_display()}" return f"{self.mode.label} - {self.get_type_display()}"
class Domain(models.Model):
name = models.CharField(max_length=255)
cycle = models.IntegerField(choices=[(1, 'Cycle 1'), (2, 'Cycle 2'), (3, 'Cycle 3'), (4, 'Cycle 4')])
def __str__(self):
return f"{self.name} (Cycle {self.cycle})"
class Category(models.Model):
name = models.CharField(max_length=255)
domain = models.ForeignKey(Domain, on_delete=models.CASCADE, related_name='categories')
def __str__(self):
return self.name
class Competency(models.Model):
name = models.TextField()
end_of_cycle = models.BooleanField(default=False, null=True, blank=True)
level = models.CharField(max_length=50, null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='competencies')
establishments = models.ManyToManyField(
'Establishment.Establishment',
through='EstablishmentCompetency',
related_name='competencies',
blank=True
)
def __str__(self):
return self.name
class EstablishmentCompetency(models.Model): class EstablishmentCompetency(models.Model):
""" """
Relation entre un établissement et une compétence. Relation entre un établissement et une compétence.
@ -170,12 +123,12 @@ class EstablishmentCompetency(models.Model):
""" """
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE) establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE)
competency = models.ForeignKey( competency = models.ForeignKey(
Competency, on_delete=models.CASCADE, null=True, blank=True, 'Common.Competency', on_delete=models.CASCADE, null=True, blank=True,
help_text="Compétence de référence (optionnelle si custom)" help_text="Compétence de référence (optionnelle si custom)"
) )
custom_name = models.TextField(null=True, blank=True, help_text="Nom de la compétence custom") custom_name = models.TextField(null=True, blank=True, help_text="Nom de la compétence custom")
custom_category = models.ForeignKey( custom_category = models.ForeignKey(
Category, on_delete=models.CASCADE, null=True, blank=True, 'Common.Category', on_delete=models.CASCADE, null=True, blank=True,
help_text="Catégorie de la compétence custom" help_text="Catégorie de la compétence custom"
) )
is_required = models.BooleanField(default=True) is_required = models.BooleanField(default=True)

View File

@ -9,9 +9,6 @@ from .models import (
Fee, Fee,
PaymentPlan, PaymentPlan,
PaymentMode, PaymentMode,
Domain,
Category,
Competency,
EstablishmentCompetency EstablishmentCompetency
) )
from Auth.models import Profile, ProfileRole from Auth.models import Profile, ProfileRole
@ -22,21 +19,6 @@ from N3wtSchool import settings
from django.utils import timezone from django.utils import timezone
import pytz import pytz
class DomainSerializer(serializers.ModelSerializer):
class Meta:
model = Domain
fields = '__all__'
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
class CompetencySerializer(serializers.ModelSerializer):
class Meta:
model = Competency
fields = '__all__'
class EstablishmentCompetencySerializer(serializers.ModelSerializer): class EstablishmentCompetencySerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = EstablishmentCompetency model = EstablishmentCompetency
@ -203,7 +185,6 @@ class SchoolClassSerializer(serializers.ModelSerializer):
number_of_students=validated_data.get('number_of_students', 0), number_of_students=validated_data.get('number_of_students', 0),
teaching_language=validated_data.get('teaching_language', ''), teaching_language=validated_data.get('teaching_language', ''),
school_year=validated_data.get('school_year', ''), school_year=validated_data.get('school_year', ''),
levels=levels_data,
type=validated_data.get('type', 1), type=validated_data.get('type', 1),
time_range=validated_data.get('time_range', ['08:30', '17:30']), time_range=validated_data.get('time_range', ['08:30', '17:30']),
opening_days=validated_data.get('opening_days', [1, 2, 4, 5]), opening_days=validated_data.get('opening_days', [1, 2, 4, 5]),
@ -211,6 +192,7 @@ class SchoolClassSerializer(serializers.ModelSerializer):
) )
school_class.teachers.set(teachers_data) school_class.teachers.set(teachers_data)
school_class.levels.set(levels_data)
for planning_data in plannings_data: for planning_data in plannings_data:
Planning.objects.create( Planning.objects.create(
@ -231,7 +213,6 @@ class SchoolClassSerializer(serializers.ModelSerializer):
instance.number_of_students = validated_data.get('number_of_students', instance.number_of_students) instance.number_of_students = validated_data.get('number_of_students', instance.number_of_students)
instance.teaching_language = validated_data.get('teaching_language', instance.teaching_language) instance.teaching_language = validated_data.get('teaching_language', instance.teaching_language)
instance.school_year = validated_data.get('school_year', instance.school_year) instance.school_year = validated_data.get('school_year', instance.school_year)
instance.levels = levels_data
instance.type = validated_data.get('type', instance.type) instance.type = validated_data.get('type', instance.type)
instance.time_range = validated_data.get('time_range', instance.time_range) instance.time_range = validated_data.get('time_range', instance.time_range)
instance.opening_days = validated_data.get('opening_days', instance.opening_days) instance.opening_days = validated_data.get('opening_days', instance.opening_days)
@ -239,6 +220,7 @@ class SchoolClassSerializer(serializers.ModelSerializer):
instance.save() instance.save()
instance.teachers.set(teachers_data) instance.teachers.set(teachers_data)
instance.levels.set(levels_data)
existing_plannings = {planning.level: planning for planning in instance.plannings.all()} existing_plannings = {planning.level: planning for planning in instance.plannings.all()}

View File

@ -9,9 +9,6 @@ from .views import (
DiscountListCreateView, DiscountDetailView, DiscountListCreateView, DiscountDetailView,
PaymentPlanListCreateView, PaymentPlanDetailView, PaymentPlanListCreateView, PaymentPlanDetailView,
PaymentModeListCreateView, PaymentModeDetailView, PaymentModeListCreateView, PaymentModeDetailView,
DomainListCreateView, DomainDetailView,
CategoryListCreateView, CategoryDetailView,
CompetencyListCreateView, CompetencyDetailView,
EstablishmentCompetencyListCreateView, EstablishmentCompetencyDetailView, EstablishmentCompetencyListCreateView, EstablishmentCompetencyDetailView,
) )
@ -40,15 +37,6 @@ urlpatterns = [
re_path(r'^paymentModes$', PaymentModeListCreateView.as_view(), name="payment_mode_list_create"), re_path(r'^paymentModes$', PaymentModeListCreateView.as_view(), name="payment_mode_list_create"),
re_path(r'^paymentModes/(?P<id>[0-9]+)$', PaymentModeDetailView.as_view(), name="payment_mode_detail"), re_path(r'^paymentModes/(?P<id>[0-9]+)$', PaymentModeDetailView.as_view(), name="payment_mode_detail"),
re_path(r'^domains$', DomainListCreateView.as_view(), name="domain_list_create"),
re_path(r'^domains/(?P<id>[0-9]+)$', DomainDetailView.as_view(), name="domain_detail"),
re_path(r'^categories$', CategoryListCreateView.as_view(), name="category_list_create"),
re_path(r'^categories/(?P<id>[0-9]+)$', CategoryDetailView.as_view(), name="category_detail"),
re_path(r'^competencies$', CompetencyListCreateView.as_view(), name="competency_list_create"),
re_path(r'^competencies/(?P<id>[0-9]+)$', CompetencyDetailView.as_view(), name="competency_detail"),
re_path(r'^establishmentCompetencies$', EstablishmentCompetencyListCreateView.as_view(), name="establishment_competency_list_create"), re_path(r'^establishmentCompetencies$', EstablishmentCompetencyListCreateView.as_view(), name="establishment_competency_list_create"),
re_path(r'^establishmentCompetencies/(?P<id>[0-9]+)$', EstablishmentCompetencyDetailView.as_view(), name="establishment_competency_detail"), re_path(r'^establishmentCompetencies/(?P<id>[0-9]+)$', EstablishmentCompetencyDetailView.as_view(), name="establishment_competency_detail"),
] ]

View File

@ -13,9 +13,6 @@ from .models import (
Fee, Fee,
PaymentPlan, PaymentPlan,
PaymentMode, PaymentMode,
Domain,
Category,
Competency,
EstablishmentCompetency EstablishmentCompetency
) )
from .serializers import ( from .serializers import (
@ -27,11 +24,9 @@ from .serializers import (
FeeSerializer, FeeSerializer,
PaymentPlanSerializer, PaymentPlanSerializer,
PaymentModeSerializer, PaymentModeSerializer,
DomainSerializer,
CategorySerializer,
CompetencySerializer,
EstablishmentCompetencySerializer EstablishmentCompetencySerializer
) )
from Common.models import Domain, Category, Competency
from N3wtSchool.bdd import delete_object, getAllObjects, getObject from N3wtSchool.bdd import delete_object, getAllObjects, getObject
from django.db.models import Q from django.db.models import Q
from collections import defaultdict from collections import defaultdict
@ -422,156 +417,6 @@ class PaymentModeDetailView(APIView):
def delete(self, request, id): def delete(self, request, id):
return delete_object(PaymentMode, id) return delete_object(PaymentMode, id)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class DomainListCreateView(APIView):
def get(self, request):
domains = Domain.objects.all()
serializer = DomainSerializer(domains, many=True)
return JsonResponse(serializer.data, safe=False)
def post(self, request):
data = JSONParser().parse(request)
serializer = DomainSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class DomainDetailView(APIView):
def get(self, request, id):
try:
domain = Domain.objects.get(id=id)
serializer = DomainSerializer(domain)
return JsonResponse(serializer.data, safe=False)
except Domain.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
def put(self, request, id):
try:
domain = Domain.objects.get(id=id)
except Domain.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
data = JSONParser().parse(request)
serializer = DomainSerializer(domain, data=data, partial=True)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id):
try:
domain = Domain.objects.get(id=id)
domain.delete()
return JsonResponse({'message': 'Deleted'}, safe=False)
except Domain.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
# Répète la même logique pour Category, Competency, EstablishmentCompetency
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class CategoryListCreateView(APIView):
def get(self, request):
categories = Category.objects.all()
serializer = CategorySerializer(categories, many=True)
return JsonResponse(serializer.data, safe=False)
def post(self, request):
data = JSONParser().parse(request)
serializer = CategorySerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class CategoryDetailView(APIView):
def get(self, request, id):
try:
category = Category.objects.get(id=id)
serializer = CategorySerializer(category)
return JsonResponse(serializer.data, safe=False)
except Category.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
def put(self, request, id):
try:
category = Category.objects.get(id=id)
except Category.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
data = JSONParser().parse(request)
serializer = CategorySerializer(category, data=data, partial=True)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id):
try:
category = Category.objects.get(id=id)
category.delete()
return JsonResponse({'message': 'Deleted'}, safe=False)
except Category.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class CompetencyListCreateView(APIView):
def get(self, request):
cycle = request.GET.get('cycle')
if cycle is None:
return JsonResponse({'error': 'cycle est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
competencies_list = getAllObjects(Competency)
competencies_list = competencies_list.filter(
category__domain__cycle=cycle
).distinct()
serializer = CompetencySerializer(competencies_list, many=True)
return JsonResponse(serializer.data, safe=False)
def post(self, request):
data = JSONParser().parse(request)
serializer = CompetencySerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class CompetencyDetailView(APIView):
def get(self, request, id):
try:
competency = Competency.objects.get(id=id)
serializer = CompetencySerializer(competency)
return JsonResponse(serializer.data, safe=False)
except Competency.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
def put(self, request, id):
try:
competency = Competency.objects.get(id=id)
except Competency.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
data = JSONParser().parse(request)
serializer = CompetencySerializer(competency, data=data, partial=True)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, safe=False)
return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id):
try:
competency = Competency.objects.get(id=id)
competency.delete()
return JsonResponse({'message': 'Deleted'}, safe=False)
except Competency.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
@method_decorator(csrf_protect, name='dispatch') @method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch')
class EstablishmentCompetencyListCreateView(APIView): class EstablishmentCompetencyListCreateView(APIView):

View File

@ -4,7 +4,6 @@ from django.conf import settings
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from datetime import datetime from datetime import datetime
from School.models import PaymentModeType, PaymentPlanType
import os import os
@ -85,7 +84,7 @@ class Student(models.Model):
last_name = models.CharField(max_length=200, default="") last_name = models.CharField(max_length=200, default="")
first_name = models.CharField(max_length=200, default="") first_name = models.CharField(max_length=200, default="")
gender = models.IntegerField(choices=StudentGender, default=StudentGender.NONE, blank=True) gender = models.IntegerField(choices=StudentGender, default=StudentGender.NONE, blank=True)
level = models.IntegerField(choices=StudentLevel, default=StudentLevel.NONE, blank=True) level = models.ForeignKey('Common.Level', on_delete=models.SET_NULL, null=True, blank=True, related_name='students')
nationality = models.CharField(max_length=200, default="", blank=True) nationality = models.CharField(max_length=200, default="", blank=True)
address = models.CharField(max_length=200, default="", blank=True) address = models.CharField(max_length=200, default="", blank=True)
birth_date = models.DateField(null=True, blank=True) birth_date = models.DateField(null=True, blank=True)
@ -247,10 +246,10 @@ class RegistrationForm(models.Model):
blank=True) blank=True)
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='register_forms') establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='register_forms')
registration_payment = models.ForeignKey('School.PaymentModeType', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_modes_forms') registration_payment = models.ForeignKey('School.PaymentMode', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_modes_forms')
tuition_payment = models.ForeignKey('School.PaymentModeType', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_modes_forms') tuition_payment = models.ForeignKey('School.PaymentMode', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_modes_forms')
registration_payment_plan = models.ForeignKey('School.PaymentPlanType', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_plans_forms') registration_payment_plan = models.ForeignKey('School.PaymentPlan', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_plans_forms')
tuition_payment_plan = models.ForeignKey('School.PaymentPlanType', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_plans_forms') tuition_payment_plan = models.ForeignKey('School.PaymentPlan', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_plans_forms')
def __str__(self): def __str__(self):
return "RF_" + self.student.last_name + "_" + self.student.first_name return "RF_" + self.student.last_name + "_" + self.student.first_name
@ -323,6 +322,18 @@ class RegistrationSchoolFileTemplate(models.Model):
filenames.append(reg_file.file.path) filenames.append(reg_file.file.path)
return filenames return filenames
class StudentCompetency(models.Model):
student = models.ForeignKey('Subscriptions.Student', on_delete=models.CASCADE, related_name='competency_scores')
competency = models.ForeignKey('Common.Competency', on_delete=models.CASCADE, related_name='student_scores')
score = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
comment = models.TextField(blank=True, null=True)
class Meta:
unique_together = ('student', 'competency')
def __str__(self):
return f"{self.student} - {self.competency.name} - Score: {self.score}"
####### Parent files templates (par dossier d'inscription) ####### ####### Parent files templates (par dossier d'inscription) #######
class RegistrationParentFileTemplate(models.Model): class RegistrationParentFileTemplate(models.Model):
master = models.ForeignKey(RegistrationParentFileMaster, on_delete=models.CASCADE, related_name='parent_file_templates', blank=True) master = models.ForeignKey(RegistrationParentFileMaster, on_delete=models.CASCADE, related_name='parent_file_templates', blank=True)

View File

@ -1,5 +1,17 @@
from rest_framework import serializers from rest_framework import serializers
from .models import RegistrationFileGroup, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationSchoolFileMaster, RegistrationSchoolFileTemplate, RegistrationParentFileMaster, RegistrationParentFileTemplate, AbsenceManagement from .models import (
RegistrationFileGroup,
RegistrationForm,
Student,
Guardian,
Sibling,
Language,
RegistrationSchoolFileMaster,
RegistrationSchoolFileTemplate,
RegistrationParentFileMaster,
RegistrationParentFileTemplate,
AbsenceManagement
)
from School.models import SchoolClass, Fee, Discount, FeeType from School.models import SchoolClass, Fee, Discount, FeeType
from School.serializers import FeeSerializer, DiscountSerializer from School.serializers import FeeSerializer, DiscountSerializer
from Auth.models import ProfileRole, Profile from Auth.models import ProfileRole, Profile
@ -406,15 +418,19 @@ class GuardianByDICreationSerializer(serializers.ModelSerializer):
class StudentByRFCreationSerializer(serializers.ModelSerializer): class StudentByRFCreationSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False) id = serializers.IntegerField(required=False)
guardians = GuardianByDICreationSerializer(many=True, required=False) guardians = GuardianByDICreationSerializer(many=True, required=False)
associated_class_name = serializers.SerializerMethodField()
class Meta: class Meta:
model = Student model = Student
fields = ['id', 'last_name', 'first_name', 'guardians', 'level'] fields = ['id', 'last_name', 'first_name', 'guardians', 'level', 'associated_class_name']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(StudentByRFCreationSerializer, self).__init__(*args, **kwargs) super(StudentByRFCreationSerializer, self).__init__(*args, **kwargs)
for field in self.fields: for field in self.fields:
self.fields[field].required = False self.fields[field].required = False
def get_associated_class_name(self, obj):
return obj.associated_class.atmosphere_name if obj.associated_class else None
class NotificationSerializer(serializers.ModelSerializer): class NotificationSerializer(serializers.ModelSerializer):
notification_type_label = serializers.ReadOnlyField() notification_type_label = serializers.ReadOnlyField()

View File

@ -35,7 +35,10 @@ def getTuitionPaymentMethod(pk):
@register.filter @register.filter
def getStudentLevel(pk): def getStudentLevel(pk):
registerForm = RegistrationForm.objects.get(student=pk) registerForm = RegistrationForm.objects.get(student=pk)
return Student.StudentLevel(int(registerForm.student.level)).label level = registerForm.student.level
if level:
return level.name
return ""
@register.filter @register.filter
def getStudentGender(pk): def getStudentGender(pk):

View File

@ -17,7 +17,9 @@ from .views import (
RegistrationParentFileTemplateSimpleView, RegistrationParentFileTemplateSimpleView,
RegistrationParentFileTemplateView, RegistrationParentFileTemplateView,
AbsenceManagementListCreateView, AbsenceManagementListCreateView,
AbsenceManagementDetailView AbsenceManagementDetailView,
StudentCompetencyListCreateView,
StudentCompetencySimpleView
) )
from .views import RegistrationFileGroupView, RegistrationFileGroupSimpleView, get_registration_files_by_group from .views import RegistrationFileGroupView, RegistrationFileGroupSimpleView, get_registration_files_by_group
@ -63,4 +65,7 @@ urlpatterns = [
re_path(r'^absences$', AbsenceManagementListCreateView.as_view(), name="absence_list_create"), re_path(r'^absences$', AbsenceManagementListCreateView.as_view(), name="absence_list_create"),
re_path(r'^absences/(?P<id>[0-9]+)$', AbsenceManagementDetailView.as_view(), name="absence_detail"), re_path(r'^absences/(?P<id>[0-9]+)$', AbsenceManagementDetailView.as_view(), name="absence_detail"),
re_path(r'^studentCompetencies$', StudentCompetencyListCreateView.as_view(), name="student_competency_list_create"),
re_path(r'^studentCompetencies/(?P<id>[0-9]+)$', StudentCompetencySimpleView.as_view(), name="student_competency_detail"),
] ]

View File

@ -13,6 +13,7 @@ from .registration_file_group_views import RegistrationFileGroupView, Registrati
from .student_views import StudentView, StudentListView, ChildrenListView from .student_views import StudentView, StudentListView, ChildrenListView
from .guardian_views import GuardianView, DissociateGuardianView from .guardian_views import GuardianView, DissociateGuardianView
from .absences_views import AbsenceManagementDetailView, AbsenceManagementListCreateView from .absences_views import AbsenceManagementDetailView, AbsenceManagementListCreateView
from .student_competencies_views import StudentCompetencyListCreateView, StudentCompetencySimpleView
__all__ = [ __all__ = [
'RegisterFormView', 'RegisterFormView',
@ -39,5 +40,7 @@ __all__ = [
'GuardianView', 'GuardianView',
'DissociateGuardianView', 'DissociateGuardianView',
'AbsenceManagementDetailView', 'AbsenceManagementDetailView',
'AbsenceManagementListCreateView' 'AbsenceManagementListCreateView',
'StudentCompetencyListCreateView',
'StudentCompetencySimpleView'
] ]

View File

@ -16,10 +16,22 @@ import Subscriptions.util as util
from Subscriptions.serializers import RegistrationFormSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileTemplateSerializer from Subscriptions.serializers import RegistrationFormSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileTemplateSerializer
from Subscriptions.pagination import CustomSubscriptionPagination from Subscriptions.pagination import CustomSubscriptionPagination
from Subscriptions.models import Student, Guardian, RegistrationForm, RegistrationSchoolFileTemplate, RegistrationFileGroup, RegistrationParentFileTemplate from Subscriptions.models import (
Student,
Guardian,
RegistrationForm,
RegistrationSchoolFileTemplate,
RegistrationFileGroup,
RegistrationParentFileTemplate,
StudentCompetency
)
from Subscriptions.automate import updateStateMachine from Subscriptions.automate import updateStateMachine
from Common.models import Competency
from N3wtSchool import settings, bdd from N3wtSchool import settings, bdd
from django.db.models import Q
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -236,7 +248,6 @@ class RegisterFormWithIdView(APIView):
studentForm_data = request.data.get('data', '{}') studentForm_data = request.data.get('data', '{}')
try: try:
data = json.loads(studentForm_data) data = json.loads(studentForm_data)
print(f'data : {data}')
except json.JSONDecodeError: except json.JSONDecodeError:
return JsonResponse({"error": "Invalid JSON format in 'data'"}, status=status.HTTP_400_BAD_REQUEST) return JsonResponse({"error": "Invalid JSON format in 'data'"}, status=status.HTTP_400_BAD_REQUEST)
@ -366,6 +377,26 @@ class RegisterFormWithIdView(APIView):
save=True save=True
) )
# Valorisation des StudentCompetency pour l'élève
try:
student = registerForm.student
cycle = None
if student.level:
cycle = student.level.cycle.number
if cycle:
competencies = Competency.objects.filter(
category__domain__cycle=cycle
).filter(
Q(end_of_cycle=True) | Q(level=student.level.name)
)
for comp in competencies:
StudentCompetency.objects.get_or_create(
student=student,
competency=comp
)
except Exception as e:
logger.error(f"Erreur lors de la valorisation des StudentCompetency: {e}")
updateStateMachine(registerForm, 'EVENT_VALIDATE') updateStateMachine(registerForm, 'EVENT_VALIDATE')
# Retourner les données mises à jour # Retourner les données mises à jour

View File

@ -0,0 +1,91 @@
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 drf_yasg import openapi
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, Competency
from N3wtSchool.bdd import delete_object
@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')
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)
student_competencies = StudentCompetency.objects.filter(student=student).select_related('competency', 'competency__category', 'competency__category__domain')
# On ne garde que les IDs des compétences de l'élève
student_competency_ids = set(sc.competency_id for sc in student_competencies)
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:
comp = sc.competency
if comp.category_id == categorie.id:
categorie_dict["competences"].append({
"competence_id": comp.id,
"nom": comp.name,
"score": sc.score,
"comment": sc.comment 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 post(self, request):
# serializer = AbsenceManagementSerializer(data=request.data)
# if serializer.is_valid():
# serializer.save()
# return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED)
# return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@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)
# 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)

View File

@ -10,6 +10,7 @@ APPS = [
"GestionMessagerie", "GestionMessagerie",
"Auth", "Auth",
"School", "School",
"Common"
] ]
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) BASE_DIR = os.path.dirname(os.path.abspath(__file__))

View File

@ -15,6 +15,7 @@ test_mode = os.getenv('TEST_MODE', 'False') == 'True'
commands = [ commands = [
["python", "manage.py", "collectstatic", "--noinput"], ["python", "manage.py", "collectstatic", "--noinput"],
["python", "manage.py", "flush", "--noinput"], ["python", "manage.py", "flush", "--noinput"],
["python", "manage.py", "makemigrations", "Common", "--noinput"],
["python", "manage.py", "makemigrations", "Establishment", "--noinput"], ["python", "manage.py", "makemigrations", "Establishment", "--noinput"],
["python", "manage.py", "makemigrations", "Settings", "--noinput"], ["python", "manage.py", "makemigrations", "Settings", "--noinput"],
["python", "manage.py", "makemigrations", "Subscriptions", "--noinput"], ["python", "manage.py", "makemigrations", "Subscriptions", "--noinput"],