# Opgave 11 (2025)

## Opgave 1: Efteling Attractiebeheer (70%)

### Klassendiagram

```mermaid
classDiagram
    class IRapporteerbaar {
        <<interface>>
        +GenereerKorteInfo() string
    }

    class IGeheim {
        <<interface>>
        +Onthul() void
        +AantalGluurPogingen() int
    }

    class Attractie {
        <<abstract>>
        +string Naam
        +int DuurMinuten
        +int MinimumLengteCm
        +static int TotaalAantalAttracties
        +Attractie(naam, duur, minLengte)
        +GetTypeSpecifiekeInfo()* string
        +ToString() string
        +GenereerKorteInfo() string
    }

    class Achtbaan {
        +int AantalInversies
        +GetTypeSpecifiekeInfo() string
    }

    class Darkride {
        +int AantalScenes
        +GetTypeSpecifiekeInfo() string
    }

    class Waterattractie {
        +bool PonchoVerplicht
        +GetTypeSpecifiekeInfo() string
    }

    class TopSecretAttractie {
        +string GeheimeOmschrijving
        -bool _onthuld
        -int _gluurPogingen
        +Onthul() void
        +AantalGluurPogingen() int
        +GetTypeSpecifiekeInfo() string
        +ToString() string
    }

    class Park {
        +string Naam
        -List~Attractie~ _attracties
        +VoegAttractieToe(Attractie a) void
        +ToonOverzicht() void
        +FilterOpDuur(int maxMinuten) List~Attractie~
        +ToonIntenss() void
        +OnthulAlles() void
        +GenereerRapport() void
    }

    class ParkenBattle {
        +Park Park1
        +Park Park2
        +ParkenBattle(p1, p2)
        +Simuleer() string
    }

    Attractie <|-- Achtbaan
    Attractie <|-- Darkride
    Attractie <|-- Waterattractie
    Attractie <|-- TopSecretAttractie
    IRapporteerbaar <|.. Attractie
    IGeheim <|.. TopSecretAttractie
    Park "1" o-- "0..25" Attractie : bevat
    ParkenBattle "1" --> "2" Park : vergelijkt
```

De Efteling wil de attracties centraal beheren in één console-applicatie. Elke attractie is een object met naam, ritduur (minuten) en minimumlengte (cm). Er zijn verschillende types attracties met extra, type-specifieke kenmerken:

* Achtbaan: aantal inversies (0, 1, 2, …)
* Darkride: aantal scènes (1, 2, 3, …)
* Waterattractie: poncho verplicht (ja/nee) \[een poncho is een regenkap]

Daarnaast bouwt de Efteling elk jaar in het geheim een nieuwe attractie: een **TopSecretAttractie**. Zolang deze nog niet officieel is onthuld mag niemand de details ervan kennen.

De applicatie moet attracties kunnen toevoegen, tonen, filteren, rapporteren, en zelfs twee pretparken tegen elkaar laten wedijveren.

### Deel 1: Klassenstructuur en gedragsregels (6 punten)

#### Abstracte klasse `Attractie`

Properties (met validatie):

* `Naam` (string, niet leeg en niet gelijk aan het woord "Walibi").
* `DuurMinuten` (int, 1–180).
* `MinimumLengteCm` (int, 0–220).

Bij een foute waarde moet er een passende exception (bv. `ArgumentException` of `ArgumentOutOfRangeException`) geworpen worden.

Gedrag:

* Abstracte methode `GetTypeSpecifiekeInfo(): string` (zie verder).
* `ToString()` die Naam, Duur, MinimumLengte toont en `GetTypeSpecifiekeInfo()` integreert.

Daarnaast moet er een **`static` property `TotaalAantalAttracties`** zijn die bijhoudt hoeveel attractie-objecten er in totaal al zijn aangemaakt. Deze teller verhoogt automatisch bij elke creatie van een nieuwe attractie (ook bij afgeleide klassen).

Constructoren:

* Eén volledige (alle properties) met validatie (exceptions bij fout).
* Parameterloze alleen indien zinvol (mag weggelaten).

#### Afgeleide klassen

* `Achtbaan`: extra property `AantalInversies` (int, ≥ 0).
* `Darkride`: extra property `AantalScenes` (int, ≥ 1).
* `Waterattractie`: extra property `PonchoVerplicht` (bool).

Iedere klasse implementeert de `GetTypeSpecifiekeInfo` en zal de extra property-informatie teruggeven. Bijvoorbeeld de klasse `Darkride` zal via deze methode de tekst `"Aantalscenes: 5"` teruggeven (uiteraard met 5 afhankelijk van de waarde van het object).

#### Interface `IRapporteerbaar`

* Methode: `string GenereerKorteInfo();`
* Wordt door alle attracties geïmplementeerd.
* Korte info bevat: type, naam, duur, minimumlengte.

#### Interface `IGeheim`

Deze interface definieert attracties waarvan de details pas zichtbaar worden nadat ze onthuld zijn.

* `Onthul()` activeert de attractie (maakt ze publiek).
* `AantalGluurPogingen()` geeft een int terug die telt hoe vaak er naar de gegevens gevraagd werd **vóórdat** de attractie werd onthuld.

#### Klasse `TopSecretAttractie`

`TopSecretAttractie` erft van `Attractie` en implementeert `IGeheim`.

* Zolang de attractie niet is onthuld:
  * Toont `GetTypeSpecifiekeInfo()` steeds de tekst `"???"` in plaats van de echte inhoud.
  * `ToString()` toont `"TopSecretAttractie (nog niet onthuld) ##### X"` waarbij X het aantal gluurpogingen is.
  * Elke oproep van `ToString()` of `GetTypeSpecifiekeInfo()` verhoogt de interne teller van gluurpogingen.
* Na aanroep van `Onthul()`:
  * Worden naam, duur, minimumlengte en type-info normaal getoond.
  * De teller van gluurpogingen wordt bevroren (niet meer verhoogd).

Een `TopSecretAttractie` heeft bovenop de standaard eigenschappen één extra eigenschap: `GeheimeOmschrijving` (string, niet leeg).

#### Klasse `Park`

Een park:

* Heeft een `Naam` (string).
* Bevat een lijst van `Attractie`-objecten met een maximum van **25** attracties.

Methoden:

* `VoegAttractieToe(Attractie a)`: voegt toe indien er nog plek is, anders wordt een `Exception` opgeworpen met de boodschap *"Park is vol. Attractie niet toegevoegd."*.
* `ToonOverzicht()`: toont de parknaam als hoofding, daarna alle attracties via hun `ToString()`.
* `FilterOpDuur(int maxMinuten)`: geeft een lijst terug van attracties waarvan de duur ≤ maxMinuten.
* `ToonIntenss()`: toont alle *mogelijk intense* attracties:
  * Achtbanen met meer dan 2 inversies,
  * Darkrides met meer dan 10 scènes,
  * Waterattracties waar poncho verplicht is.
* `OnthulAlles()`: roept `Onthul()` aan op alle `IGeheim`-attracties in het park.

De `ToString()` van een attractie wordt gebruikt in het overzicht. Bij een `TopSecretAttractie` die niet-onthuld is zal de output dus een `#####X` bevatten (met X het aantal gluurpogingen).

### Deel 2: Console-applicatie (4 punten)

Bij opstart wordt de naam van het park gevraagd. Vervolgens wordt een menu getoond dat steeds terugkeert tot de gebruiker het programma afsluit.

#### Hoofdmenu

1. Attractie toevoegen
2. Overzicht tonen
3. Filter op duur (≤ X minuten, door gebruiker ingevoerd)
4. Toon *mogelijk intense* attracties
5. Onthul alle geheime attracties
6. Programma afsluiten

Onderaan het hoofdmenu toon je steeds het totaal aantal attracties dat ooit is aangemaakt (via de static property).

#### Actie 1: Attractie toevoegen

1. Kies type (Achtbaan / Darkride / Waterattractie / TopSecretAttractie).
2. Vraag alle relevante velden uit.
3. Bij TopSecretAttractie: vraag of ze meteen onthuld moet worden.
4. Voeg toe aan het park.

Foute invoer mag niet de applicatie doen crashen: vang exceptions op en toon een **rode** foutboodschap. Daarna keer je terug naar het hoofdmenu.

### Deel 3: Extra functionaliteit (3 punten)

#### 3.1 `GenereerRapport()` methode op `Park` (1 punt)

Deze methode maakt gebruik van `IRapporteerbaar` en toont een rapport met:

* Parknaam
* Per attractie de output van `GenereerKorteInfo()`
* Totaal aantal attracties in het park
* Gemiddelde duur van alle attracties (in minuten, afgerond op 1 decimaal)

Voeg dit toe als menu-optie.

#### 3.2 Sortering (1 punt)

Voeg in het hoofdmenu een extra optie toe "Overzicht gesorteerd tonen". De gebruiker kiest:

* op duur oplopend, of
* op minimumlengte aflopend.

#### 3.3 `ToString()` uitbreiding voor `TopSecretAttractie` (1 punt)

Zie de beschrijving in Deel 1 voor de specifieke output-eisen van een niet-onthulde TopSecretAttractie (met `#####X`).

### Deel 4: Park vs Park-simulatie (2 punten)

De Efteling wil z'n park vergelijken met een concurrent (bv. Phantasialand). Hiervoor voeg je een aparte klasse toe.

#### Klasse `ParkenBattle`

Eigenschappen:

* `Park1`: een `Park`
* `Park2`: een `Park`

Constructor neemt twee `Park`-objecten aan.

Methode `Simuleer()`:

Bepaalt de "winnaar" op basis van volgende criteria, in deze volgorde:

1. Park met het **meeste aantal attracties** wint.
2. Bij gelijkstand: park met de **hoogste gemiddelde duur** wint.
3. Bij opnieuw gelijkstand: **gelijkspel**.

Geeft een string terug in de stijl:

```
Efteling wint van Phantasialand met 18 tegen 15 attracties.
```

Of:

```
Gelijkspel tussen Efteling en Phantasialand (beide 12 attracties, gem. duur 7,3).
```

Opmerking: deze klasse hoeft niet via het hoofdmenu opgeroepen te worden, maar moet wel instantieerbaar en bruikbaar zijn in code (bv. voor latere uitbreiding).

## Opgave 2: LogSpy (30%)

Bij een grote cloud-provider staan er dagelijks honderden log-bestanden op de servers. Een sysadmin wil een tool *LogSpy* die recursief een map scant, de log-bestanden analyseert op fouten, waarschuwingen en info-meldingen, en hierover een samenvatting genereert.

### Werking programma

#### Fase 1: Bronmap opvragen

* Vraag de gebruiker om een bronmap (waar de logs staan).
  * Indien de map niet bestaat, toon een **rode** foutboodschap en stop het programma.

#### Fase 2: Scanmodus kiezen

Toon een submenu waarin de gebruiker kiest wat er moet gebeuren:

1. Alleen tellen (per bestand: aantal ERROR/WARN/INFO regels)
2. Alleen extraheren (alle ERROR-regels worden samengevoegd in één bestand)
3. Beide: tellen én extraheren

#### Fase 3: Zoeken en verwerken

* Zoek in de bronmap naar alle bestanden met de extensie `.log`.
  * Ook in submappen (recursief).
  * Indien er geen log-bestanden aanwezig zijn, toon een melding en stop het programma.
* Voor elk gevonden log-bestand:
  * Lees alle regels in.
  * Tel het aantal regels dat **begint** met `"ERROR"`, `"WARN"` en `"INFO"` (hoofdletter-gevoelig, elke regel heeft hoogstens één niveau aan het begin).
  * Hou ook het **totaal aantal regels** per bestand bij.
  * Indien de scanmodus dit vereist: schrijf alle ERROR-regels uit dit bestand, telkens voorafgegaan door de bestandsnaam in vierkante haken, naar één bestand `fouten.txt` in de bronmap.
    * Als `fouten.txt` al bestaat, wordt het **aangevuld** (append).

#### Fase 4: Rapport

* Maak **naast** `fouten.txt` ook een tweede bestand `lograpport.txt` in de bronmap. Dit bestand wordt telkens **volledig overschreven** bij een nieuwe run.
* Inhoud van `lograpport.txt`:
  * Datum en tijd van de scan.
  * Aantal verwerkte bestanden.
  * Aantal overgeslagen bestanden (zie Randvoorwaarden).
  * Totaal aantal regels over alle bestanden.
  * Totaal aantal ERROR, WARN en INFO (gesommeerd over alle bestanden).
  * Naam van het bestand met de meeste ERROR-regels.
  * Naam van het bestand met de meeste regels in totaal.

#### Fase 5: Bevestiging op scherm

Toon onderaan in de console een kort overzicht met:

* Aantal verwerkte bestanden.
* Aantal overgeslagen bestanden.
* Totaal aantal ERROR-regels (in het **rood**).
* Totaal aantal WARN-regels (in het **geel**).
* Totaal aantal INFO-regels (in het **groen**).
* Pad naar `fouten.txt` (indien relevant voor de gekozen scanmodus).
* Pad naar `lograpport.txt`.

### Klassenstructuur (verplicht)

Je moet minstens volgende klassen gebruiken:

#### Klasse `LogBestand`

* Properties: `Pad` (string), `AantalError` (int), `AantalWarn` (int), `AantalInfo` (int), `TotaalRegels` (int).
* Constructor die een pad aanvaardt; de tellers beginnen op 0.
* Methode `ExtraheerErrorRegels()` die een lijst van strings teruggeeft met alle regels uit dit bestand die met `"ERROR"` beginnen.
* `ToString()` die de bestandsnaam (niet het volledige pad), totaal aantal regels en de drie tellers netjes toont.

#### Klasse `LogAnalyser`

* Property `Bronmap` (string).
* Property `Modus` (een enum `ScanModus { Tellen, Extraheren, Beide }`).
* Methode `ZoekLogs()` die recursief alle `.log`-bestanden vindt en een lijst van `LogBestand`-objecten teruggeeft.
* Methode `Analyseer()` die over de lijst itereert en per bestand de tellers invult.
* Methode `SchrijfFouten(string doelPad)` die afhankelijk van `Modus` de fouten naar `fouten.txt` schrijft (append).
* Methode `SchrijfRapport(string doelPad)` die `lograpport.txt` volledig overschrijft.

De `Main` roept deze methoden in de juiste volgorde op.

### Randvoorwaarden

* Bestanden die niet leesbaar zijn (bv. door rechten-problemen, corrupt bestand, lock door andere proces) worden overgeslagen met een duidelijke foutmelding in het **rood**. Ze tellen mee in "overgeslagen bestanden" in het rapport.
* Lege bestanden worden genegeerd (niet meegeteld in "verwerkt", wél in "overgeslagen").
* Een uitzondering op één bestand mag de verwerking van de andere bestanden **niet** stoppen.
* Geslaagde verwerkingen mogen in **groen** worden aangekondigd.

### Voorbeeld uitvoer

*Zinnen die starten met `>` zijn invoer van de gebruiker:*

```
Geef de map waarin LogSpy moet zoeken:
>C:\Logs\prod

Wat wil je doen?
1. Alleen tellen
2. Alleen ERROR-regels extraheren
3. Beide
>3

5 log-bestanden gevonden.

Verwerken van bestand: web-2026-04-20.log
   OK — 1243 regels (ERROR: 12, WARN: 34, INFO: 1197)

Verwerken van bestand: db-2026-04-20.log
   ERROR: bestand kon niet geopend worden. Overgeslagen.

Verwerken van bestand: web-2026-04-21.log
   OK — 987 regels (ERROR: 3, WARN: 18, INFO: 966)

Verwerken van bestand: auth-2026-04-21.log
   OK — 455 regels (ERROR: 0, WARN: 2, INFO: 453)

Verwerken van bestand: kernel.log
   OK — 12 regels (ERROR: 0, WARN: 0, INFO: 12)

Fouten weggeschreven naar: C:\Logs\prod\fouten.txt
Rapport weggeschreven naar: C:\Logs\prod\lograpport.txt

Totaal 4 bestanden verwerkt, 1 overgeslagen.
ERRORS: 15   WARNS: 54   INFOS: 2628
```

#### Voorbeeldinhoud `lograpport.txt`

```
LogSpy rapport - 2026-04-23 10:45:12
Verwerkte bestanden:   4
Overgeslagen bestanden: 1
Totaal aantal regels:  2697
Totaal ERROR: 15
Totaal WARN:  54
Totaal INFO:  2628
Meeste ERRORs in:      web-2026-04-20.log (12)
Meeste regels in:      web-2026-04-20.log (1243 regels)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://apwt.gitbook.io/ziescherp-oefeningen/oefeningen-h13-tot-en-met-h18/voorbeeld-vaardigheidsproeven/opgave_2425b.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
