Skip to main content

PDF-export met QuestPDF

16.1 Doel en scope

Dit hoofdstuk beschrijft hoe OefenHub PDF-export technisch realiseert met QuestPDF. De PDF-export is bedoeld voor resultaatweergaven van afgeronde oefenruns en gebruikt dezelfde historische brondata als de schermweergave van resultaten.

De PDF-export is geen zelfstandige bron van waarheid. Een PDF-bestand is een tijdelijke representatie die op aanvraag wordt opgebouwd uit opgeslagen rungegevens, voortgangsdata, snapshots, statistiekvelden en module-specifieke exportrepresentaties.

Dit hoofdstuk beschrijft:

  • de verantwoordelijkheid van OefenHub.Reporting;
  • de relatie met OefenHub.Practice, OefenHub.ExerciseModuleHost en concrete oefenmodules;
  • de technische opbouw van PDF-documenten met QuestPDF;
  • de bestandsnaamlogica;
  • de omgang met tabellen, pagina-einden, headers en footers;
  • tijdelijke opslag en cleanup;
  • autorisatie, logging, privacy en foutafhandeling;
  • teststrategie en implementatiecontrole.

Dit hoofdstuk definieert geen nieuwe functionele inhoud van de PDF. De functionele inhoud volgt uit Functioneel Ontwerp, Software Requirements Specification, schermdocumentatie en de historische runcontext.

16.2 Eigenaarschap en modulegrenzen

PDF-export valt technisch onder OefenHub.Reporting. Deze module is verantwoordelijk voor exportorkestratie, documentopbouw en tijdelijke output. De module is niet eigenaar van oefenruns, vraaginhoud, resultaten, relaties, autorisaties of moduleconfiguraties.

OnderdeelEigenaarToelichting
ExportorkestratieOefenHub.ReportingBouwt exportmodel op via publieke query-services en rendercontracten.
OefenrunbrondataOefenHub.PracticeLevert afgeronde run, voortgang, antwoorden, totalen, statistieken en snapshots.
Module-specifieke vraag-/antwoordrepresentatieConcrete oefenmodule via OefenHub.ExerciseModuleHostLevert PDF-veilige representaties voor bijvoorbeeld breuken, machten of samengestelde antwoorden.
AutorisatiecontroleOefenHub.Authorization met context uit betrokken domeinenControleert of gebruiker de run mag exporteren.
BestandresponseOefenHub.WebStuurt tijdelijke PDF als download naar de gebruiker.
Tijdelijke bestandsopslagOefenHub.Reporting / infrastructuurconfiguratieAlleen voor tijdelijke output, niet als bronrecord.
CleanupOefenHub.Scheduling triggert, Reporting voert uitVerwijdert verlopen tijdelijke exportbestanden.

OefenHub.Reporting mag geen directe toegang hebben tot PracticeDbContext, CatalogDbContext of module-interne entities. Alle brondata wordt opgehaald via publieke contracts/query-services.

Voorbeeld van toegestane afhankelijkheden:

OefenHub.Reporting
gebruikt:
- IExerciseRunExportReader uit OefenHub.Practice.Contracts
- IAuthorizationContextService uit OefenHub.Authorization.Contracts
- IExerciseModuleExportRenderer via OefenHub.ExerciseModuleHost
- IClock / Correlation primitives uit SharedKernel

Niet toegestaan:

OefenHub.Reporting
gebruikt niet:
- PracticeDbContext
- ExerciseRun entity
- CatalogDbContext
- concrete module-interne services
- Web componenten of Razor markup

16.3 Plaats in de solution

De technische PDF-export gebruikt de volgende projectstructuur.

src/
OefenHub.Reporting/
Contracts/
IResultPdfExportService.cs
Models/
Enums/
Models/
ReadModels/
Internal/
Services/
Interfaces/
Rendering/
Documents/
Files/
Extensions/

OefenHub.Practice/
Contracts/
IExerciseRunExportReader.cs
Models/
ExerciseRunExportModel.cs

OefenHub.ExerciseModuleHost/
Contracts of public services for module resolving

OefenHub.Modules.Abstractions/
IExerciseModulePdfRenderer.cs
Models for module export representation

OefenHub.Web/
Pages or Components that request export

OefenHub.Reporting krijgt alleen een eigen ReportingDbContext en schema reporting als via een expliciet technisch ontwerpbesluit persistente exportmetadata, exportjobs of exporthistorie functioneel nodig zijn. In de eerste technische baseline is PDF-output tijdelijk en wordt er geen verplicht permanent documentrecord aangemaakt.

16.4 Exportflow op hoofdlijnen

Een PDF-export verloopt technisch in vaste stappen.

StapVerantwoordelijke moduleResultaat
1. Gebruiker vraagt export aanWebExportcommand met run-id en actuele rolcontext.
2. AutorisatiecontroleAuthorization en brondomeinAlleen toegestane gebruiker mag verder.
3. Exportbrondata ophalenPracticeHistorisch exportmodel met run, voortgang, snapshots en statistieken.
4. Module-exportrepresentaties ophalenExerciseModuleHost en concrete modulePDF-veilige vraag- en antwoordweergave.
5. PDF-document opbouwenReportingQuestPDF-documentobject.
6. Tijdelijk bestand of stream makenReportingPDF-bytearray, stream of tijdelijk bestand.
7. Downloadresponse leverenWebBrowser ontvangt PDF met veilige bestandsnaam.
8. Tijdelijke output opruimenScheduling + ReportingOude tijdelijke bestanden worden verwijderd.

De export schrijft de oefenrun, voortgang, score, statistieken, relaties of gebruikersinstellingen niet bij.

16.5 Exportbronnen

De export gebruikt uitsluitend historische en geautoriseerde brondata. Er wordt niet opnieuw functioneel berekend vanuit clientstate.

BronGebruik in PDF
ExerciseRun-exportmodelHoofdcontext, gebruiker, niveau, categorie, oefening, module, aantallen en statussen.
ExerciseRunProgress/exportregelsVraagvolgorde, opgave, gegeven antwoord, juiste antwoord, resultaat, Geen-idee-markering en timings.
RunstatistiekenGemiddelde tijd, mediaan, grenzen, uitschieters, totale doorlooptijd en overige opgeslagen statistiekvelden.
SnapshotsHistorische naamgeving en context wanneer actuele brondata is gewijzigd of niet meer bruikbaar is.
Module-exportrepresentatiePDF-veilige weergave van module-specifieke inhoud.
ExportaanvragercontextAutorisatie, logging en bestandsnaamcontext.

De export mag actuele catalogus- of moduledetaildata alleen gebruiken wanneer deze volgens het Practice-exportmodel nog geldig en passend is. Bij conflict of ontbrekende actuele data zijn de snapshots en opgeslagen payloads leidend.

16.6 Autorisatie per exportcontext

Iedere PDF-export herhaalt server-side de autorisatiecontrole. Een run-id, routeparameter, browsergeschiedenis of eerder geopende resultaatweergave is nooit voldoende.

ExportcontextAutorisatiegrens
LeerlingEigen afgeronde niet-test run binnen toegestane context, of eigen historische run volgens leerlingregels.
DocentAfgeronde leerlingrun binnen de eigen docentcontext en niveauautorisatiegrens.
Ouder/voogdAfgeronde run van actief gekoppeld kind, over alle historische niveaus.
BeheerderAlleen wanneer een expliciete beheer- of supportflow dit toestaat; reguliere live meekijk- of leerlingcontext wordt niet geïmpliceerd.

Bij ontbrekende toegang retourneert de export geen gedeeltelijke resultaatdata en geen PDF met foutinhoud. De gebruiker krijgt een veilige foutafhandeling vanuit Web.

16.7 Exportmodel vanuit Practice

OefenHub.Practice levert een exportmodel dat onafhankelijk is van EF Core entities.

Voorbeeldstructuur:

ExerciseRunExportModel
RunId
ViewerContext
StudentSnapshot
LevelSnapshot
CategorySnapshot
ExerciseSnapshot
ModuleKey
ModuleVersionSnapshot
CompletedAtUtc
TotalQuestionCount
TotalCorrect
TotalIncorrect
TotalDunno
Statistics
Questions[]

Voor een vraagregel:

ExerciseRunExportQuestionModel
QuestionNumber
ModuleQuestionPayload
ModuleAnswerPayload
GivenAnswerPayload
IsCorrect
IsDunno
FirstShownAtUtc
CompletedAtUtc
Duration

Reporting mag deze payloads niet zelf inhoudelijk interpreteren buiten generieke velden om. Module-specifieke payloads worden via ExerciseModuleHost aan de juiste module-exportrenderer aangeboden.

16.8 Module-specifieke PDF-representatie

Concrete oefenmodules kunnen een PDF-representatie leveren voor vraag, gegeven antwoord en juiste antwoord. Dit voorkomt dat Reporting kennis nodig heeft van breuken, wortels, machten, taalopgaven of samengestelde antwoordstructuren.

Het contract kan conceptueel bestaan uit:

IExerciseModulePdfRenderer
RenderQuestionForPdf(modulePayload, exportContext)
RenderGivenAnswerForPdf(modulePayload, answerPayload, exportContext)
RenderCorrectAnswerForPdf(modulePayload, exportContext)

De concrete returnvorm moet QuestPDF-veilig zijn. Dit kan bijvoorbeeld een neutrale export-DTechnisch Ontwerp zijn en geen directe afhankelijkheid op Web/Razor.

Voorbeeld:

ModulePdfContent
PlainText
InlineParts[]
AccessibilityText
RenderKind

Mogelijke renderkinderen:

RenderKindGebruik
PlainTextSimpele tekst of getallen.
RichTextPartsBeperkte opmaak, bijvoorbeeld vet of superscript.
MathExpressionModule-specifieke wiskundige notatie.
FallbackTextVeilige fallback wanneer rijke rendering niet beschikbaar is.

Een concrete module mag geen QuestPDF-documentlayout van het hele resultaat bepalen. De module levert alleen inhoudsrepresentatie voor haar eigen vraag-/antwoordonderdelen. De generieke PDF-structuur blijft eigendom van Reporting.

16.9 QuestPDF-documentopbouw

De PDF wordt opgebouwd als generiek resultaatdocument met vaste secties.

SectieInhoud
TitelblokTitel, subtitel met categorie en oefening, eventueel contextlabel.
SamenvattingGebruiker, afrondmoment, aantal vragen, goed, fout, Geen idee indien van toepassing.
ResultaattabelVraagnummer, vraag, gegeven antwoord, juiste antwoord, resultaat en markering.
StatistiekenGemiddelde, mediaan, ondergrens, bovengrens, doorlooptijd en uitschieters.
Duplicaat-/deelcontextAlleen wanneer de historische runcontext dit vereist.
FooterApplicatienaam, paginanummering en exportdatum.

De documentopbouw is generiek. Module-specifieke inhoud wordt alleen binnen tabelcellen of detailblokken geplaatst via de module-exportrepresentatie.

16.10 Layoutregels

De PDF moet leesbaar blijven bij lange vragen, lange antwoorden en meerdere pagina’s.

OnderdeelRegel
PaginaformaatStandaard A4, portrait, tenzij via een expliciet technisch ontwerpbesluit expliciet anders besloten.
MargesVaste marges met voldoende ruimte voor footer.
LettertypeTechnisch te kiezen beschikbaar font; geen fontbestand meeleveren in repository zonder licentiecontrole.
ResultaattabelKolomheaders worden op vervolgpagina’s herhaald.
TabelrijenEen rij wordt bij voorkeur niet over pagina’s gesplitst.
Lange tekstVraag en antwoorden mogen over meerdere regels afbreken.
VervolgtekstBij voortzetting van de resultaattabel wordt een duidelijke vervolgindicatie getoond.
StatistiekensectieWaar mogelijk als geheel bij elkaar houden.
FooterOp iedere pagina dezelfde structuur.

De export mag tekst niet inhoudelijk inkorten om layoutproblemen op te lossen. Als tekst lang is, moet de layout daarop reageren met regelafbreking en paginaovergang.

De footer is generiek en consistent.

Links: OefenHub
Midden: Pagina X van Y
Rechts: Exportdatum

De exportdatum wordt bepaald op het moment dat de PDF wordt gegenereerd. Het afrondmoment van de oefenrun blijft de datum die in de samenvatting wordt getoond voor het resultaat.

De footer mag geen persoonsgegevens tonen.

16.12 Bestandsnaamlogica

De bestandsnaam wordt server-side opgebouwd en gesanitized.

Basisvorm:

<yyyy_MM_dd-HH.mm>_OefenHub_Resultaat_<Categorie>_<Oefening>.pdf

Regels:

RegelToelichting
DatumdeelGebaseerd op exportmoment of functioneel gekozen exporttijd; standaard exportmoment.
Ongeldige tekensVerwijderen of vervangen door veilige underscore.
SpatiesNormaliseren en vervangen door underscores.
Dubbele underscoresSamenvoegen.
Lengte categorieBegrenzen volgens functionele bestandsnaamregels.
Lengte oefeningBegrenzen volgens functionele bestandsnaamregels.
ExtensieAltijd .pdf.
PersoonsgegevensNiet opnemen in bestandsnaam.

Voorbeeld:

2026_05_08-14.32_OefenHub_Resultaat_Rekenen_Optellen.pdf

De bestandsnaam gebruikt snapshots wanneer de historische context dat vereist.

16.13 Tijdelijke output en opslag

PDF-bestanden zijn tijdelijke output. Zij worden niet als permanente documenten opgeslagen tenzij via een expliciet technisch ontwerpbesluit een aparte functionele eis wordt toegevoegd.

Mogelijke technische varianten:

VariantGebruik
Directe streamKleine exports die direct naar de browser kunnen worden gestuurd.
Tijdelijk bestandGrotere exports of situaties waarin streaming technisch onhandig is.
In-memory bytearrayAlleen voor kleine exports en testscenario’s.

Tijdelijke bestanden worden opgeslagen op een geconfigureerde locatie buiten publieke webroot. De opslaglocatie mag niet rechtstreeks via URL benaderbaar zijn.

Voor tijdelijke bestanden moet minimaal worden vastgelegd in technische logging:

CorrelationId
ExportRequestId indien aanwezig
RunId
ViewerUserId
ViewerRoleContext
FileName
CreatedAtUtc
StorageKind
CleanupEligibleAtUtc

De logging mag geen vraaginhoud, antwoorden of volledige payloads bevatten.

16.14 Cleanup van tijdelijke exports

Cleanup wordt uitgevoerd via OefenHub.Scheduling en domeinlogica in OefenHub.Reporting.

OnderdeelRegel
TriggerPeriodieke TickerQ-job.
Eigenaarschap joblifecycleOefenHub.Scheduling.
Eigenaarschap exportcleanupOefenHub.Reporting.
SelectieTijdelijke exportbestanden met CleanupEligibleAtUtc in het verleden; standaard na 24 uur.
PlanningMinimaal iedere 6 uur of vaker wanneer de opslaggroei dat vereist.
Hard maximumBestanden ouder dan 48 uur signaleren als cleanupachterstand.
IdempotentieOpnieuw uitvoeren mag geen fout geven wanneer bestand al weg is.
FoutafhandelingFout bij één bestand blokkeert cleanup van andere bestanden niet.
LoggingCorrelationId/job-id, aantal verwerkt, aantal fouten en foutcategorieën.

De bewaartermijn is configureerbaar, maar de V1.0-startwaarde is 24 uur. Verlenging van de bewaartermijn is alleen toegestaan wanneer daar een technische beheerreden voor is en privacy, opslagruimte en monitoring opnieuw zijn beoordeeld.

16.15 Transaction boundaries

PDF-export is in principe read-only voor de functionele brondomeinen.

ActieTransactiebeleid
Runexportmodel ophalenRead-only query binnen Practice.
Module-exportrepresentatie opbouwenGeen databasewrite in concrete module.
PDF genererenGeen functionele databasewrite.
Tijdelijk bestand schrijvenTechnische IO-actie, geen bronmutatie.
ExportloggingTechnische logging, geen functioneel exportrecord tenzij via een expliciet technisch ontwerpbesluit besloten.
CleanupTechnische verwijdering van tijdelijke output.

Als via een expliciet besluit persistente exportrecords worden toegevoegd, moet opnieuw worden bepaald of exportregistratie onderdeel is van de gebruikersactie of alleen technische logging is.

16.16 Foutafhandeling

PDF-export kent veilige foutcategorieën.

FoutcategorieGebruikersreactieTechnische afhandeling
Geen toegangVeilige toegang-geweigerdmeldingSecurity/access-denied logging zonder payload.
Run niet gevondenNiet-beschikbaarmeldingLogging met run-id en correlation-id.
Run niet afgerondExport niet beschikbaarGeen PDF genereren.
Module niet beschikbaarExport probeert fallbackrepresentatie indien toegestaanLogging met modulekey en versie.
Modulepayload ongeldigExport faalt veilig of gebruikt expliciete fallbackGeen stacktrace naar gebruiker.
QuestPDF-renderfoutExportfoutmeldingTechnische foutlog met correlation-id.
Tijdelijke opslag foutExportfoutmeldingLogging met storagekind, geen payload.
CleanupfoutGeen gebruikersimpactJoblogging en retry/failurestatus.

Gebruikers krijgen nooit technische details, stacktraces, padnamen, modulepayloads of interne identifiers te zien.

16.17 Fallback bij moduleproblemen

Historische runs moeten zo lang mogelijk exporteerbaar blijven. Toch kan een gekoppelde module in de toekomst gewijzigd, verwijderd of incompatibel zijn.

Fallbackvolgorde:

  1. Gebruik de moduleversie of compatibele renderer die bij de run hoort.
  2. Gebruik een backwards-compatible renderer van dezelfde modulefamilie.
  3. Gebruik opgeslagen modulepayload en snapshots om veilige tekstrepresentatie te tonen als de module dit ondersteunt.
  4. Toon een expliciete niet-beschikbare representatie voor de betreffende vraag of export faalt veilig, afhankelijk van functionele afspraken.

De PDF mag niet stilzwijgend verkeerde antwoorden of foutieve vraagrepresentaties tonen. Bij twijfel faalt de export veiliger dan dat onjuiste onderwijsinhoud wordt geproduceerd.

16.18 Privacy en gegevensbescherming

PDF-export bevat mogelijk persoonsgegevens en onderwijsresultaten. Daarom gelden strikte privacyregels.

OnderwerpRegel
AutorisatieIedere export hercontroleert server-side toegang.
BestandsnaamGeen leerlingnaam of andere persoonsgegevens.
Tijdelijke opslagNiet publiek benaderbaar.
LoggingGeen vraaginhoud, antwoorden of volledige payloads.
CleanupTijdelijke bestanden worden periodiek verwijderd.
BrowsercacheDownloadheaders moeten zo worden gekozen dat onbedoelde langdurige caching wordt beperkt waar passend.
AnonimiseringBij herexport na anonimisering worden geanonimiseerde snapshotwaarden gebruikt volgens privacybeleid.

Wanneer een eerder afgeronde run nog exporteerbaar blijft na accountanonimisering, moet de export de dan geldende geanonimiseerde waarden gebruiken en geen actuele persoonsgegevens reconstrueren.

16.19 Securitygrenzen

PDF-export volgt dezelfde securitybaseline als de rest van OefenHub.

GrensRegel
RouteparametersGeen autorisatiebewijs.
QuerystringGeen gevoelige payloads.
Tijdelijke downloadtokenAlleen gebruiken indien nodig, kortlevend en server-side gevalideerd.
BestandspadenNiet zichtbaar voor gebruiker.
HTML/Rich textNiet als actieve inhoud renderen; alleen veilige exportrepresentatie.
Module-outputWordt beschouwd als onbetrouwbaar totdat gevalideerd door modulecontract.
Externe resourcesPDF gebruikt geen ongecontroleerde externe resources.

Concrete oefenmodules mogen geen externe afbeeldingen, scripts, fonts of bestanden in de PDF trekken zonder expliciete platformondersteuning en securitycontrole.

16.20 Performance en schaalbaarheid

PDF-generatie kan relatief zwaar zijn. Daarom gelden technische grenzen.

OnderwerpRegel
ExportgrootteBegrens maximale vraag-/rijaantallen of bewaak timeouts.
RenderingtijdGebruik cancellation tokens en timeouts waar technisch mogelijk.
Parallelle exportsRate limiting of throttling kan nodig zijn.
GeheugenVermijd onnodig grote in-memory buffers bij grote exports.
Module-renderingModule-exportrendering moet deterministisch en snel zijn.
CleanupTijdelijke bestanden mogen opslag niet onbeperkt laten groeien.

Als exports in een toekomstige uitbreiding groot of langdurig worden, kan een async exportflow met jobstatus nodig zijn. In de eerste technische baseline is directe export het uitgangspunt voor normale resultaat-PDF’s.

16.21 Logging en correlation

Iedere PDF-export krijgt een correlation-id. Deze wordt doorgegeven aan betrokken modules.

Minimale technische logvelden:

CorrelationId
ViewerUserId
ViewerRoleContext
RunId
ExportType
ModuleKey
ModuleVersionSnapshot indien beschikbaar
StartedAtUtc
CompletedAtUtc or FailedAtUtc
DurationMs
FailureCategory indien van toepassing

Voor module-rendering kan aanvullend worden gelogd:

QuestionCount
RendererKey
RendererVersion
FallbackUsed

Niet loggen:

Volledige vraagpayload
Gegeven antwoorden
Juiste antwoorden
PDF-bytes
Bestandspad richting gebruiker
Tokens
Credentials

16.22 Publieke contracts van Reporting

OefenHub.Reporting biedt een beperkte publieke ingang.

Voorbeelden:

IResultPdfExportService
GenerateResultPdfAsync(request, cancellationToken)

IExportFileCleanupService
CleanupExpiredExportsAsync(request, cancellationToken)

Voor request/response DTO’s:

ResultPdfExportRequest
RunId
ViewerUserId
ViewerRoleContext
PreferredCulture
CorrelationId

ResultPdfExportResult
FileName
ContentType
Stream or FileReference
Length

Web gebruikt deze service voor downloadresponses. Scheduling gebruikt cleanupcontracts voor tijdelijke exportopruiming.

16.23 Interactie met Web

OefenHub.Web toont exportacties alleen wanneer ze functioneel beschikbaar lijken, maar de server-side exportservice voert altijd opnieuw autorisatie en statuscontrole uit.

Web is verantwoordelijk voor:

  • knop of link tonen;
  • command naar Reporting sturen;
  • loading state tonen;
  • veilige foutmelding tonen;
  • downloadresponse teruggeven.

Web is niet verantwoordelijk voor:

  • PDF-layoutlogica;
  • QuestPDF-documentconstructie;
  • directe runqueries;
  • modulepayloadinterpretatie;
  • tijdelijke bestandscleanup.

16.24 Teststrategie

PDF-export gebruikt een gelaagde regressieteststrategie. De strategie is gericht op stabiele bewaking van inhoud, structuur, paginering, module-rendering, privacy en downloadgedrag zonder dat tests onnodig afhankelijk worden van byte-identieke PDF-output.

16.24.1 Uitgangspunt

Een PDF is een presentatielaag boven historische runcontext. Regressietests controleren daarom primair de technische en functionele eigenschappen van de export, niet de interne byteopbouw van het PDF-bestand.

Niet toegestaan als primaire regressietest:

  • byte-voor-bytevergelijking van volledige PDF-bestanden;
  • snapshots met productiegegevens;
  • tests die slagen doordat actuele catalogusdata toevallig nog beschikbaar is;
  • visuele golden masters voor ieder PDF-scenario;
  • screenshots of mockup-HTML als bron voor PDF-layout.

Wel verplicht:

  • deterministische testfixtures met vaste klok, vaste cultuurinstelling en vaste snapshots;
  • expliciete controle op verplichte tekstinhoud, secties en totalen;
  • controle op pagina-aantallen en herhaalde headers/footers voor meerpagina-exports;
  • controle op module-specifieke vraag-, antwoord- en fallbackrepresentaties;
  • controle dat persoonsgegevens, antwoordpayloads en technische details niet in logs of tijdelijke paden lekken.

16.24.2 Testlagen

TesttypeDoelVerplichting
Unit testsBestandsnaamnormalisatie, layoutbeslissingen, fallbacklogica, formattering en exportmodelmapping.Verplicht in pull-requestchecks.
ContracttestsPractice-exportreader en module-PDF-rendercontracten.Verplicht per modulecontractwijziging.
IntegratietestsExport van afgeronde run met module-rendering, autorisatie en tijdelijke output.Verplicht voor kernscenario's.
Structurele regressietestsGenormaliseerde PDF-inhoud, metadata, pagina-aantallen, headers, footers en sectievolgorde.Verplicht voor stabiele exports.
Beperkte visuele snapshotsGerenderde pagina-afbeelding of SVG voor enkele stabiele kernlayouts.Alleen voor geselecteerde scenario's; standaard niet voor iedere modulevariant.
SecuritytestsGeen export zonder toegang, geen payloads in logs, geen publieke tijdelijke bestanden.Verplicht.
Cleanup testsIdempotente verwijdering van verlopen tijdelijke exports.Verplicht zodra tijdelijke bestanden worden gebruikt.
Performance testsExporttijd en geheugengebruik bij grotere resultaatsets.Nightly of releasecheck zodra grenswaarden zijn vastgesteld.

16.24.3 Toolingbaseline

Voor PDF-regressietests geldt de volgende toolingbaseline:

OnderdeelKeuzeToelichting
PDF-generatieQuestPDFProductie- en testpad gebruiken dezelfde documentopbouw.
Snapshot/assertion toolingVerifyVoor genormaliseerde snapshotasserties van tekst, metadata, objectmodellen en eventueel image-output.
PDF-tekstextractiePdfPig of Verify.PdfPigVoor inhoudscontrole op gegenereerde PDF's zonder bytevergelijking.
TestadapterVolgt het gekozen .NET-testframeworkBijvoorbeeld Verify.Xunit wanneer xUnit de testbaseline wordt.
Visuele outputQuestPDF image- of SVG-output waar stabiel toepasbaarAlleen voor geselecteerde kernlayouts en bij voorkeur in een vaste runtime/container.

Packageversies worden tijdens implementatie centraal vastgepind. De toolkeuze zelf is onderdeel van de technische baseline.

16.24.4 Snapshot- en baselinebeheer

PDF-snapshots zijn reviewartefacten en geen informele bijlage.

Regels:

  1. snapshots staan in het testproject, niet in productiemappen;
  2. snapshots bevatten uitsluitend fictieve of geanonimiseerde testdata;
  3. gegenereerde timestamps, correlation-id's, tijdelijke paden en niet-deterministische metadata worden genormaliseerd;
  4. wijziging van een verified snapshot vereist code review met duidelijke reden;
  5. snapshotupdates verwijzen waar relevant naar een Software Requirements Specification-, acceptatiecriterium-, module- of Technisch Ontwerp-wijziging;
  6. ontvangen outputbestanden uit mislukte tests worden niet automatisch gecommit;
  7. visuele snapshots worden beperkt tot stabiele kernlayouts om fragiele diffs te voorkomen.

16.24.5 Verplichte scenario's

De minimale PDF-regressieset bevat:

ScenarioControle
Leerling exporteert eigen afgeronde runInhoud, totalen, bestandsnaam, footer en autorisatie.
Docent exporteert leerlingresultaat binnen autorisatiegrensContextlabel, leerling-snapshot, niveau/categorie en autorisatiehercontrole.
Ouder/voogd exporteert resultaat van gekoppeld kindKind-snapshot, privacygrens en historische context.
Lange resultaatset over meerdere pagina'sPagina-aantal, tabelheaders, rijgedrag en footerpagina's.
Module met rijke representatieVraag, gegeven antwoord en juiste antwoord via modulecontract.
ModulefallbackVeilige fallbacktekst zonder technische details of payloadlek.
Ontbrekende of gewijzigde actuele catalogusdataHistorische snapshots blijven leidend.
Onbevoegde exportpogingGeen PDF-output, veilige foutafhandeling en beperkte logging.
Tijdelijke output en cleanupBestand buiten webroot, retentie en idempotente verwijdering.

PDF-binaire vergelijking blijft uitgesloten als primaire regressiemethode. Wanneer een visuele regressietest faalt, bepaalt review of de layoutwijziging bedoeld, acceptabel of regressie is.

16.25 Implementatiechecklist

Bij implementatie van PDF-export moet minimaal worden gecontroleerd:

  • OefenHub.Reporting gebruikt geen module-interne DbContexts of entities.
  • Exportbrondata komt via publieke Practice contracts.
  • Autorisatie wordt server-side per export herhaald.
  • Module-specifieke vraag-/antwoordweergave loopt via ExerciseModuleHost.
  • PDF gebruikt historische snapshots wanneer nodig.
  • Resultaattabel ondersteunt lange vragen en antwoorden.
  • Headers worden op vervolgpagina’s herhaald.
  • Footer bevat OefenHub, pagina X van Y en exportdatum.
  • Bestandsnaam wordt gesanitized en bevat geen persoonsgegevens.
  • Tijdelijke opslag staat buiten webroot.
  • Cleanup is idempotent en via Scheduling/TickerQ ingericht.
  • Logging gebruikt correlation-id en bevat geen antwoordpayloads.
  • Foutafhandeling lekt geen technische details.
  • Tests dekken autorisatie, fallback, rendering, cleanup, bestandsnaamlogica, structurele PDF-regressie en geselecteerde visuele kernlayoutscenario's.

16.26 Implementatieverificaties

De volgende punten moeten bij implementatie concreet worden vastgesteld of gevalideerd:

PuntTe bepalen
QuestPDF-licentie/configuratieExacte licentie-instelling en projectconfiguratie.
FontkeuzeBeschikbare fontfamilie zonder ongeoorloofde fontdistributie.
Tijdelijke opslaglocatiePad, rechten en hostingvolume controleren tegen OefenHub:Reporting:TempExportPath; standaardretentie 24 uur en cleanupachterstand vanaf 48 uur.
Direct streamen of tijdelijk bestandDefinitieve keuze per exportgrootte.
BrowsercacheheadersExacte downloadheaders voor privacygevoelige exports.
Module-rendercontractDefinitieve DTO-vorm voor PDF-veilige module-output.
FallbackbeleidExact gedrag wanneer module of payload niet meer renderbaar is.
PerformancegrenzenMaximale exportgrootte, timeout en throttling.
TickerQ-cleanupjobPlanning minimaal iedere 6 uur, retrybeleid en dashboardzichtbaarheid inrichten.