Initial commit

This commit is contained in:
Kristof committed 2026-10-14 12:29:29 +02:00
commit 536ff43ce4
158 files changed
+30355

No files matched your search

+157
View File
@@ -0,0 +1,157 @@
import csv
import io
from datetime import date, datetime
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, Response
from tenancy import huidige_school, per_school
from blueprints.auth import login_required
from rechten import recht_vereist
from services.afwezigheden_service import AfwezighedenService, schooljaar_start
afwezigheden_bp = Blueprint('afwezigheden', __name__)
service = per_school(AfwezighedenService)
_tables_ready = set() # scholen waarvoor de tabellen al gecontroleerd zijn
def _ensure_tables():
if huidige_school().code not in _tables_ready:
try:
service.ensure_tables()
_tables_ready.add(huidige_school().code)
except Exception as e:
flash(f"Kon de afwezigheidstabellen niet aanmaken: {e}", "error")
def _filters():
"""Leest de filters uit de URL, met het huidige schooljaar als standaard."""
def parse(v, standaard):
try:
return datetime.strptime(v, '%Y-%m-%d').date() if v else standaard
except ValueError:
return standaard
van = parse(request.args.get('van'), schooljaar_start())
tot = parse(request.args.get('tot'), date.today())
if van > tot:
van, tot = tot, van
code = (request.args.get('code') or '').upper() or None
if code and code not in service.codes:
code = None
try:
minimum = max(1, int(request.args.get('min') or 1))
except ValueError:
minimum = 1
return dict(van=van, tot=tot, code=code, zoek=(request.args.get('zoek') or '').strip(), minimum=minimum)
#---------------------------------------------
# Overzicht
#---------------------------------------------
@afwezigheden_bp.route('/', methods=['GET'])
@login_required
@recht_vereist('afwezigheden')
def overzicht():
_ensure_tables()
f = _filters()
rows, fout, laatste = [], None, {'m': None, 'd': None}
try:
rows = service.overzicht(f['van'], f['tot'], f['code'], f['zoek'], f['minimum'])
laatste = service.laatste_sync()
except Exception as e:
fout = f"Gegevens konden niet gelezen worden: {e}"
totaal_halve_dagen = sum(r['Halve_dagen'] for r in rows)
return render_template('afwezigheden.html', rows=rows, f=f, fout=fout, laatste=laatste,
codes=service.codes, status=service.status,
totaal_halve_dagen=totaal_halve_dagen)
#---------------------------------------------
# Detail per leerling (JSON, voor het uitklappen in de tabel)
#---------------------------------------------
@afwezigheden_bp.route('/leerling/<internnummer>.json', methods=['GET'])
@login_required
@recht_vereist('afwezigheden')
def leerling(internnummer):
f = _filters()
dagen = service.detail(internnummer, f['van'], f['tot'], f['code'])
return jsonify([{'datum': d['Datum'].strftime('%d-%m-%Y'),
'weekdag': ['ma', 'di', 'wo', 'do', 'vr', 'za', 'zo'][d['Datum'].weekday()],
'vm': d['VM'], 'nm': d['NM']} for d in dagen])
#---------------------------------------------
# Synchronisatie
#---------------------------------------------
@afwezigheden_bp.route('/sync', methods=['POST'])
@login_required
@recht_vereist('afwezigheden', 'laatkomers')
def sync():
if not huidige_school().heeft('SS_API_PLATFORM', 'SS_API_PW'):
flash("Smartschool is nog niet ingesteld voor deze school: vul het platform (bv. school.smartschool.be) "
"en het webservice-wachtwoord in via 🔌 Koppelingen van deze school.", "error")
return redirect(url_for('afwezigheden.laatkomers' if request.form.get('terug') == 'laatkomers'
else 'afwezigheden.overzicht'))
_ensure_tables()
volledig = request.form.get('volledig') == '1'
if service.sync_in_background(volledig=volledig):
flash("Synchronisatie met Smartschool gestart. Dit kan enkele minuten duren.", "info")
else:
flash("Er loopt al een synchronisatie.", "warning")
if request.form.get('terug') == 'laatkomers':
return redirect(url_for('afwezigheden.laatkomers', **{k: v for k, v in request.args.items()}))
return redirect(url_for('afwezigheden.overzicht', **{k: v for k, v in request.args.items()}))
@afwezigheden_bp.route('/status.json', methods=['GET'])
@login_required
@recht_vereist('afwezigheden', 'laatkomers')
def status():
s = service.status
return jsonify(bezig=s['bezig'], voortgang=s['voortgang'], totaal=s['totaal'],
resultaat=s['laatste_resultaat'], fout=s['laatste_fout'])
#---------------------------------------------
# Export
#---------------------------------------------
@afwezigheden_bp.route('/export.csv', methods=['GET'])
@login_required
@recht_vereist('afwezigheden')
def export_csv():
f = _filters()
rows = service.overzicht(f['van'], f['tot'], f['code'], f['zoek'], f['minimum'])
buf = io.StringIO()
w = csv.writer(buf, delimiter=';')
w.writerow(['Internnummer', 'Voornaam', 'Naam', 'Gebruikersnaam', 'Halve dagen', 'Dagen',
'Eerste', 'Laatste', 'Datums'])
for r in rows:
dagen = service.detail(r['Internnummer'], f['van'], f['tot'], f['code'])
datums = ', '.join(d['Datum'].strftime('%d/%m') + ('' if d['VM'] and d['NM'] else (' VM' if d['VM'] else ' NM'))
for d in sorted(dagen, key=lambda x: x['Datum']))
w.writerow([r['Internnummer'], r['Voornaam'] or '', r['Naam'] or '', r['Gebruikersnaam'] or '',
r['Halve_dagen'], r['Dagen'], r['Eerste'].strftime('%d-%m-%Y'),
r['Laatste'].strftime('%d-%m-%Y'), datums])
naam = f"afwezigheden_{f['code'] or '-'.join(service.codes)}_{f['van']:%Y%m%d}-{f['tot']:%Y%m%d}.csv"
return Response('' + buf.getvalue(), mimetype='text/csv',
headers={'Content-Disposition': f'attachment; filename={naam}'})
#---------------------------------------------
# Laatkomers
#---------------------------------------------
@afwezigheden_bp.route('/laatkomers', methods=['GET'])
@login_required
@recht_vereist('laatkomers')
def laatkomers():
"""Alle leerlingen die dit schooljaar (of in de gekozen periode) te laat waren."""
_ensure_tables()
f = _filters()
rows, fout, laatste = [], None, {'m': None, 'd': None}
try:
rows = service.laatkomers(f['van'], f['tot'])
laatste = service.laatste_sync()
except Exception as e:
fout = f"Gegevens konden niet gelezen worden: {e}"
return render_template('laatkomers.html', rows=rows, f=f, fout=fout, laatste=laatste,
codes=service.laat_codes, status=service.status,
totaal=sum(r['Aantal'] for r in rows))