"""DRF-serializers voor het kerndomein.""" from rest_framework import serializers from .models import ( Activiteit, Afwezigheid, BlokUitzondering, Functie, Groep, KalenderDag, Locatie, Persoon, Roosterblok, Schooljaar, Subgroep, Tijdslot, ) class FunctieSerializer(serializers.ModelSerializer): aantal_personen = serializers.IntegerField(source="personen.count", read_only=True) class Meta: model = Functie fields = ["id", "naam", "omschrijving", "actief", "aantal_personen", "aangemaakt_op"] read_only_fields = ["aangemaakt_op"] class GroepSerializer(serializers.ModelSerializer): aantal_leerlingen = serializers.IntegerField(source="leerlingen.count", read_only=True) class Meta: model = Groep fields = ["id", "naam", "omschrijving", "actief", "aantal_leerlingen", "aangemaakt_op"] read_only_fields = ["aangemaakt_op"] class PersoonSerializer(serializers.ModelSerializer): volledige_naam = serializers.CharField(read_only=True) rol_label = serializers.CharField(source="get_rol_display", read_only=True) groep_naam = serializers.CharField(source="groep.naam", read_only=True, default=None) stamgroep_naam = serializers.CharField(source="stamgroep.naam", read_only=True, default=None) functies = serializers.PrimaryKeyRelatedField( many=True, queryset=Functie.objects.all(), required=False ) functies_namen = serializers.SerializerMethodField() vakken = serializers.PrimaryKeyRelatedField( many=True, queryset=Activiteit.objects.all(), required=False ) vakken_namen = serializers.SerializerMethodField() begeleide_subgroepen = serializers.PrimaryKeyRelatedField( many=True, queryset=Subgroep.objects.all(), required=False ) begeleide_subgroepen_namen = serializers.SerializerMethodField() class Meta: model = Persoon fields = [ "id", "voornaam", "achternaam", "volledige_naam", "rol", "rol_label", "groep", "groep_naam", "stamgroep", "stamgroep_naam", "functies", "functies_namen", "vakken", "vakken_namen", "begeleide_subgroepen", "begeleide_subgroepen_namen", "geboortedatum", "actief", "opmerkingen", "aangemaakt_op", ] read_only_fields = ["aangemaakt_op"] def get_functies_namen(self, obj): return [{"id": f.id, "naam": f.naam} for f in obj.functies.all()] def get_vakken_namen(self, obj): return [{"id": a.id, "naam": a.naam} for a in obj.vakken.all()] def get_begeleide_subgroepen_namen(self, obj): return [{"id": s.id, "naam": s.naam} for s in obj.begeleide_subgroepen.all()] def validate(self, data): # Functies/vakken/subgroep-begeleiding horen alleen bij medewerkers. # (De M2M wordt door DRF ná save() gezet, dus hier afdwingen.) rol = data.get("rol", getattr(self.instance, "rol", None)) if rol == "leerling": data["functies"] = [] data["vakken"] = [] data["begeleide_subgroepen"] = [] return data @staticmethod def _met_theorievakken(gekozen): """Voeg de theorievakken toe aan de gekozen vakken (zonder dubbelen).""" theorie = list(Activiteit.objects.filter(theorievak=True, actief=True)) ids = {v.id for v in gekozen} return gekozen + [a for a in theorie if a.id not in ids] def create(self, validated_data): # begeleide_subgroepen is een reverse-M2M: apart afhandelen na save. subg = validated_data.pop("begeleide_subgroepen", None) if validated_data.get("rol") == "leerkracht": validated_data["vakken"] = self._met_theorievakken(validated_data.get("vakken", [])) instance = super().create(validated_data) if subg is not None: instance.begeleide_subgroepen.set(subg) return instance def update(self, instance, validated_data): subg = validated_data.pop("begeleide_subgroepen", "__keep__") rol = validated_data.get("rol", instance.rol) if rol == "leerkracht" and "vakken" in validated_data: validated_data["vakken"] = self._met_theorievakken(validated_data["vakken"]) instance = super().update(instance, validated_data) if subg != "__keep__": instance.begeleide_subgroepen.set(subg) return instance class SubgroepSerializer(serializers.ModelSerializer): leden = serializers.PrimaryKeyRelatedField( many=True, queryset=Persoon.objects.filter(rol="leerling"), required=False ) leden_namen = serializers.SerializerMethodField() aantal_leden = serializers.SerializerMethodField() groep_naam = serializers.CharField(source="groep.naam", read_only=True, default=None) begeleiders = serializers.PrimaryKeyRelatedField( many=True, queryset=Persoon.objects.exclude(rol="leerling"), required=False ) begeleiders_namen = serializers.SerializerMethodField() class Meta: model = Subgroep fields = [ "id", "naam", "omschrijving", "actief", "groep", "groep_naam", "leden", "leden_namen", "aantal_leden", "begeleiders", "begeleiders_namen", "aangemaakt_op", ] read_only_fields = ["aangemaakt_op"] def get_leden_namen(self, obj): return [{"id": p.id, "naam": p.volledige_naam} for p in obj.leden.all()] def get_aantal_leden(self, obj): return obj.leden.count() def get_begeleiders_namen(self, obj): return [{"id": p.id, "naam": p.volledige_naam} for p in obj.begeleiders.all()] # --- Fase 2 --------------------------------------------------------------- class SchooljaarSerializer(serializers.ModelSerializer): class Meta: model = Schooljaar fields = ["id", "naam", "start_datum", "eind_datum", "actief"] def validate(self, data): start = data.get("start_datum", getattr(self.instance, "start_datum", None)) eind = data.get("eind_datum", getattr(self.instance, "eind_datum", None)) if start and eind and eind <= start: raise serializers.ValidationError("De einddatum moet na de startdatum liggen.") return data class KalenderDagSerializer(serializers.ModelSerializer): type_label = serializers.CharField(source="get_type_display", read_only=True) class Meta: model = KalenderDag fields = ["id", "schooljaar", "datum", "type", "type_label", "omschrijving"] class TijdslotSerializer(serializers.ModelSerializer): dag_label = serializers.CharField(source="get_dag_display", read_only=True) groep_naam = serializers.CharField(source="groep.naam", read_only=True, default=None) class Meta: model = Tijdslot fields = ["id", "naam", "dag", "dag_label", "start_tijd", "eind_tijd", "volgorde", "groep", "groep_naam", "actief"] def validate(self, data): start = data.get("start_tijd", getattr(self.instance, "start_tijd", None)) eind = data.get("eind_tijd", getattr(self.instance, "eind_tijd", None)) if start and eind and eind <= start: raise serializers.ValidationError("De eindtijd moet na de starttijd liggen.") return data class ActiviteitSerializer(serializers.ModelSerializer): kleur = serializers.RegexField( regex=r"^#[0-9a-fA-F]{6}$", error_messages={"invalid": "Kleur moet een hex-code zijn, bv. #2563eb."}, ) class Meta: model = Activiteit fields = ["id", "naam", "kleur", "omschrijving", "theorievak", "actief"] class LocatieSerializer(serializers.ModelSerializer): class Meta: model = Locatie fields = ["id", "naam", "omschrijving", "actief"] class RoosterblokSerializer(serializers.ModelSerializer): activiteit_naam = serializers.CharField(source="activiteit.naam", read_only=True) activiteit_kleur = serializers.CharField(source="activiteit.kleur", read_only=True) tijdslot_label = serializers.CharField(source="tijdslot.__str__", read_only=True) locatie_naam = serializers.CharField(source="locatie.naam", read_only=True, default=None) doel_type = serializers.CharField(read_only=True) doel_naam = serializers.SerializerMethodField() begeleiders_namen = serializers.SerializerMethodField() class Meta: model = Roosterblok fields = [ "id", "schooljaar", "tijdslot", "tijdslot_label", "activiteit", "activiteit_naam", "activiteit_kleur", "locatie", "locatie_naam", "groep", "subgroep", "leerling", "doel_type", "doel_naam", "begeleiders", "begeleiders_namen", "opmerkingen", "aangemaakt_op", ] read_only_fields = ["aangemaakt_op"] def get_doel_naam(self, obj): if obj.groep_id: return obj.groep.naam if obj.subgroep_id: return obj.subgroep.naam if obj.leerling_id: return obj.leerling.volledige_naam return None def get_begeleiders_namen(self, obj): return [{"id": p.id, "naam": p.volledige_naam} for p in obj.begeleiders.all()] def validate(self, data): # Precies één doelgroep (groep / subgroep / leerling). groep = data.get("groep", getattr(self.instance, "groep", None)) subgroep = data.get("subgroep", getattr(self.instance, "subgroep", None)) leerling = data.get("leerling", getattr(self.instance, "leerling", None)) gevuld = [x for x in (groep, subgroep, leerling) if x] if len(gevuld) != 1: raise serializers.ValidationError( "Kies precies één doelgroep: groep, subgroep of leerling." ) # Inzetregels voor begeleiders t.o.v. het vak (de activiteit): # - een leerling kan geen begeleider zijn; # - een klassenondersteuner kan overal worden ingezet; # - een leerkracht mag op theorievakken én op de aan hem/haar gekoppelde # vakken; een praktijkondersteuner alleen op de gekoppelde vakken. activiteit = data.get("activiteit", getattr(self.instance, "activiteit", None)) begeleiders = data.get("begeleiders", None) if begeleiders is None and self.instance is not None: begeleiders = list(self.instance.begeleiders.all()) for p in (begeleiders or []): if p.rol == "leerling": raise serializers.ValidationError( f"'{p.volledige_naam}' is een leerling en kan geen begeleider zijn." ) if p.rol == "klassenondersteuner": continue if activiteit is None: continue mag = p.vakken.filter(pk=activiteit.pk).exists() if p.rol == "leerkracht" and getattr(activiteit, "theorievak", False): mag = True if not mag: raise serializers.ValidationError( f"'{p.volledige_naam}' is niet gekoppeld aan het vak '{activiteit.naam}'." ) return data class AfwezigheidSerializer(serializers.ModelSerializer): persoon_naam = serializers.CharField(source="persoon.volledige_naam", read_only=True) persoon_rol = serializers.CharField(source="persoon.rol", read_only=True) class Meta: model = Afwezigheid fields = [ "id", "persoon", "persoon_naam", "persoon_rol", "start_datum", "eind_datum", "reden", "aangemaakt_op", ] read_only_fields = ["aangemaakt_op"] def validate(self, data): start = data.get("start_datum", getattr(self.instance, "start_datum", None)) eind = data.get("eind_datum", getattr(self.instance, "eind_datum", None)) if start and eind and eind < start: raise serializers.ValidationError("De einddatum ligt vóór de startdatum.") return data class BlokUitzonderingSerializer(serializers.ModelSerializer): vervanger_naam = serializers.CharField( source="vervanger.volledige_naam", read_only=True, default=None ) class Meta: model = BlokUitzondering fields = [ "id", "roosterblok", "datum", "vervalt", "vervanger", "vervanger_naam", "opmerking", ] def validate_vervanger(self, value): if value is not None and value.rol == "leerling": raise serializers.ValidationError( "Een leerling kan geen vervanger zijn." ) return value