150 lines
5.3 KiB
Python
150 lines
5.3 KiB
Python
"""Urenoverzicht: berekent lesuren per groep/vak en per leerling.
|
||
|
||
Roosterblokken zijn wekelijks: per schooljaar telt een blok zo vaak mee als er
|
||
lesdagen op die weekdag zijn (vrije dagen/vakanties/studiedagen tellen niet mee),
|
||
minus de keren dat het blok via een uitzondering vervalt.
|
||
"""
|
||
from datetime import timedelta
|
||
|
||
from rest_framework.decorators import api_view
|
||
from rest_framework.response import Response
|
||
|
||
from core.models import KalenderDag, Persoon, Roosterblok, Rol, Schooljaar, Subgroep, Tijdslot
|
||
|
||
|
||
def _duur_uren(ts):
|
||
s, e = ts.start_tijd, ts.eind_tijd
|
||
return ((e.hour * 60 + e.minute) - (s.hour * 60 + s.minute)) / 60.0
|
||
|
||
|
||
@api_view(["GET"])
|
||
def urenoverzicht(request):
|
||
sjid = request.query_params.get("schooljaar")
|
||
if not sjid:
|
||
return Response({"detail": "Geef een schooljaar op."}, status=400)
|
||
try:
|
||
sj = Schooljaar.objects.get(pk=sjid)
|
||
except Schooljaar.DoesNotExist:
|
||
return Response({"detail": "Schooljaar niet gevonden."}, status=404)
|
||
|
||
# Lesdagen per weekdag (ma–vr), vrije dagen uitgezonderd.
|
||
vrije = set(KalenderDag.objects.filter(schooljaar=sj).values_list("datum", flat=True))
|
||
lesdagen = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0}
|
||
d = sj.start_datum
|
||
while d <= sj.eind_datum:
|
||
wd = d.weekday()
|
||
if wd <= 4 and d not in vrije:
|
||
lesdagen[wd] += 1
|
||
d += timedelta(days=1)
|
||
|
||
blokken = list(
|
||
Roosterblok.objects.filter(schooljaar=sj)
|
||
.select_related("tijdslot", "activiteit", "groep", "subgroep", "leerling", "leerling__groep")
|
||
.prefetch_related("uitzonderingen")
|
||
)
|
||
subgroepen = {s.id: s for s in Subgroep.objects.prefetch_related("leden").all()}
|
||
sub_leden = {sid: set(s.leden.values_list("id", flat=True)) for sid, s in subgroepen.items()}
|
||
leerlingen = list(Persoon.objects.filter(rol=Rol.LEERLING).select_related("groep"))
|
||
|
||
def _tel_weekdag(van, tot, wd):
|
||
n = 0
|
||
d = van
|
||
while d <= tot:
|
||
if d.weekday() == wd and d not in vrije:
|
||
n += 1
|
||
d += timedelta(days=1)
|
||
return n
|
||
|
||
def occurrences(blok):
|
||
wd = blok.tijdslot.dag
|
||
# Geldigheidsperiode binnen het schooljaar clampen.
|
||
van = max(blok.geldig_van or sj.start_datum, sj.start_datum)
|
||
tot = min(blok.geldig_tot or sj.eind_datum, sj.eind_datum)
|
||
if van > tot:
|
||
return 0
|
||
if blok.geldig_van is None and blok.geldig_tot is None:
|
||
basis = lesdagen.get(wd, 0)
|
||
else:
|
||
basis = _tel_weekdag(van, tot, wd)
|
||
weg = 0
|
||
for u in blok.uitzonderingen.all():
|
||
if u.vervalt and van <= u.datum <= tot and u.datum.weekday() == wd and u.datum not in vrije:
|
||
weg += 1
|
||
return max(basis - weg, 0)
|
||
|
||
def groep_van_blok(b):
|
||
if b.groep_id:
|
||
return b.groep
|
||
if b.subgroep_id:
|
||
s = subgroepen.get(b.subgroep_id)
|
||
return s.groep if s else None
|
||
if b.leerling_id:
|
||
return b.leerling.groep
|
||
return None
|
||
|
||
blok_uren = {}
|
||
groep_map = {}
|
||
for b in blokken:
|
||
u = _duur_uren(b.tijdslot) * occurrences(b)
|
||
blok_uren[b.id] = u
|
||
g = groep_van_blok(b)
|
||
if g:
|
||
gm = groep_map.setdefault(g.id, {"naam": g.naam, "vakken": {}})
|
||
gm["vakken"][b.activiteit.naam] = gm["vakken"].get(b.activiteit.naam, 0) + u
|
||
|
||
per_groep = []
|
||
for gm in groep_map.values():
|
||
vakken = [{"vak": k, "uren": round(v, 1)} for k, v in sorted(gm["vakken"].items())]
|
||
per_groep.append({
|
||
"groep": gm["naam"],
|
||
"totaal": round(sum(x["uren"] for x in vakken), 1),
|
||
"vakken": vakken,
|
||
})
|
||
per_groep.sort(key=lambda x: x["groep"])
|
||
|
||
# Stage-uren (module optioneel).
|
||
stages = []
|
||
alle_ts = []
|
||
try:
|
||
from modules.stage.models import Stage
|
||
alle_ts = list(Tijdslot.objects.all())
|
||
stages = list(Stage.objects.filter(actief=True))
|
||
except Exception:
|
||
pass
|
||
|
||
per_leerling = []
|
||
for l in leerlingen:
|
||
vakken = {}
|
||
for b in blokken:
|
||
hoort = (
|
||
(b.groep_id and l.groep_id == b.groep_id)
|
||
or (b.subgroep_id and l.id in sub_leden.get(b.subgroep_id, set()))
|
||
or (b.leerling_id == l.id)
|
||
)
|
||
if hoort:
|
||
vakken[b.activiteit.naam] = vakken.get(b.activiteit.naam, 0) + blok_uren[b.id]
|
||
stage_uren = 0
|
||
for st in stages:
|
||
if st.leerling_id != l.id:
|
||
continue
|
||
for ts in alle_ts:
|
||
if st.dekt_tijdslot(ts):
|
||
stage_uren += _duur_uren(ts) * lesdagen.get(ts.dag, 0)
|
||
vak_list = [{"vak": k, "uren": round(v, 1)} for k, v in sorted(vakken.items())]
|
||
totaal = sum(x["uren"] for x in vak_list) + stage_uren
|
||
if vak_list or stage_uren:
|
||
per_leerling.append({
|
||
"leerling": l.volledige_naam,
|
||
"groep": l.groep.naam if l.groep else None,
|
||
"vakken": vak_list,
|
||
"stage_uren": round(stage_uren, 1),
|
||
"totaal": round(totaal, 1),
|
||
})
|
||
per_leerling.sort(key=lambda x: x["leerling"])
|
||
|
||
return Response({
|
||
"schooljaar": sj.naam,
|
||
"lesdagen": lesdagen,
|
||
"per_groep": per_groep,
|
||
"per_leerling": per_leerling,
|
||
})
|