Initial commit
This commit is contained in:
commit
536ff43ce4
158 files changed
+30355
No files matched your search
@@ -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))
|
||||
Reference in new issue
Block a user