mirror of
https://github.com/nelsbrock/NateMan.git
synced 2026-08-14 21:36:49 +02:00
325 lines
11 KiB
Python
325 lines
11 KiB
Python
# NateMan – Nachschreibtermin-Manager
|
||
# blueprints/klausuren.py
|
||
# Copyright © 2020 Niklas Elsbrock
|
||
#
|
||
# This program is free software: you can redistribute it and/or modify
|
||
# it under the terms of the GNU General Public License as published by
|
||
# the Free Software Foundation, either version 3 of the License, or
|
||
# (at your option) any later version.
|
||
#
|
||
# This program is distributed in the hope that it will be useful,
|
||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
# GNU General Public License for more details.
|
||
#
|
||
# You should have received a copy of the GNU General Public License
|
||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
||
"""
|
||
Klausurlisten-Blueprint
|
||
"""
|
||
|
||
from datetime import datetime
|
||
|
||
from flask import Blueprint, abort, current_app, flash, g, redirect, render_template, request, url_for, Response
|
||
|
||
from .auth import login_required, beratungslehrer_required
|
||
from ..config_manager import config
|
||
from ..models import Klausur, Klausurteilnahme, Lehrer, Schueler, db, get_next_new_schueler_id, Stufe
|
||
|
||
bp = Blueprint("klausuren", __name__, url_prefix="/klausuren")
|
||
|
||
|
||
@bp.route("/")
|
||
@login_required
|
||
def mine():
|
||
""" Ansicht der eigenen Klausuren (Seite *Meine Klausuren*) """
|
||
klausur_query = Klausur.query.filter(Klausur.lehrer == g.lehrer).order_by(Klausur.date.desc())
|
||
|
||
klausuren_anstehend = klausur_query.filter(Klausur.date > datetime.now().date()).all()
|
||
klausuren_vergangen = klausur_query.filter(Klausur.date <= datetime.now().date()).all()
|
||
|
||
return render_template("klausuren/mine.html.j2", klausuren_anstehend=klausuren_anstehend,
|
||
klausuren_vergangen=klausuren_vergangen)
|
||
|
||
|
||
@bp.route("/<stufe_name>/", endpoint="stufe")
|
||
def stufe_(stufe_name):
|
||
""" Ansicht der Klausuren einer Stufe (Seite *Klausuren [Stufe]*) """
|
||
stufe = Stufe.query.filter_by(name=stufe_name).first()
|
||
if stufe is None:
|
||
abort(404)
|
||
return
|
||
|
||
dates_query = db.session.query(Klausur.date).filter_by(stufe=stufe).order_by(Klausur.date.desc())
|
||
dates_anstehend = [t.date for t in dates_query.filter(Klausur.date > datetime.now().date()).distinct()]
|
||
dates_vergangen = [t.date for t in dates_query.filter(Klausur.date <= datetime.now().date()).distinct()]
|
||
|
||
return render_template("klausuren/stufe.html.j2", stufe=stufe, dates_anstehend=dates_anstehend,
|
||
dates_vergangen=dates_vergangen)
|
||
|
||
|
||
@bp.route("/<stufe_name>/add", methods=("GET", "POST"))
|
||
@beratungslehrer_required
|
||
def add(stufe_name):
|
||
""" Klausurhinzufügung """
|
||
reload_resp = redirect(url_for(".add", stufe_name=stufe_name), code=303)
|
||
|
||
stufe = Stufe.query.filter_by(name=stufe_name).first()
|
||
if stufe is None:
|
||
abort(404)
|
||
return
|
||
|
||
if not g.lehrer.can_access(stufe):
|
||
abort(403)
|
||
return
|
||
|
||
if request.method != "POST":
|
||
return render_template("klausuren/add.html.j2", stufe=stufe)
|
||
|
||
# ELSE
|
||
|
||
kursname = request.form["kursname"]
|
||
lehrer_id = request.form["lehrer"]
|
||
date_str = request.form["date"]
|
||
try:
|
||
startperiod = int(request.form["startperiod"])
|
||
endperiod = int(request.form["endperiod"])
|
||
except ValueError:
|
||
abort(400)
|
||
return
|
||
|
||
if startperiod < 0 or startperiod > endperiod or endperiod.bit_length() > 32:
|
||
flash("Ungültiger Zeitraum", "error")
|
||
return reload_resp
|
||
|
||
if not kursname:
|
||
flash("Bitte geben Sie einen Kursnamen an.", "error")
|
||
return reload_resp
|
||
|
||
try:
|
||
date = datetime.strptime(date_str, "%Y-%m-%d").date()
|
||
except ValueError:
|
||
abort(400)
|
||
return
|
||
|
||
lehrer = Lehrer.query.filter_by(id=lehrer_id).first()
|
||
if lehrer is None:
|
||
abort(400)
|
||
return
|
||
|
||
new_klausur = Klausur(kursname=kursname, date=date, startperiod=startperiod, endperiod=endperiod, stufe=stufe,
|
||
lehrer=lehrer)
|
||
db.session.add(new_klausur)
|
||
db.session.commit()
|
||
|
||
flash("Die Klausur wurde erfolgreich hinzugefügt.", "success")
|
||
current_app.logger.info(f"{g.lehrer} hat die Klausur {new_klausur} hinzugefügt.")
|
||
return redirect(url_for(".edit", klausur_id=new_klausur.id), code=303)
|
||
|
||
|
||
@bp.route("/<int:klausur_id>", methods=("GET", "POST"))
|
||
@login_required
|
||
def edit(klausur_id):
|
||
""" Klausurbearbeitung """
|
||
klausur = Klausur.query.filter_by(id=klausur_id).first()
|
||
if klausur is None:
|
||
abort(404)
|
||
return
|
||
|
||
if not g.lehrer.can_access(klausur.stufe) and klausur.lehrer != g.lehrer:
|
||
abort(403)
|
||
return
|
||
|
||
# ELSE
|
||
if request.method != "POST":
|
||
kt_list = Klausurteilnahme.query \
|
||
.filter_by(klausur=klausur) \
|
||
.join(Schueler) \
|
||
.order_by(Schueler.nachname, Schueler.vorname) \
|
||
.all()
|
||
|
||
not_in_klausur_list = Schueler.query \
|
||
.filter_by(stufe=klausur.stufe) \
|
||
.filter(Schueler.id.notin_(db.session.query(Klausurteilnahme.schueler_id).filter_by(klausur=klausur))) \
|
||
.order_by(Schueler.nachname, Schueler.vorname) \
|
||
.all()
|
||
|
||
return render_template("klausuren/edit.html.j2", klausur=klausur, kt_list=kt_list,
|
||
not_in_klausur_list=not_in_klausur_list)
|
||
|
||
# ELSE
|
||
reload_resp = redirect(url_for(".edit", klausur_id=klausur_id), code=303)
|
||
|
||
exit_resp: Response
|
||
if klausur.lehrer == g.lehrer:
|
||
exit_resp = redirect(url_for(".mine"), code=303)
|
||
else:
|
||
exit_resp = redirect(url_for(".stufe", stufe_name=klausur.stufe.name), code=303)
|
||
|
||
if klausur.edited and not g.lehrer.can_access(klausur.stufe):
|
||
abort(400)
|
||
return
|
||
|
||
action = request.form["action"]
|
||
|
||
if action == "edit":
|
||
lehrer_can_access = g.lehrer.can_access(klausur.stufe)
|
||
|
||
annotation = request.form["annotation"].strip()
|
||
flag_as_edited = ("flag-as-edited" in request.form)
|
||
|
||
klausur_laenge_str = request.form.get("laenge", None)
|
||
if lehrer_can_access and not klausur_laenge_str:
|
||
klausur_laenge = None
|
||
else:
|
||
try:
|
||
klausur_laenge = int(klausur_laenge_str)
|
||
except ValueError:
|
||
abort(400)
|
||
return
|
||
klausur.laenge = klausur_laenge
|
||
|
||
for kt in Klausurteilnahme.query.filter_by(klausur=klausur).all():
|
||
kt.versaeumt = (f"s_{kt.schueler.id}" in request.form)
|
||
|
||
if annotation:
|
||
if len(annotation) > config["klausuren"]["max-annotation-length"]:
|
||
abort(400)
|
||
return
|
||
klausur.annotation = annotation
|
||
else:
|
||
klausur.annotation = None
|
||
|
||
if lehrer_can_access:
|
||
klausur.edited = flag_as_edited
|
||
else:
|
||
klausur.edited = True
|
||
|
||
current_app.logger.info(f"{g.lehrer} hat die Klausur {klausur} bearbeitet.")
|
||
flash("Klausur erfolgreich bearbeitet.", "success")
|
||
db.session.commit()
|
||
|
||
return exit_resp
|
||
|
||
elif action == "edit-advanced":
|
||
if not g.lehrer.can_access(klausur.stufe):
|
||
abort(400)
|
||
return
|
||
|
||
new_lehrer_id = request.form["new-lehrer"]
|
||
new_date_str = request.form["new-date"]
|
||
try:
|
||
new_startperiod = int(request.form["new-startperiod"])
|
||
new_endperiod = int(request.form["new-endperiod"])
|
||
except ValueError:
|
||
abort(400)
|
||
return
|
||
|
||
if new_startperiod < 0 or new_startperiod > new_endperiod or new_endperiod.bit_length() > 32:
|
||
flash("Ungültiger Zeitraum", "error")
|
||
return reload_resp
|
||
|
||
try:
|
||
new_date = datetime.strptime(new_date_str, "%Y-%m-%d").date()
|
||
except ValueError:
|
||
abort(400)
|
||
return
|
||
|
||
new_lehrer = Lehrer.query.filter_by(id=new_lehrer_id).first()
|
||
if new_lehrer is None:
|
||
abort(400)
|
||
return
|
||
|
||
klausur.lehrer = new_lehrer
|
||
klausur.date = new_date
|
||
klausur.startperiod = new_startperiod
|
||
klausur.endperiod = new_endperiod
|
||
|
||
current_app.logger.info(f"{g.lehrer} hat die Klausur {klausur} erweitert bearbeitet.")
|
||
flash("Klausur erfolgreich bearbeitet.", "success")
|
||
db.session.commit()
|
||
|
||
return reload_resp
|
||
|
||
elif action == "delete":
|
||
if not g.lehrer.can_access(klausur.stufe):
|
||
abort(400)
|
||
return
|
||
|
||
current_app.logger.info(f"{g.lehrer} hat die Klausur {klausur} gelöscht.")
|
||
flash("Die Klausur wurde erfolgreich gelöscht.", "success")
|
||
|
||
db.session.delete(klausur)
|
||
db.session.commit()
|
||
return exit_resp
|
||
|
||
elif action == "add-schueler":
|
||
schueler_id = request.form.get("added-schueler", None)
|
||
add_to = request.form["add-to"]
|
||
|
||
if schueler_id is None:
|
||
flash("Bitte wählen Sie einen Schüler zum Hinzufügen aus.", "error")
|
||
return reload_resp
|
||
|
||
is_new = False
|
||
if schueler_id == "new-schueler":
|
||
is_new = True
|
||
new_schueler_nachname = request.form["new-schueler-nachname"].strip()
|
||
new_schueler_vorname = request.form["new-schueler-vorname"].strip()
|
||
new_schueler_koop = ("new-schueler-koop" in request.form)
|
||
|
||
if not new_schueler_nachname or not new_schueler_vorname:
|
||
flash("Bitte geben Sie einen vollständigen Namen für den neuen Schüler an.", "error")
|
||
return reload_resp
|
||
|
||
schueler = Schueler(id=get_next_new_schueler_id(), nachname=new_schueler_nachname,
|
||
vorname=new_schueler_vorname, stufe=klausur.stufe,
|
||
koop=new_schueler_koop)
|
||
db.session.add(schueler)
|
||
|
||
else:
|
||
schueler = Schueler.query.filter_by(id=schueler_id).first()
|
||
if schueler is None:
|
||
abort(400)
|
||
return
|
||
|
||
if Klausurteilnahme.query.filter_by(klausur=klausur, schueler=schueler).first() is not None:
|
||
abort(400)
|
||
return
|
||
|
||
if add_to == "klausur":
|
||
klausuren = [klausur]
|
||
elif add_to == "kurs":
|
||
klausuren = Klausur.query.filter_by(stufe=klausur.stufe).filter_by(kursname=klausur.kursname).all()
|
||
else:
|
||
abort(400)
|
||
return
|
||
|
||
for k in klausuren:
|
||
kt_entity = Klausurteilnahme(klausur=k, schueler=schueler)
|
||
db.session.add(kt_entity)
|
||
|
||
current_app.logger.info(f"{g.lehrer} hat den{' neuen' if is_new else ''} Schüler {schueler} "
|
||
f"zur Klausur {klausur} hinzugefügt.")
|
||
|
||
db.session.commit()
|
||
return reload_resp
|
||
|
||
elif action == "remove-schueler":
|
||
for kt in Klausurteilnahme.query.join(Klausur)\
|
||
.filter_by(stufe=klausur.stufe)\
|
||
.filter_by(kursname=klausur.kursname)\
|
||
.all():
|
||
if f"r_{kt.schueler.id}" in request.form:
|
||
db.session.delete(kt)
|
||
current_app.logger.info(f"{g.lehrer} hat den Schüler {kt.schueler} aus der Klausur "
|
||
f"{kt.klausur} entfernt.")
|
||
|
||
db.session.commit()
|
||
return reload_resp
|
||
|
||
else:
|
||
# Bad Request, wenn ungültige action
|
||
abort(400)
|