# Fase A — Agendakoppeling Lions GCC (Proactieve Agendabesturing)

**Status:** ✅ Gebouwd & lokaal getest (13/13 pytest groen) · **Nog NIET live** — integratie hieronder is een patch-instructie voor Kas.

## Wat is er gebouwd

| Bestand | Inhoud |
|---|---|
| `agenda_module.py` | Zelfstandige Flask-module: tabel-migratie, xlsx-parser, idempotente sync, routes, alerts-helper, seed-CLI |
| `tests/test_agenda_module.py` + `tests/conftest.py` | 13 tests: routes, upload, sync, status-behoud, seed, alerts, migratie-idempotentie |
| `data/agenda-seed-nieuwsbrief1.json` | Seed (6 items uit nieuwsbrief #1) — reeds aanwezig |
| `deliverables/` | output-map |

### Routes (na registratie, achter de bestaande login van de app)
- `GET /agenda` — overzichtspagina (tabs Komende / Volledige lijst + upload-formulier + sync-rapport)
- `POST /agenda/upload` — admin-only (sessie-check), `.xlsx` via openpyxl, idempotente sync
- `GET /agenda/items` — JSON, volledige lijst
- `GET /agenda/api/items?days=14` — JSON voor dashboard-widgets (incl. `dagen_tot` + `badge` T-14/T-7/T-2/vandaag/lopend)

### Tabel `agenda_items`
`id, datum, datum_eind, titel, type, locatie, beschrijving, status (default 'open'), aangemaakt, bijgewerkt` + index op `datum`.
Migratie via `init_agenda_db(db)` — `CREATE TABLE IF NOT EXISTS`, veilig herhaaldelijk te draaien. Draait automatisch bij `register_agenda_routes(...)`.

### Sync-logica (idempotent)
- Match-key: **(datum + titel)**, case-insensitive
- Bestaand item → velden (type/locatie/beschrijving/datum_eind) bijgewerkt, **status behouden** (tenzij oude status `open` is én de upload bevat een expliciete geldige status)
- Nieuw item → toegevoegd met status `open`
- Item niet in upload → verwijderd (agenda = 1 bron van waarheid)
- Ongeldige rijen (geen datum/titel, onparsable datum) → overgeslagen + gemeld (rommeldetectie)
- Kolommen flexibel herkend via synoniemen (datum/date/begindatum, titel/onderwerp/activiteit, type/soort/categorie, locatie/plaats/waar, beschrijving/omschrijving/context, t/m/einddatum); header-rij wordt gezocht in de eerste 15 rijen (titel-rij boven de header wordt geskipt)

## ⚠️ Belangrijke bevinding over de productie-omgeving

De opdracht zei "draait in Docker", maar het **live pad is pm2-native**:

- **Live:** pm2 `lions-command` → `python3 app.py --port 5084` (host, interpreter `/usr/bin/python3`, cwd `/root/.openclaw/workspace/lions-gcc`)
- **nginx:** `/lgcc/` en `/lions-command/` → `proxy_pass http://127.0.0.1:5084/` (`/etc/nginx/sites-enabled/plaud-kas`)
- **Live DB:** `/app/data/lions_gcc.db` (host-bestand! `database.py` default `DB_PATH`), geen `DB_PATH` override in pm2-env of `.env`
- **Docker container** `lions-gcc-lions-gcc-1` (port 8080→5000) draait wel, maar zit **niet** in het nginx-serving pad voor `/lgcc` — vermoedelijk achterhaalde parallelle kopie. → **Advies voor Kas: verifiëren en opruimen of updaten vóór Fase B**, anders bestaat het risico "dubbele waarheden".

Host-python heeft `flask 3.1.3` en `openpyxl 3.1.5` — de module draait daar zonder nieuwe packages.

## Integratie in de productie-app (stappen voor Kas)

### Stap 1 — bestanden kopiëren
```bash
cp /root/projects/jg/lgcc-proactieve-agenda/agenda_module.py \
   /root/.openclaw/workspace/lions-gcc/agenda_module.py
```
(alleen `agenda_module.py`; tests blijven in de repo.)

### Stap 2 — app.py aanpassen (3 mini-patches)

**2a. Import** — na regel 23 (`from newsletter_module import ...`, rond regel 22–23):
```python
from agenda_module import register_agenda_routes, init_agenda_db, render_agenda_alerts_html
```

**2b. Route-registratie** — na `register_preview_routes(app, BASE_PATH)` (regel ~2426):
```python
# ─── MODULE 6: Proactieve Agendabesturing (Fase A) ─────────────────────────
register_agenda_routes(app, page, BASE_PATH, ai_complete, db)  # draait ook de idempotente migratie
```

**2c. Dashboard-alerts op de homepage** — in `def dashboard():` (regel ~1248):
- na het sluiten van de DB-connection (regel ~1255) toevoegen:
  ```python
  agenda_alerts = render_agenda_alerts_html(db, BASE_PATH, days=14)
  ```
- in de body-template: direct vóór `<!-- DG VOORBEREIDING CHECKLIST (compact) -->` (regel ~1244):
  ```
  {{ AGENDA_ALERTS|safe }}
  ```
- in de `page(...)`-aanroep onderaan dashboard (regel ~1415): toevoegen aan de kwargs:
  ```python
  AGENDA_ALERTS=agenda_alerts,
  ```
  ⚠️ Gebruik `|safe` in de template (zelfde patroon als `{{member_spark_large|safe}}` regel ~1373): `render_template_string` staat in deze Flask-versie standaard aan en zou de HTML anders escapen.

### Stap 3 — herstart & migratie
```bash
pm2 restart lions-command
pm2 logs lions-command --lines 20 --nostream   # geen errors, app start
```
De tabel wordt automatisch aangemaakt bij het registreren van de routes (idempotent).

### Stap 4 — seed laden (op de host, tegen de live DB)
```bash
cd /root/projects/jg/lgcc-proactieve-agenda
DB_PATH=/app/data/lions_gcc.db python3 agenda_module.py seed data/agenda-seed-nieuwsbrief1.json
# nogmaals draaien is veilig: 0 toegevoegd, geen duplicaten, statussen behouden
```
(Back-up eerst: `cp /app/data/lions_gcc.db /app/data/lions_gcc.db.bak-$(date +%Y%m%d)`)

### Stap 5 — verifiëren
1. Login → homepage: card **"📅 Komende agendapunten"** verschijnt (oranje linkerborder, T-7/T-2 oranje badges) zodra er items binnen 14 dagen zijn — let op: seed-items liggen >14d vooruit, dus vóór 7 sep 2026 is de card leeg/onzichtbaar (by design). Test eventueel met `?days=` op de API.
2. `/lgcc/agenda` → overzichtspagina met de 6 seed-items.
3. `/lgcc/agenda/items` en `/lgcc/agenda/api/items?days=90` → JSON.
4. Upload-test: zelfde pagina → Excel uploaden → sync-rapport (toegevoegd/bijgewerkt/verwijderd/statussen behouden).

## Hoe getest (lokale repo)

```bash
cd /root/projects/jg/lgcc-proactieve-agenda
python3 -m pytest tests/ -v     # 53 passed (Fase A 8 + Fase B 29 + Fase C 16; laatste run: 53 passed in 2.89s)
```

> **Fase C (dossiers) is afgerond** — zie `README-dossier.md` en `dossier_module.py`.
> Productie-integratie gebeurt door Kas volgens de stappen in `README-dossier.md`.

Gedekte scenario's: lege pagina-state, JSON-endpoints + days-filter, admin-guard op upload (403 zonder sessie), niet-xlsx afwijzing (400), upload met echte date-cél + dd-mm-yyyy-string + ISO-string + 2 rommelrijen, flexibele header met titel-rij erboven, sync met status-behoud/vervanging/idempotentie, seed-lading (6 items incl. datum_eind en context→beschrijving) + herhaalde seed zonder duplicaten, badge-logica T-2/T-7/T-14/vandaag/lopend, alerts-HTML-rendering, ongeldig bestand, tabelstructuur + dubbele migratie.

Daarnaast CLI-test tegen een losse sqlite-DB: seed 2× draaien → 6 rows, geen duplicaten (rapport: 2e run `toegevoegd: 0, bijgewerkt: 6`).

## Vooruitblik Fase B
- `get_upcoming_items(db, days)` + badge-logica liggen al in deze module — alerts-engine kan hierop bouwen
- signaal-drempels T-14/T-7/T-2 zitten in de seed-JSON (`signaal_t*`) maar worden in Fase A nog niet als kolommen opgeslagen
- `/agenda/api/items` is klaar voor dashboard-widgets en de Telegram-push route
