Files
Pivot/OVERDRACHT.md
T
2026-10-14 12:29:29 +02:00

8.8 KiB
Raw Blame History

PIVOT 1.1.0 (meerdere scholen) – overdracht

Dit document vat samen hoe PIVOT in elkaar zit, welke afspraken er in de code gelden en wat er recent bijkwam. Het is bedoeld voor wie verder werkt aan PIVOT (zelf, of samen met een AI-assistent: geef dan dit bestand en de zip van het project mee). Lees ook MEERDERE_SCHOLEN.md (installatie, rollen) en DATABASE.md (tabellen).

⚠️ config.ini en de map geheimen/ bevatten echte wachtwoorden en sleutels. Deel ze niet en zet ze nooit in een chat, ticket of e-mail. Geef een collega een kopie van het project zonder die bestanden en laat hem/haar een eigen config.ini invullen.


1. Architectuur in het kort

  • Flask-app (app.py) met ±40 blueprints (blueprints/) en de logica in services/; HTML in templates/ (Jinja, base.html + static/global.css).
  • Multi-tenant (tenancy.py): één centrale database (Scholen, Gebruikers, rechten, logs, ...) en één database per school (toestellen, leerlingen, ...).
    • huidige_school() = de school van het verzoek; in_school(school) / met_school(fn) voor achtergrondthreads.
    • Instellingen per school = Veld(...) in tenancy.VELDEN (verschijnen vanzelf in 🔌 Koppelingen); lezen met school.get('SLEUTEL') of Config.SLEUTEL (metaclass leest de huidige school).
    • school.cache = per-school geheugen (wordt leeggemaakt bij wijzigingen aan de school).
    • school_singleton(cls, init) = één object per school (gebruikt voor achtergrondtaken met status-dict).
    • google_credentials(scopes, subject) = serviceaccount van de school, gedelegeerd naar subject.
  • Database (database.py): execute_query (schooldatabase), central_query (centraal); commit=True geeft het aantal rijen terug. Pools per database (config.ini › [database] › pool_size, standaard 3).
  • Rechten (rechten.py): ONDERDELEN = lijst (code, naam, categorie); routes met @recht_vereist('code'), templates met {% if mag('code') %}. Een nieuw onderdeel verschijnt vanzelf in 🔐 Gebruikers & rechten. log(...) schrijft in het logboek.
  • Achtergrondtaken: threads gestart in app.py (enkel in het echte proces, niet in de reloader): afwezigheden, 2FA, Skore, GO! Pro (nachtelijk verzamelen), Gmail-handtekeningen (nachtelijk). Taken die maar één keer per dag mogen lopen, "claimen" de dag met een UPDATE ... WHERE (werkt ook met meerdere processen).
  • Opstarten: tenancy.ensure_schema() (centrale tabellen) en services/tabelnamen.py (zie §5).

2. Afspraken in de code

  • Alles in het Nederlands (namen, meldingen, commentaar).
  • Tabellen die een module zelf nodig heeft: CREATE TABLE IF NOT EXISTS in een ensure_tables()/*_ensure() in de service, én dezelfde DDL in sql/school.sql of sql/centraal.sql + sql/installatie_pivot_26-27.sql, én een regel in DATABASE.md. Kolommen die later bijkomen: SHOW COLUMNS + ALTER TABLE in de ensure-functie.
  • Tabelnamen met hoofdletters moeten exact kloppen (Linux!); zie §5.
  • Templates:
    • {% block main_class %}wide{% endblock %} voor brede tabellen; tabellen met zoeken/sorteren/paginering in de browser (helpers perPagina(sleutel, standaard) en perPaginaKiezer(...) uit base.html).
    • In HTML-attributen (onsubmit="...", onchange, data-...) altijd |tojson|forceescape, anders breekt het attribuut op de aanhalingstekens (bevestigingsvragen werkten daardoor niet meer).
    • Elementen die met het hidden-attribuut verborgen worden: zet [hidden] { display: none !important; } in de pagina-CSS (knopstijlen overschrijven het anders).
  • CSV-exports: ; als scheidingsteken en UTF-8 met BOM (Excel). Excel: openpyxl.
  • Google-fouten vertalen naar begrijpelijke meldingen (vriendelijk(e) in de services) mét de scope die ontbreekt.

3. Nieuw onderdeel toevoegen (checklist)

  1. services/<naam>_service.py (logica, tabellen) en blueprints/<naam>_bp.py (routes).
  2. Blueprint registreren in app.py.
  3. Recht in rechten.ONDERDELEN (code, naam, categorie).
  4. Link in het juiste blok van templates/dashboard.html ({% if mag('code') %}; voeg de code ook toe aan de mag(...)-voorwaarde van het blok).
  5. Nieuwe Google-scope? Toevoegen aan de lijst + tabel in services/handleidingen_standaard.py (de handleiding in de database wordt niet automatisch bijgewerkt: zelf aanpassen in 📘 Handleidingen).
  6. SQL-bestanden en DATABASE.md bijwerken.

4. Overzicht van recente onderdelen

Onderdeel Bestanden Opmerkingen
IP-blokkades / whitelist ipblokkade_bp.py, services/ipblokkade.py gewone gebruikers met recht ip_whitelist zien/beheren enkel hun eigen school
Aanmelden blueprints/auth.py geen schoolkeuze meer: laatste school → domein → cookie → laagste instellingsnummer; domeinen mogen gedeeld worden door meerdere scholen
GO! Pro + verzameltabel gopro_bp.py, gopro_service.py, gopro*.html verzameltabel (centraal), rode lijst, verzonden, nachtelijk verzamelen; periode = lopend schooljaar (1/9–31/8)
Leerlingfacturatie facturatie_bp.py, facturatie_service.py Informat Invoice API (GraphQL); scope per toegangsgroep (<GUID>_<installatie>), wordt zelf gezocht
Internaat internaat_bp.py, internaat_service.py, aanwezigheid_service.py Internaat WEB API; aanwezigheden per week; Smartschool-check "voornaam achternaam"
Gmail gmail_delegatie_bp.py, gmail_labels_bp.py, handtekeningen_bp.py, contacten_bp.py, verzendlijsten_bp.py delegaties (+ overzicht), mails verplaatsen, handtekeningen (functie/telefoon uit Google, nachtelijk), contactlijsten in mailboxen + zichtbaarheid adresboek
Google google_licenties_bp.py, gedeelde_drives_bp.py licenties (customerId: domein of klant-ID), gedeelde drives: leden/rechten, "waar is iemand lid van", gebruiker vervangen (zelfde rol)
Smartschool sgr_groepen_bp.py, sgr_groepen_service.py subgroepen + leden van een hoofdgroep (standaard "groepen GO-Antwerpen")
Basisonderwijs basisonderwijs_bp.py, bingel_service.py blok enkel bij Onderwijsniveau = basis; Bingel-Excel (klas uit Informat-inschrijvingen, klasnummer alfabetisch)
Toestellen devices.py, inventaris.html, toestellen.html eigendom: alle leerlingen + filter met/zonder toestel, historie per leerling en per toestel, knoppen uitdelen/inleveren; Alle toestellen: blokkeren/deblokkeren in Google (kolom Toestellen.Geblokkeerd)
Dashboard dashboard.html blokken Gmail, Internaat, Basisonderwijs, Leerlingfacturatie, Links (website + Smartschool)

Benodigde Google-scopes (domeinbrede delegatie) staan in 📘 Handleidingen; recent bijgekomen: gmail.modify, gmail.settings.basic, contacts, drive. De bijbehorende API's (Gmail, People, Drive, Enterprise License Manager) moeten aanstaan in het Cloud-project van het serviceaccount.

5. Omgeving en installatie-aandachtspunten

  • Windows → Linux: MySQL op Windows bewaart tabelnamen in kleine letters; op Linux (lower_case_table_names = 0) herstelt services/tabelnamen.py dat bij het opstarten en bij 🩺 Testen (enkel voor de tabellen die PIVOT kent; zijn beide versies gevuld, dan enkel een melding in het logboek).
  • "1040 Too many connections": (aantal scholen + 1) × pool_size per PIVOT-proces moet onder max_connections van MySQL blijven. Oplossing: SET PERSIST max_connections = 500; of pool_size verlagen.
  • Productie: liefst nginx (HTTPS, Let's Encrypt) + gunicorn/waitress i.p.v. de Flask-ontwikkelserver, en de poort van PIVOT enkel intern openzetten. Het logo van Gmail-handtekeningen (/handtekening-logo/...) moet wel publiek bereikbaar zijn (de IP-blokkering slaat die route over).

6. Nog niet getest tegen de echte omgeving / openstaand

Onderstaande onderdelen werden enkel getest met nagebootste antwoorden; bij het eerste echte gebruik goed nakijken:

  • Bingel: het veld met de klas in de Informat-inschrijvingen (inschrKlassen); de pagina toont de veldnamen die Informat teruggeeft als iets leeg blijft.
  • SGR groepen: formaat van getAllGroupsAndClasses (XML/base64) en de leden per groep.
  • Contactlijsten (People API), gedeelde drives (Drive API), handtekeningen (Gmail sendAs).
  • Licenties: terugval op de klant-ID als het schooldomein geen primair domein is.
  • Leerlingfacturatie: automatisch zoeken van de scope (identityserver).
  • Informat-fout "SqlDateTime overflow" bij één school = foute datum in Informat zelf (niet in PIVOT).

7. Verder werken

  • Elke wijziging werd telkens als volledige zip van Pivot_1.1.0 aangeleverd (zonder __pycache__).
  • Bij een nieuwe vraag aan een AI-assistent: geef dit bestand, DATABASE.md, MEERDERE_SCHOLEN.md en de betrokken bestanden mee (of de hele zip zonder config.ini/geheimen/), en beschrijf wat je wil met een schermafbeelding erbij.