diff --git a/GEBRUIKSHANDLEIDING-IMPORT.md b/GEBRUIKSHANDLEIDING-IMPORT.md new file mode 100644 index 0000000..c29eadb --- /dev/null +++ b/GEBRUIKSHANDLEIDING-IMPORT.md @@ -0,0 +1,492 @@ +# Gebruikershandleiding Import Naar OnlineAcademy + +Deze handleiding beschrijft een eenvoudige en vaste werkwijze voor het aanleveren en importeren van een training op basis van markdown. + +Het doel is dat je per training altijd dezelfde structuur gebruikt, zodat je niet hoeft te onthouden: + +- waar bestanden moeten staan +- hoe afbeeldingen gekoppeld worden +- waar de URL moet worden ingevuld +- hoe je eerst een controle uitvoert en daarna pas de echte import start + +## 1. Wat moet ik op macOS installeren? + +Om de robotisering te gebruiken heb je op macOS het volgende nodig: + +- Terminal +- Node.js +- npm +- de projectmap `/Users/nico/skillstown` + +### Node.js installeren + +Controleer eerst of Node.js al aanwezig is: + +```bash +node -v +npm -v +``` + +Als beide opdrachten een versienummer tonen, dan is Node.js al goed geïnstalleerd. + +Als dat niet zo is, installeer dan: + +1. Node.js LTS via de officiële website +2. daarna opnieuw controleren met `node -v` en `npm -v` + +Praktisch minimum: + +- gebruik een recente LTS-versie van Node.js + +### Playwright-dependencies installeren + +Ga daarna in Terminal naar de projectmap: + +```bash +cd /Users/nico/skillstown +``` + +Installeer vervolgens de benodigde pakketten: + +```bash +npm install +``` + +Als dit klaar is, is de lokale omgeving gereed. + +## 2. Waar zet ik mijn OnlineAcademy login neer? + +De runner heeft twee inloggegevens nodig: + +- `OA_EMAIL` +- `OA_PASSWORD` + +Zet deze in een lokaal `.env` bestand in de projectmap: + +`/Users/nico/skillstown/.env` + +Voorbeeld: + +```dotenv +OA_EMAIL=jouw.email@voorbeeld.nl +OA_PASSWORD=jouwWachtwoordHier +``` + +Belangrijk: + +- het bestand heet precies `.env` +- het staat in de hoofdmap van het project +- zonder dit bestand kan de runner niet inloggen + +### Moet ik `.env` nog handmatig laden? + +Nee, dat hoeft niet meer als je `--env-file` gebruikt. + +De runner kan het `.env` bestand zelf inlezen. + +Gebruik dus gewoon: + +```bash +cd /Users/nico/skillstown +node onlineacademy-playwright-runner.mjs \ + --training-dir trainings/intro-sql \ + --env-file .env +``` + +Alleen als je geen `--env-file` gebruikt, moet je nog handmatig variabelen laden. + +Als de runner geen credentials kan vinden, krijg je een foutmelding zoals: + +`Zet OA_EMAIL en OA_PASSWORD in .env of in de omgeving.` + +## 3. Waar maak ik de map aan? + +Maak de trainingsmap altijd aan in deze hoofdmap: + +`/Users/nico/skillstown/trainings/` + +Elke training krijgt daar een eigen submap. + +Voorbeeld: + +- `/Users/nico/skillstown/trainings/sql-basics-01/` +- `/Users/nico/skillstown/trainings/excel-gevorderd/` +- `/Users/nico/skillstown/trainings/pilot-hoofdstuk-04/` + +Gebruik bij voorkeur een korte en herkenbare mapnaam, zonder rare tekens. + +Aanbevolen stijl: + +- alleen kleine letters +- woorden gescheiden met `-` + +Voorbeeld: + +`/Users/nico/skillstown/trainings/intro-sql/` + +## 4. Welke bestanden moeten daarin staan? + +Elke trainingsmap bevat precies deze onderdelen: + +- `content.md` +- `training.json` +- `assets/` + +De structuur is dus: + +```text +trainings/ + intro-sql/ + content.md + training.json + assets/ +``` + +## 5. Wat is `content.md`? + +`content.md` is het hoofdbestand met de inhoud van de training. + +Hierin staat: + +- hoofdstukken +- pagina's +- tekstblokken +- vragen +- tabellen +- verwijzingen naar afbeeldingen + +De afspraak is: + +- de inhoud staat altijd in `content.md` +- de bestandsnaam is dus niet vrij, maar vast + +Dat voorkomt verwarring. + +## 6. Wat is `assets/`? + +`assets/` is de map voor alle bijbehorende bestanden die vanuit de markdown nodig zijn. + +Denk aan: + +- afbeeldingen +- screenshots +- illustraties + +Dus: + +- `assets/` is geen apart systeemonderdeel +- het is gewoon een submap binnen de trainingsmap + +Voorbeeld: + +```text +trainings/ + intro-sql/ + content.md + training.json + assets/ + query-voorbeeld.png + joins-overzicht.png + database-schema.jpg +``` + +## 7. Hoe verwijs ik in de markdown naar bestanden in `assets/`? + +Gebruik in de markdown altijd relatieve paden vanaf `content.md`. + +Dus verwijs naar bestanden als: + +- `assets/query-voorbeeld.png` +- `assets/joins-overzicht.png` + +Niet doen: + +- absolute paden zoals `/Users/nico/...` +- bestanden buiten de trainingsmap + +De werkafspraak is: + +- alles wat de training nodig heeft staat in de eigen trainingsmap +- alle afbeeldingen staan in `assets/` + +## 8. Wat is `training.json`? + +`training.json` is het configuratiebestand van die ene training. + +Daarin staat niet de inhoud, maar alleen de instellingen die nodig zijn om de import te draaien. + +De belangrijkste instelling is: + +- de OnlineAcademy edit-URL + +`training.json` staat dus hier: + +`/Users/nico/skillstown/trainings//training.json` + +Voorbeeld: + +`/Users/nico/skillstown/trainings/intro-sql/training.json` + +## 9. Wat zet ik in `training.json`? + +Minimaal alleen de URL. + +Voorbeeld: + +```json +{ + "url": "https://create.onlineacademy.nl/.../edit?idsVersion=6" +} +``` + +Je hoeft hier dus geen markdown of afbeeldingen in te zetten. Alleen de doel-URL van de training. + +## 10. Volledig voorbeeld van een trainingsmap + +```text +/Users/nico/skillstown/trainings/intro-sql/ + content.md + training.json + assets/ + query-voorbeeld.png + joins-overzicht.png +``` + +Met: + +- `content.md` voor de inhoud +- `training.json` voor de URL +- `assets/` voor de afbeeldingen + +## 11. Hoe start ik de import? + +De import start je vanuit de terminal, in de hoofdmap van het project: + +`/Users/nico/skillstown` + +Je hoeft dus niet op een knop te drukken. De import wordt gestart met één commando. + +Er zijn twee modi: + +- reviewmodus +- uitvoermodus + +Binnen de uitvoermodus zijn er twee varianten: + +- uitvoeren zonder opslaan +- uitvoeren en daarna `Opslaan als` klikken + +## 12. Hoe start ik alleen de reviewmodus? + +Gebruik reviewmodus om eerst te controleren of: + +- het `.env` bestand gevonden wordt +- de login werkt +- de juiste training wordt geopend +- de markdown kan worden gelezen +- de afbeeldingen gevonden worden +- de structuur logisch is + +Commando: + +```bash +node onlineacademy-playwright-runner.mjs \ + --training-dir trainings/intro-sql \ + --env-file .env +``` + +Wat gebeurt er in reviewmodus: + +- de browser opent +- er wordt ingelogd +- de URL wordt automatisch uit `training.json` gelezen +- de doelpagina wordt geopend +- er wordt een controle uitgevoerd +- er worden screenshots en controlegegevens opgeslagen +- er worden nog geen muterende importacties uitgevoerd + +Belangrijk: + +- zonder `--execute` blijft de runner in reviewmodus + +## 13. Hoe start ik de echte import? + +Als de review goed is, start je dezelfde opdracht opnieuw, maar dan met `--execute`. + +### Variant A: uitvoeren zonder opslaan + +```bash +node onlineacademy-playwright-runner.mjs \ + --training-dir trainings/intro-sql \ + --env-file .env \ + --execute +``` + +Wat gebeurt er dan: + +- de browser opent +- de training wordt geopend +- pagina's en blokken worden in de editor opgebouwd +- de runner vult de inhoud in op basis van `content.md` +- de runner stopt daarna op het reviewpunt +- het scherm blijft open zodat je kunt controleren wat er is ingevoerd +- de inhoud is nog niet opgeslagen + +### Variant B: uitvoeren en daarna opslaan + +Gebruik hiervoor de extra flag `--save`. + +Commando: + +```bash +node onlineacademy-playwright-runner.mjs \ + --training-dir trainings/intro-sql \ + --env-file .env \ + --execute \ + --save +``` + +Wat gebeurt er dan: + +- de browser opent +- de training wordt geopend +- pagina's en blokken worden in de editor opgebouwd +- de runner vult de inhoud in op basis van `content.md` +- de runner klikt daarna op de knop `Opslaan als` +- de runner kiest daarna in de popup de optie `Concept` + +## 14. Hoe weet ik of de review goed is? + +De review is goed als: + +- de juiste OnlineAcademy-training opent +- de runner zonder foutmelding door de controle komt +- de screenshots laten zien dat je in de editor zit +- de markdown en assets correct gevonden zijn + +Je hoeft dus niet technisch te beoordelen wat er intern gebeurt. Je kijkt vooral naar: + +- opent de juiste training +- zie ik de editor +- ontbreken er geen bestanden +- krijg ik geen foutmelding + +## 15. Hoe weet ik wat ik moet verbeteren als de review niet goed is? + +Als de review niet goed is, kijk je eerst naar vier dingen: + +1. Bevat `.env` de velden `OA_EMAIL` en `OA_PASSWORD`? +2. Klopt de URL in `training.json`? +3. Bestaat `content.md` echt in de trainingsmap? +4. Bestaan alle bestanden in `assets/` waarnaar de markdown verwijst? + +De meest voorkomende oorzaken zijn: + +- `.env` ontbreekt +- `OA_EMAIL` of `OA_PASSWORD` ontbreekt +- verkeerde URL +- typefout in bestandsnaam +- afbeelding staat niet in `assets/` +- markdown verwijst naar een bestand dat niet bestaat +- inhoud gebruikt een bloktype dat nog niet ondersteund is + +Praktische werkwijze: + +1. Lees de foutmelding. +2. Controleer de trainingsmap. +3. Controleer `.env`. +4. Corrigeer `content.md`, `training.json` of `assets/`. +5. Start opnieuw in reviewmodus. +6. Pas als de review goed is, draai je `--execute`. + +## 16. Waar vind ik de controlebestanden en screenshots terug? + +De runner slaat resultaten op in `artifacts/`. + +Daar vind je onder andere: + +- screenshots +- plan-overzicht +- validatie-uitvoer +- foutstatus bij mislukte runs + +Dat helpt om achteraf te zien: + +- wat er geopend is +- hoe ver de runner kwam +- waar het mogelijk misging + +## 17. Welke uitvoer zie ik in de terminal? + +De runner geeft tijdens een run korte voortgangsmeldingen, bijvoorbeeld: + +- `[STAP] Voorbereiden` +- `[STAP] Browser openen` +- `[STAP] Inloggen` +- `[STAP] Editor controleren` +- `[STAP] Inhoud opbouwen` +- `[INFO] Hoofdstuk 1 van 1: ...` +- `[INFO] Pagina 3 van 8: ...` +- `[INFO] Blok 2 van 4: image - ...` +- `[STAP] Resultaat` + +Bij een geslaagde execute-run zie je nu: + +- `Execute-run afgerond tot het reviewpunt.` +- `De inhoud is opgebouwd in de editor.` +- `Er is nog niet opgeslagen.` + +Dat betekent: + +- de invoer is uitgevoerd +- de inhoud staat in de editor +- de inhoud is nog niet definitief bewaard in OnlineAcademy + +Bij een execute-run met `--save` zie je: + +- `Execute-run afgerond.` +- `De runner heeft op 'Opslaan als' geklikt en daarna 'Concept' gekozen.` + +Dat betekent: + +- de invoer is uitgevoerd +- de runner heeft de save-flow bediend +- je moet in de browser nog controleren of OnlineAcademy die actie zichtbaar bevestigt + +## 18. Aanbevolen vaste werkwijze per training + +Gebruik steeds deze volgorde: + +1. Installeer Node.js en voer `npm install` uit. +2. Maak `.env` aan in `/Users/nico/skillstown/`. +3. Maak een map aan in `trainings/`. +4. Zet de inhoud in `content.md`. +5. Zet alle afbeeldingen in `assets/`. +6. Zet de doel-URL in `training.json`. +7. Start eerst de reviewmodus met `--training-dir` en `--env-file`. +8. Controleer of de juiste training opent en of er geen fouten zijn. +9. Start daarna de uitvoermodus met `--execute`. +10. Voeg `--save` alleen toe als de runner ook echt op `Opslaan als` moet klikken. + +## 19. Samenvatting + +De vaste afspraak is: + +- Node.js en `npm install` zijn vooraf geregeld +- inloggegevens staan in `.env` +- elke training staat in een eigen map onder `trainings/` +- de inhoud staat in `content.md` +- de URL staat in `training.json` +- de afbeeldingen staan in `assets/` +- eerst review draaien met `--training-dir` en `--env-file` +- daarna pas `--execute` +- voeg alleen `--save` toe als de runner aan het eind op `Opslaan als` moet klikken + +De standaardstructuur is: + +```text +/Users/nico/skillstown/trainings// + content.md + training.json + assets/ +``` diff --git a/HANDOVER.md b/HANDOVER.md index 0a64b5f..fdac301 100644 --- a/HANDOVER.md +++ b/HANDOVER.md @@ -103,7 +103,8 @@ Wel: - intern trainingsmodel - UI-plan - zichtbare Playwright runner -- reviewstop vóór save +- review en execute via de editor-UI +- optionele save via `Opslaan als` -> `Concept` Reden: - minder risico in productie @@ -115,8 +116,30 @@ Reden: ### Input / referentie -- [create-md-format.md](/Users/nico/skillstown/create-md-format.md) -- [body.html](/Users/nico/skillstown/body.html) +- [GEBRUIKSHANDLEIDING-IMPORT.md](/Users/nico/skillstown/GEBRUIKSHANDLEIDING-IMPORT.md) + +### Gebruikersinvoer volgens huidige werkwijze + +De beoogde gebruikersflow is nu: + +- per training een eigen map onder `/Users/nico/skillstown/trainings/` +- vaste bestandsnamen binnen die map: + - `content.md` + - `training.json` + - `assets/` + +Concreet voorbeeld dat nu is ingericht: + +- [trainings/hoofdstuk-04-basisqueries/content.md](/Users/nico/skillstown/trainings/hoofdstuk-04-basisqueries/content.md) +- [trainings/hoofdstuk-04-basisqueries/training.json](/Users/nico/skillstown/trainings/hoofdstuk-04-basisqueries/training.json) +- [trainings/hoofdstuk-04-basisqueries/assets](/Users/nico/skillstown/trainings/hoofdstuk-04-basisqueries/assets) + +Belangrijk: + +- de runner ondersteunt nu direct `--training-dir ` +- daarbij wordt automatisch `content.md` en `training.json` gebruikt +- de URL wordt dus standaard uit `training.json` gelezen +- credentials kunnen via `--env-file .env` worden geladen zonder `source .env` ### Scripts @@ -130,30 +153,43 @@ Reden: Zichtbare Playwright runner met: - reviewmodus - execute-modus + - optionele `--save` flag - login + - inlezen van `.env` via `--env-file` + - inlezen van `training.json` via `--training-dir` - editorverificatie - pagina-aanmaak - blokinsertie/invulling + - asset-resolutie relatief aan `content.md` + - menselijk leesbare voortgangsmeldingen - DOM-snapshot voor detectie van nieuw ingevoegde blokken - drop-targeting op de onderkant van het laatste bestaande blok - - reviewstop vóór save - -### Testdata - -- [pilot-mvp.md](/Users/nico/skillstown/pilot-mvp.md) -- [pilot-multi-response.md](/Users/nico/skillstown/pilot-multi-response.md) -- [pilot-image.md](/Users/nico/skillstown/pilot-image.md) -- [pilot-table.md](/Users/nico/skillstown/pilot-table.md) -- [pilot-table-large.md](/Users/nico/skillstown/pilot-table-large.md) -- [pilot-open-question.md](/Users/nico/skillstown/pilot-open-question.md) -- [pilot-chapters.md](/Users/nico/skillstown/pilot-chapters.md) -- [pilot-chapters-large.md](/Users/nico/skillstown/pilot-chapters-large.md) -- [create-md-format.onlineacademy.json](/Users/nico/skillstown/create-md-format.onlineacademy.json) -- [pilot-mvp.onlineacademy.json](/Users/nico/skillstown/pilot-mvp.onlineacademy.json) + - execute zonder save laat het scherm open voor handmatige controle + - save klikt `Opslaan als` en kiest daarna `Concept` ### Config - [package.json](/Users/nico/skillstown/package.json) +- [\.env](/Users/nico/skillstown/.env) + +De runner verwacht credentials via environment variables: + +- `OA_EMAIL` +- `OA_PASSWORD` + +Praktisch worden die lokaal in `.env` gezet en tijdens een run automatisch ingelezen met: + +```bash +node onlineacademy-playwright-runner.mjs \ + --training-dir trainings/hoofdstuk-04-basisqueries \ + --env-file .env +``` + +Zonder deze stap stopt de runner met: + +```text +Error: Zet OA_EMAIL en OA_PASSWORD in /Users/nico/skillstown/.env of in de omgeving. +``` ## Wat werkt al @@ -162,6 +198,8 @@ Reden: - Markdown parsing werkt - dry-run JSON generatie werkt - plan/validatie-export werkt +- trainingsmap-structuur werkt +- `.env` en `training.json` worden automatisch ingelezen via flags ### Live in de zichtbare browser @@ -169,6 +207,9 @@ Bewezen: - login werkt - editorherkenning werkt - reviewmodus zonder mutaties werkt +- menselijk leesbare statusuitvoer werkt +- execute zonder `--save` laat het scherm nu open voor handmatige controle +- execute met `--save` bewaart nu via `Opslaan als` -> `Concept` - nieuwe pagina `Intro` aanmaken werkt - paginatitel invullen werkt - drag/drop insertie van blokken werkt aantoonbaar stabieler wanneer op de onderkant van het laatste bestaande blok wordt gedropt @@ -207,140 +248,24 @@ Bewezen: - de execute-flow maakt nu altijd een nieuw hoofdstuk aan via `Nieuw hoofdstuk` - automatisch aangemaakte eerste pagina in nieuwe hoofdstukken hernoemen en hergebruiken - extra pagina's per hoofdstuk aanmaken wanneer Markdown meer pagina's bevat -- er is niet opgeslagen +- asset-paden in trainingsmappen werken nu correct via relatieve resolutie vanaf `content.md` +- save via `Opslaan als` -> `Concept` werkt nu ## Artifacts / bewijs -Belangrijkste artifacts: +De repo is opgeschoond; oude pilots en artifacts zijn verwijderd. -- [editor-ready.png](/Users/nico/skillstown/artifacts/oa-runner/editor-ready.png) - Editor succesvol geladen. +Huidige relevante artifacts worden per run geschreven naar: -- [page-1-structure.png](/Users/nico/skillstown/artifacts/oa-runner/page-1-structure.png) - Nieuwe pagina `Intro` succesvol aangemaakt. +- `artifacts//` -- [page-1-block-1-text.png](/Users/nico/skillstown/artifacts/oa-runner/page-1-block-1-text.png) - Tekstblok zichtbaar ingevoegd en gevuld. +Daar vind je typisch: -- [page-1-block-2-heading.png](/Users/nico/skillstown/artifacts/oa-runner/page-1-block-2-heading.png) - Oud artifact uit een eerdere run waarin blokvolgorde/targeting nog niet stabiel was. - -- [page-1-block-2-quote.png](/Users/nico/skillstown/artifacts/oa-runner/page-1-block-2-quote.png) - Quoteblok succesvol ingevuld in een latere geharde run. - -- [review-stop.png](/Users/nico/skillstown/artifacts/oa-runner/review-stop.png) - Laat de huidige reviewstop zien vóór save, met de blokken in de goede volgorde bovenaan zichtbaar. - -- [plan.json](/Users/nico/skillstown/artifacts/oa-runner/plan.json) -- [validation.json](/Users/nico/skillstown/artifacts/oa-runner/validation.json) - -Pilot na drag/drop-hardening: - -- [page-1-block-3-quote.png](/Users/nico/skillstown/artifacts/oa-runner-pilot/page-1-block-3-quote.png) - Laat zien dat `text`, `heading` en `quote` in natuurlijke volgorde onder elkaar staan. - -- [page-1-block-4-multiple-choice.png](/Users/nico/skillstown/artifacts/oa-runner-pilot/page-1-block-4-multiple-choice.png) - Laat zien dat daarna ook `multiple-choice` onder het quote-blok is ingevoegd en dat de vraag is ingevuld. - -- [review-stop.png](/Users/nico/skillstown/artifacts/oa-runner-pilot/review-stop.png) - Bevestigt de pagina-opbouw in volgorde `text` -> `heading` -> `quote` -> `multiple-choice`, zonder save. - -- [plan.json](/Users/nico/skillstown/artifacts/oa-runner-pilot/plan.json) -- [validation.json](/Users/nico/skillstown/artifacts/oa-runner-pilot/validation.json) - -Pilot voor `multiple-choice` afwerking: - -- [page-1-block-4-multiple-choice.png](/Users/nico/skillstown/artifacts/oa-runner-mc/page-1-block-4-multiple-choice.png) - Laat zien dat de meerkeuzevraag nu met exact 2 antwoorden uit Markdown is ingevuld, met het correcte antwoord geselecteerd. - -- [review-stop.png](/Users/nico/skillstown/artifacts/oa-runner-mc/review-stop.png) - Laat zien dat ook de feedback/toelichting is ingevuld en dat de run weer stopt vóór save. - -- [plan.json](/Users/nico/skillstown/artifacts/oa-runner-mc/plan.json) -- [validation.json](/Users/nico/skillstown/artifacts/oa-runner-mc/validation.json) - -Pilot voor `multiple-response` afwerking: - -- [page-1-block-1-multiple-response.png](/Users/nico/skillstown/artifacts/oa-runner-mr/page-1-block-1-multiple-response.png) - Laat zien dat de multi-antwoordvraag nu exact 3 antwoorden uit Markdown overneemt, met 2 correcte checkboxen geselecteerd. - -- [review-stop.png](/Users/nico/skillstown/artifacts/oa-runner-mr/review-stop.png) - Laat zien dat ook de toelichting is ingevuld en dat de run weer stopt vóór save. - -- [plan.json](/Users/nico/skillstown/artifacts/oa-runner-mr/plan.json) -- [validation.json](/Users/nico/skillstown/artifacts/oa-runner-mr/validation.json) - -Pilot voor `image` afwerking: - -- [page-1-block-1-image.png](/Users/nico/skillstown/artifacts/oa-runner-image/page-1-block-1-image.png) - Laat zien dat de afbeelding is geüpload en dat `Titel`, `Onderschrift` en `Alternatieve tekst` zijn ingevuld. - -- [review-stop.png](/Users/nico/skillstown/artifacts/oa-runner-image/review-stop.png) - Laat zien dat de image-run ook weer stopt vóór save. - -- [plan.json](/Users/nico/skillstown/artifacts/oa-runner-image/plan.json) -- [validation.json](/Users/nico/skillstown/artifacts/oa-runner-image/validation.json) - -Pilot voor `table` verkenning: - -- [page-1-block-1-table.png](/Users/nico/skillstown/artifacts/oa-runner-table/page-1-block-1-table.png) - Laat zien dat `Titel`, `Top header`, kolomuitbreiding, rijuitbreiding en de volledige celvulling nu werken voor de tabelpilot. - -- [review-stop.png](/Users/nico/skillstown/artifacts/oa-runner-table/review-stop.png) - Laat zien dat de tabel-run ook stopt vóór save. - -- [plan.json](/Users/nico/skillstown/artifacts/oa-runner-table/plan.json) -- [validation.json](/Users/nico/skillstown/artifacts/oa-runner-table/validation.json) -- [failure-state.png](/Users/nico/skillstown/artifacts/oa-runner-table/failure-state.png) - Artifact uit een tussenrun waarin de plusknoppen voor kolom/rij-uitbreiding nog niet betrouwbaar werden geraakt. - -Pilot voor grotere `table` validatie: - -- [page-1-block-1-table.png](/Users/nico/skillstown/artifacts/oa-runner-table-large/page-1-block-1-table.png) - Laat zien dat de tabel ook opschaalt naar 4 kolommen en 10 regels totaal, inclusief volledige celvulling uit Markdown. - -- [review-stop.png](/Users/nico/skillstown/artifacts/oa-runner-table-large/review-stop.png) - Laat zien dat ook de grotere tabel-run stopt vóór save. - -- [plan.json](/Users/nico/skillstown/artifacts/oa-runner-table-large/plan.json) -- [validation.json](/Users/nico/skillstown/artifacts/oa-runner-table-large/validation.json) - -Pilot voor `open-question` afwerking: - -- [page-1-block-1-open-question.png](/Users/nico/skillstown/artifacts/oa-runner-open-question/page-1-block-1-open-question.png) - Laat zien dat de open vraag en de toelichting correct uit Markdown zijn ingevuld. - -- [review-stop.png](/Users/nico/skillstown/artifacts/oa-runner-open-question/review-stop.png) - Laat zien dat ook de open-vraag-run stopt vóór save. - -- [plan.json](/Users/nico/skillstown/artifacts/oa-runner-open-question/plan.json) -- [validation.json](/Users/nico/skillstown/artifacts/oa-runner-open-question/validation.json) - -Pilot voor hoofdstukaanmaak: - -- [chapter-2-structure.png](/Users/nico/skillstown/artifacts/oa-runner-chapters/chapter-2-structure.png) - Laat zien dat een tweede hoofdstuk wordt toegevoegd en dat de automatisch aangemaakte eerste pagina daarvan wordt hernoemd en gevuld. - -- [review-stop.png](/Users/nico/skillstown/artifacts/oa-runner-chapters/review-stop.png) - Laat zien dat de tweekoppige hoofdstukpilot stopt vóór save. - -- [plan.json](/Users/nico/skillstown/artifacts/oa-runner-chapters/plan.json) -- [validation.json](/Users/nico/skillstown/artifacts/oa-runner-chapters/validation.json) - -Pilot voor grotere hoofdstukvalidatie: - -- [review-stop.png](/Users/nico/skillstown/artifacts/oa-runner-chapters-large/review-stop.png) - Laat zien dat 4 hoofdstukken met wisselende aantallen pagina's goed worden opgebouwd: - - `Hoofdstuk Alfa`: 1 pagina - - `Hoofdstuk Bravo`: 3 pagina's - - `Hoofdstuk Charlie`: 2 pagina's - - `Hoofdstuk Delta`: 4 pagina's - -- [chapter-2-structure.png](/Users/nico/skillstown/artifacts/oa-runner-chapters-large/chapter-2-structure.png) -- [chapter-3-structure.png](/Users/nico/skillstown/artifacts/oa-runner-chapters-large/chapter-3-structure.png) -- [chapter-4-structure.png](/Users/nico/skillstown/artifacts/oa-runner-chapters-large/chapter-4-structure.png) -- [plan.json](/Users/nico/skillstown/artifacts/oa-runner-chapters-large/plan.json) -- [validation.json](/Users/nico/skillstown/artifacts/oa-runner-chapters-large/validation.json) +- `plan.json` +- `validation.json` +- `editor-ready.png` +- `review-stop.png` of `after-save.png` +- `failure-state.png` als een run mislukt ## Belangrijkste observatie over drag/drop @@ -377,13 +302,12 @@ Pragmatische koerswijziging: ## Huidige tekortkomingen Nog niet robuust: -- de nieuwe drop-strategie en execute-flow zijn live gevalideerd voor pilots met `text`, `heading`, `quote`, `multiple-choice`, `multiple-response`, `image`, `table`, `open-question` en hoofdstukken/pagina's +- de execute-flow is nu gevalideerd voor de gebruikte bloksets in `hoofdstuk-04-basisqueries` - scoped targeting per bloktype blijft verder hardening nodig hebben voor bredere bloksets en afwijkende editorstates -- de nieuwe "altijd nieuw hoofdstuk"-strategie moet nog live opnieuw gevalideerd worden Nog niet opgepakt: - matching-pairs UI-flow -- save-flow +- functionele bevestiging van succesvol opslaan nog explicieter maken in de output - opschonen van oude, ongebruikte delete-helpers in de runner ## Veiligheidsafspraken @@ -392,13 +316,13 @@ Tot nu toe gehanteerd: - headed browser - slow motion - reviewmodus zonder mutaties -- execute stopt vóór save +- execute zonder `--save` stopt vóór save en laat het scherm open - geen directe backend writes - artifacts per stap - execute-flow gebruikt nu geen delete-acties meer Bewust nog niet gedaan: -- automatisch opslaan +- extra verificatie van succesvol opslaan na klik op `Opslaan als` -> `Concept` - API writes - bulkimport in productie @@ -410,88 +334,87 @@ Aanbevolen aanvullende veiligheidsafspraak: In `/Users/nico/skillstown`: +Benodigd op macOS: + +- Terminal +- Node.js +- npm + Installeren: ```bash npm install ``` +Credentials lokaal in `.env` zetten: + +```dotenv +OA_EMAIL=jouw.email@voorbeeld.nl +OA_PASSWORD=jouwWachtwoordHier +``` + Dry-run parser: ```bash -node markdown-to-onlineacademy-json.mjs pilot-mvp.md -``` - -Zichtbare review-run zonder mutaties: - -```bash -# verwacht een lokaal `.env` bestand met ten minste: -# OA_EMAIL='...' -# OA_PASSWORD='...' -``` - -Credentials laden uit `.env`: - -```bash -set -a -source .env -set +a +node markdown-to-onlineacademy-json.mjs trainings/hoofdstuk-04-basisqueries/content.md ``` Zichtbare review-run zonder mutaties: ```bash node onlineacademy-playwright-runner.mjs \ - --markdown pilot-mvp.md \ - --url 'https://create.onlineacademy.nl/8602e048-d7ee-4764-aafb-43412d0f65f3/edit?idsVersion=6' + --training-dir trainings/hoofdstuk-04-basisqueries \ + --env-file .env +``` + +Zichtbare review-run op een trainingsmap volgens de nieuwe gebruikersstructuur: + +```bash +node onlineacademy-playwright-runner.mjs \ + --training-dir trainings/hoofdstuk-04-basisqueries \ + --env-file .env ``` Zichtbare execute-run tot reviewstop, zonder save: ```bash -set -a; source .env; set +a node onlineacademy-playwright-runner.mjs \ - --markdown pilot-mvp.md \ - --url 'https://create.onlineacademy.nl/8602e048-d7ee-4764-aafb-43412d0f65f3/edit?idsVersion=6' \ + --training-dir trainings/hoofdstuk-04-basisqueries \ + --env-file .env \ --execute \ - --output-dir artifacts/oa-runner-pilot + --output-dir artifacts/hoofdstuk-04-basisqueries ``` Zichtbare execute-run zonder save: ```bash -set -a -source .env -set +a node onlineacademy-playwright-runner.mjs \ - --markdown pilot-mvp.md \ - --url 'https://create.onlineacademy.nl/8602e048-d7ee-4764-aafb-43412d0f65f3/edit?idsVersion=6' \ + --training-dir trainings/hoofdstuk-04-basisqueries \ + --env-file .env \ --execute ``` +Zichtbare execute-run met save-klik: + +```bash +node onlineacademy-playwright-runner.mjs \ + --training-dir trainings/hoofdstuk-04-basisqueries \ + --env-file .env \ + --execute \ + --save +``` + ## Directe volgende stap -Niet meer werken met "laatste blok = nieuw blok". +Pragmatische vervolgstappen: -Hardeningvoorstel: - -1. `multiple-choice` volledig afmaken: - Status: afgerond voor de pilot-run. -2. `multiple-response` - Status: afgerond voor de pilot-run. -3. `image` - Status: afgerond voor de pilot-run. -4. `table` - Status: afgerond voor de pilot-run. -5. `open-question` - Status: afgerond voor de pilot-run. -6. hoofdstukaanmaak - Status: afgerond voor de pilot-run, maar execute-flow is inmiddels vereenvoudigd naar "altijd nieuw hoofdstuk" en moet daarop opnieuw live gevalideerd worden. -7. verdere hardening voor andere bloktypes en editorvarianten -8. save-flow +1. save-bevestiging explicieter maken in de output +2. package scripts laten aansluiten op de huidige `--training-dir` flow +3. ondersteuning uitbreiden voor extra bloktypes zoals `matching-pairs` +4. oude, ongebruikte helpers verder opschonen ## Eindconclusie De drag/drop Playwright-route is technisch haalbaar en veiliger passend bij deze productiecontext dan direct backend writes. -De basis is gelegd en live gevalideerd. De veiligste huidige koers is om in execute-runs alleen nieuwe hoofdstukken op te bouwen en geen bestaande content te verwijderen. Verdere hardening blijft nodig op blokpositie, veldtargeting en uiteindelijk de save-flow voordat dit betrouwbaar genoeg is voor herhaalbaar gebruik. +De basis is gelegd en live gevalideerd. De huidige flow ondersteunt nu review, execute zonder save en execute met save via `Opslaan als` -> `Concept`, vanuit een vaste trainingsmap-structuur met `content.md`, `training.json` en `assets/`. diff --git a/Hoofdstuk 04 - Basisqueries.md b/Hoofdstuk 04 - Basisqueries.md deleted file mode 100644 index 7462d27..0000000 --- a/Hoofdstuk 04 - Basisqueries.md +++ /dev/null @@ -1,600 +0,0 @@ -# Hoofdstuk 4 - Basisqueries - ---- - -## Terugblik - -In hoofdstuk 3 heb je kennisgemaakt met SQL als declaratieve taal: je schrijft wat je wilt, niet hoe de database dat uitrekent. Je hebt de drie groepen van SQL leren kennen - DML, DDL en DCL - en de fitworks-database geïnstalleerd. Die database staat nu klaar. - -> **Neem even 30 seconden.** In hoofdstuk 3 stond al een korte SELECT-query als voorbeeld. Kon je op dat moment al raden wat hij deed? En nu, na het lezen van de beschrijving van DDL en DML, zou je dan anders antwoorden? Bewaar dat gevoel - aan het einde van dit hoofdstuk kijk je terug op je eigen redenering. - ---- - -## Opening - -Je bent systeembeheerder bij FitWorks. De vestigingsmanager loopt op je af: "Kun jij me snel een lijst geven van alle leden die geen telefoonnummer hebben ingevuld? We willen ze via e-mail aanschrijven." Een simpele vraag. Maar hoe stel je die aan een database? - -Je kunt niet op een knop klikken. Er bestaat geen menu-optie "geef me leden zonder telefoon". Je hebt een instructie nodig die de database begrijpt - precies, ondubbelzinnig, in de juiste volgorde. Dat is een query. En er zit een addertje onder het gras in die vraag over telefoonnummers. Dat addertje heet NULL. Je ontdekt hem in sectie 4.3. - ---- - -## Leerdoelen - -Na dit hoofdstuk kun je: - -- een query schrijven met `SELECT`, `FROM` en `WHERE` en uitleggen wat elk onderdeel doet; -- resultaten filteren met vergelijkingsoperatoren, `BETWEEN`, `IN` en logische operators `AND`, `OR` en `NOT`; -- uitleggen wat NULL is, waarom je er niet op kunt filteren met `=`, en hoe je dat correct doet met `IS NULL`; -- resultaten sorteren met `ORDER BY` en dubbele waarden verwijderen met `DISTINCT`; -- kolomaliassen toewijzen met `AS` en vaste tekst (literals) in een query opnemen; -- voor- en achternamen samenvoegen met `CONCAT` en datatypes omzetten met `CAST`. - ---- - -## 4.1 SELECT en FROM - je eerste query - -### De balie vraagt, de database antwoordt - -Het is dinsdagochtend bij FitWorks en een trainer vraagt aan de receptie: "Wie staat er allemaal ingeschreven?" De receptioniste opent phpMyAdmin, typt een instructie en krijgt binnen een seconde een lijst op het scherm. Die instructie is klein maar compleet: - -```sql -SELECT voornaam, achternaam -FROM leden; -``` - -Dit is een volledige SQL-query. Ze bestaat uit twee onderdelen. `SELECT` zegt welke kolommen je wilt zien, gescheiden door komma's. `FROM` zegt uit welke tabel die kolommen komen. Zonder `FROM` weet de database niet waar hij moet zoeken. Zonder `SELECT` weet hij niet wat hij moet tonen. Ze zijn onlosmakelijk verbonden. - -![Anatomie van een SELECT-query met SELECT, FROM en WHERE](afbeeldingen/hoofdstuk-04/hoofdstuk_04_scherm_01_select_from_anatomie.svg) - -De volgorde is vastgelegd: `SELECT` altijd eerst, dan `FROM`, dan pas eventuele aanvullingen zoals `WHERE`. SQL klaagt direct als je die volgorde omdraait. Het is geen aanbeveling - het is een syntaxregel. - -### Alle kolommen tegelijk: de asterisk - -Soms wil je snel alle kolommen zien die een tabel bevat, zonder ze één voor één op te noemen. Dat doe je met de asterisk (`*`): - -```sql -SELECT * -FROM leden; -``` - -De `*` is een verkorting voor "geef me alles". Handig bij het verkennen van een tabel. In de praktijk gebruik je `*` spaarzaam: het haalt onnodig veel data op, maakt je query minder leesbaar en kan vertragen als een tabel tientallen kolommen heeft. Maar als leergereedschap bij het doorzoeken van fitworks is het prima. - -> **Kolomnamen in fitworks zijn altijd lowercase.** Schrijf dus `voornaam`, niet `Voornaam` of `VOORNAAM`. MySQL maakt op de meeste systemen geen onderscheid tussen hoofd- en kleine letters in kolomnamen, maar consequent lowercase schrijven voorkomt verwarring. - -### Mini-oefening - -Je wilt de e-mailadressen en geboortedatums van alle leden zien. Welke query schrijf je? - -```sql --- Schrijf hier jouw query - - - -``` - -**Antwoord:** - -```sql -SELECT email, geboortedatum -FROM leden; -``` - ---- - -## 4.2 WHERE - filteren op voorwaarden - -### Niet 400 rijen, maar precies de rijen die je nodig hebt - -De tabel `leden` bevat zo'n 400 rijen. Als je de manager een overzicht stuurt van actieve leden, heb je die 400 rijen niet nodig. Je wilt alleen de rijen waarbij `actief` gelijk is aan `1`. Daarvoor gebruik je `WHERE`. - -`WHERE` is een filter. De database loopt alle rijen van de opgegeven tabel langs en geeft alleen de rijen terug die voldoen aan de voorwaarde die jij opgeeft. Rijen die er niet aan voldoen, worden stilletjes weggelaten. - -```sql -SELECT voornaam, achternaam, email -FROM leden -WHERE actief = 1; -``` - -`WHERE` staat altijd na `FROM`. De volgorde `SELECT - FROM - WHERE` is onwrikbaar. Typ je `WHERE` voor `FROM`, dan krijg je een foutmelding. - -### Vergelijkingsoperatoren - -In een `WHERE`-clausule vergelijk je een kolom met een waarde. Voor die vergelijking gebruik je een operator. Het diagram hieronder geeft een overzicht van de meestgebruikte operators met voorbeelden uit de fitworks-database. - -![Overzicht van WHERE-operatoren met voorbeelden uit fitworks](afbeeldingen/hoofdstuk-04/hoofdstuk_04_scherm_02_where_operatoren.svg) - -Twee operators verdienen extra toelichting. - -**BETWEEN** filtert op een bereik, inclusief de grenswaarden zelf. Dit is handig als je iets zoekt binnen een prijsklasse of een tijdvak: - -```sql -SELECT naam, prijs_per_maand -FROM abonnementen -WHERE prijs_per_maand BETWEEN 20 AND 40; -``` - -Dit geeft abonnementen terug waarvan de prijs 20, 40 of iets daartussenin bedraagt. Let op: beide grenzen tellen mee. - -**IN** laat je meerdere toegestane waarden opgeven als een lijst, zonder een reeks `OR`-voorwaarden te schrijven. Het resultaat is hetzelfde, maar de query is compacter en beter leesbaar: - -```sql -SELECT betaling_id, bedrag, status -FROM betalingen -WHERE status IN ('betaald', 'openstaand'); -``` - -Dit is identiek aan `WHERE status = 'betaald' OR status = 'openstaand'`. Bij twee waarden maakt het weinig uit, maar bij vijf of zes wordt `IN` een stuk overzichtelijker. - -### Logische operators: AND, OR en NOT - -Voorwaarden kun je combineren. `AND` eist dat beide voorwaarden gelden: - -```sql -SELECT voornaam, achternaam -FROM leden -WHERE actief = 1 - AND locatie_id = 2; -``` - -Alleen rijen waarbij actief gelijk is aan 1 én locatie_id gelijk aan 2, komen door het filter. Als één van beide niet klopt, valt de rij af. - -`OR` is ruimer: minstens één voorwaarde moet kloppen: - -```sql -SELECT betaling_id, bedrag, status -FROM betalingen -WHERE status = 'mislukt' - OR status = 'openstaand'; -``` - -Elke betaling die mislukt is, of openstaand is, of allebei - al die rijen verschijnen in het resultaat. - -`NOT` keert een voorwaarde om. Gebruik het spaarzaam, want `!=` of `<>` is in de meeste gevallen leesbaarder: - -```sql -SELECT voornaam, achternaam -FROM leden -WHERE NOT actief = 0; -``` - -Dit is logisch gelijk aan `WHERE actief = 1` - maar minder direct leesbaar. - -### Tekst en getallen: aanhalingstekens - -Er is een belangrijk verschil in notatie. Getallen schrijf je zonder aanhalingstekens: `actief = 1`. Tekst schrijf je altijd tussen enkele aanhalingstekens: `status = 'betaald'`. Gebruik je dubbele aanhalingstekens voor tekst, dan klaagt MySQL. In de fitworks-database zijn kolomnamen nooit geciteerd - ze staan altijd zonder aanhalingstekens in de query. - -### Reflectievraag - -De vestigingsmanager wil weten welke trainers bij FitWorks locatie 1 werken en ook een specialisatie hebben opgegeven. Welke query schrijf je? Denk goed na over de voorwaarde voor "heeft een specialisatie". - -*(Schrijf je antwoord op voordat je verder gaat.)* - ---- - -### Modelantwoord - reflectievraag 4.2 - -> **Mogelijk antwoord** -> -> De tabel `trainers` bevat een kolom `specialisatie`. Die kolom kan leeg zijn - dat betekent in een database niet een lege string, maar NULL. Om alleen de trainers te tonen die een specialisatie hebben ingevuld, gebruik je `IS NOT NULL`. Dat concept leer je in de volgende sectie uitgebreid kennen. -> -> ```sql -> SELECT voornaam, achternaam, specialisatie -> FROM trainers -> WHERE locatie_id = 1 -> AND specialisatie IS NOT NULL; -> ``` -> -> `AND` zorgt ervoor dat beide voorwaarden tegelijk waar moeten zijn. Een trainer zonder specialisatie of van een andere locatie valt af. - ---- - -## 4.3 NULL - de onzichtbare valkuil - -### Een waarde die geen waarde is - -Terug naar de vraag van de vestigingsmanager: leden zonder telefoonnummer. Wat staat er in de database als iemand geen nummer heeft opgegeven? Geen nul. Geen lege string. Er staat iets anders: **NULL**. - -NULL is de afwezigheid van een waarde. Het is geen getal, geen tekst, geen spatie. Het is simpelweg: er is geen informatie. De database zegt letterlijk "ik weet het niet." Dat is een fundamenteel ander concept dan "de waarde is nul" of "de waarde is leeg". - -In fitworks kom je NULL op meerdere plekken tegen. De kolom `tussenvoegsel` in de tabel `leden` is NULL voor iedereen zonder tussenvoegsel - niet "", maar NULL. De kolom `telefoon` is NULL als een lid geen nummer heeft opgegeven. De kolom `specialisatie` bij `trainers` is NULL als dat veld bij indiensttreding niet is ingevuld. - -### Waarom werkt `= NULL` niet? - -Dit is de meest gemaakte fout bij beginners, en de meest gevaarlijke - want je krijgt geen foutmelding. Je schrijft: - -```sql --- Dit werkt NIET zoals je denkt -SELECT voornaam, achternaam -FROM leden -WHERE telefoon = NULL; -``` - -De query wordt uitgevoerd. MySQL klaagt niet. Maar je krijgt nul resultaten, ook als er tientallen leden zonder telefoonnummer zijn. Hoe kan dat? - -In SQL is NULL geen gewone waarde die je kunt vergelijken. De uitdrukking `NULL = NULL` is niet waar. Ze is ook niet onwaar. Ze is onbekend. En een vergelijking met een onbekende uitkomst beschouwt SQL als niet-waar. Elke rij valt dan af, ook als het telefoonnummer werkelijk leeg is. - -De database redeneert zo: "Is het telefoonnummer van dit lid gelijk aan NULL? Ik weet het niet - want NULL is onbekend." En als het onbekend is, wordt de rij niet meegenomen. - -### De juiste aanpak: IS NULL en IS NOT NULL - -De database heeft speciale commando's gemaakt voor NULL-controles, omdat de gewone vergelijkingsoperator niet werkt: - -```sql --- Leden zonder telefoonnummer -SELECT voornaam, achternaam -FROM leden -WHERE telefoon IS NULL; - --- Leden met telefoonnummer -SELECT voornaam, achternaam -FROM leden -WHERE telefoon IS NOT NULL; -``` - -![NULL-afhandeling: het verschil tussen = NULL en IS NULL](afbeeldingen/hoofdstuk-04/hoofdstuk_04_scherm_03_null_afhandeling.svg) - -`IS NULL` is de enige correcte manier om te controleren of een kolom geen waarde bevat. `IS NOT NULL` doet het omgekeerde: die filtert op rijen waar wél een waarde staat. - -### Wat er misgaat als je dit negeert - -Het probleem met `= NULL` is verraderlijk: de query geeft geen foutmelding en lijkt normaal te werken - maar geeft stilletjes een leeg resultaat. Stel, je levert als systeembeheerder een rapport op basis van die query aan de manager. Ze ziet een lege lijst en denkt: "Alle leden hebben een telefoonnummer." Niemand ziet dat de data ontbreekt. Beslissingen op basis van dat rapport zijn daardoor onjuist, zonder dat iemand het weet. - -> **Onthoud:** NULL vergelijk je nooit met `=`. Altijd `IS NULL` of `IS NOT NULL`. - -### Mini-oefening - -De vestigingsmanager wil een lijst van alle lestypes waarbij de beschrijving niet is ingevuld. Schrijf de query. - -```sql --- Schrijf hier jouw query - - - -``` - -**Antwoord:** - -```sql -SELECT naam, duur_minuten, niveau -FROM lestypes -WHERE beschrijving IS NULL; -``` - ---- - -## 4.4 ORDER BY en DISTINCT - sorteren en ontdubbelen - -### ORDER BY: jij bepaalt de volgorde - -De database geeft rijen terug in de volgorde die hem uitkomt - dat is meestal de volgorde waarin de rijen zijn opgeslagen, maar daar kun je niet op rekenen. Als je een ledenlijst wilt gesorteerd op inschrijfdatum, heb je `ORDER BY` nodig. - -```sql -SELECT voornaam, achternaam, lid_sinds -FROM leden -ORDER BY lid_sinds; -``` - -Standaard sorteert `ORDER BY` **oplopend** (van vroeg naar laat, van klein naar groot). Dat heet `ASC` (ascending). Wil je de nieuwste inschrijvingen bovenaan, draai je de volgorde om met `DESC` (descending): - -```sql -SELECT voornaam, achternaam, lid_sinds -FROM leden -ORDER BY lid_sinds DESC; -``` - -Je kunt ook op meerdere kolommen sorteren. De database sorteert eerst op de eerste kolom en, bij gelijke waarden daarin, op de tweede: - -```sql -SELECT achternaam, voornaam, lid_sinds -FROM leden -ORDER BY achternaam, voornaam; -``` - -Dit levert een alfabetische ledenlijst op achternaam, met bij gelijke achternaam een alfabetische sortering op voornaam. `ORDER BY` staat altijd als laatste in de query - na `WHERE`: - -```sql -SELECT voornaam, achternaam, lid_sinds -FROM leden -WHERE actief = 1 -ORDER BY lid_sinds DESC; -``` - -![ORDER BY sorteert resultaten, DISTINCT verwijdert duplicaten](afbeeldingen/hoofdstuk-04/hoofdstuk_04_scherm_04_order_by_distinct.svg) - -### DISTINCT: elk resultaat slechts één keer - -Stel, je wilt weten welke specialisaties er bij FitWorks-trainers voorkomen. Je schrijft: - -```sql -SELECT specialisatie -FROM trainers; -``` - -Je krijgt 12 rijen - één per trainer. Maar meerdere trainers kunnen dezelfde specialisatie hebben: "Yoga" verschijnt misschien drie keer. Als je alleen de unieke specialisaties wilt zien, gebruik je `DISTINCT`: - -```sql -SELECT DISTINCT specialisatie -FROM trainers; -``` - -`DISTINCT` zet je direct na `SELECT`. Het verwijdert rijen die in alle geselecteerde kolommen identiek zijn. Staat er vier keer "Yoga" in de resultaten, dan zie je er nog maar één. - -Een veelgemaakte misvatting: `DISTINCT` werkt niet per kolom apart, maar op de combinatie van alle geselecteerde kolommen. `SELECT DISTINCT specialisatie, voornaam` geeft unieke combinaties van specialisatie én voornaam - niet unieke specialisaties per kolom. Wil je alleen unieke specialisaties, selecteer dan ook alleen die kolom. - -### Reflectievraag - -Je wilt een lijst van alle abonnementsnamen met hun maandprijs, gesorteerd van duurste naar goedkoopste. Welke query schrijf je? En wat verandert er als je `DISTINCT` toevoegt - en wanneer maakt dat verschil? - -*(Schrijf je antwoord op voordat je verder gaat.)* - ---- - -### Modelantwoord - reflectievraag 4.4 - -> **Mogelijk antwoord** -> -> Abonnementsnamen en prijzen staan in de tabel `abonnementen`. Sorteren van duurste naar goedkoopste vraagt om `ORDER BY prijs_per_maand DESC`. -> -> ```sql -> SELECT naam, prijs_per_maand -> FROM abonnementen -> ORDER BY prijs_per_maand DESC; -> ``` -> -> `DISTINCT` toevoegen verandert hier niets, want in fitworks zijn abonnementsnamen uniek. Maar als je alleen `SELECT DISTINCT naam` zou schrijven zonder prijs, dan filtert `DISTINCT` wel dubbele namen eruit. Het verschil zit hem dus in wat je selecteert: voeg je meer kolommen toe, dan worden meer combinaties als uniek beschouwd. - ---- - -## 4.5 Kolomaliassen en literals - -### Een kolom een andere naam geven - -Stel, je draait een rapport voor de vestigingsmanager. Ze ziet een overzicht met kolomnamen als `voornaam`, `lid_sinds` en `locatie_id`. Die namen zijn prima voor een databaseontwikkelaar - maar minder leesbaar voor iemand die gewend is aan Excel. Met een **alias** geef je een kolom in het resultaat een andere naam, zonder de tabel te veranderen. - -Een alias schrijf je met het sleutelwoord `AS`, direct na de kolomnaam: - -```sql -SELECT voornaam AS Voornaam, - achternaam AS Achternaam, - lid_sinds AS 'Lid sinds' -FROM leden; -``` - -Het resultaat toont de kolomkoppen "Voornaam", "Achternaam" en "Lid sinds". De tabeldefinitie in de database verandert niet - de alias bestaat alleen in dit resultaat. Als een alias een spatie bevat, zet je hem tussen enkele aanhalingstekens. - -![Kolomaliassen maken query-resultaten leesbaarder](afbeeldingen/hoofdstuk-04/hoofdstuk_04_scherm_05_aliassen_literals.svg) - -Waarom is dit nuttig? Omdat dezelfde query in de ene context voor een manager draait en in een andere context voor een ontwikkelaar. De alias laat je het resultaat aanpassen aan de lezer, zonder de query zelf fundamenteel te veranderen. - -### Literals: vaste tekst in elk resultaat - -Een **literal** is een vaste waarde die je direct in een query schrijft - geen kolom, maar een stuk tekst of een getal dat voor elke rij hetzelfde is. Literals zijn handig om context mee te geven aan een resultaat. - -```sql -SELECT voornaam, - achternaam, - 'FitWorks lid' AS type -FROM leden -WHERE actief = 1; -``` - -Elke rij in het resultaat krijgt een extra kolom "type" met de waarde `FitWorks lid`. De database vult die waarde automatisch in voor elke rij die het filter doorkomt. Een literal verandert nooit per rij - hij is altijd hetzelfde, vandaar de naam. Dit is nuttig als je twee queryresultaten samenvoegt en ze wilt markeren met een label, of als je een rapport met vaste context wilt exporteren. - -### Mini-oefening - -Schrijf een query die de naam en maandprijs van alle abonnementen toont, met als kolomkoppen "Abonnementsnaam" en "Maandprijs". Sorteer op prijs, laagste eerst. - -```sql --- Schrijf hier jouw query - - - -``` - -**Antwoord:** - -```sql -SELECT naam AS Abonnementsnaam, - prijs_per_maand AS Maandprijs -FROM abonnementen -ORDER BY prijs_per_maand; -``` - ---- - -## 4.6 Concatenatie en CAST - -### Kolommen samenvoegen: CONCAT - -Bij FitWorks is de voornaam opgeslagen in de kolom `voornaam` en de achternaam in `achternaam`. Dat is goed ontwerp - want dan kun je sorteren op achternaam, zoeken op voornaam, en filteren op elk deel afzonderlijk. Maar als je een overzicht wilt met de volledige naam als één kolom, heb je `CONCAT` nodig. - -`CONCAT` plakt twee of meer waarden achter elkaar. Je geeft de waarden als argumenten mee, gescheiden door komma's: - -```sql -SELECT CONCAT(voornaam, ' ', achternaam) AS volledige_naam -FROM leden; -``` - -Het resultaat bevat één kolom "volledige_naam" met waarden als `Sophie Bakker` of `Tom Jansen`. De spatie tussen voornaam en achternaam voeg je zelf toe als literal - de database doet dat niet automatisch. - -Maar in fitworks hebben sommige leden een tussenvoegsel. Je wilt dan iets als `Tom van der Berg`, niet `Tom van der Berg` met een dubbele spatie of `Tom Berg` zonder tussenvoegsel. De oplossing gebruikt `COALESCE`, een functie die NULL vervangt door een opgegeven standaardwaarde: - -```sql -SELECT CONCAT( - voornaam, ' ', - COALESCE(CONCAT(tussenvoegsel, ' '), ''), - achternaam - ) AS volledige_naam -FROM leden; -``` - -`COALESCE(CONCAT(tussenvoegsel, ' '), '')` zegt: als `tussenvoegsel` NULL is, gebruik dan een lege string. Als het er wél is, plak er een spatie achter. Zo staat de naam altijd correct opgemaakt. - -> **Even terzijde:** `COALESCE` is een handige functie voor NULL-situaties. Je ziet hem vaker terugkomen in latere hoofdstukken. Voor nu is het genoeg om te weten dat hij bestaat en wat hij doet. - -### Datatypes omzetten: CAST - -Elke waarde in een database heeft een datatype: tekst (`VARCHAR`, `CHAR`), getal (`INT`, `DECIMAL`), datum (`DATE`), enzovoort. Soms wil je een waarde als een ander type gebruiken - een getal weergeven als tekst zodat je er iets voor kunt plakken, of een datum omzetten naar een leesbaar formaat. Daarvoor is `CAST`. - -```sql -SELECT naam, - CONCAT('Prijs: ', CAST(prijs_per_maand AS CHAR)) AS prijsweergave -FROM abonnementen; -``` - -`CAST(prijs_per_maand AS CHAR)` zet het decimale getal `29.95` om naar de tekst `'29.95'`. Daarna kan `CONCAT` er de vaste tekst `'Prijs: '` voor plakken. Zonder `CAST` zou MySQL in sommige situaties klagen dat je een getal en een string probeert samen te voegen. - -![CONCAT plakt waarden samen, CAST zet datatypes om](afbeeldingen/hoofdstuk-04/hoofdstuk_04_scherm_06_concatenatie_cast.svg) - -De meestgebruikte CAST-conversies in MySQL zijn: - -| Van | Naar | Gebruik | -|-----|------|---------| -| Getal | Tekst | `CAST(prijs_per_maand AS CHAR)` | -| Tekst | Geheel getal | `CAST('42' AS UNSIGNED)` | -| Datum | Tekst | `CAST(geboortedatum AS CHAR)` | - -### Mini-oefening - -Schrijf een query die de volledige naam van elke trainer toont (voornaam, spatie, achternaam) in één kolom genaamd "Trainer". Gebruik de tabel `trainers`. - -```sql --- Schrijf hier jouw query - - - -``` - -**Antwoord:** - -```sql -SELECT CONCAT(voornaam, ' ', achternaam) AS Trainer -FROM trainers; -``` - ---- - -## Quiz - -### Vraag 1 - -Je wilt alle betalingen zien met status `mislukt` of `openstaand`, gesorteerd van hoog naar laag bedrag. Welke query is correct? - -**A)** -```sql -SELECT betaling_id, bedrag, status -FROM betalingen -WHERE status = 'mislukt' OR 'openstaand' -ORDER BY bedrag DESC; -``` - -**B)** -```sql -SELECT betaling_id, bedrag, status -FROM betalingen -WHERE status IN ('mislukt', 'openstaand') -ORDER BY bedrag DESC; -``` - -**C)** -```sql -SELECT betaling_id, bedrag, status -FROM betalingen -WHERE status IN ('mislukt', 'openstaand') -ORDER BY bedrag ASC; -``` - -**D)** -```sql -SELECT betaling_id, bedrag, status -FROM betalingen -WHERE status = 'mislukt' OR status = 'openstaand' -ORDER BY bedrag ASC; -``` - -**Correct antwoord: B** - -*Toelichting:* Optie B gebruikt `IN` voor de statusfilter - dat is syntactisch correct en compact. `ORDER BY bedrag DESC` sorteert van hoog naar laag, zoals gevraagd. Optie A is syntactisch fout: `OR 'openstaand'` zonder kolomnaam werkt niet. Opties C en D gebruiken `ASC`, waardoor de volgorde omgekeerd is aan wat gevraagd wordt. - ---- - -### Vraag 2 - -Welke query geeft correct alle leden terug die geen tussenvoegsel hebben? - -**A)** -```sql -SELECT voornaam, achternaam -FROM leden -WHERE tussenvoegsel = NULL; -``` - -**B)** -```sql -SELECT voornaam, achternaam -FROM leden -WHERE tussenvoegsel = ''; -``` - -**C)** -```sql -SELECT voornaam, achternaam -FROM leden -WHERE tussenvoegsel IS NULL; -``` - -**D)** -```sql -SELECT voornaam, achternaam -FROM leden -WHERE tussenvoegsel IS NOT NULL; -``` - -**Correct antwoord: C** - -*Toelichting:* Optie C is de enige correcte aanpak. NULL vergelijk je nooit met `=` - die vergelijking levert altijd "onbekend" op en geeft nul resultaten, ook als er tientallen rijen zonder tussenvoegsel zijn. Optie B zoekt naar een lege string, maar in fitworks is een ontbrekend tussenvoegsel opgeslagen als NULL, niet als een lege string - die zijn fundamenteel anders. Optie D geeft het tegenovergestelde: leden mét tussenvoegsel. - ---- - -### Vraag 3 - -Een collega schrijft deze query en ziet dat de kolom `specialisatie` nog steeds herhaalde waarden bevat: - -```sql -SELECT DISTINCT specialisatie, voornaam -FROM trainers; -``` - -Wat is de oorzaak? - -**A)** `DISTINCT` werkt alleen op de eerste geselecteerde kolom. - -**B)** `DISTINCT` verwijdert dubbele rijen op basis van alle geselecteerde kolommen tegelijk. Twee trainers met dezelfde specialisatie maar een andere voornaam zijn geen duplicaat. - -**C)** `DISTINCT` moet altijd gecombineerd worden met `ORDER BY`. - -**D)** `DISTINCT` werkt niet op tekst-kolommen als `specialisatie`. - -**Correct antwoord: B** - -*Toelichting:* `DISTINCT` kijkt naar de combinatie van alle geselecteerde kolommen. Als twee trainers dezelfde specialisatie maar een andere voornaam hebben, beschouwt MySQL ze als twee unieke rijen. Wil je alleen unieke specialisaties zien, selecteer dan ook alleen die kolom: `SELECT DISTINCT specialisatie FROM trainers;` - ---- - -## Samenvatting - -`SELECT` en `FROM` vormen het hart van elke query. `SELECT` bepaalt welke kolommen je ziet, `FROM` bepaalt de tabel. Ze zijn verplicht en staan altijd in die volgorde. - -Met `WHERE` filter je welke rijen je terugkrijgt. Je gebruikt vergelijkingsoperatoren (`=`, `!=`, `<`, `>`, `BETWEEN`, `IN`) en logische operators (`AND`, `OR`, `NOT`) om voorwaarden te combineren. Tekst staat altijd tussen enkele aanhalingstekens. - -NULL is de afwezigheid van een waarde - niet nul, niet leeg, maar onbekend. Je kunt er nooit op filteren met `=`. Gebruik altijd `IS NULL` of `IS NOT NULL`. - -Met `ORDER BY` sorteer je resultaten op een of meerdere kolommen, oplopend (`ASC`) of aflopend (`DESC`). `DISTINCT` verwijdert rijen die op alle geselecteerde kolommen identiek zijn. - -Kolomaliassen (`AS`) geven een kolom een andere naam in het resultaat. Literals zijn vaste waarden die je direct in de query opneemt. `CONCAT` plakt meerdere waarden aaneen. `CAST` zet een waarde om naar een ander datatype. - ---- - -## Vooruitblik - -Je hebt nu de basis in handen om de fitworks-database te bevragen. Maar er is meer. Wat als je leden wilt zoeken op een deel van hun achternaam - iedereen wiens naam begint met "van"? Of wil weten hoe oud iemand is op basis van hun geboortedatum? Daarvoor heb je gereedschap nodig dat verder gaat dan de basisoperatoren. In hoofdstuk 5 leer je werken met tekstbewerkingen (waaronder `LIKE` voor zoeken op patronen) en datumfuncties. Je gaat de data niet alleen ophalen - je gaat er echt mee werken. diff --git a/artifacts/oa-runner-chapters-large/chapter-2-structure.png b/artifacts/oa-runner-chapters-large/chapter-2-structure.png deleted file mode 100644 index 3b173ba..0000000 Binary files a/artifacts/oa-runner-chapters-large/chapter-2-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/chapter-3-structure.png b/artifacts/oa-runner-chapters-large/chapter-3-structure.png deleted file mode 100644 index fe00ad6..0000000 Binary files a/artifacts/oa-runner-chapters-large/chapter-3-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/chapter-4-structure.png b/artifacts/oa-runner-chapters-large/chapter-4-structure.png deleted file mode 100644 index 56d9985..0000000 Binary files a/artifacts/oa-runner-chapters-large/chapter-4-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/editor-ready.png b/artifacts/oa-runner-chapters-large/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-chapters-large/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-1-block-1-text.png b/artifacts/oa-runner-chapters-large/page-1-block-1-text.png deleted file mode 100644 index 83c61ff..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-1-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-1-structure.png b/artifacts/oa-runner-chapters-large/page-1-structure.png deleted file mode 100644 index c5774e7..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-10-block-1-text.png b/artifacts/oa-runner-chapters-large/page-10-block-1-text.png deleted file mode 100644 index 4ab486a..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-10-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-10-structure.png b/artifacts/oa-runner-chapters-large/page-10-structure.png deleted file mode 100644 index 9a94467..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-10-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-2-block-1-text.png b/artifacts/oa-runner-chapters-large/page-2-block-1-text.png deleted file mode 100644 index 86ae50d..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-2-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-2-structure.png b/artifacts/oa-runner-chapters-large/page-2-structure.png deleted file mode 100644 index 2ff2056..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-2-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-3-block-1-text.png b/artifacts/oa-runner-chapters-large/page-3-block-1-text.png deleted file mode 100644 index 37c5d9e..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-3-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-3-structure.png b/artifacts/oa-runner-chapters-large/page-3-structure.png deleted file mode 100644 index c0009a6..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-3-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-4-block-1-text.png b/artifacts/oa-runner-chapters-large/page-4-block-1-text.png deleted file mode 100644 index a227e65..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-4-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-4-structure.png b/artifacts/oa-runner-chapters-large/page-4-structure.png deleted file mode 100644 index b870a96..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-4-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-5-block-1-text.png b/artifacts/oa-runner-chapters-large/page-5-block-1-text.png deleted file mode 100644 index 656b950..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-5-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-5-structure.png b/artifacts/oa-runner-chapters-large/page-5-structure.png deleted file mode 100644 index 9e4f140..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-5-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-6-block-1-text.png b/artifacts/oa-runner-chapters-large/page-6-block-1-text.png deleted file mode 100644 index 72c6073..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-6-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-6-structure.png b/artifacts/oa-runner-chapters-large/page-6-structure.png deleted file mode 100644 index 344a123..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-6-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-7-block-1-text.png b/artifacts/oa-runner-chapters-large/page-7-block-1-text.png deleted file mode 100644 index 2cc55ce..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-7-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-7-structure.png b/artifacts/oa-runner-chapters-large/page-7-structure.png deleted file mode 100644 index 2c67a20..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-7-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-8-block-1-text.png b/artifacts/oa-runner-chapters-large/page-8-block-1-text.png deleted file mode 100644 index 905d8e1..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-8-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-8-structure.png b/artifacts/oa-runner-chapters-large/page-8-structure.png deleted file mode 100644 index 9db4465..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-8-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-9-block-1-text.png b/artifacts/oa-runner-chapters-large/page-9-block-1-text.png deleted file mode 100644 index b385642..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-9-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/page-9-structure.png b/artifacts/oa-runner-chapters-large/page-9-structure.png deleted file mode 100644 index 7b52789..0000000 Binary files a/artifacts/oa-runner-chapters-large/page-9-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/plan.json b/artifacts/oa-runner-chapters-large/plan.json deleted file mode 100644 index e7ac428..0000000 --- a/artifacts/oa-runner-chapters-large/plan.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "training": "Pilot Multi Chapter Run", - "chapters": [ - { - "title": "Hoofdstuk Alfa", - "pages": [ - { - "title": "Alfa 1", - "blocks": [ - { - "type": "text", - "summary": "Alfa 1" - } - ] - } - ] - }, - { - "title": "Hoofdstuk Bravo", - "pages": [ - { - "title": "Bravo 1", - "blocks": [ - { - "type": "text", - "summary": "Bravo 1" - } - ] - }, - { - "title": "Bravo 2", - "blocks": [ - { - "type": "text", - "summary": "Bravo 2" - } - ] - }, - { - "title": "Bravo 3", - "blocks": [ - { - "type": "text", - "summary": "Bravo 3" - } - ] - } - ] - }, - { - "title": "Hoofdstuk Charlie", - "pages": [ - { - "title": "Charlie 1", - "blocks": [ - { - "type": "text", - "summary": "Charlie 1" - } - ] - }, - { - "title": "Charlie 2", - "blocks": [ - { - "type": "text", - "summary": "Charlie 2" - } - ] - } - ] - }, - { - "title": "Hoofdstuk Delta", - "pages": [ - { - "title": "Delta 1", - "blocks": [ - { - "type": "text", - "summary": "Delta 1" - } - ] - }, - { - "title": "Delta 2", - "blocks": [ - { - "type": "text", - "summary": "Delta 2" - } - ] - }, - { - "title": "Delta 3", - "blocks": [ - { - "type": "text", - "summary": "Delta 3" - } - ] - }, - { - "title": "Delta 4", - "blocks": [ - { - "type": "text", - "summary": "Delta 4" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-chapters-large/review-stop.png b/artifacts/oa-runner-chapters-large/review-stop.png deleted file mode 100644 index 982e74f..0000000 Binary files a/artifacts/oa-runner-chapters-large/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters-large/validation.json b/artifacts/oa-runner-chapters-large/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-chapters-large/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-chapters/chapter-2-structure.png b/artifacts/oa-runner-chapters/chapter-2-structure.png deleted file mode 100644 index cac2ca9..0000000 Binary files a/artifacts/oa-runner-chapters/chapter-2-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters/editor-ready.png b/artifacts/oa-runner-chapters/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-chapters/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters/page-1-block-1-text.png b/artifacts/oa-runner-chapters/page-1-block-1-text.png deleted file mode 100644 index 2752002..0000000 Binary files a/artifacts/oa-runner-chapters/page-1-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters/page-1-structure.png b/artifacts/oa-runner-chapters/page-1-structure.png deleted file mode 100644 index 13abb46..0000000 Binary files a/artifacts/oa-runner-chapters/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters/page-2-block-1-text.png b/artifacts/oa-runner-chapters/page-2-block-1-text.png deleted file mode 100644 index 4cd665e..0000000 Binary files a/artifacts/oa-runner-chapters/page-2-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters/page-2-structure.png b/artifacts/oa-runner-chapters/page-2-structure.png deleted file mode 100644 index 57ae9b4..0000000 Binary files a/artifacts/oa-runner-chapters/page-2-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters/plan.json b/artifacts/oa-runner-chapters/plan.json deleted file mode 100644 index 53a6c0a..0000000 --- a/artifacts/oa-runner-chapters/plan.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "training": "Pilot Chapter Run", - "chapters": [ - { - "title": "Eerste hoofdstuk", - "pages": [ - { - "title": "Intro hoofdstuk 1", - "blocks": [ - { - "type": "text", - "summary": "Hoofdstuk 1" - } - ] - } - ] - }, - { - "title": "Tweede hoofdstuk", - "pages": [ - { - "title": "Intro hoofdstuk 2", - "blocks": [ - { - "type": "text", - "summary": "Hoofdstuk 2" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-chapters/review-stop.png b/artifacts/oa-runner-chapters/review-stop.png deleted file mode 100644 index 4cd665e..0000000 Binary files a/artifacts/oa-runner-chapters/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-chapters/validation.json b/artifacts/oa-runner-chapters/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-chapters/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/chapter-1-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/chapter-1-structure.png deleted file mode 100644 index 79c357b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/chapter-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/editor-ready.png b/artifacts/oa-runner-hoofdstuk-04-v2/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-1-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-1-block-1-text.png deleted file mode 100644 index 5432ac4..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-1-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-1-block-2-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-1-block-2-text.png deleted file mode 100644 index e964bb8..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-1-block-2-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-1-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-1-structure.png deleted file mode 100644 index abf0c65..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-10-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-10-block-1-text.png deleted file mode 100644 index 281388b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-10-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-10-block-2-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-10-block-2-text.png deleted file mode 100644 index 2dbffeb..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-10-block-2-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-10-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-10-structure.png deleted file mode 100644 index 6552456..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-10-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-2-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-2-block-1-text.png deleted file mode 100644 index d6e1c89..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-2-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-2-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-2-structure.png deleted file mode 100644 index 6838830..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-2-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-1-text.png deleted file mode 100644 index 946d66f..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-2-image.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-2-image.png deleted file mode 100644 index 279cd27..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-2-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-3-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-3-text.png deleted file mode 100644 index 44d6aea..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-3-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-4-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-4-open-question.png deleted file mode 100644 index 5748eb0..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-3-block-4-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-3-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-3-structure.png deleted file mode 100644 index dd20236..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-3-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-1-text.png deleted file mode 100644 index daa9647..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-2-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-2-text.png deleted file mode 100644 index d2c34a5..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-2-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-3-image.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-3-image.png deleted file mode 100644 index afdc831..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-3-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-4-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-4-text.png deleted file mode 100644 index 1293a27..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-4-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-5-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-5-text.png deleted file mode 100644 index fddb0ca..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-5-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-6-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-6-open-question.png deleted file mode 100644 index 9fd8a6f..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-block-6-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-4-structure.png deleted file mode 100644 index 35f6f66..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-4-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-1-text.png deleted file mode 100644 index 3ee67da..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-2-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-2-text.png deleted file mode 100644 index b6b947d..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-2-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-3-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-3-text.png deleted file mode 100644 index ef8695b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-3-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-4-image.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-4-image.png deleted file mode 100644 index 8d6025b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-4-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-5-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-5-text.png deleted file mode 100644 index 3fbe057..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-5-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-6-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-6-open-question.png deleted file mode 100644 index fcaca3f..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-block-6-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-5-structure.png deleted file mode 100644 index 750c7a0..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-5-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-1-text.png deleted file mode 100644 index 5817ea3..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-2-image.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-2-image.png deleted file mode 100644 index f7cf018..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-2-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-3-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-3-text.png deleted file mode 100644 index 3ce181e..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-3-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-4-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-4-open-question.png deleted file mode 100644 index a528645..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-6-block-4-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-6-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-6-structure.png deleted file mode 100644 index 626360a..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-6-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-1-text.png deleted file mode 100644 index 6882948..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-2-image.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-2-image.png deleted file mode 100644 index c77f05b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-2-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-3-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-3-text.png deleted file mode 100644 index 82aba1e..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-3-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-4-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-4-open-question.png deleted file mode 100644 index 7518a76..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-7-block-4-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-7-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-7-structure.png deleted file mode 100644 index 02d871c..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-7-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-1-text.png deleted file mode 100644 index 70b05c8..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-2-text.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-2-text.png deleted file mode 100644 index d2cd743..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-2-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-3-image.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-3-image.png deleted file mode 100644 index 6664d9f..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-3-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-4-table.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-4-table.png deleted file mode 100644 index 5349fd8..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-4-table.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-5-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-5-open-question.png deleted file mode 100644 index 68bbc51..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-block-5-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-8-structure.png deleted file mode 100644 index 51503d0..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-8-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-9-block-1-multiple-choice.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-9-block-1-multiple-choice.png deleted file mode 100644 index b562001..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-9-block-1-multiple-choice.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-9-block-2-multiple-choice.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-9-block-2-multiple-choice.png deleted file mode 100644 index 39e67a6..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-9-block-2-multiple-choice.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-9-block-3-multiple-choice.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-9-block-3-multiple-choice.png deleted file mode 100644 index 9e2de46..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-9-block-3-multiple-choice.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/page-9-structure.png b/artifacts/oa-runner-hoofdstuk-04-v2/page-9-structure.png deleted file mode 100644 index 537863b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/page-9-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/plan.json b/artifacts/oa-runner-hoofdstuk-04-v2/plan.json deleted file mode 100644 index 67a8105..0000000 --- a/artifacts/oa-runner-hoofdstuk-04-v2/plan.json +++ /dev/null @@ -1,208 +0,0 @@ -{ - "training": "SQL met FitWorks", - "chapters": [ - { - "title": "Hoofdstuk 4 - Basisqueries", - "pages": [ - { - "title": "Terugblik en opening", - "blocks": [ - { - "type": "text", - "summary": "Terugblik" - }, - { - "type": "text", - "summary": "Opening" - } - ] - }, - { - "title": "Leerdoelen", - "blocks": [ - { - "type": "text", - "summary": "Leerdoelen" - } - ] - }, - { - "title": "4.1 SELECT en FROM", - "blocks": [ - { - "type": "text", - "summary": "De balie vraagt, de database antwoordt" - }, - { - "type": "image", - "summary": "Anatomie van een SELECT-query" - }, - { - "type": "text", - "summary": "Alle kolommen tegelijk" - }, - { - "type": "open-question", - "summary": "Je wilt de e-mailadressen en geboortedatums van alle leden zien. Welke query schrijf je?" - } - ] - }, - { - "title": "4.2 WHERE", - "blocks": [ - { - "type": "text", - "summary": "Filteren op voorwaarden" - }, - { - "type": "text", - "summary": "Vergelijkingsoperatoren" - }, - { - "type": "image", - "summary": "WHERE-operatoren" - }, - { - "type": "text", - "summary": "Logische operators" - }, - { - "type": "text", - "summary": "Tekst en getallen" - }, - { - "type": "open-question", - "summary": "Welke query schrijf je om trainers op locatie 1 te tonen die ook een specialisatie hebben opgegeven?" - } - ] - }, - { - "title": "4.3 NULL", - "blocks": [ - { - "type": "text", - "summary": "Een waarde die geen waarde is" - }, - { - "type": "text", - "summary": "Waarom werkt = NULL niet" - }, - { - "type": "text", - "summary": "De juiste aanpak" - }, - { - "type": "image", - "summary": "NULL-afhandeling" - }, - { - "type": "text", - "summary": "Wat gaat er mis als je dit negeert" - }, - { - "type": "open-question", - "summary": "Schrijf de query die alle lestypes toont waarvan de beschrijving niet is ingevuld." - } - ] - }, - { - "title": "4.4 ORDER BY en DISTINCT", - "blocks": [ - { - "type": "text", - "summary": "ORDER BY" - }, - { - "type": "image", - "summary": "ORDER BY en DISTINCT" - }, - { - "type": "text", - "summary": "DISTINCT" - }, - { - "type": "open-question", - "summary": "Welke query schrijf je om alle abonnementsnamen met maandprijs te tonen, gesorteerd van duurste naar goedkoopste? Wat verandert er als je DISTINCT toevoegt?" - } - ] - }, - { - "title": "4.5 Kolomaliassen en literals", - "blocks": [ - { - "type": "text", - "summary": "Kolomaliassen" - }, - { - "type": "image", - "summary": "Aliassen en literals" - }, - { - "type": "text", - "summary": "Literals" - }, - { - "type": "open-question", - "summary": "Schrijf een query die de naam en maandprijs van alle abonnementen toont, met als kolomkoppen Abonnementsnaam en Maandprijs. Sorteer op prijs, laagste eerst." - } - ] - }, - { - "title": "4.6 CONCAT en CAST", - "blocks": [ - { - "type": "text", - "summary": "CONCAT" - }, - { - "type": "text", - "summary": "CAST" - }, - { - "type": "image", - "summary": "CONCAT en CAST" - }, - { - "type": "table", - "summary": "Veelgebruikte CAST-conversies" - }, - { - "type": "open-question", - "summary": "Schrijf een query die de volledige naam van elke trainer toont in een kolom genaamd Trainer." - } - ] - }, - { - "title": "Quiz", - "blocks": [ - { - "type": "multiple-choice", - "summary": "Je wilt alle betalingen zien met status `mislukt` of `openstaand`, gesorteerd van hoog naar laag bedrag. Welke query is correct?" - }, - { - "type": "multiple-choice", - "summary": "Welke query geeft correct alle leden terug die geen tussenvoegsel hebben?" - }, - { - "type": "multiple-choice", - "summary": "Een collega ziet met `SELECT DISTINCT specialisatie, voornaam FROM trainers;` nog steeds herhaalde waarden in `specialisatie`. Wat is de oorzaak?" - } - ] - }, - { - "title": "Samenvatting en vooruitblik", - "blocks": [ - { - "type": "text", - "summary": "Samenvatting" - }, - { - "type": "text", - "summary": "Vooruitblik" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/review-stop.png b/artifacts/oa-runner-hoofdstuk-04-v2/review-stop.png deleted file mode 100644 index 2dbffeb..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v2/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v2/validation.json b/artifacts/oa-runner-hoofdstuk-04-v2/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-hoofdstuk-04-v2/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/chapter-1-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/chapter-1-structure.png deleted file mode 100644 index 62f7815..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/chapter-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/editor-ready.png b/artifacts/oa-runner-hoofdstuk-04-v3/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-1-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-1-block-1-text.png deleted file mode 100644 index ae4f845..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-1-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-1-block-2-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-1-block-2-text.png deleted file mode 100644 index 0ee781d..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-1-block-2-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-1-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-1-structure.png deleted file mode 100644 index 6426465..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-10-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-10-block-1-text.png deleted file mode 100644 index 281388b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-10-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-10-block-2-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-10-block-2-text.png deleted file mode 100644 index 2dbffeb..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-10-block-2-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-10-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-10-structure.png deleted file mode 100644 index d495305..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-10-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-2-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-2-block-1-text.png deleted file mode 100644 index d6e1c89..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-2-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-2-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-2-structure.png deleted file mode 100644 index e40da5a..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-2-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-1-text.png deleted file mode 100644 index 946d66f..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-2-image.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-2-image.png deleted file mode 100644 index 45defda..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-2-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-3-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-3-text.png deleted file mode 100644 index 44d6aea..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-3-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-4-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-4-open-question.png deleted file mode 100644 index 5deb664..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-3-block-4-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-3-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-3-structure.png deleted file mode 100644 index 610a131..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-3-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-1-text.png deleted file mode 100644 index daa9647..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-2-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-2-text.png deleted file mode 100644 index d2c34a5..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-2-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-3-image.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-3-image.png deleted file mode 100644 index afdc831..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-3-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-4-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-4-text.png deleted file mode 100644 index 1293a27..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-4-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-5-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-5-text.png deleted file mode 100644 index fddb0ca..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-5-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-6-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-6-open-question.png deleted file mode 100644 index 9fd8a6f..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-block-6-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-4-structure.png deleted file mode 100644 index 38c1295..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-4-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-1-text.png deleted file mode 100644 index 75f6e6b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-2-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-2-text.png deleted file mode 100644 index b6b947d..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-2-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-3-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-3-text.png deleted file mode 100644 index ef8695b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-3-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-4-image.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-4-image.png deleted file mode 100644 index 8d6025b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-4-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-5-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-5-text.png deleted file mode 100644 index 3fbe057..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-5-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-6-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-6-open-question.png deleted file mode 100644 index e04d993..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-block-6-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-5-structure.png deleted file mode 100644 index 981a1e1..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-5-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-1-text.png deleted file mode 100644 index 5817ea3..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-2-image.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-2-image.png deleted file mode 100644 index c1e90ac..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-2-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-3-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-3-text.png deleted file mode 100644 index 3ce181e..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-3-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-4-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-4-open-question.png deleted file mode 100644 index b0204c6..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-6-block-4-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-6-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-6-structure.png deleted file mode 100644 index 94d545a..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-6-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-1-text.png deleted file mode 100644 index c91b997..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-2-image.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-2-image.png deleted file mode 100644 index 0a2b19b..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-2-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-3-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-3-text.png deleted file mode 100644 index 7f08f83..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-3-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-4-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-4-open-question.png deleted file mode 100644 index 9cfb295..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-7-block-4-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-7-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-7-structure.png deleted file mode 100644 index a107c1a..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-7-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-1-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-1-text.png deleted file mode 100644 index 4a9e43e..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-2-text.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-2-text.png deleted file mode 100644 index d2cd743..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-2-text.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-3-image.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-3-image.png deleted file mode 100644 index 6664d9f..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-3-image.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-4-table.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-4-table.png deleted file mode 100644 index 5349fd8..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-4-table.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-5-open-question.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-5-open-question.png deleted file mode 100644 index 197681f..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-block-5-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-8-structure.png deleted file mode 100644 index db690ca..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-8-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-9-block-1-multiple-choice.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-9-block-1-multiple-choice.png deleted file mode 100644 index 5b48390..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-9-block-1-multiple-choice.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-9-block-2-multiple-choice.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-9-block-2-multiple-choice.png deleted file mode 100644 index 8b134c5..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-9-block-2-multiple-choice.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-9-block-3-multiple-choice.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-9-block-3-multiple-choice.png deleted file mode 100644 index dc276e0..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-9-block-3-multiple-choice.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/page-9-structure.png b/artifacts/oa-runner-hoofdstuk-04-v3/page-9-structure.png deleted file mode 100644 index 34f2b76..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/page-9-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/plan.json b/artifacts/oa-runner-hoofdstuk-04-v3/plan.json deleted file mode 100644 index 67a8105..0000000 --- a/artifacts/oa-runner-hoofdstuk-04-v3/plan.json +++ /dev/null @@ -1,208 +0,0 @@ -{ - "training": "SQL met FitWorks", - "chapters": [ - { - "title": "Hoofdstuk 4 - Basisqueries", - "pages": [ - { - "title": "Terugblik en opening", - "blocks": [ - { - "type": "text", - "summary": "Terugblik" - }, - { - "type": "text", - "summary": "Opening" - } - ] - }, - { - "title": "Leerdoelen", - "blocks": [ - { - "type": "text", - "summary": "Leerdoelen" - } - ] - }, - { - "title": "4.1 SELECT en FROM", - "blocks": [ - { - "type": "text", - "summary": "De balie vraagt, de database antwoordt" - }, - { - "type": "image", - "summary": "Anatomie van een SELECT-query" - }, - { - "type": "text", - "summary": "Alle kolommen tegelijk" - }, - { - "type": "open-question", - "summary": "Je wilt de e-mailadressen en geboortedatums van alle leden zien. Welke query schrijf je?" - } - ] - }, - { - "title": "4.2 WHERE", - "blocks": [ - { - "type": "text", - "summary": "Filteren op voorwaarden" - }, - { - "type": "text", - "summary": "Vergelijkingsoperatoren" - }, - { - "type": "image", - "summary": "WHERE-operatoren" - }, - { - "type": "text", - "summary": "Logische operators" - }, - { - "type": "text", - "summary": "Tekst en getallen" - }, - { - "type": "open-question", - "summary": "Welke query schrijf je om trainers op locatie 1 te tonen die ook een specialisatie hebben opgegeven?" - } - ] - }, - { - "title": "4.3 NULL", - "blocks": [ - { - "type": "text", - "summary": "Een waarde die geen waarde is" - }, - { - "type": "text", - "summary": "Waarom werkt = NULL niet" - }, - { - "type": "text", - "summary": "De juiste aanpak" - }, - { - "type": "image", - "summary": "NULL-afhandeling" - }, - { - "type": "text", - "summary": "Wat gaat er mis als je dit negeert" - }, - { - "type": "open-question", - "summary": "Schrijf de query die alle lestypes toont waarvan de beschrijving niet is ingevuld." - } - ] - }, - { - "title": "4.4 ORDER BY en DISTINCT", - "blocks": [ - { - "type": "text", - "summary": "ORDER BY" - }, - { - "type": "image", - "summary": "ORDER BY en DISTINCT" - }, - { - "type": "text", - "summary": "DISTINCT" - }, - { - "type": "open-question", - "summary": "Welke query schrijf je om alle abonnementsnamen met maandprijs te tonen, gesorteerd van duurste naar goedkoopste? Wat verandert er als je DISTINCT toevoegt?" - } - ] - }, - { - "title": "4.5 Kolomaliassen en literals", - "blocks": [ - { - "type": "text", - "summary": "Kolomaliassen" - }, - { - "type": "image", - "summary": "Aliassen en literals" - }, - { - "type": "text", - "summary": "Literals" - }, - { - "type": "open-question", - "summary": "Schrijf een query die de naam en maandprijs van alle abonnementen toont, met als kolomkoppen Abonnementsnaam en Maandprijs. Sorteer op prijs, laagste eerst." - } - ] - }, - { - "title": "4.6 CONCAT en CAST", - "blocks": [ - { - "type": "text", - "summary": "CONCAT" - }, - { - "type": "text", - "summary": "CAST" - }, - { - "type": "image", - "summary": "CONCAT en CAST" - }, - { - "type": "table", - "summary": "Veelgebruikte CAST-conversies" - }, - { - "type": "open-question", - "summary": "Schrijf een query die de volledige naam van elke trainer toont in een kolom genaamd Trainer." - } - ] - }, - { - "title": "Quiz", - "blocks": [ - { - "type": "multiple-choice", - "summary": "Je wilt alle betalingen zien met status `mislukt` of `openstaand`, gesorteerd van hoog naar laag bedrag. Welke query is correct?" - }, - { - "type": "multiple-choice", - "summary": "Welke query geeft correct alle leden terug die geen tussenvoegsel hebben?" - }, - { - "type": "multiple-choice", - "summary": "Een collega ziet met `SELECT DISTINCT specialisatie, voornaam FROM trainers;` nog steeds herhaalde waarden in `specialisatie`. Wat is de oorzaak?" - } - ] - }, - { - "title": "Samenvatting en vooruitblik", - "blocks": [ - { - "type": "text", - "summary": "Samenvatting" - }, - { - "type": "text", - "summary": "Vooruitblik" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/review-stop.png b/artifacts/oa-runner-hoofdstuk-04-v3/review-stop.png deleted file mode 100644 index 2dbffeb..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04-v3/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04-v3/validation.json b/artifacts/oa-runner-hoofdstuk-04-v3/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-hoofdstuk-04-v3/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-hoofdstuk-04/editor-ready.png b/artifacts/oa-runner-hoofdstuk-04/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-hoofdstuk-04/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-hoofdstuk-04/plan.json b/artifacts/oa-runner-hoofdstuk-04/plan.json deleted file mode 100644 index 67a8105..0000000 --- a/artifacts/oa-runner-hoofdstuk-04/plan.json +++ /dev/null @@ -1,208 +0,0 @@ -{ - "training": "SQL met FitWorks", - "chapters": [ - { - "title": "Hoofdstuk 4 - Basisqueries", - "pages": [ - { - "title": "Terugblik en opening", - "blocks": [ - { - "type": "text", - "summary": "Terugblik" - }, - { - "type": "text", - "summary": "Opening" - } - ] - }, - { - "title": "Leerdoelen", - "blocks": [ - { - "type": "text", - "summary": "Leerdoelen" - } - ] - }, - { - "title": "4.1 SELECT en FROM", - "blocks": [ - { - "type": "text", - "summary": "De balie vraagt, de database antwoordt" - }, - { - "type": "image", - "summary": "Anatomie van een SELECT-query" - }, - { - "type": "text", - "summary": "Alle kolommen tegelijk" - }, - { - "type": "open-question", - "summary": "Je wilt de e-mailadressen en geboortedatums van alle leden zien. Welke query schrijf je?" - } - ] - }, - { - "title": "4.2 WHERE", - "blocks": [ - { - "type": "text", - "summary": "Filteren op voorwaarden" - }, - { - "type": "text", - "summary": "Vergelijkingsoperatoren" - }, - { - "type": "image", - "summary": "WHERE-operatoren" - }, - { - "type": "text", - "summary": "Logische operators" - }, - { - "type": "text", - "summary": "Tekst en getallen" - }, - { - "type": "open-question", - "summary": "Welke query schrijf je om trainers op locatie 1 te tonen die ook een specialisatie hebben opgegeven?" - } - ] - }, - { - "title": "4.3 NULL", - "blocks": [ - { - "type": "text", - "summary": "Een waarde die geen waarde is" - }, - { - "type": "text", - "summary": "Waarom werkt = NULL niet" - }, - { - "type": "text", - "summary": "De juiste aanpak" - }, - { - "type": "image", - "summary": "NULL-afhandeling" - }, - { - "type": "text", - "summary": "Wat gaat er mis als je dit negeert" - }, - { - "type": "open-question", - "summary": "Schrijf de query die alle lestypes toont waarvan de beschrijving niet is ingevuld." - } - ] - }, - { - "title": "4.4 ORDER BY en DISTINCT", - "blocks": [ - { - "type": "text", - "summary": "ORDER BY" - }, - { - "type": "image", - "summary": "ORDER BY en DISTINCT" - }, - { - "type": "text", - "summary": "DISTINCT" - }, - { - "type": "open-question", - "summary": "Welke query schrijf je om alle abonnementsnamen met maandprijs te tonen, gesorteerd van duurste naar goedkoopste? Wat verandert er als je DISTINCT toevoegt?" - } - ] - }, - { - "title": "4.5 Kolomaliassen en literals", - "blocks": [ - { - "type": "text", - "summary": "Kolomaliassen" - }, - { - "type": "image", - "summary": "Aliassen en literals" - }, - { - "type": "text", - "summary": "Literals" - }, - { - "type": "open-question", - "summary": "Schrijf een query die de naam en maandprijs van alle abonnementen toont, met als kolomkoppen Abonnementsnaam en Maandprijs. Sorteer op prijs, laagste eerst." - } - ] - }, - { - "title": "4.6 CONCAT en CAST", - "blocks": [ - { - "type": "text", - "summary": "CONCAT" - }, - { - "type": "text", - "summary": "CAST" - }, - { - "type": "image", - "summary": "CONCAT en CAST" - }, - { - "type": "table", - "summary": "Veelgebruikte CAST-conversies" - }, - { - "type": "open-question", - "summary": "Schrijf een query die de volledige naam van elke trainer toont in een kolom genaamd Trainer." - } - ] - }, - { - "title": "Quiz", - "blocks": [ - { - "type": "multiple-choice", - "summary": "Je wilt alle betalingen zien met status `mislukt` of `openstaand`, gesorteerd van hoog naar laag bedrag. Welke query is correct?" - }, - { - "type": "multiple-choice", - "summary": "Welke query geeft correct alle leden terug die geen tussenvoegsel hebben?" - }, - { - "type": "multiple-choice", - "summary": "Een collega ziet met `SELECT DISTINCT specialisatie, voornaam FROM trainers;` nog steeds herhaalde waarden in `specialisatie`. Wat is de oorzaak?" - } - ] - }, - { - "title": "Samenvatting en vooruitblik", - "blocks": [ - { - "type": "text", - "summary": "Samenvatting" - }, - { - "type": "text", - "summary": "Vooruitblik" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-hoofdstuk-04/validation.json b/artifacts/oa-runner-hoofdstuk-04/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-hoofdstuk-04/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-image/editor-ready.png b/artifacts/oa-runner-image/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-image/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-image/page-1-block-1-image.png b/artifacts/oa-runner-image/page-1-block-1-image.png deleted file mode 100644 index d290d27..0000000 Binary files a/artifacts/oa-runner-image/page-1-block-1-image.png and /dev/null differ diff --git a/artifacts/oa-runner-image/page-1-structure.png b/artifacts/oa-runner-image/page-1-structure.png deleted file mode 100644 index a9344d7..0000000 Binary files a/artifacts/oa-runner-image/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-image/plan.json b/artifacts/oa-runner-image/plan.json deleted file mode 100644 index fb6de7c..0000000 --- a/artifacts/oa-runner-image/plan.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "training": "Pilot Image Run", - "chapters": [ - { - "title": "Playwright Testmodule", - "pages": [ - { - "title": "Afbeelding", - "blocks": [ - { - "type": "image", - "summary": "SQL-groepen overzicht" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-image/review-stop.png b/artifacts/oa-runner-image/review-stop.png deleted file mode 100644 index 3982334..0000000 Binary files a/artifacts/oa-runner-image/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-image/validation.json b/artifacts/oa-runner-image/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-image/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-mc/editor-ready.png b/artifacts/oa-runner-mc/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-mc/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-mc/failure-state.png b/artifacts/oa-runner-mc/failure-state.png deleted file mode 100644 index 3f1b600..0000000 Binary files a/artifacts/oa-runner-mc/failure-state.png and /dev/null differ diff --git a/artifacts/oa-runner-mc/page-1-block-1-text.png b/artifacts/oa-runner-mc/page-1-block-1-text.png deleted file mode 100644 index 9b88612..0000000 Binary files a/artifacts/oa-runner-mc/page-1-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-mc/page-1-block-2-heading.png b/artifacts/oa-runner-mc/page-1-block-2-heading.png deleted file mode 100644 index 5ee9073..0000000 Binary files a/artifacts/oa-runner-mc/page-1-block-2-heading.png and /dev/null differ diff --git a/artifacts/oa-runner-mc/page-1-block-3-quote.png b/artifacts/oa-runner-mc/page-1-block-3-quote.png deleted file mode 100644 index 7b2ec52..0000000 Binary files a/artifacts/oa-runner-mc/page-1-block-3-quote.png and /dev/null differ diff --git a/artifacts/oa-runner-mc/page-1-block-4-multiple-choice.png b/artifacts/oa-runner-mc/page-1-block-4-multiple-choice.png deleted file mode 100644 index fc1e26c..0000000 Binary files a/artifacts/oa-runner-mc/page-1-block-4-multiple-choice.png and /dev/null differ diff --git a/artifacts/oa-runner-mc/page-1-structure.png b/artifacts/oa-runner-mc/page-1-structure.png deleted file mode 100644 index 8fca311..0000000 Binary files a/artifacts/oa-runner-mc/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-mc/plan.json b/artifacts/oa-runner-mc/plan.json deleted file mode 100644 index 4403513..0000000 --- a/artifacts/oa-runner-mc/plan.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "training": "Pilot Playwright Run", - "chapters": [ - { - "title": "Playwright Testmodule", - "pages": [ - { - "title": "Intro", - "blocks": [ - { - "type": "text", - "summary": "Welkom" - }, - { - "type": "heading", - "summary": "Waarom deze pilot" - }, - { - "type": "quote", - "summary": "Eerst veilig structureren, daarna pas opslaan." - }, - { - "type": "multiple-choice", - "summary": "Wat is hier het doel?" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-mc/review-stop.png b/artifacts/oa-runner-mc/review-stop.png deleted file mode 100644 index fc1e26c..0000000 Binary files a/artifacts/oa-runner-mc/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-mc/validation.json b/artifacts/oa-runner-mc/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-mc/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-mr/editor-ready.png b/artifacts/oa-runner-mr/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-mr/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-mr/page-1-block-1-multiple-response.png b/artifacts/oa-runner-mr/page-1-block-1-multiple-response.png deleted file mode 100644 index 79d35eb..0000000 Binary files a/artifacts/oa-runner-mr/page-1-block-1-multiple-response.png and /dev/null differ diff --git a/artifacts/oa-runner-mr/page-1-structure.png b/artifacts/oa-runner-mr/page-1-structure.png deleted file mode 100644 index c3f5d8c..0000000 Binary files a/artifacts/oa-runner-mr/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-mr/plan.json b/artifacts/oa-runner-mr/plan.json deleted file mode 100644 index 12ab0df..0000000 --- a/artifacts/oa-runner-mr/plan.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "training": "Pilot Multi Response Run", - "chapters": [ - { - "title": "Playwright Testmodule", - "pages": [ - { - "title": "Multi antwoord", - "blocks": [ - { - "type": "multiple-response", - "summary": "Welke punten horen bij de gecontroleerde UI-run?" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-mr/review-stop.png b/artifacts/oa-runner-mr/review-stop.png deleted file mode 100644 index 79d35eb..0000000 Binary files a/artifacts/oa-runner-mr/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-mr/validation.json b/artifacts/oa-runner-mr/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-mr/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-open-question/editor-ready.png b/artifacts/oa-runner-open-question/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-open-question/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-open-question/page-1-block-1-open-question.png b/artifacts/oa-runner-open-question/page-1-block-1-open-question.png deleted file mode 100644 index 59a7887..0000000 Binary files a/artifacts/oa-runner-open-question/page-1-block-1-open-question.png and /dev/null differ diff --git a/artifacts/oa-runner-open-question/page-1-structure.png b/artifacts/oa-runner-open-question/page-1-structure.png deleted file mode 100644 index cfae1a4..0000000 Binary files a/artifacts/oa-runner-open-question/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-open-question/plan.json b/artifacts/oa-runner-open-question/plan.json deleted file mode 100644 index 5ffb313..0000000 --- a/artifacts/oa-runner-open-question/plan.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "training": "Pilot Open Question Run", - "chapters": [ - { - "title": "Playwright Testmodule", - "pages": [ - { - "title": "Open vraag", - "blocks": [ - { - "type": "open-question", - "summary": "Leg in je eigen woorden uit waarom deze UI-run niet direct opslaat." - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-open-question/review-stop.png b/artifacts/oa-runner-open-question/review-stop.png deleted file mode 100644 index 903331f..0000000 Binary files a/artifacts/oa-runner-open-question/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-open-question/validation.json b/artifacts/oa-runner-open-question/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-open-question/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-pilot/editor-ready.png b/artifacts/oa-runner-pilot/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-pilot/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-pilot/page-1-block-1-text.png b/artifacts/oa-runner-pilot/page-1-block-1-text.png deleted file mode 100644 index 9b88612..0000000 Binary files a/artifacts/oa-runner-pilot/page-1-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner-pilot/page-1-block-2-heading.png b/artifacts/oa-runner-pilot/page-1-block-2-heading.png deleted file mode 100644 index f24681a..0000000 Binary files a/artifacts/oa-runner-pilot/page-1-block-2-heading.png and /dev/null differ diff --git a/artifacts/oa-runner-pilot/page-1-block-3-quote.png b/artifacts/oa-runner-pilot/page-1-block-3-quote.png deleted file mode 100644 index eff8688..0000000 Binary files a/artifacts/oa-runner-pilot/page-1-block-3-quote.png and /dev/null differ diff --git a/artifacts/oa-runner-pilot/page-1-block-4-multiple-choice.png b/artifacts/oa-runner-pilot/page-1-block-4-multiple-choice.png deleted file mode 100644 index bb12048..0000000 Binary files a/artifacts/oa-runner-pilot/page-1-block-4-multiple-choice.png and /dev/null differ diff --git a/artifacts/oa-runner-pilot/page-1-structure.png b/artifacts/oa-runner-pilot/page-1-structure.png deleted file mode 100644 index 48422d7..0000000 Binary files a/artifacts/oa-runner-pilot/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-pilot/plan.json b/artifacts/oa-runner-pilot/plan.json deleted file mode 100644 index 4403513..0000000 --- a/artifacts/oa-runner-pilot/plan.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "training": "Pilot Playwright Run", - "chapters": [ - { - "title": "Playwright Testmodule", - "pages": [ - { - "title": "Intro", - "blocks": [ - { - "type": "text", - "summary": "Welkom" - }, - { - "type": "heading", - "summary": "Waarom deze pilot" - }, - { - "type": "quote", - "summary": "Eerst veilig structureren, daarna pas opslaan." - }, - { - "type": "multiple-choice", - "summary": "Wat is hier het doel?" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-pilot/review-stop.png b/artifacts/oa-runner-pilot/review-stop.png deleted file mode 100644 index 06fb683..0000000 Binary files a/artifacts/oa-runner-pilot/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-pilot/validation.json b/artifacts/oa-runner-pilot/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-pilot/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-table-large/editor-ready.png b/artifacts/oa-runner-table-large/editor-ready.png deleted file mode 100644 index 4526a78..0000000 Binary files a/artifacts/oa-runner-table-large/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-table-large/page-1-block-1-table.png b/artifacts/oa-runner-table-large/page-1-block-1-table.png deleted file mode 100644 index 638001f..0000000 Binary files a/artifacts/oa-runner-table-large/page-1-block-1-table.png and /dev/null differ diff --git a/artifacts/oa-runner-table-large/page-1-structure.png b/artifacts/oa-runner-table-large/page-1-structure.png deleted file mode 100644 index 49ae147..0000000 Binary files a/artifacts/oa-runner-table-large/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-table-large/plan.json b/artifacts/oa-runner-table-large/plan.json deleted file mode 100644 index 48d1d3d..0000000 --- a/artifacts/oa-runner-table-large/plan.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "training": "Pilot Table Large Run", - "chapters": [ - { - "title": "Playwright Testmodule", - "pages": [ - { - "title": "Grote tabel", - "blocks": [ - { - "type": "table", - "summary": "SQL-overzicht uitgebreid" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-table-large/review-stop.png b/artifacts/oa-runner-table-large/review-stop.png deleted file mode 100644 index 0dfecf9..0000000 Binary files a/artifacts/oa-runner-table-large/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-table-large/validation.json b/artifacts/oa-runner-table-large/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-table-large/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner-table/editor-ready.png b/artifacts/oa-runner-table/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner-table/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner-table/failure-state.png b/artifacts/oa-runner-table/failure-state.png deleted file mode 100644 index a7e5218..0000000 Binary files a/artifacts/oa-runner-table/failure-state.png and /dev/null differ diff --git a/artifacts/oa-runner-table/page-1-block-1-table.png b/artifacts/oa-runner-table/page-1-block-1-table.png deleted file mode 100644 index d5cd185..0000000 Binary files a/artifacts/oa-runner-table/page-1-block-1-table.png and /dev/null differ diff --git a/artifacts/oa-runner-table/page-1-structure.png b/artifacts/oa-runner-table/page-1-structure.png deleted file mode 100644 index dcd1526..0000000 Binary files a/artifacts/oa-runner-table/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner-table/plan.json b/artifacts/oa-runner-table/plan.json deleted file mode 100644 index 2054727..0000000 --- a/artifacts/oa-runner-table/plan.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "training": "Pilot Table Run", - "chapters": [ - { - "title": "Playwright Testmodule", - "pages": [ - { - "title": "Tabel", - "blocks": [ - { - "type": "table", - "summary": "SQL-overzicht" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner-table/review-stop.png b/artifacts/oa-runner-table/review-stop.png deleted file mode 100644 index 18dcc29..0000000 Binary files a/artifacts/oa-runner-table/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner-table/validation.json b/artifacts/oa-runner-table/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner-table/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/artifacts/oa-runner/editor-ready.png b/artifacts/oa-runner/editor-ready.png deleted file mode 100644 index 3bb9e38..0000000 Binary files a/artifacts/oa-runner/editor-ready.png and /dev/null differ diff --git a/artifacts/oa-runner/page-1-block-1-multiple-choice.png b/artifacts/oa-runner/page-1-block-1-multiple-choice.png deleted file mode 100644 index 144d9ce..0000000 Binary files a/artifacts/oa-runner/page-1-block-1-multiple-choice.png and /dev/null differ diff --git a/artifacts/oa-runner/page-1-block-1-text.png b/artifacts/oa-runner/page-1-block-1-text.png deleted file mode 100644 index 9b88612..0000000 Binary files a/artifacts/oa-runner/page-1-block-1-text.png and /dev/null differ diff --git a/artifacts/oa-runner/page-1-block-2-heading.png b/artifacts/oa-runner/page-1-block-2-heading.png deleted file mode 100644 index 2296066..0000000 Binary files a/artifacts/oa-runner/page-1-block-2-heading.png and /dev/null differ diff --git a/artifacts/oa-runner/page-1-block-2-quote.png b/artifacts/oa-runner/page-1-block-2-quote.png deleted file mode 100644 index 4474226..0000000 Binary files a/artifacts/oa-runner/page-1-block-2-quote.png and /dev/null differ diff --git a/artifacts/oa-runner/page-1-block-3-heading.png b/artifacts/oa-runner/page-1-block-3-heading.png deleted file mode 100644 index aaa9709..0000000 Binary files a/artifacts/oa-runner/page-1-block-3-heading.png and /dev/null differ diff --git a/artifacts/oa-runner/page-1-block-4-text.png b/artifacts/oa-runner/page-1-block-4-text.png deleted file mode 100644 index a47e0df..0000000 Binary files a/artifacts/oa-runner/page-1-block-4-text.png and /dev/null differ diff --git a/artifacts/oa-runner/page-1-structure.png b/artifacts/oa-runner/page-1-structure.png deleted file mode 100644 index 644e656..0000000 Binary files a/artifacts/oa-runner/page-1-structure.png and /dev/null differ diff --git a/artifacts/oa-runner/plan.json b/artifacts/oa-runner/plan.json deleted file mode 100644 index 4403513..0000000 --- a/artifacts/oa-runner/plan.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "training": "Pilot Playwright Run", - "chapters": [ - { - "title": "Playwright Testmodule", - "pages": [ - { - "title": "Intro", - "blocks": [ - { - "type": "text", - "summary": "Welkom" - }, - { - "type": "heading", - "summary": "Waarom deze pilot" - }, - { - "type": "quote", - "summary": "Eerst veilig structureren, daarna pas opslaan." - }, - { - "type": "multiple-choice", - "summary": "Wat is hier het doel?" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/artifacts/oa-runner/review-stop.png b/artifacts/oa-runner/review-stop.png deleted file mode 100644 index a47e0df..0000000 Binary files a/artifacts/oa-runner/review-stop.png and /dev/null differ diff --git a/artifacts/oa-runner/validation.json b/artifacts/oa-runner/validation.json deleted file mode 100644 index 0637a08..0000000 --- a/artifacts/oa-runner/validation.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/body.html b/body.html deleted file mode 100644 index 1dcfacc..0000000 --- a/body.html +++ /dev/null @@ -1,207 +0,0 @@ - - - - -
\ No newline at end of file diff --git a/onlineacademy-playwright-runner.mjs b/onlineacademy-playwright-runner.mjs index ddcc94e..9433f34 100644 --- a/onlineacademy-playwright-runner.mjs +++ b/onlineacademy-playwright-runner.mjs @@ -24,11 +24,181 @@ const MAX_MULTI_RESPONSE_ANSWERS = 10; const MAX_TABLE_COLUMNS = 5; const MAX_TABLE_ROWS = 20; +function parseEnvText(source) { + const values = {}; + + for (const rawLine of source.split(/\r?\n/)) { + const line = rawLine.trim(); + if (!line || line.startsWith("#")) { + continue; + } + + const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/); + if (!match) { + continue; + } + + const [, key, rawValue] = match; + let value = rawValue.trim(); + + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + + values[key] = value; + } + + return values; +} + +async function loadEnvFile(envFilePath) { + const absolutePath = path.resolve(envFilePath); + const source = await fs.readFile(absolutePath, "utf8"); + const values = parseEnvText(source); + + for (const [key, value] of Object.entries(values)) { + if (!process.env[key]) { + process.env[key] = value; + } + } + + return absolutePath; +} + +function logInfo(message) { + console.log(`[INFO] ${message}`); +} + +function logStep(message) { + console.log(`\n[STAP] ${message}`); +} + +function logWarn(message) { + console.log(`[LET OP] ${message}`); +} + +function summarizePlan(plan) { + const chapterCount = plan.chapters.length; + const pageCount = plan.chapters.reduce((total, chapter) => total + chapter.pages.length, 0); + const blockCount = plan.chapters.reduce( + (total, chapter) => + total + chapter.pages.reduce((pageTotal, page) => pageTotal + page.blocks.length, 0), + 0 + ); + + return { chapterCount, pageCount, blockCount }; +} + +function describeValidationIssues(validationIssues) { + if (validationIssues.length === 0) { + return "Geen validatieproblemen gevonden."; + } + return `${validationIssues.length} validatieprobleem/problemen gevonden. Zie validation.json voor details.`; +} + +function shortenText(value, maxLength = 60) { + const normalized = String(value ?? "").replace(/\s+/g, " ").trim(); + if (!normalized) { + return ""; + } + if (normalized.length <= maxLength) { + return normalized; + } + return `${normalized.slice(0, maxLength - 3)}...`; +} + +function summarizeBlock(block) { + switch (block.type) { + case "text": + return shortenText(block.data.title || block.data.content || "tekstblok"); + case "heading": + return shortenText(block.data.title || "kopblok"); + case "quote": + return shortenText(block.data.quote || "quoteblok"); + case "image": + return shortenText(block.data.title || block.data.caption || block.data.sourcePath || "afbeelding"); + case "table": + return shortenText(block.data.title || "tabel"); + case "multiple-choice": + case "multiple-response": + case "open-question": + return shortenText(block.data.question || block.type); + default: + return shortenText(block.type); + } +} + +function resolveTrainingAssetPath(baseDir, assetPath) { + if (path.isAbsolute(assetPath)) { + return assetPath; + } + return path.resolve(baseDir, assetPath); +} + +function buildFriendlyErrorMessage(error, options) { + const message = error instanceof Error ? error.message : String(error); + const failureArtifact = path.join(options.outputDir, "failure-state.png"); + + if (message.includes("OA_EMAIL") || message.includes("OA_PASSWORD")) { + return [ + "De runner kon niet inloggen omdat de credentials ontbreken.", + `Controleer ${path.resolve(options.envFile)} en kijk of OA_EMAIL en OA_PASSWORD zijn ingevuld.`, + ].join("\n"); + } + + if (message.includes("Kon env-bestand niet laden")) { + return [ + "Het env-bestand kon niet worden gelezen.", + `Controleer of ${path.resolve(options.envFile)} bestaat en leesbaar is.`, + ].join("\n"); + } + + if (message.includes("Geen url gevonden")) { + return [ + "De URL van de training ontbreekt.", + `Controleer ${options.trainingConfigPath ?? "training.json"} en vul daar de sleutel "url" in.`, + ].join("\n"); + } + + if ( + message.includes("locator('[data-testid=\"page-button\"]')") || + message.includes('locator(\'[data-testid="page-button"]\')') || + message.includes('[data-testid="page-button"]') + ) { + return [ + "De browser heeft de OnlineAcademy-editor niet volledig herkend.", + "Verwachting: na het openen en inloggen moet links de hoofdstuk/pagina-navigatie zichtbaar worden.", + "Wat waarschijnlijk misging: de pagina is nog niet volledig geladen, de URL opent niet de editor, of er staat nog een tussenstap open na het inloggen.", + `Controleer de browser en kijk ook naar ${failureArtifact}.`, + ].join("\n"); + } + + if (message.includes('[data-testid="editor-main"]')) { + return [ + "De editor is niet zichtbaar geworden.", + "Verwachting: het hoofdcanvas van de editor moet in beeld komen.", + `Controleer de browser en kijk ook naar ${failureArtifact}.`, + ].join("\n"); + } + + return [ + "De run is gestopt door een fout.", + `Technische melding: ${message}`, + `Controleer de browser en kijk naar ${failureArtifact}.`, + ].join("\n"); +} + function parseArgs(argv) { const options = { + trainingDir: null, markdownPath: null, targetUrl: null, + envFile: ".env", execute: false, + save: false, pauseAtEnd: false, headed: true, slowMo: 250, @@ -37,12 +207,18 @@ function parseArgs(argv) { for (let i = 2; i < argv.length; i += 1) { const arg = argv[i]; - if (arg === "--markdown") { + if (arg === "--training-dir") { + options.trainingDir = argv[++i]; + } else if (arg === "--markdown") { options.markdownPath = argv[++i]; } else if (arg === "--url") { options.targetUrl = argv[++i]; + } else if (arg === "--env-file") { + options.envFile = argv[++i]; } else if (arg === "--execute") { options.execute = true; + } else if (arg === "--save") { + options.save = true; } else if (arg === "--pause-at-end") { options.pauseAtEnd = true; } else if (arg === "--headless") { @@ -56,16 +232,53 @@ function parseArgs(argv) { } } - if (!options.markdownPath) { - throw new Error("Gebruik --markdown ."); + if (options.trainingDir) { + const resolvedTrainingDir = path.resolve(options.trainingDir); + options.trainingDir = resolvedTrainingDir; + options.markdownPath = path.join(resolvedTrainingDir, "content.md"); + options.trainingConfigPath = path.join(resolvedTrainingDir, "training.json"); + + if (options.outputDir === path.resolve("artifacts/oa-runner")) { + options.outputDir = path.resolve("artifacts", path.basename(resolvedTrainingDir)); + } } - if (!options.targetUrl) { - throw new Error("Gebruik --url ."); + + if (!options.markdownPath) { + throw new Error( + "Gebruik --training-dir of --markdown ." + ); } return options; } +async function applyTrainingConfig(options) { + if (!options.trainingConfigPath) { + if (!options.targetUrl) { + throw new Error( + "Gebruik --url of werk met --training-dir met training.json." + ); + } + return options; + } + + const rawConfig = await fs.readFile(options.trainingConfigPath, "utf8"); + const config = JSON.parse(rawConfig); + const targetUrl = options.targetUrl ?? config.url; + + if (!targetUrl) { + throw new Error( + `Geen url gevonden. Vul "url" in in ${options.trainingConfigPath} of geef --url mee.` + ); + } + + return { + ...options, + targetUrl, + trainingConfig: config, + }; +} + function buildPlan(courseModel) { return { training: courseModel.title, @@ -120,10 +333,63 @@ async function waitForUserReview() { rl.close(); } +async function clickSaveButton(page) { + const candidates = [ + page.locator(".save-button button").first(), + page.locator("button:has(.button__text:text-is('Opslaan als'))").first(), + page.locator("button").filter({ hasText: "Opslaan als" }).first(), + ]; + + let clicked = false; + for (const candidate of candidates) { + const isVisible = await candidate.isVisible().catch(() => false); + if (!isVisible) { + continue; + } + await candidate.click(); + clicked = true; + break; + } + + if (!clicked) { + throw new Error("Knop 'Opslaan als' niet gevonden of niet zichtbaar."); + } + + await page.waitForLoadState("networkidle").catch(() => {}); + await page.waitForTimeout(1000); + + const conceptCandidates = [ + page.getByRole("button", { name: /^Concept$/i }).first(), + page.getByRole("menuitem", { name: /^Concept$/i }).first(), + page.locator("button").filter({ hasText: /^Concept$/ }).first(), + page.locator("[role='menuitem']").filter({ hasText: /^Concept$/ }).first(), + page.locator("text=Concept").first(), + ]; + + let conceptClicked = false; + for (const candidate of conceptCandidates) { + const isVisible = await candidate.isVisible().catch(() => false); + if (!isVisible) { + continue; + } + await candidate.click(); + conceptClicked = true; + break; + } + + if (!conceptClicked) { + throw new Error("Keuze 'Concept' niet gevonden of niet zichtbaar na 'Opslaan als'."); + } + + await page.waitForLoadState("networkidle").catch(() => {}); + await page.waitForTimeout(2000); +} + async function loginIfNeeded(page, email, password) { await page.waitForLoadState("networkidle").catch(() => {}); const passwordField = page.locator('input[type="password"]'); if ((await passwordField.count()) === 0) { + logInfo("Geen loginformulier gevonden. Waarschijnlijk bestaat er al een geldige sessie."); return; } @@ -151,10 +417,31 @@ async function loginIfNeeded(page, email, password) { async function verifyEditor(page) { await page.waitForLoadState("networkidle").catch(() => {}); - const editorMain = page.locator('[data-testid="editor-main"]'); + const editorMain = page.locator('[data-testid="editor-main"], .page__content, .page-blocks').first(); await editorMain.waitFor({ state: "visible", timeout: 15000 }); - const chapterButton = page.locator('[data-testid="page-button"]'); - await chapterButton.waitFor({ state: "visible", timeout: 15000 }); + + const leftSidebar = page.locator(".sidebar-left, [class*='sidebar-left']").first(); + await leftSidebar.waitFor({ state: "visible", timeout: 15000 }); + + const blockPanel = page + .locator('[data-testid="drag-block-text"], .content-block, .dragblock-group__title, .sidebar-right') + .first(); + await blockPanel.waitFor({ state: "visible", timeout: 15000 }); + + const pageCreateButton = page + .locator(".sidebar-left-actions button") + .filter({ hasText: "Pagina" }) + .first(); + const hasPageCreateButton = await pageCreateButton.isVisible().catch(() => false); + + const pageListItem = page.locator(".sidebar-left .editor-chapter-wrapper, .sidebar-left [data-testid='page-button']").first(); + const hasPageListItem = await pageListItem.isVisible().catch(() => false); + + if (!hasPageCreateButton && !hasPageListItem) { + throw new Error( + "Editor gevonden, maar geen paginanavigatie of paginaknop zichtbaar." + ); + } } async function getCurrentTrainingTitle(page) { @@ -430,13 +717,17 @@ async function fillQuoteBlock(insertedBlock, block) { await fields.nth(1).fill(block.data.author ?? ""); } -async function fillImageBlock(page, insertedBlock, block) { +async function fillImageBlock(page, insertedBlock, block, contentBaseDir) { if (!block.data.sourcePath) { throw new Error("Afbeeldingsblok mist bronbestand."); } - const absolutePath = path.resolve(block.data.sourcePath); - await fs.access(absolutePath); + const absolutePath = resolveTrainingAssetPath(contentBaseDir, block.data.sourcePath); + await fs.access(absolutePath).catch(() => { + throw new Error( + `Afbeelding niet gevonden: ${block.data.sourcePath}. Verwacht pad: ${absolutePath}` + ); + }); const fileInput = insertedBlock.locator("input[type='file']").first(); if ((await fileInput.count()) === 0) { @@ -861,7 +1152,7 @@ async function fillQuestionBlock(insertedBlock, block) { await textareas.nth(0).fill(block.data.question); } -async function fillBlock(page, insertedBlock, block) { +async function fillBlock(page, insertedBlock, block, contentBaseDir) { switch (block.type) { case "text": await fillTextBlock(insertedBlock, block); @@ -873,7 +1164,7 @@ async function fillBlock(page, insertedBlock, block) { await fillQuoteBlock(insertedBlock, block); return; case "image": - await fillImageBlock(page, insertedBlock, block); + await fillImageBlock(page, insertedBlock, block, contentBaseDir); return; case "table": await fillTableBlock(page, insertedBlock, block); @@ -910,25 +1201,36 @@ function blockTypeToTestId(type) { return testId; } -async function executePlan(page, courseModel, outputDir) { +async function executePlan(page, courseModel, outputDir, contentBaseDir) { if (courseModel.chapters.length === 0) { throw new Error("Geen hoofdstuk/pagina in markdownmodel."); } + const executableChapters = courseModel.chapters.filter((chapter) => chapter.pages.length > 0); + const totalChapters = executableChapters.length; + const totalPages = executableChapters.reduce((total, chapter) => total + chapter.pages.length, 0); + let completedPages = 0; let pageSequence = 0; - for (let chapterIndex = 0; chapterIndex < courseModel.chapters.length; chapterIndex += 1) { - const chapter = courseModel.chapters[chapterIndex]; + for (let chapterIndex = 0; chapterIndex < executableChapters.length; chapterIndex += 1) { + const chapter = executableChapters[chapterIndex]; if (chapter.pages.length === 0) { continue; } + logInfo( + `Hoofdstuk ${chapterIndex + 1} van ${totalChapters}: ${chapter.title} (${chapter.pages.length} pagina's)` + ); await createChapter(page, chapter.title); await capture(page, outputDir, `chapter-${chapterIndex + 1}-structure`); for (let i = 0; i < chapter.pages.length; i += 1) { const modelPage = chapter.pages[i]; pageSequence += 1; + completedPages += 1; + logInfo( + `Pagina ${completedPages} van ${totalPages}: ${modelPage.title} (${modelPage.blocks.length} blokken)` + ); if (i === 0) { await renameCurrentPage(page, modelPage.title); } else { @@ -942,12 +1244,15 @@ async function executePlan(page, courseModel, outputDir) { throw new Error(`Bloktype ${block.type} valt buiten MVP UI-scope.`); } + logInfo( + `Blok ${j + 1} van ${modelPage.blocks.length}: ${block.type} - ${summarizeBlock(block)}` + ); const beforeCount = await blockCards(page).count(); const beforeSnapshot = await snapshotBlocks(page); await dragBlockIntoCanvas(page, blockTypeToTestId(block.type)); await waitForNewBlockCount(page, beforeCount); const insertedBlock = await findInsertedBlock(page, beforeSnapshot); - await fillBlock(page, insertedBlock, block); + await fillBlock(page, insertedBlock, block, contentBaseDir); await capture(page, outputDir, `page-${pageSequence}-block-${j + 1}-${block.type}`); } } @@ -955,29 +1260,48 @@ async function executePlan(page, courseModel, outputDir) { } async function main() { - const options = parseArgs(process.argv); + let options = parseArgs(process.argv); + logStep("Voorbereiden"); + logInfo(`Trainingsmap: ${options.trainingDir ?? "niet opgegeven"}`); + logInfo(`Env-bestand: ${path.resolve(options.envFile)}`); + await loadEnvFile(options.envFile).catch((error) => { + if (options.envFile === ".env" && error?.code === "ENOENT") { + return null; + } + throw new Error(`Kon env-bestand niet laden: ${path.resolve(options.envFile)}`); + }); + options = await applyTrainingConfig(options); const email = process.env.OA_EMAIL; const password = process.env.OA_PASSWORD; if (!email || !password) { - throw new Error("Zet OA_EMAIL en OA_PASSWORD in de omgeving."); + throw new Error( + `Zet OA_EMAIL en OA_PASSWORD in ${path.resolve(options.envFile)} of in de omgeving.` + ); } const markdown = await fs.readFile(options.markdownPath, "utf8"); const courseModel = parseMarkdownToCourseModel(markdown); const validationIssues = validateCourseModel(courseModel, SUPPORTED_UI_BLOCKS); const plan = buildPlan(courseModel); + const planSummary = summarizePlan(plan); await ensureDir(options.outputDir); await writeJson(path.join(options.outputDir, "plan.json"), plan); await writeJson(path.join(options.outputDir, "validation.json"), validationIssues); - console.log("Plan:"); - console.log(JSON.stringify(plan, null, 2)); - - if (validationIssues.length > 0) { - console.log("Validatieproblemen:"); - console.log(JSON.stringify(validationIssues, null, 2)); - } + logInfo(`Doel-URL: ${options.targetUrl}`); + logInfo( + `Inhoud gelezen: ${planSummary.chapterCount} hoofdstuk(ken), ${planSummary.pageCount} pagina('s), ${planSummary.blockCount} blok(ken).` + ); + logInfo(describeValidationIssues(validationIssues)); + logInfo(`Artifacts worden opgeslagen in: ${options.outputDir}`); + logInfo( + options.execute + ? options.save + ? "Modus: execute + save. De runner bouwt de inhoud op, klikt op 'Opslaan als' en kiest daarna 'Concept'." + : "Modus: execute zonder save. De runner bouwt de inhoud op en laat het scherm daarna open voor controle." + : "Modus: review. De runner controleert login en editor, maar voert geen invoeracties uit." + ); const browser = await chromium.launch({ headless: !options.headed, @@ -990,16 +1314,26 @@ async function main() { }); const page = await context.newPage(); try { + logStep("Browser openen"); await page.goto(options.targetUrl, { waitUntil: "domcontentloaded" }); + logStep("Inloggen"); await loginIfNeeded(page, email, password); + logStep("Editor controleren"); await verifyEditor(page); await capture(page, options.outputDir, "editor-ready"); const currentTitle = await getCurrentTrainingTitle(page); - console.log(`Huidige geopende paginatitel: ${currentTitle}`); + logInfo( + currentTitle + ? `Editor gevonden. Huidige geopende paginatitel: ${currentTitle}` + : "Editor gevonden." + ); if (!options.execute) { - console.log("Reviewmodus klaar. Geen muterende acties uitgevoerd."); + logStep("Resultaat"); + logInfo("Review geslaagd."); + logInfo("Er zijn geen muterende acties uitgevoerd."); + logInfo(`Controleer eventueel de screenshots in: ${options.outputDir}`); return; } @@ -1007,21 +1341,43 @@ async function main() { throw new Error("Uitvoering geblokkeerd: markdown bevat bloktypes buiten de MVP-scope."); } - await executePlan(page, courseModel, options.outputDir); - console.log("Uitvoering gestopt na reviewpunt. Er is niet opgeslagen."); - await capture(page, options.outputDir, "review-stop"); - if (options.pauseAtEnd) { - await waitForUserReview(); + logStep("Inhoud opbouwen"); + await executePlan( + page, + courseModel, + options.outputDir, + path.dirname(options.markdownPath) + ); + + if (options.save) { + logStep("Opslaan"); + logInfo("De inhoud is opgebouwd. De runner klikt nu op 'Opslaan als' en kiest daarna 'Concept'."); + await clickSaveButton(page); + await capture(page, options.outputDir, "after-save"); + logStep("Resultaat"); + logInfo("Execute-run afgerond."); + logInfo("De runner heeft op 'Opslaan als' geklikt en daarna 'Concept' gekozen."); + logInfo("Controleer in de browser of OnlineAcademy de save-actie bevestigt."); } else { - await page.waitForTimeout(8000); + logStep("Resultaat"); + logInfo("Execute-run afgerond tot het reviewpunt."); + logInfo("De inhoud is opgebouwd in de editor."); + logInfo("Er is nog niet opgeslagen."); + logInfo("Het scherm blijft open zodat je kunt controleren wat er is ingevoerd."); + await capture(page, options.outputDir, "review-stop"); + await waitForUserReview(); } } catch (error) { await capture(page, options.outputDir, "failure-state").catch(() => {}); - throw error; + throw new Error(buildFriendlyErrorMessage(error, options)); } } finally { await browser.close(); } } -await main(); +await main().catch((error) => { + console.error(`\n[RESULTAAT] Mislukt`); + console.error(error instanceof Error ? error.message : String(error)); + process.exitCode = 1; +}); diff --git a/pilot-chapters-large.md b/pilot-chapters-large.md deleted file mode 100644 index 2e1f6ac..0000000 --- a/pilot-chapters-large.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -training: Pilot Multi Chapter Run ---- - -# Module: Hoofdstuk Alfa - -## Pagina: Alfa 1 - -### Tekst -**Titel:** Alfa 1 - -Eerste pagina van hoofdstuk Alfa. - -# Module: Hoofdstuk Bravo - -## Pagina: Bravo 1 - -### Tekst -**Titel:** Bravo 1 - -Eerste pagina van hoofdstuk Bravo. - -## Pagina: Bravo 2 - -### Tekst -**Titel:** Bravo 2 - -Tweede pagina van hoofdstuk Bravo. - -## Pagina: Bravo 3 - -### Tekst -**Titel:** Bravo 3 - -Derde pagina van hoofdstuk Bravo. - -# Module: Hoofdstuk Charlie - -## Pagina: Charlie 1 - -### Tekst -**Titel:** Charlie 1 - -Eerste pagina van hoofdstuk Charlie. - -## Pagina: Charlie 2 - -### Tekst -**Titel:** Charlie 2 - -Tweede pagina van hoofdstuk Charlie. - -# Module: Hoofdstuk Delta - -## Pagina: Delta 1 - -### Tekst -**Titel:** Delta 1 - -Eerste pagina van hoofdstuk Delta. - -## Pagina: Delta 2 - -### Tekst -**Titel:** Delta 2 - -Tweede pagina van hoofdstuk Delta. - -## Pagina: Delta 3 - -### Tekst -**Titel:** Delta 3 - -Derde pagina van hoofdstuk Delta. - -## Pagina: Delta 4 - -### Tekst -**Titel:** Delta 4 - -Vierde pagina van hoofdstuk Delta. diff --git a/pilot-chapters.md b/pilot-chapters.md deleted file mode 100644 index 0d76cbc..0000000 --- a/pilot-chapters.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -training: Pilot Chapter Run ---- - -# Module: Eerste hoofdstuk - -## Pagina: Intro hoofdstuk 1 - -### Tekst -**Titel:** Hoofdstuk 1 - -Eerste hoofdstuk voor de zichtbare hoofdstukpilot. - -# Module: Tweede hoofdstuk - -## Pagina: Intro hoofdstuk 2 - -### Tekst -**Titel:** Hoofdstuk 2 - -Tweede hoofdstuk om hoofdstukaanmaak in de editor te verifiëren. diff --git a/pilot-image.md b/pilot-image.md deleted file mode 100644 index f20ab38..0000000 --- a/pilot-image.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -training: Pilot Image Run ---- - -# Module: Playwright Testmodule - -## Pagina: Afbeelding - -### Afbeelding -**Bestand:** afbeeldingen/02-sql-groepen.png -**Titel:** SQL-groepen overzicht -**Onderschrift:** Voorbeeldafbeelding voor de zichtbare uploadpilot. -**Alt:** Diagram met SQL-groepen in de trainingsmodule. diff --git a/pilot-multi-response.md b/pilot-multi-response.md deleted file mode 100644 index 4cc489b..0000000 --- a/pilot-multi-response.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -training: Pilot Multi Response Run ---- - -# Module: Playwright Testmodule - -## Pagina: Multi antwoord - -### Multi antwoord -**Vraag:** Welke punten horen bij de gecontroleerde UI-run? -- [x] De gebruiker kan meekijken -- [x] Er worden geen directe backend-writes gedaan -- [ ] Er wordt automatisch opgeslagen -**Toelichting:** Deze pilot gebruikt de zichtbare editor, stopt voor save en laat meerdere correcte antwoorden toe. diff --git a/pilot-mvp.md b/pilot-mvp.md deleted file mode 100644 index bcecf7f..0000000 --- a/pilot-mvp.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -training: Pilot Playwright Run ---- - -# Module: Playwright Testmodule - -## Pagina: Intro - -### Tekst -**Titel:** Welkom - -Dit is een gecontroleerde pilot voor de zichtbare Playwright-run. - -### Kop -Waarom deze pilot - -### Quote -**Tekst:** Eerst veilig structureren, daarna pas opslaan. -**Auteur:** Automation Bot - -### Meerkeuze -**Vraag:** Wat is hier het doel? -- [x] Veilig testen zonder directe backend-writes -- [ ] Productdata ongecontroleerd aanpassen -**Toelichting:** De UI-run is bedoeld als gecontroleerde operator. diff --git a/pilot-mvp.onlineacademy.json b/pilot-mvp.onlineacademy.json deleted file mode 100644 index 748c732..0000000 --- a/pilot-mvp.onlineacademy.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Pilot Playwright Run", - "jsonContent": "{\"chapters\":[{\"id\":\"95fe06a9-a2df-41ae-accc-7d61cd27a75e\",\"title\":\"Playwright Testmodule\",\"pages\":[{\"id\":\"403678b2-61e7-4c77-b7ca-2c62d7085394\",\"pageType\":\"number\",\"title\":\"Intro\",\"blocks\":[{\"id\":\"4f27cc31-1065-454e-92ed-d8f8bfa50bf9\",\"type\":\"text\",\"data\":{\"title\":\"Welkom\",\"content\":\"

Dit is een gecontroleerde pilot voor de zichtbare Playwright-run.

\"}},{\"id\":\"e27f81bb-e878-4b8e-8603-954943000122\",\"type\":\"heading\",\"data\":{\"title\":\"Waarom deze pilot\"}},{\"id\":\"617e1ea4-8146-4b6e-8311-a5c5b3d3a569\",\"type\":\"quote\",\"data\":{\"quote\":\"Eerst veilig structureren, daarna pas opslaan.\",\"author\":\"Automation Bot\"}},{\"id\":\"fa98bf49-8850-4d99-b43b-7d190a080fef\",\"type\":\"multiple-choice\",\"data\":{\"question\":\"Wat is hier het doel?\",\"answers\":[{\"id\":\"0a85eb90-5cc8-497c-aa93-811d5fef4018\",\"text\":\"Veilig testen zonder directe backend-writes\",\"correct\":true},{\"id\":\"6bef35bb-8b9b-4c86-9ca9-26c1a5e980d7\",\"text\":\"Productdata ongecontroleerd aanpassen\"}],\"feedback\":\"

De UI-run is bedoeld als gecontroleerde operator.

\"}}]}]}],\"examPage\":{\"enabled\":false,\"blocks\":[],\"examSettings\":{\"showFeedbackAfterExam\":false,\"questionPool\":false,\"numberOfQuestions\":0,\"minimumPassingScore\":60}}}" -} \ No newline at end of file diff --git a/pilot-open-question.md b/pilot-open-question.md deleted file mode 100644 index d5706fb..0000000 --- a/pilot-open-question.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -training: Pilot Open Question Run ---- - -# Module: Playwright Testmodule - -## Pagina: Open vraag - -### Open vraag -**Vraag:** Leg in je eigen woorden uit waarom deze UI-run niet direct opslaat. -**Toelichting:** De operator kan meekijken en de run stopt bewust voor save zodat productie-inhoud niet ongecontroleerd wordt vastgelegd. diff --git a/pilot-table-large.md b/pilot-table-large.md deleted file mode 100644 index 8532ddf..0000000 --- a/pilot-table-large.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -training: Pilot Table Large Run ---- - -# Module: Playwright Testmodule - -## Pagina: Grote tabel - -### Tabel -**Titel:** SQL-overzicht uitgebreid -**Top header:** Aan -**Left header:** Uit -| Groep | Functie | Voorbeeld | Opmerking | -|-------|---------|-----------|-----------| -| DML | Data wijzigen | UPDATE | Wijzigt bestaande data | -| DQL | Data lezen | SELECT | Vraagt data op | -| DDL | Structuur wijzigen | ALTER TABLE | Past schema aan | -| DCL | Rechten beheren | GRANT | Regelt permissies | -| TCL | Transacties beheren | COMMIT | Bevestigt transactie | -| DML | Data invoegen | INSERT | Voeg rij toe | -| DML | Data verwijderen | DELETE | Verwijder rij | -| DQL | Filteren | WHERE | Beperkt resultaat | -| DQL | Sorteren | ORDER BY | Sorteert output | diff --git a/pilot-table.md b/pilot-table.md deleted file mode 100644 index 31e65d4..0000000 --- a/pilot-table.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -training: Pilot Table Run ---- - -# Module: Playwright Testmodule - -## Pagina: Tabel - -### Tabel -**Titel:** SQL-overzicht -**Top header:** Aan -**Left header:** Uit -| Groep | Functie | Voorbeeld | -|-------|---------|-----------| -| DML | Data wijzigen | UPDATE | -| DQL | Data lezen | SELECT | -| DDL | Structuur wijzigen | ALTER TABLE | diff --git a/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_01_select_from_anatomie.png b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_01_select_from_anatomie.png new file mode 100644 index 0000000..155d053 Binary files /dev/null and b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_01_select_from_anatomie.png differ diff --git a/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_02_where_operatoren.png b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_02_where_operatoren.png new file mode 100644 index 0000000..39ad92c Binary files /dev/null and b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_02_where_operatoren.png differ diff --git a/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_03_null_afhandeling.png b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_03_null_afhandeling.png new file mode 100644 index 0000000..5f53262 Binary files /dev/null and b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_03_null_afhandeling.png differ diff --git a/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_04_order_by_distinct.png b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_04_order_by_distinct.png new file mode 100644 index 0000000..a24c964 Binary files /dev/null and b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_04_order_by_distinct.png differ diff --git a/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_05_aliassen_literals.png b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_05_aliassen_literals.png new file mode 100644 index 0000000..1a65d6f Binary files /dev/null and b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_05_aliassen_literals.png differ diff --git a/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_06_concatenatie_cast.png b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_06_concatenatie_cast.png new file mode 100644 index 0000000..857950e Binary files /dev/null and b/trainings/hoofdstuk-04-basisqueries/assets/hoofdstuk_04_scherm_06_concatenatie_cast.png differ diff --git a/Hoofdstuk 04 - Basisqueries.onlineacademy.md b/trainings/hoofdstuk-04-basisqueries/content.md similarity index 97% rename from Hoofdstuk 04 - Basisqueries.onlineacademy.md rename to trainings/hoofdstuk-04-basisqueries/content.md index 192ab6d..bf4814f 100644 --- a/Hoofdstuk 04 - Basisqueries.onlineacademy.md +++ b/trainings/hoofdstuk-04-basisqueries/content.md @@ -51,7 +51,7 @@ Dit is een volledige SQL-query. Ze bestaat uit twee onderdelen. `SELECT` zegt we De volgorde is vastgelegd: `SELECT` altijd eerst, dan `FROM`, dan pas eventuele aanvullingen zoals `WHERE`. SQL klaagt direct als je die volgorde omdraait. Het is geen aanbeveling, maar een syntaxregel. ### Afbeelding -**Bestand:** afbeeldingen/hoofdstuk_04_scherm_01_select_from_anatomie.png +**Bestand:** assets/hoofdstuk_04_scherm_01_select_from_anatomie.png **Titel:** Anatomie van een SELECT-query **Onderschrift:** Overzicht van de onderdelen `SELECT`, `FROM` en later `WHERE`. **Alt:** Diagram van een SELECT-query met de onderdelen SELECT, FROM en WHERE. @@ -107,7 +107,7 @@ WHERE status IN ('betaald', 'openstaand'); Dit is identiek aan `WHERE status = 'betaald' OR status = 'openstaand'`, maar compacter en beter leesbaar. ### Afbeelding -**Bestand:** afbeeldingen/hoofdstuk_04_scherm_02_where_operatoren.png +**Bestand:** assets/hoofdstuk_04_scherm_02_where_operatoren.png **Titel:** WHERE-operatoren **Onderschrift:** Overzicht van veelgebruikte vergelijkingsoperatoren in fitworks. **Alt:** Overzicht van WHERE-operatoren met voorbeelden uit de fitworks-database. @@ -186,7 +186,7 @@ WHERE telefoon IS NOT NULL; `IS NULL` is de enige correcte manier om te controleren of een kolom geen waarde bevat. `IS NOT NULL` doet het omgekeerde. ### Afbeelding -**Bestand:** afbeeldingen/hoofdstuk_04_scherm_03_null_afhandeling.png +**Bestand:** assets/hoofdstuk_04_scherm_03_null_afhandeling.png **Titel:** NULL-afhandeling **Onderschrift:** Vergelijking tussen `= NULL` en `IS NULL`. **Alt:** Diagram dat het verschil laat zien tussen = NULL en IS NULL. @@ -228,7 +228,7 @@ ORDER BY achternaam, voornaam; `ORDER BY` staat altijd als laatste in de query, dus na `WHERE` als die ook aanwezig is. ### Afbeelding -**Bestand:** afbeeldingen/hoofdstuk_04_scherm_04_order_by_distinct.png +**Bestand:** assets/hoofdstuk_04_scherm_04_order_by_distinct.png **Titel:** ORDER BY en DISTINCT **Onderschrift:** Sorteren van resultaten en verwijderen van duplicaten. **Alt:** Diagram dat laat zien hoe ORDER BY sorteert en DISTINCT duplicaten verwijdert. @@ -262,7 +262,7 @@ FROM leden; Het resultaat toont leesbaardere kolomkoppen. De alias bestaat alleen in dit queryresultaat. Als een alias een spatie bevat, zet je hem tussen enkele aanhalingstekens. ### Afbeelding -**Bestand:** afbeeldingen/hoofdstuk_04_scherm_05_aliassen_literals.png +**Bestand:** assets/hoofdstuk_04_scherm_05_aliassen_literals.png **Titel:** Aliassen en literals **Onderschrift:** Kolomaliassen maken queryresultaten leesbaarder. **Alt:** Voorbeeld van aliassen en literals in een SQL-resultaat. @@ -317,7 +317,7 @@ FROM abonnementen; `CAST(prijs_per_maand AS CHAR)` zet een getal om naar tekst, zodat je het veilig kunt combineren met andere tekst. ### Afbeelding -**Bestand:** afbeeldingen/hoofdstuk_04_scherm_06_concatenatie_cast.png +**Bestand:** assets/hoofdstuk_04_scherm_06_concatenatie_cast.png **Titel:** CONCAT en CAST **Onderschrift:** Kolommen samenvoegen en datatypes omzetten. **Alt:** Diagram dat laat zien hoe CONCAT waarden samenvoegt en CAST datatypes omzet. diff --git a/trainings/hoofdstuk-04-basisqueries/training.json b/trainings/hoofdstuk-04-basisqueries/training.json new file mode 100644 index 0000000..395cfce --- /dev/null +++ b/trainings/hoofdstuk-04-basisqueries/training.json @@ -0,0 +1,3 @@ +{ + "url": "https://create.onlineacademy.nl/09953181-2b02-4859-9d2a-137dab417e1b/edit?idsVersion=6" +}