flaschengeist/flaschengeist/plugins/events/event_controller.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

355 lines
9.8 KiB
Python
Raw Normal View History

from datetime import datetime, timedelta, timezone
from enum import IntEnum
2021-03-20 23:55:50 +00:00
from typing import Optional
2021-11-13 13:49:28 +00:00
from werkzeug.exceptions import BadRequest, Conflict, NotFound
2020-09-05 20:26:00 +00:00
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.util import was_deleted
2020-09-05 20:26:00 +00:00
from flaschengeist import logger
from flaschengeist.database import db
from flaschengeist.plugins.events import EventPlugin
from flaschengeist.plugins.events.models import EventType, Event, Invitation, Job, JobType, Service
from flaschengeist.utils.scheduler import scheduled
# STUB
def _(x):
return x
class NotifyType(IntEnum):
# Invitations 0x00..0x0F
INVITE = 0x01
TRANSFER = 0x02
# Invitation responsed 0x10..0x1F
INVITATION_ACCEPTED = 0x10
INVITATION_REJECTED = 0x11
def update():
db.session.commit()
def get_event_types():
return EventType.query.all()
def get_event_type(identifier):
"""Get EventType by ID (int) or name (string)"""
if isinstance(identifier, int):
et = EventType.query.get(identifier)
elif isinstance(identifier, str):
et = EventType.query.filter(EventType.name == identifier).one_or_none()
else:
logger.debug("Invalid identifier type for EventType")
raise BadRequest
if not et:
raise NotFound
return et
def create_event_type(name):
try:
event = EventType(name=name)
db.session.add(event)
db.session.commit()
return event
except IntegrityError:
2021-11-13 13:49:28 +00:00
raise Conflict("Name already exists")
def rename_event_type(identifier, new_name):
event_type = get_event_type(identifier)
event_type.name = new_name
try:
db.session.commit()
except IntegrityError:
2021-11-13 13:49:28 +00:00
raise Conflict("Name already exists")
def delete_event_type(name):
event_type = get_event_type(name)
db.session.delete(event_type)
try:
db.session.commit()
except IntegrityError:
raise BadRequest("Type still in use")
2020-09-05 20:26:00 +00:00
def get_job_types():
return JobType.query.all()
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
def create_job_type(name):
try:
job_type = JobType(name=name)
db.session.add(job_type)
db.session.commit()
return job_type
except IntegrityError:
raise BadRequest("Name already exists")
def rename_job_type(name, new_name):
job_type = get_job_type(name)
job_type.name = new_name
try:
db.session.commit()
except IntegrityError:
raise BadRequest("Name already exists")
def delete_job_type(name):
job_type = get_job_type(name)
db.session.delete(job_type)
try:
db.session.commit()
except IntegrityError:
raise BadRequest("Type still in use")
def clear_backup(event: Event):
for job in event.jobs:
services = []
for service in job.services:
if not service.is_backup:
services.append(service)
job.services = services
def get_event(event_id, with_backup=False) -> Event:
event = Event.query.get(event_id)
if event is None:
raise NotFound
if not with_backup:
2021-11-11 11:23:45 +00:00
clear_backup(event)
return event
2020-09-07 14:13:18 +00:00
2021-03-20 23:55:50 +00:00
def get_templates():
return Event.query.filter(Event.is_template == True).all()
2021-11-18 22:06:03 +00:00
def get_events(
start: Optional[datetime] = None,
end: Optional[datetime] = None,
limit: Optional[int] = None,
offset: Optional[int] = None,
descending: Optional[bool] = False,
with_backup=False,
):
2020-09-07 14:13:18 +00:00
"""Query events which start from begin until end
Args:
start (datetime): Earliest start
2020-09-07 14:13:18 +00:00
end (datetime): Latest start
with_backup (bool): Export also backup services
2020-09-07 14:13:18 +00:00
Returns: collection of Event objects
"""
2021-03-20 23:55:50 +00:00
query = Event.query.filter(Event.is_template.__eq__(False))
if start is not None:
query = query.filter(start <= Event.start)
if end is not None:
query = query.filter(Event.start < end)
2021-11-18 22:06:03 +00:00
if descending:
query = query.order_by(Event.start.desc())
else:
query = query.order_by(Event.start)
if limit is not None:
query = query.limit(limit)
if offset is not None and offset > 0:
query = query.offset(offset)
events = query.all()
if not with_backup:
for event in events:
clear_backup(event)
return events
2020-09-07 14:13:18 +00:00
def delete_event(event_id):
2020-09-07 14:13:18 +00:00
"""Delete event with given ID
Args:
event_id: id of Event to delete
2020-09-07 14:13:18 +00:00
Raises:
NotFound if not found
2020-09-07 14:13:18 +00:00
"""
event = get_event(event_id, True)
for job in event.jobs:
delete_job(job)
db.session.delete(event)
db.session.commit()
2020-09-07 14:13:18 +00:00
2021-03-20 23:55:50 +00:00
def create_event(event_type, start, end=None, jobs=[], is_template=None, name=None, description=None):
2020-09-05 20:26:00 +00:00
try:
logger.debug(event_type)
event = Event(
2021-03-24 19:47:04 +00:00
start=start,
end=end,
name=name,
description=description,
type=event_type,
is_template=is_template,
jobs=jobs,
)
2020-09-05 20:26:00 +00:00
db.session.add(event)
db.session.commit()
return event
except IntegrityError:
logger.debug("Database error when creating new event", exc_info=True)
raise BadRequest
2021-11-21 16:52:06 +00:00
def get_job(job_id, event_id=None) -> Job:
query = Job.query.filter(Job.id == job_id)
if event_id is not None:
query = query.filter(Job.event_id_ == event_id)
job = query.one_or_none()
if job is None:
raise NotFound
2021-11-21 16:52:06 +00:00
return job
2020-09-07 14:13:18 +00:00
def add_job(event, job_type, required_services, start, end=None, comment=None):
2021-11-18 22:06:03 +00:00
job = Job(
required_services=required_services,
type=job_type,
start=start,
end=end,
comment=comment,
)
event.jobs.append(job)
update()
return job
def update():
try:
db.session.commit()
except IntegrityError:
2021-11-18 22:06:03 +00:00
logger.debug(
"Error, looks like a Job with that type already exists on an event",
exc_info=True,
)
raise BadRequest()
def delete_job(job: Job):
for service in job.services:
unassign_job(service=service, notify=True)
for invitation in job.invitations_:
respond_invitation(invitation, False)
db.session.delete(job)
db.session.commit()
2021-11-21 16:52:06 +00:00
def assign_job(job: Job, user, value, is_backup=False):
assert value > 0
service = Service.query.get((job.id, user.id_))
if service:
service.value = value
else:
job.services.append(Service(user_=user, value=value, is_backup=is_backup, job_=job))
db.session.commit()
def unassign_job(job: Job = None, user=None, service=None, notify=False):
if service is None:
2021-11-18 22:06:03 +00:00
assert job is not None and user is not None
service = Service.query.get((job.id, user.id_))
else:
user = service.user_
if not service:
raise BadRequest
2021-11-18 22:06:03 +00:00
event_id = service.job_.event_id_
2021-11-18 22:06:03 +00:00
db.session.delete(service)
db.session.commit()
if notify:
2021-11-18 22:06:03 +00:00
EventPlugin.plugin.notify(user, "Your assignmet was cancelled", {"event_id": event_id})
def invite(job: Job, invitee, inviter, transferee=None):
inv = Invitation(job_=job, inviter_=inviter, invitee_=invitee, transferee_=transferee)
db.session.add(inv)
update()
if transferee is None:
EventPlugin.plugin.notify(invitee, _("Job invitation"), {"type": NotifyType.INVITE, "invitation": inv.id})
else:
EventPlugin.plugin.notify(invitee, _("Job transfer"), {"type": NotifyType.TRANSFER, "invitation": inv.id})
return inv
def get_invitation(id: int):
inv: Invitation = Invitation.query.get(id)
if inv is None:
raise NotFound
return inv
def cancel_invitation(inv: Invitation):
db.session.delete(inv)
db.session.commit()
def respond_invitation(invite: Invitation, accepted=True):
inviter = invite.inviter_
job = invite.job_
db.session.delete(invite)
db.session.commit()
if not was_deleted(invite):
raise Conflict
if not accepted:
EventPlugin.plugin.notify(inviter, _("Invitation rejected"), {"type": NotifyType.INVITATION_REJECTED, "event": job.event_id_, "job": invite.job_id, "invitee": invite.invitee_id})
else:
if invite.transferee_id is None:
assign_job(job, invite.invitee_, 1)
else:
service = filter(lambda s: s.userid == invite.transferee_id, job.services)
if not service:
raise Conflict
unassign_job(job, invite.transferee_, service[0], True)
assign_job(job, invite.invitee_, service[0].value)
EventPlugin.plugin.notify(inviter, _("Invitation accepted"), {"type": NotifyType.INVITATION_ACCEPTED, "event": job.event_id_, "job": invite.job_id, "invitee": invite.invitee_id})
@scheduled
def assign_backups():
logger.debug("Notifications")
now = datetime.now(tz=timezone.utc)
# now + backup_time + next cron tick
start = now + timedelta(hours=16) + timedelta(minutes=30)
services = Service.query.filter(Service.is_backup == True).join(Job).filter(Job.start <= start).all()
for service in services:
if service.job_.start <= now or service.job_.is_full():
EventPlugin.plugin.notify(
2021-11-18 22:06:03 +00:00
service.user_,
"Your backup assignment was cancelled.",
{"event_id": service.job_.event_id_},
)
logger.debug(f"Service is outdated or full, removing. {service.serialize()}")
db.session.delete(service)
else:
service.is_backup = False
logger.debug(f"Service not full, assigning backup. {service.serialize()}")
EventPlugin.plugin.notify(
2021-11-18 22:06:03 +00:00
service.user_,
"Your backup assignment was accepted.",
{"event_id": service.job_.event_id_},
)
db.session.commit()