From 7caaea71a72bdddd71cd163461b99fe7d211fabb Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 7 Sep 2020 16:13:18 +0200 Subject: [PATCH] Some new routes for schedule plugin --- flaschengeist/__init__.py | 2 - flaschengeist/modules/auth/__init__.py | 2 +- flaschengeist/modules/schedule/__init__.py | 527 +++++------------- .../system/controller/eventController.py | 61 +- .../system/controller/userController.py | 5 +- 5 files changed, 196 insertions(+), 401 deletions(-) diff --git a/flaschengeist/__init__.py b/flaschengeist/__init__.py index b116846..7882802 100644 --- a/flaschengeist/__init__.py +++ b/flaschengeist/__init__.py @@ -4,8 +4,6 @@ Initialize also a singleton for the AccessTokenController and start the Thread. """ -from datetime import datetime - import yaml import logging import pkg_resources diff --git a/flaschengeist/modules/auth/__init__.py b/flaschengeist/modules/auth/__init__.py index 0ec7358..41fcf7f 100644 --- a/flaschengeist/modules/auth/__init__.py +++ b/flaschengeist/modules/auth/__init__.py @@ -1,7 +1,7 @@ ############################################# # Plugin: Auth # # Functionality: Allow management of # -# authentication, login, logout, etc # +# authentication, login, logout, etc # ############################################# from flask import Blueprint, request, jsonify diff --git a/flaschengeist/modules/schedule/__init__.py b/flaschengeist/modules/schedule/__init__.py index ec02035..be354ac 100644 --- a/flaschengeist/modules/schedule/__init__.py +++ b/flaschengeist/modules/schedule/__init__.py @@ -6,27 +6,57 @@ from datetime import datetime, timedelta from flaschengeist.system.controller import eventController from flaschengeist.system.database import db from flaschengeist.system.decorator import login_required -from flaschengeist.system.models.event import Event, EventKind +from flaschengeist.system.models.event import EventKind -schedule_bp = Blueprint("schedule", __name__) +schedule_bp = Blueprint("schedule", __name__, url_prefix="/schedule") def register(): return schedule_bp -@schedule_bp.route("/schedule/events", methods=['GET']) -@schedule_bp.route("/schedule/events/", methods=['GET']) -@schedule_bp.route("/schedule/events//", methods=['GET']) -@schedule_bp.route("/schedule/events///", methods=['GET']) +#################################################################################### +# Routes # +# # +# /schedule/events POST: create new # +# GET: get all events this month # +# /schedule/events// GET: get events by month / date # +# /// # +# /schedule/events/ GET: get event by ID # +# DELETE: delete specified event # +# PUT: modify specified event # +# /schedule/events//slots GET: get EventSlots of Event # +# POST: add new EventSlot to Event # +# /schedule/events//slots/ PUT: modify EventSlot # +# GET: get specified EventSlot # +# /schedule/events//slots//jobs POST: add User # +# /schedule/eventKinds +# /schedule/eventKinds/ +# PUT: modify user # +# DELETE: remove user # +#################################################################################### + + +@schedule_bp.route("/events/", methods=['GET']) @login_required() # roles=['schedule_read']) -def _get_events(year=datetime.now().year, month=None, day=None, **kwrags): +def __get_event(id, **kwargs): + event = eventController.get_event(id) + if not event: + raise NotFound + return jsonify(event) + + +@schedule_bp.route("/events", methods=['GET']) +@schedule_bp.route("/events//", methods=['GET']) +@schedule_bp.route("/events///", methods=['GET']) +@login_required() # roles=['schedule_read']) +def __get_events(year=datetime.now().year, month=datetime.now().month, day=None, **kwrags): """Get Event objects for specified date (or month or year), - if nothing set then events for current year are returned + if nothing set then events for current month are returned Args: year (int, optional): year to query, defaults to current year - month (int, optional): month to query (if set) + month (int, optional): month to query (if set), defaults to current month day (int, optional): day to query events for (if set) **kwrags: contains at least access_token (see flaschengeist.decorator) Returns: @@ -35,35 +65,40 @@ def _get_events(year=datetime.now().year, month=None, day=None, **kwrags): BadRequest: If date is invalid """ try: - begin = datetime(year=year, month=1, day=1) - end = None - if month: - begin = datetime(year=year, month=month, day=1) - if day: - begin += timedelta(days=day - 1) - end = begin + timedelta(days=1) - else: - end = datetime(year=year, month=month + 1, day=1) + begin = datetime(year=year, month=month, day=1) + if day: + begin += timedelta(days=day - 1) + end = begin + timedelta(days=1) else: - end = datetime(year=year + 1, month=1, day=1) + end = datetime(year=year, month=month + 1, day=1) - events = Event.query.filter((begin <= Event.begin), (Event.begin < end)) + events = eventController.get_events(begin, end) return jsonify(events) except ValueError: raise BadRequest("Invalid date given") -@schedule_bp.route("/schedule/eventKinds", methods=['POST']) +@schedule_bp.route("/eventKinds", methods=['POST']) @login_required() def __new_event_kind(**kwargs): data = request.get_json() if "name" not in data: raise BadRequest - event = eventController.create_event_kind(data["name"]) - return jsonify({"ok": "ok", "id": event.id}) + kind = eventController.create_event_kind(data["name"]) + return jsonify({"ok": "ok", "id": kind.id}) -@schedule_bp.route("/schedule/events", methods=['POST']) +@schedule_bp.route("/slotKinds", methods=["POST"]) +@login_required() +def __new_slot_kind(**kwargs): + data = request.get_json() + if not data or "name" not in data: + raise BadRequest + kind = eventController.create_job_kind(data["name"]) + return jsonify({"ok": "ok", "id": kind.id}) + + +@schedule_bp.route("/events", methods=['POST']) @login_required() def __new_event(**kwargs): data = request.get_json() @@ -74,383 +109,85 @@ def __new_event(**kwargs): return jsonify({"ok": "ok", "id": event.id}) -@schedule_bp.route("/schedule/events/", methods=["DELETE"]) +@schedule_bp.route("/events/", methods=["DELETE"]) @login_required() def __delete_event(id, **kwargs): - if Event.query.filter(Event.id == id).delete() != 1: + if not eventController.delete_event(id): raise NotFound db.session.commit() return jsonify({'ok': 'ok'}) +@schedule_bp.route("/eventKinds/", methods=["PUT"]) +@login_required() +def __edit_event_kind(id, **kwargs): + data = request.get_json() + if not data or "name" not in data: + raise BadRequest + eventController.rename_event_kind(id, data["name"]) + return jsonify({"ok": "ok"}) + + +@schedule_bp.route("/events//slots", methods=["GET"]) +@login_required() +def __get_slots(event_id, **kwargs): + event = eventController.get_event(event_id) + if not event: + raise NotFound + return jsonify({event.slots}) + + +@schedule_bp.route("/events//slots/", methods=["GET"]) +@login_required() +def __get_slot(event_id, slot_id, **kwargs): + slot = eventController.get_event_slot(slot_id, event_id) + if slot: + return jsonify(slot) + raise NotFound + + +@schedule_bp.route("/events//slots/", methods=["DELETE"]) +@login_required() +def __delete_slot(event_id, slot_id, **kwargs): + if eventController.delete_event_slot(slot_id, event_id): + return jsonify({"ok": "ok"}) + raise NotFound + + +@schedule_bp.route("/events//slots/", methods=["PUT"]) +@login_required() +def __update_slot(event_id, slot_id, **kwargs): + data = request.get_json() + if not data: + raise BadRequest + + for job in data['jobs']: + eventController.add_job(job.kind, job.user) + if eventController.delete_event_slot(slot_id, event_id): + return jsonify({"ok": "ok"}) + raise NotFound + + +@schedule_bp.route("/events//slots", methods=["POST"]) +@login_required() +def __add_slot(event_id, **kwargs): + event = eventController.get_event(event_id) + if not event: + raise NotFound + data = request.get_json() + attr = {"job_slots": []} + try: + if "start" in data: + attr["start"] = parser.isoparse(data["start"]) + if "end" in data: + attr["end"] = parser.isoparse(data["end"]) + for job in data["jobs"]: + attr["job_slots"].append({"needed_persons": job["needed_persons"], "kind": job["kind"]}) + except KeyError: + raise BadRequest("Missing data in request") + eventController.add_slot(event, **attr) + return jsonify({"ok": "ok"}) + + def __edit_event(): ... - -# try: -# data = request.get_json() -# from_date = data['from_date'] -# to_date = data['to_date'] -# from_date = datetime(from_date['year'], from_date['month'], from_date['day']) -# to_date = datetime(to_date['year'], to_date['month'], to_date['day']) -# lockedDays = mainController.getLockedDays(from_date, to_date) -# retVal = [] -# for lockedDay in lockedDays: -# day = datetime.combine(lockedDay['daydate'], time(12)) -# retDay = { -# "worker": mainController.getWorker(day), -# "day": { -# "date": { -# "year": day.year, -# "month": day.month, -# "day": day.day -# }, -# "locked": lockedDay['locked'] -# }, -# "jobkinddate": mainController.getJobKindDates(day.date()) -# } -# retVal.append(retDay) -# -# logger.debug("return {{ {} }}".format(retVal)) -# return jsonify(retVal) -# except Exception as err: -# logger.warn("exception", exc_info=True) -# return jsonify({"error": str(err)}), 500 -# -# @schedule.route("/user/jobsOnDates", methods=['POST']) -# @login_required(groups=[USER]) -# def _getJobsOnDates(**kwargs): -# debug.info("/user/jobsOnDates") -# try: -# data = request.get_json() -# lockedDays = mainController.getLockedDaysFromList(data) -# retVal = [] -# for lockedDay in lockedDays: -# day = datetime.combine(lockedDay['daydate'], time(12)) -# retDay = { -# "worker": mainController.getWorker(day), -# "day": { -# "date": { -# "year": day.year, -# "month": day.month, -# "day": day.day -# }, -# "locked": lockedDay['locked'] -# }, -# "jobkinddate": mainController.getJobKindDates(day.date()) -# } -# retVal.append(retDay) -# -# debug.debug("return {{ {} }}".format(retVal)) -# return jsonify(retVal) -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 500 -# -# @schedule.route("/user/job", methods=['POST']) -# @login_required(groups=[USER]) -# def _getUser(**kwargs): -# debug.info("/user/job") -# try: -# data = request.get_json() -# day = data['day'] -# month = data['month'] -# year = data['year'] -# date = datetime(year, month, day, 12) -# lockedDay = mainController.getLockedDay(date) -# if not lockedDay: -# lockedDay = { -# 'date': { -# 'year': year, -# 'month': month, -# 'day': day -# }, -# 'locked': False -# } -# else: -# lockedDay = { -# 'date': { -# 'year': year, -# 'month': month, -# 'day': day -# }, -# 'locked': lockedDay['locked'] -# } -# retVal = { -# 'worker': mainController.getWorker(date), -# 'day': lockedDay -# } -# debug.debug("retrun {{ {} }}".format(retVal)) -# return jsonify(retVal) -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 500 -# -# -# @schedule.route("/user/addJob", methods=['POST']) -# @login_required(groups=[USER]) -# def _addUser(**kwargs): -# debug.info("/user/addJob") -# try: -# if 'accToken' in kwargs: -# accToken = kwargs['accToken'] -# user = accToken.user -# data = request.get_json() -# day = data['day'] -# month = data['month'] -# year = data['year'] -# date = datetime(year, month, day, 12) -# job_kind = None -# if 'job_kind' in data: -# job_kind = data['job_kind'] -# mainController.addWorker(user.uid, date, job_kind=job_kind, userExc=True) -# retVal = mainController.getWorker(date) -# debug.debug("return {{ {} }}".format(retVal)) -# jobL.info("Mitglied {} {} schreib sich am {} zum Dienst ein.".format( -# user.firstname, user.lastname, date.date())) -# return jsonify(retVal) -# except DayLocked as err: -# debug.debug("exception", exc_info=True) -# return jsonify({'error': str(err)}), 403 -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({'error': str(err)}), 409 -# -# -# @schedule.route("/user/deleteJob", methods=['POST']) -# @login_required(groups=[USER]) -# def _deletJob(**kwargs): -# debug.info("/user/deleteJob") -# try: -# if 'accToken' in kwargs: -# accToken = kwargs['accToken'] -# user = accToken.user -# data = request.get_json() -# day = data['day'] -# month = data['month'] -# year = data['year'] -# date = datetime(year, month, day, 12) -# mainController.deleteWorker(user.uid, date, True) -# retVal = mainController.getWorker(date) -# debug.debug("return ok") -# jobL.info("Mitglied {} {} entfernt sich am {} aus dem Dienst".format( -# user.firstname, user.lastname, date.date())) -# return jsonify(retVal) -# except DayLocked as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 403 -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 409 -# -# @schedule.route("/user/getJobInvites", methods=['POST']) -# @login_required(groups=[USER]) -# def _getJobInvites(**kwargs): -# try: -# debug.info("/user/getJobInvites") -# from_user = None -# to_user = None -# on_date = None -# -# data = request.get_json() -# -# if 'from_user' in data: -# from_user = data['from_user'] -# if 'to_user' in data: -# to_user = data['to_user'] -# on_date = date(data['date']['year'], data['date']['month'], data['date']['day']) -# retVal = mainController.getJobInvites(from_user, to_user, on_date) -# debug.debug("return {{ {} }}".format(retVal)) -# return jsonify(retVal) -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 500 -# -# @schedule.route("/user/JobInvites", methods=['PUT', 'POST']) -# @login_required(groups=[USER]) -# def _JobInvites(**kwargs): -# try: -# debug.info("/user/JobInvites") -# data = request.get_json() -# if request.method == 'PUT': -# mainController.setJobInvites(data) -# retVal = mainController.getJobInvites(kwargs['accToken'].user.toJSON(), None, datetime.now().date()) -# debug.debug("return {{ {} }}".format(retVal)) -# if request.method == 'POST': -# retVal = mainController.updateJobInvites(data) -# -# return jsonify(retVal) -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 500 -# -# @schedule.route("/user/deleteJobInvite", methods=['POST']) -# @login_required(groups=[USER]) -# def _deleteJobInvite(**kwargs): -# try: -# debug.info("/user/deleteJobInvite") -# data = request.get_json() -# mainController.deleteJobInvite(data) -# retVal = mainController.getJobInvites(data['from_user'], None, datetime.now().date()) -# debug.debug("return {{ {} }}".format(retVal)) -# return jsonify(retVal) -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 500 -# -# -# @schedule.route("/user/getJobRequests", methods=['POST']) -# @login_required(groups=[USER]) -# def _getJobRequests(**kwargs): -# try: -# debug.info("/user/getJobRequests") -# from_user = None -# to_user = None -# on_date = None -# -# data = request.get_json() -# -# if 'from_user' in data: -# from_user = data['from_user'] -# if 'to_user' in data: -# to_user = data['to_user'] -# on_date = date(data['date']['year'], data['date']['month'], data['date']['day']) -# retVal = mainController.getJobRequests(from_user, to_user, on_date) -# debug.debug("return {{ {} }}".format(retVal)) -# return jsonify(retVal) -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 500 -# -# @schedule.route("/user/JobRequests", methods=['PUT', 'POST']) -# @login_required(groups=[USER]) -# def _JobRequests(**kwargs): -# try: -# debug.info("/user/JobRequests") -# data = request.get_json() -# if request.method == 'PUT': -# mainController.setJobRequests(data) -# retVal = mainController.getJobRequests(kwargs['accToken'].user.toJSON(), None, datetime.now().date()) -# debug.debug("return {{ {} }}".format(retVal)) -# if request.method == 'POST': -# data['on_date'] = date(data['on_date']['year'], data['on_date']['month'], data['on_date']['day']) -# retVal = mainController.updateJobRequests(data) -# -# return jsonify(retVal) -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 500 -# -# @schedule.route("/user/deleteJobRequest", methods=['POST']) -# @login_required(groups=[USER]) -# def _deleteJobRequest(**kwargs): -# try: -# debug.info("/user/deleteJobRequest") -# data = request.get_json() -# mainController.deleteJobRequest(data) -# retVal = mainController.getJobRequests(data['from_user'], None, datetime.now().date()) -# debug.debug("return {{ {} }}".format(retVal)) -# return jsonify(retVal) -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 500 - -# CREDIT == Gerücht? CreditList? -# creditL = getCreditLogger() -# @schedule.route("/user/main") -# @login_required(groups=[USER]) -# def _main(**kwargs): -# debug.info("/user/main") -# try: -# if 'accToken' in kwargs: -# accToken = kwargs['accToken'] -# accToken.user = mainController.getUser(accToken.user.uid) -# retVal = accToken.user.toJSON() -# retVal['creditList'] = {credit.year: credit.toJSON() -# for credit in accToken.user.geruechte} -# debug.debug("return {{ {} }}".format(retVal)) -# return jsonify(retVal) -# except Exception: -# debug.debug("exception", exc_info=True) -# return jsonify("error", "something went wrong"), 500 - - -# @schedule.route("/user/addAmount", methods=['POST']) -# @login_required(groups=[USER]) -# def _addAmount(**kwargs): -# debug.info("/user/addAmount") -# try: -# if 'accToken' in kwargs: -# accToken = kwargs['accToken'] -# data = request.get_json() -# amount = int(data['amount']) -# date = datetime.now() -# mainController.addAmount( -# accToken.user.uid, amount, year=date.year, month=date.month) -# accToken.user = mainController.getUser(accToken.user.uid) -# retVal = accToken.user.toJSON() -# retVal['creditList'] = {credit.year: credit.toJSON() -# for credit in accToken.user.geruechte} -# debug.debug("return {{ {} }}".format(retVal)) -# creditL.info("{} {} {} fügt sich selbst {} € Schulden hinzu".format( -# date, accToken.user.firstname, accToken.user.lastname, amount/100)) -# return jsonify(retVal) -# except Exception: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": "something went wrong"}), 500 - - -# @schedule.route("/user/saveConfig", methods=['POST']) -# @login_required(groups=[USER]) -# def _saveConfig(**kwargs): -# debug.info("/user/saveConfig") -# try: -# if 'accToken' in kwargs: -# accToken = kwargs['accToken'] -# data = request.get_json() -# password = data['acceptedPassword'] -# data.pop('acceptedPassword') -# accToken.user = mainController.modifyUser( -# accToken.user, data, password) -# retVal = accToken.user.toJSON() -# retVal['creditList'] = {credit.year: credit.toJSON() -# for credit in accToken.user.geruechte} -# debug.debug("return {{ {} }}".format(retVal)) -# return jsonify(retVal) -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 409 -# -# @schedule.route("/user/storno", methods=['POST']) -# @login_required(groups=[USER]) -# def _storno(**kwargs): -# """ Function for Baruser to storno amount -# -# This function added to the user with the posted userID the posted amount. -# -# Returns: -# JSON-File with userID and the amount -# or ERROR 401 Permission Denied -# """ -# -# debug.info("/user/storno") -# try: -# if 'accToken' in kwargs: -# accToken = kwargs['accToken'] -# user = accToken.user -# data = request.get_json() -# amount = int(data['amount']) -# -# date = datetime.now() -# mainController.addCredit( -# user.uid, amount, year=date.year, month=date.month) -# accToken.user = mainController.getUser(accToken.user.uid) -# retVal = accToken.user.toJSON() -# retVal['creditList'] = {credit.year: credit.toJSON() -# for credit in accToken.user.geruechte} -# debug.debug("return {{ {} }}".format(retVal)) -# creditL.info("{} {} {} storniert {} €".format( -# date, user.firstname, user.lastname, amount/100)) -# return jsonify(retVal) -# except Exception as err: -# debug.debug("exception", exc_info=True) -# return jsonify({"error": str(err)}), 500 diff --git a/flaschengeist/system/controller/eventController.py b/flaschengeist/system/controller/eventController.py index 135164c..85c1a52 100644 --- a/flaschengeist/system/controller/eventController.py +++ b/flaschengeist/system/controller/eventController.py @@ -1,12 +1,37 @@ -from werkzeug.exceptions import BadRequest +from werkzeug.exceptions import BadRequest, NotFound -from flaschengeist.system.models.event import EventKind, Event +from flaschengeist.system.models.event import EventKind, Event, EventSlot, JobSlot, JobKind from sqlalchemy.exc import IntegrityError from flaschengeist import logger from flaschengeist.system.database import db +def get_event(id): + return Event.query.get(id) + + +def get_events(begin, end): + """Query events which start from begin until end + Args: + begin (datetime): Earliest start + end (datetime): Latest start + + Returns: collection of Event objects + """ + return Event.query.filter((begin <= Event.begin), (Event.begin < end)) + + +def delete_event(id): + """Delete event with given ID + Args: + id: id of Event to delete + + Returns: True if successful, False if Event is not found + """ + return Event.query.filter(Event.id == id).delete() == 1 + + def create_event(begin, kind, end=None, description=None): try: event = Event(begin=begin, @@ -21,6 +46,17 @@ def create_event(begin, kind, end=None, description=None): raise BadRequest +def create_job_kind(name): + try: + kind = JobKind(name=name) + db.session.add(kind) + db.session.commit() + return kind + except IntegrityError: + logger.debug("IntegrityError: Looks like there is a name collision", exc_info=True) + raise BadRequest("Name already exists") + + def create_event_kind(name): try: event = EventKind(name=name) @@ -30,3 +66,24 @@ def create_event_kind(name): except IntegrityError: logger.debug("IntegrityError: Looks like there is a name collision", exc_info=True) raise BadRequest("Name already exists") + + +def rename_event_kind(id, name): + ek = EventKind.query.get(id) + if not ek: + raise NotFound + ek.name = name + try: + db.session.commit() + except IntegrityError: + raise BadRequest("Name already exists") + + +def add_slot(event, job_slots, needed_persons, start=None, end=None): + event_slot = EventSlot(start=start, end=end) + for slot in job_slots: + kind = JobKind.query.get(slot.id) + job_slot = JobSlot(kind=kind, needed_persons=slot.needed_persons) + event_slot.add_slot(job_slot) + event.add_slot(event_slot) + db.session.commit() diff --git a/flaschengeist/system/controller/userController.py b/flaschengeist/system/controller/userController.py index d0433bd..049e2f4 100644 --- a/flaschengeist/system/controller/userController.py +++ b/flaschengeist/system/controller/userController.py @@ -27,7 +27,10 @@ def modify_user(user, password, new_password=None): Args: user: User object to sync with backend - password: Current password (most backends are needing this) + password: Cu db.session.commit() + + # TODO: is this needed? + def user_has_rorrent password (most backends are needing this) new_password (optional): New password, if password should be changed Raises: