← Python/Eindproject: Urenrapport
Les 5 van 8
MODULE 14 · 05◷ 35 min

Registraties selecteren en controleerbaar optellen

Na deze les kun je
  • Een volledig rapport met metadata en teamtotalen opbouwen.
  • Inclusieve datumgrenzen en herhaalde registraties correct verwerken.
  • Een registratiebron eenmaal doorlopen en gelijk laten.

De CSV-lezer levert bruikbare registraties. De rekenmodule vertaalt die naar een overzicht: hoeveel registraties zijn geselecteerd, hoeveel minuten horen daarbij en hoe zijn die over de teams verdeeld? Deze module heeft daarvoor geen kennis van bestandsnamen of CSV-citaten nodig.

Leg ook de vorm van een leeg rapport vast

Een rapport is meer dan één getal. De aanroeper wil ook weten welke periode is gebruikt en wat de teamverdeling betekent. Onze dictionary bevat daarom een schemaversie, periodemetadata, totaalaantallen en een lijst teamregels.

Ook een lege bron krijgt die volledige vorm. Nul registraties en nul minuten zijn bruikbare resultaten wanneer de invoer geldig is. De vaste velden voorkomen dat iedere schrijver of CLI opnieuw moet raden welke sleutels ontbreken.

De schemaversie is hier één. Zij maakt zichtbaar welke rapportafspraak de uitvoer volgt. Een latere wijziging van die afspraak kan dan bewust worden behandeld.

Twee niveaus tegelijk bijhouden

Tijdens iedere bronronde werk je zowel de algemene telling als de telling van het betreffende team bij. Een dictionary op teamnaam helpt om de tussentotalen bij elkaar te bewaren. Je hoeft daarvoor geen afzonderlijke bronronde per team te starten.

Een totaal naast een verdeling bijhouden
records = [('bouw', 20), ('balie', 15), ('bouw', 10)]
totals = {}
for team, minutes in records:
    totals[team] = totals.get(team, 0) + minutes
print(sum(totals.values()))
for team in sorted(totals):
    print(team, totals[team])
Verwachte uitvoer
45
balie 15
bouw 30

Het voorbeeld laat een minutenverdeling naast een algemeen totaal zien. Ons echte rapport bewaart bovendien het aantal registraties, zowel algemeen als per team. Dat aantal vertelt iets anders dan de hoeveelheid tijd.

Een herhaalde registratie blijft opnieuw meetellen. De vereniging heeft geen regel afgesproken om gelijke modellen als dezelfde gebeurtenis te behandelen. Een set gebruiken zou hier dus onterecht registraties verwijderen.

Sorteer het eindantwoord, niet de ontvangen bron

De teamlijst staat alfabetisch op teamnaam. Dat maakt rapporten prettig te vergelijken, ook wanneer de registraties in een andere volgorde zijn ontvangen. Bewaar de tussentellingen en sorteer pas de namen waarmee je de uiteindelijke teamregels maakt.

De registratiebron zelf blijft gelijk. Zij kan bovendien een iterator zijn zonder sorteermethode en zonder mogelijkheid om opnieuw te beginnen. Eén lus is genoeg om alle gevraagde aantallen en sommen op te bouwen.

De grenzen horen bij de periode

Start en end zijn optionele kalenderdagen. Controleer aanwezige grenzen eerst met require_day, zodat een tijdstip of een tekst niet als interne datumgrens doorgaat. Een begin na het einde levert een InputError op voordat je de bron verbruikt.

Een inclusieve periode is een afspraak
from datetime import date

start = date(2026, 10, 2)
end = date(2026, 10, 3)
for day in [date(2026, 10, 1), start, end]:
    print(day.isoformat(), start <= day <= end)
Verwachte uitvoer
2026-10-01 False
2026-10-02 True
2026-10-03 True

De vergelijking maakt beide grenzen inclusief. Een registratie op de eerste of laatste gekozen dag blijft in het rapport. Wanneer slechts één grens is opgegeven, controleer je alleen die kant.

Bewaar de gevraagde grenzen in de periodemetadata als datumtekst. Geen gekozen grens blijft None. Ook een periode waarin geen registratie valt, houdt dezelfde metadata met nulresultaten.

Veelgemaakte fout: een filter als validatie gebruiken

Controleer eerst of ieder ontvangen item werkelijk een Session is. Daarna kun je zijn dag voor de selectie gebruiken. Een ongeldig item overslaan omdat het toch niet relevant lijkt, kan een probleem in de invoerketen verbergen.

De parser en het model verzorgen hun eigen afspraken, maar deze module bewaakt haar eigen ingang. Een fout blijft zichtbaar als InputError in plaats van een gedeeltelijk succesvol rapport. De regiefunctie uit de tweede les stopt dan vóór publicatie.

Het berekende rapport blijft gegevens

De rekenmodule schrijft geen JSON of CSV en opent geen uitvoerbestand. Zij levert een dictionary waarvan de betekenis vastligt. In de volgende les krijgt een aparte module de verantwoordelijkheid om die gegevens naar tekst en een bestand te vertalen.

AAN DE SLAG 0/4
1

Een volledig rapport teruggeven

Maak make_report(sessions, *, start=None, end=None). Geef een dictionary met schema_version:1, period:{start,end}, sessions, total_minutes en teams. Periodedatums zijn isoformat()-tekst of None. Lege invoer geeft nul aantallen/minuten en een lege teamlijst.

2

Per team tellen en optellen

Tel iedere geselecteerde registratie eenmaal, ook herhalingen. teams is een alfabetisch gesorteerde lijst dictionaries met precies team, sessions en minutes. Bewaar het totale aantal en de totale minuten daarnaast op rapportniveau.

3

De gekozen periode inclusief toepassen

Controleer start en end via require_day als zij niet None zijn. Start na end geeft InputError("de begindatum ligt na de einddatum"). Controleer grenzen vóór de bronronde. Selecteer start <= dag <= end voor gekozen grenzen en bewaar ook een lege geselecteerde periode als geldig rapport.

4

Eén ronde met geldige, ongewijzigde registraties

Accepteer een eindige iterable en doorloop die eenmaal. Wijzig geen bronobjecten. Ieder ontvangen item moet een Session zijn, anders InputError("verwacht een registratie"), voordat je het op datum selecteert. Een fout object mag niet stil worden overgeslagen.

Samenvatting

  • Een volledig rapport bevat metadata, totaalcijfers en teamregels.
  • Herhalingen tellen opnieuw en beide gekozen datumgrenzen zijn inclusief.
  • Eén bronronde kan alle tellingen opbouwen zonder brongegevens te veranderen.
hours/rules.py
UitvoerPython
›
›_

Hier komt je resultaat.

Voer je code uit en ontdek
wat je hebt gemaakt.

0/4 stappen
EVEN MEEKIJKEN

Bekijk hoe het kan.

Je mag de oplossing bekijken. Deze poging telt dan niet mee als afgeronde les. Met Reset kun je daarna opnieuw zelf oefenen.