[Plugin] Schedule: Mostly final backend implemented. Tested.
This commit is contained in:
parent
6612a84cd3
commit
4a4930d683
|
@ -15,6 +15,8 @@ from flaschengeist.controller import userController
|
|||
|
||||
from . import event_controller, permissions
|
||||
from . import models
|
||||
from ... import logger
|
||||
from ...utils.HTTP import no_content
|
||||
|
||||
schedule_bp = Blueprint("schedule", __name__, url_prefix="/schedule")
|
||||
|
||||
|
@ -213,7 +215,7 @@ def get_events(current_session, year=datetime.now().year, month=datetime.now().m
|
|||
raise BadRequest("Invalid date given")
|
||||
|
||||
|
||||
def _add_event_slot(event, data):
|
||||
def _add_job(event, data):
|
||||
end = None
|
||||
try:
|
||||
start = from_iso_format(data["start"])
|
||||
|
@ -221,11 +223,11 @@ def _add_event_slot(event, data):
|
|||
raise BadRequest("Missing POST parameter")
|
||||
if "end" in data:
|
||||
end = from_iso_format(data["end"])
|
||||
event_slot = event_controller.add_event_slot(event, start, end)
|
||||
if "jobs" in data:
|
||||
for job_data in data["jobs"]:
|
||||
job_type = event_controller.get_job_type(job_data["type"])
|
||||
event_controller.add_job_slot(event_slot, job_type, job_data["required_jobs"])
|
||||
|
||||
if "required_services" not in data:
|
||||
raise BadRequest
|
||||
job_type = event_controller.get_job_type(data["type"])
|
||||
event_controller.add_job(event, job_type, data["required_services"], start, end, comment=data.get("comment"))
|
||||
|
||||
|
||||
@schedule_bp.route("/events", methods=["POST"])
|
||||
|
@ -235,7 +237,7 @@ def create_event(current_session):
|
|||
|
||||
Route: ``/schedule/events`` | Method: ``POST``
|
||||
|
||||
POST-data: See interfaces for Event, can already contain slots
|
||||
POST-data: See interfaces for Event, can already contain jobs
|
||||
|
||||
Args:
|
||||
current_session: Session sent with Authorization Header
|
||||
|
@ -254,9 +256,9 @@ def create_event(current_session):
|
|||
event = event_controller.create_event(
|
||||
start=start, event_type=event_type, description=data["description"] if "description" in data else None
|
||||
)
|
||||
if "slots" in data:
|
||||
for slot in data["slots"]:
|
||||
_add_event_slot(event, slot)
|
||||
if "jobs" in data:
|
||||
for job in data["jobs"]:
|
||||
_add_job(event, job)
|
||||
return jsonify(event)
|
||||
|
||||
|
||||
|
@ -307,14 +309,14 @@ def delete_event(event_id, current_session):
|
|||
return "", NO_CONTENT
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots", methods=["POST"])
|
||||
@schedule_bp.route("/events/<int:event_id>/jobs", methods=["POST"])
|
||||
@login_required(permission=permissions.EDIT)
|
||||
def add_event_slot(event_id, current_session):
|
||||
"""Add an new EventSlot to an Event
|
||||
def add_job(event_id, current_session):
|
||||
"""Add an new Job to an Event / EventSlot
|
||||
|
||||
Route: ``/schedule/events/<event_id>/slots`` | Method: ``POST``
|
||||
Route: ``/schedule/events/<event_id>/jobs`` | Method: ``POST``
|
||||
|
||||
POST-data: See TS interface for EventSlot
|
||||
POST-data: See Job
|
||||
|
||||
Args:
|
||||
event_id: Identifier of the event
|
||||
|
@ -324,139 +326,48 @@ def add_event_slot(event_id, current_session):
|
|||
JSON encoded Event object or HTTP-error
|
||||
"""
|
||||
event = event_controller.get_event(event_id)
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
raise BadRequest("Missing POST parameters")
|
||||
|
||||
_add_event_slot(event, data)
|
||||
_add_job(event, request.get_json())
|
||||
return jsonify(event)
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["PUT"])
|
||||
@login_required(permission=permissions.EDIT)
|
||||
def update_event_slot(event_id, slot_id, current_session):
|
||||
"""Update an EventSlot
|
||||
|
||||
Route: ``/schedule/events/<event_id>/slots/<slot_id>`` | Method: ``PUT``
|
||||
|
||||
POST-data: See TS interface for EventSlot
|
||||
|
||||
Args:
|
||||
event_id: Identifier of the event
|
||||
slot_id: Identifier of the slot
|
||||
current_session: Session sent with Authorization Header
|
||||
|
||||
Returns:
|
||||
JSON encoded Event object or HTTP-error
|
||||
"""
|
||||
event = event_controller.get_event(event_id)
|
||||
slot = event_controller.get_event_slot(slot_id)
|
||||
if slot not in event.slots:
|
||||
raise NotFound
|
||||
|
||||
data = _event_slot_from_data(request.get_json())
|
||||
slot.start = data["start"]
|
||||
if "end" in data:
|
||||
slot.end = data["end"]
|
||||
event_controller.update()
|
||||
return jsonify(event)
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>", methods=["DELETE"])
|
||||
@login_required(permission=permissions.EDIT)
|
||||
def delete_event_slot(event_id, slot_id, current_session):
|
||||
"""Delete an EventSlot
|
||||
|
||||
Route: ``/schedule/events/<event_id>/slots/<slot_id>`` | Method: ``DELETE``
|
||||
|
||||
Args:
|
||||
event_id: Identifier of the event
|
||||
slot_id: Identifier of the slot
|
||||
current_session: Session sent with Authorization Header
|
||||
|
||||
Returns:
|
||||
JSON encoded Event object or HTTP-error
|
||||
"""
|
||||
event = event_controller.get_event(event_id)
|
||||
event_controller.remove_event_slot(event, slot_id)
|
||||
return jsonify(event)
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>/jobs", methods=["POST"])
|
||||
@login_required(permission=permissions.EDIT)
|
||||
def add_job_slot(event_id, slot_id, current_session):
|
||||
"""Add an new JobSlot to an Event / EventSlot
|
||||
|
||||
Route: ``/schedule/events/<event_id>/slots/<slot_id>/jobs`` | Method: ``POST``
|
||||
|
||||
POST-data: ``{type: string, required_jobs: number}``
|
||||
|
||||
Args:
|
||||
event_id: Identifier of the event
|
||||
slot_id: Identifier of the slot
|
||||
current_session: Session sent with Authorization Header
|
||||
|
||||
Returns:
|
||||
JSON encoded Event object or HTTP-error
|
||||
"""
|
||||
event = event_controller.get_event(event_id)
|
||||
slot = event_controller.get_event_slot(slot_id)
|
||||
if slot not in event.slots:
|
||||
raise NotFound
|
||||
|
||||
data = request.get_json()
|
||||
try:
|
||||
job_type = event_controller.get_job_type(data["type"])
|
||||
required_jobs = data["required_jobs"]
|
||||
except (KeyError, ValueError):
|
||||
raise BadRequest("Missing POST parameters")
|
||||
|
||||
event_controller.add_job_slot(slot, job_type, required_jobs)
|
||||
return jsonify(event)
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>/jobs/<int:job_type>", methods=["DELETE"])
|
||||
@schedule_bp.route("/events/<int:event_id>/jobs/<int:job_id>", methods=["DELETE"])
|
||||
@login_required(permission=permissions.DELETE)
|
||||
def delete_job_slot(event_id, slot_id, job_type, current_session):
|
||||
"""Delete a JobSlot
|
||||
def delete_job(event_id, job_id, current_session):
|
||||
"""Delete a Job
|
||||
|
||||
Route: ``/schedule/events/<event_id>/slots/<slot_id>/jobs/<job_type>`` | Method: ``DELETE``
|
||||
Route: ``/events/<event_id>/jobs/<job_id>`` | Method: ``DELETE``
|
||||
|
||||
Args:
|
||||
event_id: Identifier of the event
|
||||
slot_id: Identifier of the EventSlot
|
||||
job_type: Identifier of the JobSlot
|
||||
job_id: Identifier of the Job
|
||||
current_session: Session sent with Authorization Header
|
||||
|
||||
Returns:
|
||||
JSON encoded Event object or HTTP-error
|
||||
HTTP-no-content or HTTP error
|
||||
"""
|
||||
event = event_controller.get_event(event_id)
|
||||
job_slot = event_controller.get_job_slot(slot_id, job_type)
|
||||
event_controller.delete_job_slot(job_slot)
|
||||
return jsonify(event)
|
||||
job_slot = event_controller.get_job(job_id, event_id)
|
||||
event_controller.delete_job(job_slot)
|
||||
return no_content()
|
||||
|
||||
|
||||
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>/jobs/<int:job_type>", methods=["PUT"])
|
||||
@schedule_bp.route("/events/<int:event_id>/jobs/<int:job_id>", methods=["PUT"])
|
||||
@login_required()
|
||||
def update_job_slot(event_id, slot_id, job_type, current_session: Session):
|
||||
"""Edit JobSlot or add user to the Slot
|
||||
def update_job(event_id, job_id, current_session: Session):
|
||||
"""Edit Job or assign user to the Job
|
||||
|
||||
Route: ``/schedule/events/<event_id>/slots/<slot_id>/jobs/<job_type>`` | Method: ``PUT``
|
||||
Route: ``/events/<event_id>/jobs/<job_id>`` | Method: ``PUT``
|
||||
|
||||
POST-data: See TS interface for EventSlot or ``{user: {userid: string, value: number}}``
|
||||
POST-data: See TS interface for Job or ``{user: {userid: string, value: number}}``
|
||||
|
||||
Args:
|
||||
event_id: Identifier of the event
|
||||
slot_id: Identifier of the slot
|
||||
job_type: Identifier of the JobSlot
|
||||
job_id: Identifier of the Job
|
||||
current_session: Session sent with Authorization Header
|
||||
|
||||
Returns:
|
||||
JSON encoded Event object or HTTP-error
|
||||
"""
|
||||
event = event_controller.get_event(event_id)
|
||||
slot = event_controller.get_job_slot(slot_id, job_type)
|
||||
job = event_controller.get_job(job_id, event_id)
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
|
@ -473,17 +384,17 @@ def update_job_slot(event_id, slot_id, job_type, current_session: Session):
|
|||
user != current_session._user and not current_session._user.has_permission(permissions.ASSIGN_OTHER)
|
||||
):
|
||||
raise Forbidden
|
||||
event_controller.assign_job(slot, user, value)
|
||||
event_controller.assign_to_job(job, user, value)
|
||||
except (KeyError, ValueError):
|
||||
raise BadRequest
|
||||
|
||||
if "required_jobs" in data:
|
||||
slot.required_jobs = data["required_jobs"]
|
||||
if "required_services" in data:
|
||||
job.required_services = data["required_services"]
|
||||
if "type" in data:
|
||||
slot.type = event_controller.get_job_type(data["type"])
|
||||
job.type = event_controller.get_job_type(data["type"])
|
||||
event_controller.update()
|
||||
|
||||
return jsonify(event)
|
||||
return jsonify(job.event_)
|
||||
|
||||
|
||||
# TODO: JobTransfer
|
||||
|
|
|
@ -3,7 +3,7 @@ from sqlalchemy.exc import IntegrityError
|
|||
|
||||
from flaschengeist import logger
|
||||
from flaschengeist.database import db
|
||||
from flaschengeist.plugins.schedule.models import EventType, Event, EventSlot, JobSlot, JobType, Job
|
||||
from flaschengeist.plugins.schedule.models import EventType, Event, Job, JobType, Service
|
||||
|
||||
|
||||
def update():
|
||||
|
@ -55,6 +55,7 @@ def get_job_types():
|
|||
|
||||
def get_job_type(type_id):
|
||||
job_type = JobType.query.get(type_id)
|
||||
print(job_type)
|
||||
if not job_type:
|
||||
raise NotFound
|
||||
return job_type
|
||||
|
@ -119,10 +120,10 @@ def delete_event(event_id):
|
|||
db.session.commit()
|
||||
|
||||
|
||||
def create_event(event_type, start, slots=[], description=None):
|
||||
def create_event(event_type, start, jobs=[], description=None):
|
||||
try:
|
||||
logger.debug(event_type)
|
||||
event = Event(start=start, description=description, type=event_type, slots=slots)
|
||||
event = Event(start=start, description=description, type=event_type, jobs=jobs)
|
||||
db.session.add(event)
|
||||
db.session.commit()
|
||||
return event
|
||||
|
@ -131,59 +132,38 @@ def create_event(event_type, start, slots=[], description=None):
|
|||
raise BadRequest
|
||||
|
||||
|
||||
def get_event_slot(slot_id):
|
||||
slot = EventSlot.query.get(slot_id)
|
||||
if slot is None:
|
||||
def get_job(job_slot_id, event_id):
|
||||
js = Job.query.filter(Job.id == job_slot_id).filter(Job._event_id == event_id).one_or_none()
|
||||
if js is None:
|
||||
raise NotFound
|
||||
return slot
|
||||
return js
|
||||
|
||||
|
||||
def add_event_slot(event, start, end=None):
|
||||
event_slot = EventSlot(start=start, end=end, event_=event)
|
||||
if start < event.start:
|
||||
raise BadRequest("Start before event start")
|
||||
db.session.add(event_slot)
|
||||
db.session.commit()
|
||||
return event_slot
|
||||
def add_job(event, job_type, required_services, start, end=None, comment=None):
|
||||
job = Job(required_services=required_services, type=job_type, start=start, end=end, comment=comment)
|
||||
event.jobs.append(job)
|
||||
update()
|
||||
return job
|
||||
|
||||
|
||||
def remove_event_slot(event, slot_id):
|
||||
slot = get_event_slot(slot_id)
|
||||
if slot in event.slots:
|
||||
event.slots.remove(slot)
|
||||
else:
|
||||
raise NotFound
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def get_job_slot(event_slot_id, job_type):
|
||||
jt = (
|
||||
JobSlot.query.filter(JobSlot._type_id == job_type).filter(JobSlot._event_slot_id == event_slot_id).one_or_none()
|
||||
)
|
||||
if jt is None:
|
||||
raise NotFound
|
||||
return jt
|
||||
|
||||
|
||||
def add_job_slot(event_slot, job_type, required_jobs):
|
||||
job_slot = JobSlot(type=job_type, required_jobs=required_jobs, event_slot_=event_slot)
|
||||
def update():
|
||||
try:
|
||||
db.session.add(job_slot)
|
||||
db.session.commit()
|
||||
except IntegrityError:
|
||||
raise BadRequest("JobSlot with that type already exists on this EventSlot")
|
||||
logger.debug("Error, looks like a Job with that type already exists on an event", exc_info=True)
|
||||
raise BadRequest()
|
||||
|
||||
|
||||
def delete_job_slot(job_slot):
|
||||
db.session.delete(job_slot)
|
||||
def delete_job(job: Job):
|
||||
db.session.delete(job)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def assign_job(job_slot, user, value):
|
||||
job = Job.query.get((job_slot.id_, user._id))
|
||||
if job:
|
||||
job.value = value
|
||||
def assign_to_job(job: Job, user, value):
|
||||
service = Service.query.get((job.id, user._id))
|
||||
if service:
|
||||
service.value = value
|
||||
else:
|
||||
job = Job(user_=user, value=value, slot_=job_slot)
|
||||
db.session.add(job)
|
||||
service = Service(user_=user, value=value, job_=job)
|
||||
db.session.add(service)
|
||||
db.session.commit()
|
||||
|
|
|
@ -13,13 +13,13 @@ from flaschengeist.database import db
|
|||
|
||||
|
||||
class EventType(db.Model, ModelSerializeMixin):
|
||||
__tablename__ = "event_type"
|
||||
__tablename__ = "schedule_event_type"
|
||||
id_: int = db.Column("id", db.Integer, primary_key=True)
|
||||
name: str = db.Column(db.String(30), nullable=False, unique=True)
|
||||
|
||||
|
||||
class JobType(db.Model, ModelSerializeMixin):
|
||||
__tablename__ = "job_type"
|
||||
__tablename__ = "schedule_job_type"
|
||||
id: int = db.Column("id", db.Integer, primary_key=True)
|
||||
name: str = db.Column(db.String(30), nullable=False, unique=True)
|
||||
|
||||
|
@ -29,35 +29,38 @@ class JobType(db.Model, ModelSerializeMixin):
|
|||
########
|
||||
|
||||
|
||||
class Job(db.Model, ModelSerializeMixin):
|
||||
__tablename__ = "job"
|
||||
class Service(db.Model, ModelSerializeMixin):
|
||||
__tablename__ = "schedule_service"
|
||||
userid: str = ""
|
||||
value: float = db.Column(db.Numeric(precision=3, scale=2, asdecimal=False), nullable=False)
|
||||
|
||||
_slot_id = db.Column("slot_id", db.Integer, db.ForeignKey("job_slot.id"), nullable=False, primary_key=True)
|
||||
_job_id = db.Column("job_id", db.Integer, db.ForeignKey("schedule_job.id"), nullable=False, primary_key=True)
|
||||
_user_id = db.Column("user_id", db.Integer, db.ForeignKey("user.id"), nullable=False, primary_key=True)
|
||||
|
||||
user_: User = db.relationship("User")
|
||||
slot_ = db.relationship("JobSlot")
|
||||
job_ = db.relationship("Job")
|
||||
|
||||
@property
|
||||
def userid(self):
|
||||
return self.user_.userid
|
||||
|
||||
|
||||
class JobSlot(db.Model, ModelSerializeMixin):
|
||||
__tablename__ = "job_slot"
|
||||
_type_id = db.Column("type_id", db.Integer, db.ForeignKey("job_type.id"), nullable=False)
|
||||
_event_slot_id = db.Column("event_slot_id", db.Integer, db.ForeignKey("event_slot.id"), nullable=False)
|
||||
class Job(db.Model, ModelSerializeMixin):
|
||||
__tablename__ = "schedule_job"
|
||||
_type_id = db.Column("type_id", db.Integer, db.ForeignKey("schedule_job_type.id"), nullable=False)
|
||||
_event_id = db.Column("event_id", db.Integer, db.ForeignKey("schedule_event.id"), nullable=False)
|
||||
|
||||
id_: int = db.Column("id", db.Integer, primary_key=True)
|
||||
id: int = db.Column("id", db.Integer, primary_key=True)
|
||||
start: datetime = db.Column(UtcDateTime, nullable=False)
|
||||
end: Optional[datetime] = db.Column(UtcDateTime)
|
||||
comment: str = db.Column(db.String(256))
|
||||
type: JobType = db.relationship("JobType")
|
||||
users: [Job] = db.relationship("Job", back_populates="slot_")
|
||||
required_jobs: float = db.Column(db.Numeric(precision=4, scale=2, asdecimal=False))
|
||||
services: [Service] = db.relationship("Service", back_populates="job_")
|
||||
required_services: float = db.Column(db.Numeric(precision=4, scale=2, asdecimal=False), nullable=False)
|
||||
|
||||
event_slot_ = db.relationship("EventSlot", back_populates="jobs")
|
||||
event_ = db.relationship("Event", back_populates="jobs")
|
||||
|
||||
__table_args__ = (UniqueConstraint("type_id", "event_slot_id", name="_type_event_slot_uc"),)
|
||||
__table_args__ = (UniqueConstraint("type_id", "start", name="_type_start_uc"),)
|
||||
|
||||
|
||||
##########
|
||||
|
@ -65,30 +68,18 @@ class JobSlot(db.Model, ModelSerializeMixin):
|
|||
##########
|
||||
|
||||
|
||||
class EventSlot(db.Model, ModelSerializeMixin):
|
||||
"""Model for an EventSlot"""
|
||||
|
||||
__tablename__ = "event_slot"
|
||||
_event_id = db.Column("event_id", db.Integer, db.ForeignKey("event.id"), nullable=False)
|
||||
|
||||
id: int = db.Column(db.Integer, primary_key=True)
|
||||
start: datetime = db.Column(UtcDateTime)
|
||||
end: Optional[datetime] = db.Column(UtcDateTime)
|
||||
jobs: [JobSlot] = db.relationship("JobSlot", back_populates="event_slot_")
|
||||
|
||||
event_ = db.relationship("Event", back_populates="slots")
|
||||
|
||||
|
||||
class Event(db.Model, ModelSerializeMixin):
|
||||
"""Model for an Event"""
|
||||
|
||||
__tablename__ = "event"
|
||||
_type_id = db.Column("type_id", db.Integer, db.ForeignKey("event_type.id", ondelete="CASCADE"), nullable=False)
|
||||
__tablename__ = "schedule_event"
|
||||
_type_id = db.Column(
|
||||
"type_id", db.Integer, db.ForeignKey("schedule_event_type.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
|
||||
id: int = db.Column(db.Integer, primary_key=True)
|
||||
start: datetime = db.Column(UtcDateTime, nullable=False)
|
||||
description: Optional[str] = db.Column(db.String(240))
|
||||
description: Optional[str] = db.Column(db.String(255))
|
||||
type: EventType = db.relationship("EventType")
|
||||
slots: [EventSlot] = db.relationship(
|
||||
"EventSlot", back_populates="event_", cascade="all,delete,delete-orphan", order_by="EventSlot.start"
|
||||
jobs: [Job] = db.relationship(
|
||||
"Job", back_populates="event_", cascade="all,delete,delete-orphan", order_by="[Job.start, Job.end]"
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue