[events] Allow locking events
This commit is contained in:
parent
51a3a8dfc8
commit
04d5b1e83a
|
@ -65,6 +65,7 @@ class Job(db.Model, ModelSerializeMixin):
|
||||||
end: Optional[datetime] = db.Column(UtcDateTime)
|
end: Optional[datetime] = db.Column(UtcDateTime)
|
||||||
type: Union[JobType, int] = db.relationship("JobType")
|
type: Union[JobType, int] = db.relationship("JobType")
|
||||||
comment: Optional[str] = db.Column(db.String(256))
|
comment: Optional[str] = db.Column(db.String(256))
|
||||||
|
locked: bool = db.Column(db.Boolean())
|
||||||
services: list[Service] = db.relationship("Service", back_populates="job_")
|
services: list[Service] = db.relationship("Service", back_populates="job_")
|
||||||
required_services: float = db.Column(db.Numeric(precision=4, scale=2, asdecimal=False), nullable=False)
|
required_services: float = db.Column(db.Numeric(precision=4, scale=2, asdecimal=False), nullable=False)
|
||||||
|
|
||||||
|
|
|
@ -22,4 +22,7 @@ ASSIGN_OTHER = "events_assign_other"
|
||||||
SEE_BACKUP = "events_see_backup"
|
SEE_BACKUP = "events_see_backup"
|
||||||
"""Can see users assigned as backup"""
|
"""Can see users assigned as backup"""
|
||||||
|
|
||||||
|
LOCK_JOBS = "events_lock_jobs"
|
||||||
|
"""Can lock jobs, no further services can be assigned or unassigned"""
|
||||||
|
|
||||||
permissions = [value for key, value in globals().items() if not key.startswith("_")]
|
permissions = [value for key, value in globals().items() if not key.startswith("_")]
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from http.client import NO_CONTENT
|
from http.client import NO_CONTENT
|
||||||
|
from re import template
|
||||||
from flask import request, jsonify
|
from flask import request, jsonify
|
||||||
|
from sqlalchemy import exc
|
||||||
from werkzeug.exceptions import BadRequest, NotFound, Forbidden
|
from werkzeug.exceptions import BadRequest, NotFound, Forbidden
|
||||||
|
|
||||||
from flaschengeist.models.session import Session
|
from flaschengeist.models.session import Session
|
||||||
|
from flaschengeist.plugins.events.models import Job
|
||||||
from flaschengeist.utils.decorators import login_required
|
from flaschengeist.utils.decorators import login_required
|
||||||
from flaschengeist.utils.datetime import from_iso_format
|
from flaschengeist.utils.datetime import from_iso_format
|
||||||
from flaschengeist.controller import userController
|
from flaschengeist.controller import userController
|
||||||
|
@ -12,6 +15,21 @@ from . import event_controller, permissions, EventPlugin
|
||||||
from ...utils.HTTP import no_content
|
from ...utils.HTTP import no_content
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def dict_get(self, key, default=None, type=None):
|
||||||
|
"""Same as .get from MultiDict"""
|
||||||
|
try:
|
||||||
|
rv = self[key]
|
||||||
|
except KeyError:
|
||||||
|
return default
|
||||||
|
if type is not None:
|
||||||
|
try:
|
||||||
|
rv = type(rv)
|
||||||
|
except ValueError:
|
||||||
|
rv = default
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
@EventPlugin.blueprint.route("/events/templates", methods=["GET"])
|
@EventPlugin.blueprint.route("/events/templates", methods=["GET"])
|
||||||
@login_required()
|
@login_required()
|
||||||
def get_templates(current_session):
|
def get_templates(current_session):
|
||||||
|
@ -239,9 +257,7 @@ def get_events(current_session, year=datetime.now().year, month=datetime.now().m
|
||||||
def _add_job(event, data):
|
def _add_job(event, data):
|
||||||
try:
|
try:
|
||||||
start = from_iso_format(data["start"])
|
start = from_iso_format(data["start"])
|
||||||
end = None
|
end = dict_get(data, "end", None, type=from_iso_format)
|
||||||
if "end" in data:
|
|
||||||
end = from_iso_format(data["end"])
|
|
||||||
required_services = data["required_services"]
|
required_services = data["required_services"]
|
||||||
job_type = data["type"]
|
job_type = data["type"]
|
||||||
if isinstance(job_type, dict):
|
if isinstance(job_type, dict):
|
||||||
|
@ -256,7 +272,7 @@ def _add_job(event, data):
|
||||||
required_services,
|
required_services,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
comment=data.get("comment", None),
|
comment=dict_get(data, "comment", None, str),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -276,11 +292,9 @@ def create_event(current_session):
|
||||||
JSON encoded Event object or HTTP-error
|
JSON encoded Event object or HTTP-error
|
||||||
"""
|
"""
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
end = data.get("end", None)
|
|
||||||
try:
|
try:
|
||||||
start = from_iso_format(data["start"])
|
start = from_iso_format(data["start"])
|
||||||
if end is not None:
|
end = dict_get(data, "end", None, type=from_iso_format)
|
||||||
end = from_iso_format(end)
|
|
||||||
data_type = data["type"]
|
data_type = data["type"]
|
||||||
if isinstance(data_type, dict):
|
if isinstance(data_type, dict):
|
||||||
data_type = data["type"]["id"]
|
data_type = data["type"]["id"]
|
||||||
|
@ -293,10 +307,10 @@ def create_event(current_session):
|
||||||
event = event_controller.create_event(
|
event = event_controller.create_event(
|
||||||
start=start,
|
start=start,
|
||||||
end=end,
|
end=end,
|
||||||
name=data.get("name", None),
|
name=dict_get(data, "name", None),
|
||||||
is_template=data.get("is_template", None),
|
is_template=dict_get(data, "is_template", None),
|
||||||
event_type=event_type,
|
event_type=event_type,
|
||||||
description=data.get("description", None),
|
description=dict_get(data, "description", None),
|
||||||
)
|
)
|
||||||
if "jobs" in data:
|
if "jobs" in data:
|
||||||
for job in data["jobs"]:
|
for job in data["jobs"]:
|
||||||
|
@ -323,15 +337,14 @@ def modify_event(event_id, current_session):
|
||||||
"""
|
"""
|
||||||
event = event_controller.get_event(event_id)
|
event = event_controller.get_event(event_id)
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
if "start" in data:
|
event.start = dict_get(data, "start", event.start, type=from_iso_format)
|
||||||
event.start = from_iso_format(data["start"])
|
event.end = dict_get(data, "end", event.end, type=from_iso_format)
|
||||||
if "end" in data:
|
event.name = dict_get(data, "name", event.name, type=str)
|
||||||
event.end = from_iso_format(data["end"])
|
event.description = dict_get(data, "description", event.description, type=str)
|
||||||
if "description" in data:
|
|
||||||
event.description = data["description"]
|
|
||||||
if "type" in data:
|
if "type" in data:
|
||||||
event_type = event_controller.get_event_type(data["type"])
|
event_type = event_controller.get_event_type(data["type"])
|
||||||
event.type = event_type
|
event.type = event_type
|
||||||
|
|
||||||
event_controller.update()
|
event_controller.update()
|
||||||
return jsonify(event)
|
return jsonify(event)
|
||||||
|
|
||||||
|
@ -390,19 +403,19 @@ def delete_job(event_id, job_id, current_session):
|
||||||
Returns:
|
Returns:
|
||||||
HTTP-no-content or HTTP error
|
HTTP-no-content or HTTP error
|
||||||
"""
|
"""
|
||||||
job_slot = event_controller.get_job(job_id, event_id)
|
job = event_controller.get_job(job_id, event_id)
|
||||||
event_controller.delete_job(job_slot)
|
event_controller.delete_job(job)
|
||||||
return no_content()
|
return no_content()
|
||||||
|
|
||||||
|
|
||||||
@EventPlugin.blueprint.route("/events/<int:event_id>/jobs/<int:job_id>", methods=["PUT"])
|
@EventPlugin.blueprint.route("/events/<int:event_id>/jobs/<int:job_id>", methods=["PUT"])
|
||||||
@login_required()
|
@login_required()
|
||||||
def update_job(event_id, job_id, current_session: Session):
|
def update_job(event_id, job_id, current_session: Session):
|
||||||
"""Edit Job or assign user to the Job
|
"""Edit Job
|
||||||
|
|
||||||
Route: ``/events/<event_id>/jobs/<job_id>`` | Method: ``PUT``
|
Route: ``/events/<event_id>/jobs/<job_id>`` | Method: ``PUT``
|
||||||
|
|
||||||
POST-data: See TS interface for Job or ``{user: {userid: string, value: number}}``
|
POST-data: See TS interface for Job
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
event_id: Identifier of the event
|
event_id: Identifier of the event
|
||||||
|
@ -412,37 +425,89 @@ def update_job(event_id, job_id, current_session: Session):
|
||||||
Returns:
|
Returns:
|
||||||
JSON encoded Job object or HTTP-error
|
JSON encoded Job object or HTTP-error
|
||||||
"""
|
"""
|
||||||
job = event_controller.get_job(job_id, event_id)
|
if not current_session.user_.has_permission(permissions.EDIT):
|
||||||
|
raise Forbidden
|
||||||
|
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
if not data:
|
if not data:
|
||||||
raise BadRequest
|
raise BadRequest
|
||||||
|
|
||||||
if ("user" not in data or len(data) > 1) and not current_session.user_.has_permission(permissions.EDIT):
|
job = event_controller.get_job(job_id, event_id)
|
||||||
raise Forbidden
|
|
||||||
|
|
||||||
if "user" in data:
|
|
||||||
try:
|
try:
|
||||||
user = userController.get_user(data["user"]["userid"])
|
if "type" in data:
|
||||||
value = data["user"]["value"]
|
job.type = event_controller.get_job_type(data["type"])
|
||||||
|
job.start = from_iso_format(data.get("start", job.start))
|
||||||
|
job.end = from_iso_format(data.get("end", job.end))
|
||||||
|
job.comment = str(data.get("comment", job.comment))
|
||||||
|
job.locked = bool(data.get("locked", job.locked))
|
||||||
|
job.required_services = float(data.get("required_services", job.required_services))
|
||||||
|
event_controller.update()
|
||||||
|
except NotFound:
|
||||||
|
raise BadRequest("Invalid JobType")
|
||||||
|
except ValueError:
|
||||||
|
raise BadRequest("Invalid POST data")
|
||||||
|
|
||||||
|
return jsonify(job)
|
||||||
|
|
||||||
|
|
||||||
|
@EventPlugin.blueprint.route("/events/jobs/<int:job_id>/assign", methods=["POST"])
|
||||||
|
@login_required()
|
||||||
|
def assign_job(job_id, current_session: Session):
|
||||||
|
"""Assign / unassign user to the Job
|
||||||
|
|
||||||
|
Route: ``/events/jobs/<job_id>/assign`` | Method: ``POST``
|
||||||
|
|
||||||
|
POST-data: a Service object, see TS interface for Service
|
||||||
|
|
||||||
|
Args:
|
||||||
|
job_id: Identifier of the Job
|
||||||
|
current_session: Session sent with Authorization Header
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HTTP-No-Content or HTTP-error
|
||||||
|
"""
|
||||||
|
data = request.get_json()
|
||||||
|
job = event_controller.get_job(job_id)
|
||||||
|
try:
|
||||||
|
user = userController.get_user(data["userid"])
|
||||||
|
value = data["value"]
|
||||||
if (user == current_session.user_ and not user.has_permission(permissions.ASSIGN)) or (
|
if (user == current_session.user_ and not user.has_permission(permissions.ASSIGN)) or (
|
||||||
user != current_session.user_ and not current_session.user_.has_permission(permissions.ASSIGN_OTHER)
|
user != current_session.user_ and not current_session.user_.has_permission(permissions.ASSIGN_OTHER)
|
||||||
):
|
):
|
||||||
raise Forbidden
|
raise Forbidden
|
||||||
if value > 0:
|
if value > 0:
|
||||||
event_controller.assign_job(job, user, value)
|
event_controller.assign_job(job, user, value, data.get("is_backup", False))
|
||||||
else:
|
else:
|
||||||
event_controller.unassign_job(job, user, notify=user != current_session.user_)
|
event_controller.unassign_job(job, user, notify=user != current_session.user_)
|
||||||
except (KeyError, ValueError):
|
except (TypeError, KeyError, ValueError):
|
||||||
raise BadRequest
|
raise BadRequest
|
||||||
|
return no_content()
|
||||||
|
|
||||||
if "required_services" in data:
|
|
||||||
job.required_services = data["required_services"]
|
@EventPlugin.blueprint.route("/events/jobs/<int:job_id>/lock", methods=["POST"])
|
||||||
if "type" in data:
|
@login_required(permissions.LOCK_JOBS)
|
||||||
job.type = event_controller.get_job_type(data["type"])
|
def lock_job(job_id, current_session: Session):
|
||||||
|
"""Lock / unlock the Job
|
||||||
|
|
||||||
|
Route: ``/events/jobs/<job_id>/lock`` | Method: ``POST``
|
||||||
|
|
||||||
|
POST-data: ``{locked: boolean}``
|
||||||
|
|
||||||
|
Args:
|
||||||
|
job_id: Identifier of the Job
|
||||||
|
current_session: Session sent with Authorization Header
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HTTP-No-Content or HTTP-error
|
||||||
|
"""
|
||||||
|
data = request.get_json()
|
||||||
|
job = event_controller.get_job(job_id)
|
||||||
|
try:
|
||||||
|
locked = bool(userController.get_user(data["locked"]))
|
||||||
|
job.locked = locked
|
||||||
event_controller.update()
|
event_controller.update()
|
||||||
|
except (TypeError, KeyError, ValueError):
|
||||||
return jsonify(job)
|
raise BadRequest
|
||||||
|
return no_content()
|
||||||
|
|
||||||
# TODO: JobTransfer
|
# TODO: JobTransfer
|
||||||
|
|
Loading…
Reference in New Issue