Files
Pivot/blueprints/afwezigheden_bp.py
T
2026-10-14 13:34:37 +02:00

158 lines
6.6 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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))