from rest_framework import serializers from .models import ( RegistrationFileGroup, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationSchoolFileMaster, RegistrationSchoolFileTemplate, RegistrationParentFileMaster, RegistrationParentFileTemplate, AbsenceManagement, BilanCompetence ) from School.models import SchoolClass, Fee, Discount, FeeType from Auth.models import ProfileRole, Profile from Auth.serializers import ProfileSerializer, ProfileRoleSerializer from GestionNotification.models import Notification from N3wtSchool import settings from django.utils import timezone import pytz import Subscriptions.util as util class AbsenceManagementSerializer(serializers.ModelSerializer): student_name = serializers.SerializerMethodField() class Meta: model = AbsenceManagement fields = '__all__' # Ajoute les nouveaux champs au retour JSON extra_fields = ['student_name'] def get_student_name(self, obj): if obj.student: return f"{obj.student.first_name} {obj.student.last_name}" return None class RegistrationSchoolFileMasterSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) class Meta: model = RegistrationSchoolFileMaster fields = '__all__' class RegistrationParentFileMasterSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) class Meta: model = RegistrationParentFileMaster fields = '__all__' class RegistrationSchoolFileTemplateSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) file_url = serializers.SerializerMethodField() class Meta: model = RegistrationSchoolFileTemplate fields = '__all__' def get_file_url(self, obj): # Retourne l'URL complète du fichier si disponible return obj.file.url if obj.file else None class RegistrationParentFileTemplateSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) file_url = serializers.SerializerMethodField() master_name = serializers.CharField(source='master.name', read_only=True) master_description = serializers.CharField(source='master.description', read_only=True) is_required = serializers.BooleanField(source='master.is_required', read_only=True) class Meta: model = RegistrationParentFileTemplate fields = '__all__' def get_file_url(self, obj): # Retourne l'URL complète du fichier si disponible return obj.file.url if obj.file else None class GuardianSimpleSerializer(serializers.ModelSerializer): associated_profile_email = serializers.SerializerMethodField() class Meta: model = Guardian fields = ['id', 'associated_profile_email'] def get_associated_profile_email(self, obj): if obj.profile_role and obj.profile_role.profile: return obj.profile_role.profile.email return None class RegistrationFormSimpleSerializer(serializers.ModelSerializer): guardians = GuardianSimpleSerializer(many=True, source='student.guardians') last_name = serializers.SerializerMethodField() first_name = serializers.SerializerMethodField() class Meta: model = RegistrationForm fields = ['student_id', 'last_name', 'first_name', 'guardians'] def get_last_name(self, obj): return obj.student.last_name def get_first_name(self, obj): return obj.student.first_name class RegistrationFileGroupSerializer(serializers.ModelSerializer): registration_forms = serializers.SerializerMethodField() class Meta: model = RegistrationFileGroup fields = '__all__' def get_registration_forms(self, obj): forms = RegistrationForm.objects.filter(fileGroup=obj) return RegistrationFormSimpleSerializer(forms, many=True).data class LanguageSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) class Meta: model = Language fields = '__all__' class SiblingSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) class Meta: model = Sibling fields = '__all__' class GuardianSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=False) profile_role_data = ProfileRoleSerializer(write_only=True, required=False) associated_profile_email = serializers.SerializerMethodField() class Meta: model = Guardian fields = '__all__' def get_associated_profile_email(self, obj): if obj.profile_role and obj.profile_role.profile: return obj.profile_role.profile.email return None class StudentSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) guardians = GuardianSerializer(many=True, required=False) siblings = SiblingSerializer(many=True, required=False) spoken_languages = LanguageSerializer(many=True, required=False) associated_class_id = serializers.PrimaryKeyRelatedField(queryset=SchoolClass.objects.all(), source='associated_class', required=False, write_only=False, read_only=False) age = serializers.SerializerMethodField() formatted_birth_date = serializers.SerializerMethodField() birth_date = serializers.DateField(input_formats=['%d-%m-%Y', '%Y-%m-%d'], required=False, allow_null=True) associated_class_name = serializers.SerializerMethodField() class Meta: model = Student fields = '__all__' def create_or_update_guardians(self, guardians_data): guardians_ids = [] for guardian_data in guardians_data: guardian_id = guardian_data.get('id', None) profile_role_data = guardian_data.pop('profile_role_data', None) profile_role = guardian_data.pop('profile_role', None) if guardian_id: # Si un ID est fourni, récupérer ou mettre à jour le Guardian existant guardian_instance, created = Guardian.objects.update_or_create( id=guardian_id, defaults=guardian_data ) guardians_ids.append(guardian_instance.id) continue if profile_role_data: # Vérifiez si 'profile_data' est fourni pour créer un nouveau profil profile_data = profile_role_data.pop('profile_data', None) if profile_data: # Créer un nouveau profil profile_serializer = ProfileSerializer(data=profile_data) profile_serializer.is_valid(raise_exception=True) profile = profile_serializer.save() profile.set_password(profile_data['password']) profile.save() profile_role_data['profile'] = profile.id # Associer le profil créé # Vérifiez si 'profile' est un objet ou une clé primaire if isinstance(profile_role_data.get('profile'), Profile): profile_role_data['profile'] = profile_role_data['profile'].id establishment_id = profile_role_data.pop('establishment').id profile_role_data['establishment'] = establishment_id # Vérifiez si un ProfileRole existe déjà pour ce profile et cet établissement existing_profile_role = ProfileRole.objects.filter( profile_id=profile_role_data['profile'], establishment=profile_role_data['establishment'], role_type=profile_role_data['role_type'] ).first() if existing_profile_role: # Mettre à jour le ProfileRole existant profile_role_serializer = ProfileRoleSerializer(existing_profile_role, data=profile_role_data) profile_role_serializer.is_valid(raise_exception=True) profile_role = profile_role_serializer.save() else: # Créer un nouveau ProfileRole si aucun n'existe profile_role_serializer = ProfileRoleSerializer(data=profile_role_data) profile_role_serializer.is_valid(raise_exception=True) profile_role = profile_role_serializer.save() elif profile_role: # Récupérer un ProfileRole existant par son ID profile_role = ProfileRole.objects.get(id=profile_role.id) if profile_role: guardian_data['profile_role'] = profile_role # Vérifiez si un Guardian existe déjà pour ce ProfileRole existing_guardian = Guardian.objects.filter(profile_role=profile_role).first() if existing_guardian: # Mettre à jour le Guardian existant for key, value in guardian_data.items(): setattr(existing_guardian, key, value) existing_guardian.save() guardians_ids.append(existing_guardian.id) else: # Créer un nouveau Guardian guardian_instance = Guardian.objects.create(**guardian_data) guardians_ids.append(guardian_instance.id) return guardians_ids def create_or_update_siblings(self, siblings_data, student_instance): """ Crée ou met à jour les frères et sœurs associés à un étudiant. Supprime les frères et sœurs qui ne sont plus présents dans siblings_data. """ # Si siblings_data est vide, supprimer tous les frères et sœurs associés if not siblings_data: student_instance.siblings.clear() # Supprime toutes les relations return [] # Récupérer les IDs des frères et sœurs existants existing_sibling_ids = set(student_instance.siblings.values_list('id', flat=True)) # Créer ou mettre à jour les frères et sœurs updated_sibling_ids = [] for sibling_data in siblings_data: sibling_instance, created = Sibling.objects.update_or_create( id=sibling_data.get('id'), defaults=sibling_data ) updated_sibling_ids.append(sibling_instance.id) # Supprimer les frères et sœurs qui ne sont plus dans siblings_data siblings_to_delete = existing_sibling_ids - set(updated_sibling_ids) Sibling.objects.filter(id__in=siblings_to_delete).delete() return updated_sibling_ids def create_or_update_languages(self, languages_data): languages_ids = [] for language_data in languages_data: language_instance, created = Language.objects.update_or_create( id=language_data.get('id'), defaults=language_data ) languages_ids.append(language_instance.id) return languages_ids def create(self, validated_data): guardians_data = validated_data.pop('guardians', []) siblings_data = validated_data.pop('siblings', []) languages_data = validated_data.pop('spoken_languages', []) student = Student.objects.create(**validated_data) student.guardians.set(self.create_or_update_guardians(guardians_data)) student.siblings.set(self.create_or_update_siblings(siblings_data, student)) student.spoken_languages.set(self.create_or_update_languages(languages_data)) return student def update(self, instance, validated_data): guardians_data = validated_data.pop('guardians', []) siblings_data = validated_data.pop('siblings', []) languages_data = validated_data.pop('spoken_languages', []) if guardians_data: instance.guardians.set(self.create_or_update_guardians(guardians_data)) sibling_ids = self.create_or_update_siblings(siblings_data, instance) instance.siblings.set(sibling_ids) if languages_data: instance.spoken_languages.set(self.create_or_update_languages(languages_data)) for field in self.fields: try: setattr(instance, field, validated_data[field]) except KeyError: pass instance.save() return instance def get_age(self, obj): return obj.age def get_formatted_birth_date(self, obj): return obj.formatted_birth_date def get_associated_class_name(self, obj): return obj.associated_class.atmosphere_name if obj.associated_class else None class RegistrationFormSerializer(serializers.ModelSerializer): student = StudentSerializer(many=False, required=False) registration_file = serializers.FileField(required=False) sepa_file = serializers.FileField(required=False) status_label = serializers.SerializerMethodField() formatted_last_update = serializers.SerializerMethodField() registration_files = RegistrationSchoolFileTemplateSerializer(many=True, required=False) fees = serializers.PrimaryKeyRelatedField(queryset=Fee.objects.all(), many=True, required=False) discounts = serializers.PrimaryKeyRelatedField(queryset=Discount.objects.all(), many=True, required=False) totalRegistrationFees = serializers.SerializerMethodField() totalTuitionFees = serializers.SerializerMethodField() class Meta: model = RegistrationForm fields = '__all__' def create(self, validated_data): student_data = validated_data.pop('student') student = StudentSerializer.create(StudentSerializer(), student_data) fees_data = validated_data.pop('fees', []) discounts_data = validated_data.pop('discounts', []) registrationForm = RegistrationForm.objects.create(student=student, **validated_data) # Associer les IDs des objets Fee et Discount au RegistrationForm registrationForm.fees.set([fee.id for fee in fees_data]) registrationForm.discounts.set([discount.id for discount in discounts_data]) return registrationForm def update(self, instance, validated_data): student_data = validated_data.pop('student', None) fees_data = validated_data.pop('fees', None) discounts_data = validated_data.pop('discounts', None) if student_data: student = instance.student StudentSerializer.update(StudentSerializer(), student, student_data) for field in self.fields: try: setattr(instance, field, validated_data[field]) except KeyError: pass instance.last_update = util.convertToStr(util._now(), '%d-%m-%Y %H:%M') instance.save() # Associer les IDs des objets Fee et Discount au RegistrationForm if fees_data is not None: instance.fees.set([fee.id for fee in fees_data]) if discounts_data is not None: instance.discounts.set([discount.id for discount in discounts_data]) return instance def get_status_label(self, obj): return obj.get_status_display() def get_formatted_last_update(self, obj): utc_time = timezone.localtime(obj.last_update) # Convert to local time local_tz = pytz.timezone(settings.TZ_APPLI) local_time = utc_time.astimezone(local_tz) return local_time.strftime("%d-%m-%Y %H:%M") def get_totalRegistrationFees(self, obj): return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.REGISTRATION_FEE)) def get_totalTuitionFees(self, obj): return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.TUITION_FEE)) class StudentByParentSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) associated_class_name = serializers.SerializerMethodField() class Meta: model = Student fields = ['id', 'last_name', 'first_name', 'level', 'photo', 'associated_class_name'] def __init__(self, *args, **kwargs): super(StudentByParentSerializer, self).__init__(*args, **kwargs) for field in self.fields: 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 RegistrationFormByParentSerializer(serializers.ModelSerializer): student = StudentByParentSerializer(many=False, required=True) class Meta: model = RegistrationForm fields = ['student', 'status', 'sepa_file'] def __init__(self, *args, **kwargs): super(RegistrationFormByParentSerializer, self).__init__(*args, **kwargs) for field in self.fields: self.fields[field].required = False class GuardianByDICreationSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) associated_profile_email = serializers.SerializerMethodField() profile = serializers.SerializerMethodField() class Meta: model = Guardian fields = ['id', 'last_name', 'first_name', 'associated_profile_email', 'phone', 'profile_role', 'profile'] def get_associated_profile_email(self, obj): if obj.profile_role and obj.profile_role.profile: return obj.profile_role.profile.email return None def get_profile(self, obj): if obj.profile_role and obj.profile_role.profile: return obj.profile_role.profile.id # Retourne l'ID du profil associé return None class BilanCompetenceSerializer(serializers.ModelSerializer): class Meta: model = BilanCompetence fields = ['id', 'file', 'period', 'created_at'] class StudentByRFCreationSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) guardians = GuardianByDICreationSerializer(many=True, required=False) associated_class_name = serializers.SerializerMethodField() bilans = BilanCompetenceSerializer(many=True, read_only=True) class Meta: model = Student fields = ['id', 'last_name', 'first_name', 'guardians', 'level', 'associated_class_name', 'photo', 'bilans'] def __init__(self, *args, **kwargs): super(StudentByRFCreationSerializer, self).__init__(*args, **kwargs) for field in self.fields: 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): notification_type_label = serializers.ReadOnlyField() class Meta: model = Notification fields = '__all__'