Oefenruns, voortgang, resultaten, statistieken en PDF-brondata
10.1 Doel en positie binnen het Technisch Ontwerp
Dit hoofdstuk beschrijft de technische verwerking van oefenen binnen OefenHub: het starten van een oefening, het opslaan van voortgang, het verwerken van antwoorden, het afronden van een run, het vastleggen van uniforme resultaten en statistieken en het beschikbaar maken van historische brondata voor resultaatweergave, geschiedenis, live meekijken en PDF-export.
De technische eigenaar van dit domein is OefenHub.Practice. Deze module beheert de brondata voor daadwerkelijke oefenuitvoeringen. De catalogus bepaalt welke oefening, categorie, niveau en technische module gekozen worden; Practice legt vast wat er op het moment van starten daadwerkelijk is uitgevoerd en welke voortgang en resultaten daaruit zijn ontstaan.
Het hoofdstuk dupliceert geen volledige tabeldefinities uit de database-informatie. De de de database-informatie blijft de bron voor tabellen, kolommen, relaties en constraints. Dit hoofdstuk legt vast hoe die data technisch wordt gebruikt binnen EF Core, services, modulegrenzen, snapshots, transaction boundaries en exportflows.
10.2 Ontwerpuitgangspunten
| Onderwerp | Technisch uitgangspunt |
|---|---|
| Module-eigenaar | OefenHub.Practice is eigenaar van exercise runs, voortgang, gedeelde oefenrecords en resultaatbrondata. |
| Databasecontext | PracticeDbContext beheert de practice-tabellen. |
| Schema | practice, volledig in kleine letters. |
| Brongegevens | Afgeronde en lopende oefenruns zijn de bron voor voortgang, resultaten, geschiedenis, live meekijken en PDF-export. |
| Modulegrenzen | Andere modules gebruiken publieke Practice-contracts en krijgen geen directe toegang tot PracticeDbContext of entities. |
| Catalogusverwijzingen | Verwijzingen naar catalogusobjecten zijn cross-module soft links en waar nodig aangevuld met snapshots. |
| Gebruikersverwijzingen | Verwijzingen naar gebruikers zijn cross-module soft links en waar nodig aangevuld met snapshots of geanonimiseerde weergavewaarden. |
| Vraaginhoud | Module-specifieke vraag- en antwoorddata wordt generiek opgeslagen als versieerbare payload. |
| Uniforme velden | Uniforme totalen en statistieken staan als expliciete runvelden beschikbaar voor snelle en consistente uitlezing. |
| PDF-brondata | PDF-export wordt opnieuw opgebouwd uit historische runbrondata en snapshots; PDF-bestanden zijn tijdelijke output. |
| Live meekijken | Live meekijken leest server-side opgeslagen voortgang; SignalR is transport en geen bron van waarheid. |
10.3 Project- en mapstructuur
OefenHub.Practice volgt de standaard moduleprojectstructuur uit het Technisch Ontwerp. Binnen deze module worden runlogica, opslag, contracts, readmodels en domeinservices bij elkaar gehouden.
OefenHub.Practice/
Contracts/
Models/
Enums/
Data/
PracticeDbContext.cs
Entities/
Configurations/
Migrations/
Models/
Commands/
Queries/
ReadModels/
Enums/
Services/
Interfaces/
Events/
Helpers/
Extensions/
De implementatieclasses, EF Core entities en interne services zijn standaard internal. Alleen publieke module-ingangen onder Contracts zijn bedoeld voor gebruik door andere modules.
10.4 Verantwoordelijkheden van OefenHub.Practice
| Verantwoordelijkheid | Beschrijving |
|---|---|
| Run starten | Aanmaken van een nieuwe exercise run op basis van een gevalideerde catalogus- en modulecontext. |
| Run hervatten | Bepalen en openen van de laatst gestarte niet-afgeronde run binnen dezelfde oefening en niveaucontext. |
| Voortgang opslaan | Server-side verwerken en opslaan van iedere bevestigde vraagstap. |
| Antwoordresultaat vastleggen | Opslaan van gegeven antwoord, resultaatstatus, Geen idee-status en timing. |
| Uniforme totalen bijwerken | Bijwerken van aantallen goed, fout, Geen idee, afgerond en resterend waar nodig. |
| Run afronden | Vastleggen van afrondmoment, eindstatus, statistieken en historische brondata. |
| Geschiedenis leveren | Readmodels leveren voor afgeronde niet-test runs binnen geautoriseerde context. |
| Resultaatdetail leveren | Historische vraag- en antwoorddetails leveren voor resultaatweergave en export. |
| Gedeelde oefeningen beheren | Administratieve shared records beheren tot de ontvanger daadwerkelijk een eigen run start. |
| Testoefeningen ondersteunen | Docenttestruns isoleren van reguliere geschiedenis en opruimbaar houden. |
| PDF-bronmodel leveren | Historisch exportmodel leveren aan Reporting of de PDF-exportservice. |
| Live brondata leveren | Actuele voortgang beschikbaar maken voor LiveMonitoring. |
10.5 Relatie met andere modules
| Module | Relatie met Practice |
|---|---|
OefenHub.Web | Roept publieke command- en querycontracts aan voor starten, beantwoorden, hervatten, resultaten en geschiedenis. |
OefenHub.Authorization | Levert server-side autorisatiecontrole en contextcontrole voor leerling, docent, ouder/voogd en beheerder. |
OefenHub.Catalog | Levert gevalideerde oefening-, niveau-, categorie- en modulecontext bij start/configuratie. |
OefenHub.ExerciseModuleHost | Resolveert de technische module voor vraaggeneratie, antwoordcontrole, rendering en exportrepresentatie. |
OefenHub.Relationships | Levert relatiecontrole voor delen met vrienden en ouder-/voogd- of docentcontexten waar relevant. |
OefenHub.Communication | Kan systeemcommunicatie ontvangen na delen of relevante oefengebeurtenissen, uitsluitend via publieke contracts. |
OefenHub.LiveMonitoring | Leest actuele voortgang via publieke readcontracts; voert geen mutaties op runs uit. |
OefenHub.Reporting | Vraagt exportmodellen op voor PDF-generatie; PDF-output is geen brondata. |
OefenHub.Scheduling | Triggert opruiming van testruns, tijdelijke exports of retrybare naverwerking via publieke contracts. |
Practice mag geen interne entities, DbContexts of services van andere modules gebruiken. Cross-module communicatie verloopt via contracts, query-services, command-services of expliciet vastgelegde workflowstappen.
10.6 Kernobjecten op logisch niveau
Onderstaande objecten zijn logisch beschreven. De exacte tabel- en kolomdefinities blijven in de database-informatie.
| Object | Doel | Eigenaarschap |
|---|---|---|
| ExerciseRun | Hoofdrecord van één unieke oefenuitvoering. | Practice |
| ExerciseRunProgress | Vraaggebonden voortgang en status binnen een run. | Practice |
| SharedExercise | Administratief record voor een ontvangen gedeelde oefening voordat de ontvanger start. | Practice |
| PracticeHistoryReadModel | Afgeleid leesmodel voor geschiedenis. | Practice, onder Models/ReadModels |
| PracticeResultDetailReadModel | Afgeleid leesmodel voor resultaatdetail. | Practice, onder Models/ReadModels |
| PracticePdfExportModel | Tijdelijk exportmodel voor PDF-generatie. | Practice levert model, Reporting rendert output. |
| PracticeLiveProgressReadModel | Afgeleide actuele voortgang voor live meekijken. | Practice levert bron, LiveMonitoring toont. |
10.7 Run lifecycle
Een exercise run doorloopt een beperkte lifecycle. Er wordt bewust geen zware workflow-state-machine geïntroduceerd voor normale leerlingruns. De status wordt afgeleid uit start-, voortgangs- en afrondgegevens.
| Fase | Technische betekenis | Persistente bron |
|---|---|---|
| Aangemaakt | Runrecord bestaat met gekozen context, moduleversie en gegenereerde vraagset. | ExerciseRun + payload/progress. |
| Gestart | De eerste vraag kan of is getoond; starttijd is bekend. | ExerciseRun.StartedAtUtc en vraagprogressie. |
| Lopend | Eén of meer vragen zijn verwerkt, maar run is niet afgerond. | ExerciseRunProgress + uniforme voortgangsvelden. |
| Onderbroken | Gebruiker verlaat oefencontext, verbinding valt weg of logout vindt plaats zonder afronding. | Run blijft niet-afgerond. |
| Afgerond | Laatste vraag is definitief verwerkt en resultaatstap is bereikt. | ExerciseRun.CompletedAtUtc en statistiekvelden. |
| Historisch | Afgeronde run wordt gebruikt voor geschiedenis, resultaatdetail en export. | Opgeslagen runvelden + historische payload/snapshots. |
| Test-opruimbaar | Docenttestrun is afgerond of verlaten en mag volgens beleid worden verwijderd. | IsTestRun en schedulercriteria. |
Niet-afgeronde runs verschijnen niet als afgeronde geschiedenisregel. Zij kunnen wel worden gebruikt voor hervatten en live meekijken zolang de run toegankelijk en functioneel geldig is.
10.8 Starten van een nieuwe run
Een nieuwe run ontstaat alleen na een expliciete startactie vanuit een toegankelijke oefeningcontext. De UI mag deze startactie tonen, maar de server bepaalt opnieuw of starten is toegestaan.
10.8.1 Startflow
1. Web ontvangt startactie.
2. Web roept Practice-startcontract aan.
3. Practice vraagt server-side context/autorisatiecontrole aan.
4. Practice vraagt geldige catalogus- en modulecontext op.
5. Practice resolveert technische module via ExerciseModuleHost.
6. Module genereert vragen op basis van configuratie en gevraagde aantallen.
7. Practice maakt ExerciseRun en voortgangsbrondata aan.
8. Practice commit de run binnen PracticeDbContext.
9. Web navigeert naar het oefenscherm met de eerste vraag.
10.8.2 Vast te leggen startcontext
Bij start worden minimaal de volgende typen informatie vastgelegd of afgeleid:
| Gegeven | Technische behandeling |
|---|---|
| StudentUserId | Soft link naar identity/account. |
| LevelId | Soft link naar catalogusniveau. |
| CategoryId | Soft link naar cataloguscategorie of niveau-categoriekoppeling, afhankelijk van DB-model. |
| ExerciseId | Soft link naar catalogusoefening. |
| ExerciseModuleId | Soft link naar catalogusmodulemetadata. |
| ModuleKey | Snapshot of redundante technische sleutel voor module-resolving. |
| ModuleVersion | Snapshot van moduleversie/contractversie bij starten. |
| ConfigurationVersion | Snapshot of hash van gebruikte configuratiepayload. |
| LevelNameSnapshot | Historische weergave/fallback. |
| CategoryNameSnapshot | Historische weergave/fallback. |
| ExerciseNameSnapshot | Historische weergave/fallback. |
| CreatedAtUtc | Aanmaakmoment. |
| StartedAtUtc | Startmoment of eerste toonmoment, afhankelijk van implementatiekeuze. |
| TotalQuestionCount | Gevraagde/gegeneerde vraagsetgrootte. |
| IsTestRun | Onderscheid tussen reguliere run en docenttest. |
| SourceSharedExerciseId | Soft link naar gedeeld record indien gestart vanuit gedeelde oefening. |
| DuplicateOfRunId | Binnen practice te behandelen runrelatie voor opnieuw maken. |
Snapshots worden niet stilzwijgend aangepast bij toekomstige wijzigingen in catalogus, identiteit of modulemetadata. Zij waarborgen historische leesbaarheid.
10.9 Hervatten van runs
Hervatten gebruikt de laatst gestarte niet-afgeronde run binnen dezelfde oefening en actieve niveaucontext. De bepaling gebeurt server-side en gebruikt geen browserstate als autorisatiebron.
| Controle | Regel |
|---|---|
| Gebruiker | De run hoort bij de ingelogde leerling of geautoriseerde context. |
| Oefening | De run hoort bij dezelfde oefening als de geopende startpagina. |
| Niveau | De run hoort bij dezelfde actieve niveaucontext. |
| Status | De run is niet afgerond. |
| Toegang | De actuele context mag de run nog openen of hervatten. |
| Teststatus | Testoefeningen worden niet als reguliere hervatbare leerlingruns behandeld. |
Niet-afgeronde runs uit andere niveaus, niet langer toegankelijke contexten of testcontexten worden niet als normale hervatoptie aangeboden.
10.10 Vraagvoortgang
Na iedere bevestigde stap wordt voortgang server-side verwerkt. Dat geldt voor gewone antwoorden, Geen idee, antwoord tonen en de laatste stap richting resultaat.
| Veldtype | Doel |
|---|---|
| QuestionNumber | Stabiele volgordepositie binnen de run. |
| QuestionPayload | Module-specifieke vraagrepresentatie. |
| AnswerPayload | Module-specifieke antwoordrepresentatie. |
| UserAnswerPayload | Door gebruiker gegeven antwoordrepresentatie. |
| FirstShownAtUtc | Eerste toonmoment van de vraag. |
| AnsweredAtUtc | Definitieve antwoordverwerking. |
| IsCompleted | Administratieve afronding van deze vraag, niet van de hele run. |
| IsCorrect | Uniforme goed/fout-status. |
| IsDunno | Uniforme Geen idee-markering. |
| DurationMs | Afgeleide of opgeslagen doorlooptijd. |
| ResultState | Uniforme status voor UI/readmodels. |
IsCompleted op vraagniveau betekent uitsluitend dat die specifieke vraag administratief is afgerond. Dit is niet hetzelfde als runniveau-afronding.
10.11 Payloadstrategie
Technische modules kunnen sterk verschillende vraag-, antwoord- en configuratiestructuren hebben. Daarom worden module-specifieke gegevens niet volledig relationeel uitgesplitst.
| Payload | Eigenschap |
|---|---|
| Configuratiepayload | Catalogusbron voor oefeningconfiguratie; gebruikt bij start. |
| Vraagpayload | Module-specifieke vraagdata per gegenereerde vraag. |
| Antwoordpayload | Correct antwoord of evaluatiegegevens, module-specifiek. |
| Gebruikersantwoordpayload | Invoer van de leerling, module-specifiek. |
| Resultaatpayload | Eventuele module-specifieke toelichting of renderdata. |
Payloads zijn versieerbaar en moeten door de technische module geïnterpreteerd kunnen worden. De modulehost en concrete module zijn verantwoordelijk voor compatibele interpretatie van payloads binnen de ondersteunde versiegrenzen.
10.11.1 JSON/base64
De functionele basis gaat uit van JSON/base64-opslag voor module-specifieke payloads. Technisch betekent dit:
| Regel | Toelichting |
|---|---|
| JSON is de inhoudelijke structuur | De module serialiseert naar een afgesproken DTO-versie. |
| Base64 is opslag-/transportomhulling | Base64 is geen encryptie en mag niet als securitymaatregel worden gezien. |
| Payloadversie is verplicht | Iedere payload moet naar module- en schema-/contractversie herleidbaar zijn. |
| Uniforme velden blijven relationeel | Totalen, statussen, timing en filters staan niet uitsluitend in payloads. |
| Payloadherberekening is herstelpad | Normale UI, geschiedenis en PDF lezen uniforme velden en snapshots. |
Payloads mogen geen credentials, tokens of onnodige persoonsgegevens bevatten.
10.12 Antwoordverwerking
De generieke applicatie verwerkt de runstatus, voortgang en uniforme velden. De technische module voert de inhoudelijke antwoordcontrole uit.
1. Web stuurt antwoordactie naar Practice.
2. Practice valideert run, vraagpositie en autorisatie.
3. Practice resolveert de technische module en payloadversie.
4. Module evalueert het antwoord.
5. Practice slaat uniforme resultaatstatus en payloadupdates op.
6. Practice werkt runniveau-totalen bij.
7. Practice commit de vraagmutatie.
8. Practice maakt actuele voortgang beschikbaar voor live meekijken.
De evaluatie door de technische module mag geen directe databasewrites doen. Alle persistente runmutaties lopen via Practice.
10.13 Geen idee-actie
De Geen idee-actie is een aparte vraagroute. De waarschuwing en de verborgen voorkeur horen bij gebruikersinstellingen; de definitieve vraagmutatie hoort bij Practice.
| Stap | Technische regel |
|---|---|
| Eerste klik | Web vraagt, indien nodig, bevestiging via popup/voorkeur. |
| Annuleren | Er blijft geen definitieve fout- of afrondmutatie achter. |
| Bevestigen | Vraag wordt als IsDunno = true vastgelegd. |
| Score | Vraag telt uniform als fout. |
| Antwoord tonen | Juiste antwoord/renderdata wordt getoond via module-output. |
| Voorkeur | Waarschuw me niet weer wordt alleen opgeslagen bij bevestiging. |
10.14 Uniforme totalen
Uniforme totalen worden relationeel op runniveau bijgehouden zodat geschiedenis, live meekijken, resultaatweergave en PDF-export niet telkens volledige payloads hoeven te reconstrueren.
| Veldtype | Moment van bijwerken |
|---|---|
| TotalQuestionCount | Bij start/generatie. |
| CompletedQuestionCount | Na iedere bevestigde vraagafronding. |
| CorrectCount | Na iedere bevestigde antwoordverwerking. |
| IncorrectCount | Na iedere fout of Geen idee. |
| DunnoCount | Na bevestigde Geen idee. |
| CurrentQuestionNumber | Na navigatie/antwoordverwerking waar relevant. |
| StartedAtUtc | Bij start of eerste vraagtoonmoment. |
| CompletedAtUtc | Pas na definitieve runafronding. |
| IsCompleted | Pas na definitieve runafronding. |
De uniforme totalen zijn leidend voor reguliere schermweergave. Herberekening uit payloads is uitsluitend bedoeld als technische herstel- of controlemogelijkheid.
10.15 Statistiekberekening
Complexere statistiekvelden worden na afronding van een run berekend. De berekening gebruikt opgeslagen vraagtiming en uniforme vraagstatussen.
| Statistiek | Technische bron |
|---|---|
| Gemiddelde tijd per vraag | Vraagdoorlooptijden binnen afgeronde run. |
| Mediaan | Gesorteerde vraagdoorlooptijden. |
| Q1/Q3 | Onderste en bovenste helft van gesorteerde vraagdoorlooptijden. |
| IQR | Q3 minus Q1. |
| Ondergrens | Q1 - 1,5 × IQR. |
| Bovengrens | Q3 + 1,5 × IQR. |
| Uitschieters ondergrens | Vraagdoorlooptijden kleiner dan ondergrens. |
| Uitschieters bovengrens | Vraagdoorlooptijden groter dan bovengrens. |
| Totale doorlooptijd | Verschil tussen start- en afrondmoment of som van relevante vraagdoorlooptijden volgens vastgelegde definitie. |
| Snelste/langzaamste vraag | Min/max van vraagdoorlooptijden. |
De exacte berekeningsmethode moet deterministisch zijn en in tests worden vastgelegd. Afronding, eenheden en formatting worden niet door de database bepaald maar door services/readmodels/rendering.
10.15.1 Statistiekservice
De statistiekberekening hoort binnen Practice thuis, bijvoorbeeld als interne service:
Services/
Interfaces/
IPracticeStatisticsCalculator.cs
PracticeStatisticsCalculator.cs
Deze service is module-intern, tenzij andere modules expliciet dezelfde berekening nodig hebben. Andere modules lezen opgeslagen statistiekvelden via publieke readcontracts.
10.16 Afronden van een run
Afronden gebeurt pas nadat de laatste vraag definitief is verwerkt en de gebruiker de resultaatstap bereikt. De afronding is een kritieke mutatie binnen Practice.
1. Controleer run- en vraagstatus.
2. Controleer dat alle vereiste vragen administratief afgerond zijn.
3. Bereken uniforme eindtotalen en statistieken.
4. Vul CompletedAtUtc en IsCompleted.
5. Leg eventuele duplicate/shared-context vast.
6. Commit binnen PracticeDbContext.
7. Maak resultaatreadmodel beschikbaar.
8. Start alleen niet-kritieke vervolgacties na commit.
Wanneer afronding faalt, blijft de run niet-afgerond. De gebruiker krijgt een veilige foutmelding en er wordt correlation-logging vastgelegd.
10.17 Resultaatweergave
Resultaatweergave leest opgeslagen runcontext, vraagvoortgang, uniforme totalen en statistiekvelden. Het scherm rekent de functionele brondata niet opnieuw uit clientstate.
| Resultaatonderdeel | Bron |
|---|---|
| Naam gebruiker | Identity/accountcontext of snapshot/geanonimiseerde weergave. |
| Niveau/categorie/oefening | Snapshots op runniveau, met live fallback alleen als passend. |
| Vraagdetails | ExerciseRunProgress en module-renderrepresentatie. |
| Gegeven antwoord | UserAnswerPayload via module-rendering. |
| Juiste antwoord | AnswerPayload of module-evaluatie-output. |
| Resultaatstatus | Uniforme vraagstatus. |
| Statistieken | Opgeslagen runstatistieken. |
| Duplicaatmelding | Runrelatie binnen practice. |
10.18 Geschiedenis
Geschiedenisreadmodels tonen alleen afgeronde, niet-test runs binnen de geautoriseerde context.
| Context | Begrenzing |
|---|---|
| Leerling | Eigen afgeronde runs binnen gekozen oefening/niveau of alles-context. |
| Docent | Afgeronde leerlingruns binnen eigen docentcontext en niveauautorisaties. |
| Ouder/voogd | Afgeronde runs van actief gekoppelde kinderen over alle historische niveaus. |
| Beheerder | Alleen via expliciete beheer-/supportcontext en volgens autorisatieregels. |
Filters en paginering zijn readmodelgedrag en wijzigen geen run, voortgang, resultaat of statistiek.
10.19 Gedeelde oefeningen
Een gedeelde oefening is eerst een administratief record. Pas wanneer de ontvanger start, ontstaat een eigen zelfstandige exercise run.
| Stap | Technische verwerking |
|---|---|
| Delen vanuit afgeronde run | Practice maakt shared record aan na relatiecontrole. |
| Informeren ontvanger | Via Communication, kritiek of niet-kritiek afhankelijk van functionele ingang. |
| Overzicht ontvangen | Leest shared records die voor ontvanger zichtbaar zijn. |
| Eerste start door ontvanger | Maakt zelfstandige run met broninhoud en oorspronkelijke volgorde. |
| Opnieuw maken door ontvanger | Maakt nieuwe zelfstandige run, eventueel met andere volgorde. |
| Verwijderen uit ontvangen overzicht | Soft-deactivatie/verbergmarkering van shared record voor ontvanger. |
| Geschiedenis | Reeds afgeronde runs blijven zichtbaar via geschiedenis. |
10.19.1 Snapshotgedrag bij delen
Shared records bewaren tekstuele momentopnamen van niveau, categorie en oefening zoals die waren op het moment van delen. Toekomstige categorie- of modulemigraties herschrijven deze snapshots niet.
10.20 Opnieuw maken en duplicaten
Maak deze oefening opnieuw maakt altijd een nieuwe zelfstandige run. De bronrun blijft ongewijzigd.
| Onderdeel | Regel |
|---|---|
| Bronrun | Blijft historisch ongewijzigd. |
| Nieuwe run | Krijgt eigen voortgang, antwoorden, totalen en statistieken. |
| Vraaginhoud | Wordt gebaseerd op de bronrun en modulepayloads. |
| Volgorde | Mag worden gewijzigd volgens functionele regel. |
| DuplicateOfRunId | Binnen practice vastgelegd als runrelatie. |
| Modulebeschikbaarheid | Als module niet meer bruikbaar is, is opnieuw maken niet beschikbaar. |
10.21 Docenttestruns
Docenttestruns mogen technisch dezelfde runstructuren gebruiken, maar worden geïsoleerd via IsTestRun en verschijnen niet in reguliere resultaten of geschiedenis.
| Aspect | Regel |
|---|---|
| Opslag | Mag tijdelijk in practice worden opgeslagen. |
| Geschiedenis | Niet zichtbaar in reguliere leerling-, docent- of oudergeschiedenis. |
| Statistieken | Niet meetellen in normale readmodels/tellers. |
| Opruiming | Via OefenHub.Scheduling en Practice-contract. |
| Achtergebleven data | Periodiek opruimbaar op basis van status, leeftijd en testflag. |
10.22 PDF-brondata
PDF-export gebruikt dezelfde historische bron als resultaatweergave. Practice levert een exportmodel; Reporting of de PDF-exportservice rendert met QuestPDF.
Web
→ Reporting/PDF-exportcontract
→ PracticePdfExportReader
→ PracticePdfExportModel
→ QuestPDF-rendering
→ tijdelijke downloadresponse
| PDF-onderdeel | Bron |
|---|---|
| Bestandsnaamcontext | Run-snapshots en veilige formattering. |
| Samenvatting | Opgeslagen runvelden. |
| Vraag-/antwoordtabel | ExerciseRunProgress + module-exportrepresentatie. |
| Statistieken | Opgeslagen runstatistieken. |
| Duplicaatmelding | Runrelatie in practice. |
| Footer/exportdatum | Exportmoment, niet runbron. |
PDF-bestanden worden niet als permanente waarheid opgeslagen. Bij opnieuw downloaden wordt de PDF opnieuw opgebouwd uit databasegegevens, snapshots en module-exportrepresentaties voor zover beschikbaar.
10.23 Live meekijken
Live meekijken leest server-side opgeslagen voortgang uit Practice. LiveMonitoring is eigenaar van live sessie-audit en SignalR-weergave, maar niet van oefenvoortgang.
| Onderdeel | Eigenaar |
|---|---|
| Vraagvoortgang | Practice |
| Actuele livevraag | Practice, afgeleid uit runprogressie. |
| SignalR-transport | LiveMonitoring / Web. |
| LiveViewAudit | LiveMonitoring of volgens database-informatie vastgelegde eigenaar. |
| Meekijkautorisatie | Authorization + contextmodule. |
Na ieder bevestigd antwoord maakt Practice de actuele voortgang beschikbaar. SignalR-updates mogen falen zonder de oefenvoortgang terug te draaien; de volgende read of reconnect gebruikt de database als bron.
10.24 Soft links en snapshots
Omdat Practice verwijst naar gebruikers, catalogusobjecten en modules buiten de eigen module, zijn cross-module verwijzingen standaard soft links met snapshots.
| Verwijzing | Technische behandeling |
|---|---|
| StudentUserId | Soft link naar identity. |
| LevelId | Soft link naar catalog. |
| CategoryId | Soft link naar catalog. |
| ExerciseId | Soft link naar catalog. |
| ExerciseModuleId | Soft link naar catalog/modules metadata. |
| SourceSharedExerciseId | Binnen practice, harde FK mogelijk. |
| DuplicateOfRunId | Binnen practice, harde FK mogelijk met zorgvuldig delete behavior. |
| CreatedBy/ActorId bij test of deelactie | Soft link + eventueel snapshot. |
Snapshots zijn fallback en historische representatie. Zij worden niet gebruikt om autorisatie te verruimen.
10.25 Transaction boundaries
De transaction boundary wordt per workflow bepaald. Practice is verantwoordelijk voor atomaire bronmutaties binnen de eigen module. Cross-module stappen worden alleen in dezelfde functionele transactie geplaatst wanneer de gebruikersactie anders onvolledig of onbereikbaar wordt.
| Workflow | Kritieke mutaties | Naverwerking |
|---|---|---|
| Start nieuwe run | Cataloguscontext valideren, vragen genereren, run en progressie opslaan. | Eventuele readmodel/cache-update. |
| Vraag beantwoorden | Vraagstatus, payload, totalen en voortgang opslaan. | SignalR-update/live notificatie. |
| Run afronden | Laatste voortgang, totalen, statistieken en afrondstatus opslaan. | Badge/readmodel, eventuele communicatie. |
| Delen van oefening | Shared record en eventueel kritieke ingang voor ontvanger. | Niet-kritieke badges of notificaties. |
| Docenttestrun opruimen | Testdata verwijderen/markeren volgens beleid. | Technische log/update. |
| PDF-export | Geen runmutatie; exportmodel lezen. | Tijdelijk bestand opschonen via scheduler. |
Bij twijfel wordt een stap als kritiek behandeld totdat het tegendeel per workflow is onderbouwd.
10.26 Concurrency en idempotentie
Oefenruns zijn gevoelig voor dubbele submits, browserrefreshes en reconnects. De backend moet herhaald aangeboden acties veilig verwerken.
| Situatie | Technische maatregel |
|---|---|
| Dubbel antwoordsubmit | Controleer vraagstatus en accepteer geen tweede definitieve antwoordmutatie zonder expliciete regel. |
| Refresh na antwoord | Lees opgeslagen voortgang en toon passende volgende staat. |
| Afronden dubbel gestart | Afronding idempotent maken of tweede poging veilig afwijzen. |
| Gelijktijdig live lezen | Alleen read-only toegang; geen lock op oefenflow. |
| Reconnect tijdens oefening | Databaseprogressie opnieuw uitlezen. |
| Scheduler cleanup | Alleen testdata of tijdelijke data selecteren met veilige criteria. |
Optimistic concurrency via rowversion/concurrency token is gewenst op run- en/of progressierecords waar dubbele mutaties risico geven.
10.27 Autorisatie en contextcontrole
Iedere command- en queryingang van Practice herhaalt server-side autorisatie- en contextcontrole.
| Actie | Controle |
|---|---|
| Start run | Leerlingcontext, niveau, categorie, oefening, modulebeschikbaarheid. |
| Hervat run | Eigen run, niet-afgerond, juiste actieve context, toegankelijke oefening. |
| Beantwoord vraag | Eigen actieve run, juiste vraagstatus, geen afgeronde run. |
| Bekijk resultaat | Geautoriseerde afgeronde run. |
| Download PDF | Zelfde autorisatie als resultaatdetail/exportcontext. |
| Deel oefening | Eigen afgeronde run, delen toegestaan, actieve vriendrelatie. |
| Docentgeschiedenis | Docentcontext en eigen niveauautorisaties. |
| Ouder-/voogdgeschiedenis | Actieve GuardianStudent-relatie. |
| Live brondata | Geautoriseerde live-meekijkcontext. |
Routeparameters, browserstate, zichtbare knoppen en clientfilters zijn nooit autorisatiebewijs.
10.28 Logging en correlation
Practice moet alle kritieke runmutaties herleidbaar loggen zonder gevoelige payloads in technische logs op te nemen.
| Logcontext | Verplicht waar relevant |
|---|---|
| CorrelationId | Koppelt HTTP-request, workflow, job en domeinactie. |
| RunId | Alleen in technische logs; niet per se gebruikersgericht tonen. |
| ActorUserId | Soft link of geanonimiseerde context volgens privacyregels. |
| RoleContext | Leerling, docent, ouder/voogd, beheerder of systeemjob. |
| Action | Start, submit answer, complete, share, export, cleanup. |
| Result | Success, validation failed, authorization denied, technical failure. |
| ErrorCode | Gestandaardiseerde foutcode, geen ruwe exceptiontekst naar gebruiker. |
Payloadinhoud, volledige antwoorden, tokens, cookies en credentials worden niet in technische logs opgenomen.
10.29 Security- en privacygrenzen
| Onderwerp | Regel |
|---|---|
| Antwoorddata | Alleen tonen aan geautoriseerde contexten. |
| Payloads | Geen credentials/tokens; minimale noodzakelijke data. |
| Snapshots | Persoonsgegevens alleen waar functioneel noodzakelijk en anonimiseerbaar. |
| PDF-export | Tijdelijk bestand; veilige naamgeving en cleanup. |
| Testdata | Geen productiepersoonsdata in testfixtures. |
| Live meekijken | Read-only; geen mutaties door meekijkers. |
| Soft links | Mogen geen autorisatie vervangen. |
Bij accountanonimisering moeten runbrondata, snapshots en historische weergavewaarden volgens het privacyhoofdstuk worden behandeld. Historische functionele reconstructie mag blijven bestaan zonder actuele persoonsgegevens te tonen.
10.30 Readmodels binnen Practice
Readmodels staan onder Models/ReadModels en zijn module-eigen. Zij zijn geen aparte bron van waarheid.
Voorbeelden:
Models/
ReadModels/
StudentExerciseHistoryReadModel.cs
TeacherPracticeHistoryReadModel.cs
GuardianResultSummaryReadModel.cs
PracticeResultDetailReadModel.cs
PracticePdfExportModel.cs
PracticeLiveProgressReadModel.cs
Readmodels mogen query-optimalisaties bevatten, maar mogen geen autorisatiecontrole vervangen. De query-service voert of vraagt de vereiste server-side contextcontrole uit.
10.31 Publieke contracts
Andere modules en Web gebruiken alleen publieke contracts. Voorbeelden:
Contracts/
IPracticeRunCommandService.cs
IPracticeAnswerCommandService.cs
IPracticeHistoryReader.cs
IPracticeResultReader.cs
IPracticePdfExportReader.cs
IPracticeLiveProgressReader.cs
ISharedExerciseService.cs
Models/
StartRunRequest.cs
StartRunResult.cs
SubmitAnswerRequest.cs
PracticeResultDetailDto.cs
PracticePdfExportDto.cs
Interne services zoals statistiekberekening, payloadserialisatie of progressiemutatie blijven buiten Contracts.
10.32 Teststrategie
| Testtype | Dekking |
|---|---|
| Unit tests | Statistiekberekening, payloadmapping, runstatusregels, Geen idee-logica. |
| Module integration tests | PracticeDbContext, migrations, seeddata indien aanwezig, progressiewrites. |
| Contract tests | Publieke Practice-contracts en DTO-gedrag. |
| Cross-module integration tests | Startflow met Catalog/Authorization/ModuleHost, delen met Relationships/Communication. |
| Concurrency tests | Dubbele submits, dubbel afronden, reconnect-situaties. |
| Security tests | Toegang tot resultaat/PDF/livebron vanuit verkeerde context. |
| PDF-bronmodeltests | Exportmodel bevat historische snapshots en correcte statistiekdata. |
| Scheduling tests | Testrun cleanup en tijdelijke exportcleanup via Scheduling-contracts. |
10.33 Implementatiechecklist
OefenHub.Practiceheeft éénPracticeDbContexten schemapractice.- Entities, DbContext en implementatieclasses zijn standaard
internal. - Publieke module-ingangen staan onder
Contracts. - Cross-module verwijzingen naar identity, catalogus en modulemetadata zijn soft links met snapshots waar nodig.
- Binnen
practicemogen harde FK’s worden gebruikt voor eigen aggregate- en historyrelaties. - Uniforme totalen en statistieken staan relationeel beschikbaar.
- Payloads zijn versieerbaar en bevatten geen credentials of tokens.
- Antwoordverwerking en runafronding zijn server-side en transactioneel veilig.
- Resultaten, geschiedenis, live meekijken en PDF-export lezen dezelfde historische runbron.
- Docenttestruns zijn herkenbaar, niet regulier zichtbaar en opruimbaar.
- Schedulingacties gebruiken publieke contracts en correlation-id’s.
- Autorisatie wordt per command/query server-side herhaald.
- Logs bevatten correlation en technische context, maar geen gevoelige payloadinhoud.
10.34 Implementatieverificaties
| Onderwerp | Te controleren keuze |
|---|---|
| Startmomentdefinitie | Definitief bepalen of StartedAtUtc bij runcreatie of eerste vraagtoonmoment wordt gezet. |
| Payloadopslag | Exacte JSON/base64-serialisatiestrategie en compressiebeleid bepalen. |
| Concurrency token | Vastleggen op welke run/progress-tabellen rowversion nodig is. |
| Statistiekafronding | Afrondingsregels, tijdseenheden en null/edge cases testen en documenteren. |
| Moduleversiecompatibiliteit | Minimumbeleid bepalen voor oude payloads na modulemigraties. |
| PDF-exportmodel | Exacte scheiding tussen PracticePdfExportModel en QuestPDF-rendering bepalen. |
| Live progress reader | Bepalen welke progressievelden minimaal voor live meekijken nodig zijn. |
| SharedExercise-transactie | Per deelworkflow bepalen of systeembericht kritieke ingang of naverwerking is. |
| Testrun cleanup | Exacte leeftijdscriteria en batchgrootte bepalen. |
| Anonimisering | Definitieve anonimiseringswaarden voor persoonsgebonden snapshots afstemmen met privacyhoofdstuk. |