[Plugin] schedule: Basics work (all models)
This commit is contained in:
parent
ac1189ecaa
commit
5a8a4aa23d
|
@ -5,11 +5,13 @@ Provides duty schedule / duty roster functions
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from http.client import NO_CONTENT, CREATED
|
from http.client import NO_CONTENT, CREATED
|
||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
from werkzeug.exceptions import BadRequest, NotFound
|
from werkzeug.exceptions import BadRequest, NotFound, Forbidden
|
||||||
|
|
||||||
from flaschengeist.plugins import Plugin
|
from flaschengeist.plugins import Plugin
|
||||||
|
from flaschengeist.models.session import Session
|
||||||
from flaschengeist.decorator import login_required
|
from flaschengeist.decorator 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 . import event_controller, permissions
|
from . import event_controller, permissions
|
||||||
from . import models
|
from . import models
|
||||||
|
@ -32,7 +34,7 @@ class SchedulePlugin(Plugin):
|
||||||
def get_event_types(current_session):
|
def get_event_types(current_session):
|
||||||
"""Get all EventTypes
|
"""Get all EventTypes
|
||||||
|
|
||||||
Route: ``/event-types`` | Method: ``GET``
|
Route: ``/schedule/event-types`` | Method: ``GET``
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
current_session: Session sent with Authorization Header
|
current_session: Session sent with Authorization Header
|
||||||
|
@ -49,7 +51,7 @@ def get_event_types(current_session):
|
||||||
def new_event_type(current_session):
|
def new_event_type(current_session):
|
||||||
"""Create a new EventType
|
"""Create a new EventType
|
||||||
|
|
||||||
Route: ``/event-types`` | Method: ``POST``
|
Route: ``/schedule/event-types`` | Method: ``POST``
|
||||||
|
|
||||||
POST-data: ``{name: string}``
|
POST-data: ``{name: string}``
|
||||||
|
|
||||||
|
@ -71,7 +73,7 @@ def new_event_type(current_session):
|
||||||
def modify_event_type(name, current_session):
|
def modify_event_type(name, current_session):
|
||||||
"""Rename or delete an event type
|
"""Rename or delete an event type
|
||||||
|
|
||||||
Route: ``/event-types/<name>`` | Method: ``PUT`` or ``DELETE``
|
Route: ``/schedule/event-types/<name>`` | Method: ``PUT`` or ``DELETE``
|
||||||
|
|
||||||
POST-data: (if renaming) ``{name: string}``
|
POST-data: (if renaming) ``{name: string}``
|
||||||
|
|
||||||
|
@ -97,7 +99,7 @@ def modify_event_type(name, current_session):
|
||||||
def get_job_types(current_session):
|
def get_job_types(current_session):
|
||||||
"""Get all JobTypes
|
"""Get all JobTypes
|
||||||
|
|
||||||
Route: ``/job-types`` | Method: ``GET``
|
Route: ``/schedule/job-types`` | Method: ``GET``
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
current_session: Session sent with Authorization Header
|
current_session: Session sent with Authorization Header
|
||||||
|
@ -114,7 +116,7 @@ def get_job_types(current_session):
|
||||||
def new_job_type(current_session):
|
def new_job_type(current_session):
|
||||||
"""Create a new JobType
|
"""Create a new JobType
|
||||||
|
|
||||||
Route: ``/job-types`` | Method: ``POST``
|
Route: ``/schedule/job-types`` | Method: ``POST``
|
||||||
|
|
||||||
POST-data: ``{name: string}``
|
POST-data: ``{name: string}``
|
||||||
|
|
||||||
|
@ -122,38 +124,38 @@ def new_job_type(current_session):
|
||||||
current_session: Session sent with Authorization Header
|
current_session: Session sent with Authorization Header
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HTTP-Created or HTTP-error
|
JSON encoded JobType or HTTP-error
|
||||||
"""
|
"""
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
if "name" not in data:
|
if "name" not in data:
|
||||||
raise BadRequest
|
raise BadRequest
|
||||||
event_controller.create_job_type(data["name"])
|
jt = event_controller.create_job_type(data["name"])
|
||||||
return "", CREATED
|
return jsonify(jt)
|
||||||
|
|
||||||
|
|
||||||
@schedule_bp.route("/job-types/<name>", methods=["PUT", "DELETE"])
|
@schedule_bp.route("/job-types/<int:type_id>", methods=["PUT", "DELETE"])
|
||||||
@login_required(permission=permissions.JOB_TYPE)
|
@login_required(permission=permissions.JOB_TYPE)
|
||||||
def modify_job_type(name, current_session):
|
def modify_job_type(type_id, current_session):
|
||||||
"""Rename or delete a JobType
|
"""Rename or delete a JobType
|
||||||
|
|
||||||
Route: ``/job-types/<name>`` | Method: ``PUT`` or ``DELETE``
|
Route: ``/schedule/job-types/<name>`` | Method: ``PUT`` or ``DELETE``
|
||||||
|
|
||||||
POST-data: (if renaming) ``{name: string}``
|
POST-data: (if renaming) ``{name: string}``
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: Name identifying the JobType
|
type_id: Identifier of the JobType
|
||||||
current_session: Session sent with Authorization Header
|
current_session: Session sent with Authorization Header
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HTTP-NoContent or HTTP-error
|
HTTP-NoContent or HTTP-error
|
||||||
"""
|
"""
|
||||||
if request.method == "DELETE":
|
if request.method == "DELETE":
|
||||||
event_controller.delete_name_type(name)
|
event_controller.delete_job_type(type_id)
|
||||||
else:
|
else:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
if "name" not in data:
|
if "name" not in data:
|
||||||
raise BadRequest("Parameter missing in data")
|
raise BadRequest("Parameter missing in data")
|
||||||
event_controller.rename_name_type(name, data["name"])
|
event_controller.rename_job_type(type_id, data["name"])
|
||||||
return "", NO_CONTENT
|
return "", NO_CONTENT
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,7 +164,7 @@ def modify_job_type(name, current_session):
|
||||||
def get_event(event_id, current_session):
|
def get_event(event_id, current_session):
|
||||||
"""Get event by id
|
"""Get event by id
|
||||||
|
|
||||||
Route: ``/events/<event_id>`` | Method: ``GET``
|
Route: ``/schedule/events/<event_id>`` | Method: ``GET``
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
event_id: ID identifying the event
|
event_id: ID identifying the event
|
||||||
|
@ -183,7 +185,7 @@ def get_events(current_session, year=datetime.now().year, month=datetime.now().m
|
||||||
"""Get Event objects for specified date (or month or year),
|
"""Get Event objects for specified date (or month or year),
|
||||||
if nothing set then events for current month are returned
|
if nothing set then events for current month are returned
|
||||||
|
|
||||||
Route: ``/events[/<year>/<month>[/<int:day>]]`` | Method: ``GET``
|
Route: ``/schedule/events[/<year>/<month>[/<int:day>]]`` | Method: ``GET``
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
year (int, optional): year to query, defaults to current year
|
year (int, optional): year to query, defaults to current year
|
||||||
|
@ -195,15 +197,15 @@ def get_events(current_session, year=datetime.now().year, month=datetime.now().m
|
||||||
JSON encoded list containing events found or HTTP-error
|
JSON encoded list containing events found or HTTP-error
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
begin = datetime(year=year, month=month, day=1)
|
begin = datetime(year=year, month=month, day=1, tzinfo=timezone.utc)
|
||||||
if day:
|
if day:
|
||||||
begin += timedelta(days=day - 1)
|
begin += timedelta(days=day - 1)
|
||||||
end = begin + timedelta(days=1)
|
end = begin + timedelta(days=1)
|
||||||
else:
|
else:
|
||||||
if month == 12:
|
if month == 12:
|
||||||
end = datetime(year=year + 1, month=1, day=1)
|
end = datetime(year=year + 1, month=1, day=1, tzinfo=timezone.utc)
|
||||||
else:
|
else:
|
||||||
end = datetime(year=year, month=month + 1, day=1)
|
end = datetime(year=year, month=month + 1, day=1, tzinfo=timezone.utc)
|
||||||
|
|
||||||
events = event_controller.get_events(begin, end)
|
events = event_controller.get_events(begin, end)
|
||||||
return jsonify(events)
|
return jsonify(events)
|
||||||
|
@ -226,7 +228,7 @@ def _event_slot_from_data(data):
|
||||||
def create_event(current_session):
|
def create_event(current_session):
|
||||||
"""Create an new event
|
"""Create an new event
|
||||||
|
|
||||||
Route: ``/events`` | Method: ``POST``
|
Route: ``/schedule/events`` | Method: ``POST``
|
||||||
|
|
||||||
POST-data: See interfaces for Event, can already contain slots
|
POST-data: See interfaces for Event, can already contain slots
|
||||||
|
|
||||||
|
@ -258,7 +260,7 @@ def create_event(current_session):
|
||||||
def modify_event(event_id, current_session):
|
def modify_event(event_id, current_session):
|
||||||
"""Modify an event
|
"""Modify an event
|
||||||
|
|
||||||
Route: ``/events/<event_id>`` | Method: ``PUT``
|
Route: ``/schedule/events/<event_id>`` | Method: ``PUT``
|
||||||
|
|
||||||
POST-data: See interfaces for Event, can already contain slots
|
POST-data: See interfaces for Event, can already contain slots
|
||||||
|
|
||||||
|
@ -287,7 +289,7 @@ def modify_event(event_id, current_session):
|
||||||
def delete_event(event_id, current_session):
|
def delete_event(event_id, current_session):
|
||||||
"""Delete an event
|
"""Delete an event
|
||||||
|
|
||||||
Route: ``/events/<event_id>`` | Method: ``DELETE``
|
Route: ``/schedule/events/<event_id>`` | Method: ``DELETE``
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
event_id: Identifier of the event
|
event_id: Identifier of the event
|
||||||
|
@ -305,7 +307,7 @@ def delete_event(event_id, current_session):
|
||||||
def add_event_slot(event_id, current_session):
|
def add_event_slot(event_id, current_session):
|
||||||
"""Add an new EventSlot to an Event
|
"""Add an new EventSlot to an Event
|
||||||
|
|
||||||
Route: ``/events/<event_id>/slots`` | Method: ``POST``
|
Route: ``/schedule/events/<event_id>/slots`` | Method: ``POST``
|
||||||
|
|
||||||
POST-data: See TS interface for EventSlot
|
POST-data: See TS interface for EventSlot
|
||||||
|
|
||||||
|
@ -330,7 +332,7 @@ def add_event_slot(event_id, current_session):
|
||||||
def update_event_slot(event_id, slot_id, current_session):
|
def update_event_slot(event_id, slot_id, current_session):
|
||||||
"""Update an EventSlot
|
"""Update an EventSlot
|
||||||
|
|
||||||
Route: ``/events/<event_id>/slots/<slot_id>`` | Method: ``PUT``
|
Route: ``/schedule/events/<event_id>/slots/<slot_id>`` | Method: ``PUT``
|
||||||
|
|
||||||
POST-data: See TS interface for EventSlot
|
POST-data: See TS interface for EventSlot
|
||||||
|
|
||||||
|
@ -360,7 +362,7 @@ def update_event_slot(event_id, slot_id, current_session):
|
||||||
def delete_event_slot(event_id, slot_id, current_session):
|
def delete_event_slot(event_id, slot_id, current_session):
|
||||||
"""Delete an EventSlot
|
"""Delete an EventSlot
|
||||||
|
|
||||||
Route: ``/events/<event_id>/slots/<slot_id>`` | Method: ``DELETE``
|
Route: ``/schedule/events/<event_id>/slots/<slot_id>`` | Method: ``DELETE``
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
event_id: Identifier of the event
|
event_id: Identifier of the event
|
||||||
|
@ -375,5 +377,106 @@ def delete_event_slot(event_id, slot_id, current_session):
|
||||||
return jsonify(event)
|
return jsonify(event)
|
||||||
|
|
||||||
|
|
||||||
# TODO: JobSlot, Job!
|
@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"])
|
||||||
|
@login_required(permission=permissions.DELETE)
|
||||||
|
def delete_job_slot(event_id, slot_id, job_type, current_session):
|
||||||
|
"""Delete a JobSlot
|
||||||
|
|
||||||
|
Route: ``/schedule/events/<event_id>/slots/<slot_id>/jobs/<job_type>`` | Method: ``DELETE``
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event_id: Identifier of the event
|
||||||
|
slot_id: Identifier of the EventSlot
|
||||||
|
job_type: Identifier of the JobSlot
|
||||||
|
current_session: Session sent with Authorization Header
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON encoded Event object 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)
|
||||||
|
|
||||||
|
|
||||||
|
@schedule_bp.route("/events/<int:event_id>/slots/<int:slot_id>/jobs/<int:job_type>", 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
|
||||||
|
|
||||||
|
Route: ``/schedule/events/<event_id>/slots/<slot_id>/jobs/<job_type>`` | Method: ``PUT``
|
||||||
|
|
||||||
|
POST-data: See TS interface for EventSlot 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
|
||||||
|
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)
|
||||||
|
|
||||||
|
data = request.get_json()
|
||||||
|
if not data:
|
||||||
|
raise BadRequest
|
||||||
|
|
||||||
|
if ("user" not in data or len(data) > 1) and not current_session._user.has_permission(permissions.EDIT):
|
||||||
|
raise Forbidden
|
||||||
|
|
||||||
|
if "user" in data:
|
||||||
|
try:
|
||||||
|
user = userController.get_user(data["user"]["userid"])
|
||||||
|
value = data["user"]["value"]
|
||||||
|
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)):
|
||||||
|
raise Forbidden
|
||||||
|
event_controller.assign_job(slot, user, value)
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
raise BadRequest
|
||||||
|
|
||||||
|
if "required_jobs" in data:
|
||||||
|
slot.required_jobs = data["required_jobs"]
|
||||||
|
if "type" in data:
|
||||||
|
slot.type = event_controller.get_job_type(data["type"])
|
||||||
|
event_controller.update()
|
||||||
|
|
||||||
|
return jsonify(event)
|
||||||
|
|
||||||
|
|
||||||
# TODO: JobTransfer
|
# TODO: JobTransfer
|
||||||
|
|
|
@ -3,7 +3,7 @@ from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
from flaschengeist.database import db
|
from flaschengeist.database import db
|
||||||
from flaschengeist.plugins.schedule.models import EventType, Event, EventSlot, JobSlot, JobType
|
from flaschengeist.plugins.schedule.models import EventType, Event, EventSlot, JobSlot, JobType, Job
|
||||||
|
|
||||||
|
|
||||||
def update():
|
def update():
|
||||||
|
@ -53,8 +53,8 @@ def get_job_types():
|
||||||
return JobType.query.all()
|
return JobType.query.all()
|
||||||
|
|
||||||
|
|
||||||
def get_job_type(name):
|
def get_job_type(type_id):
|
||||||
job_type = JobType.query.filter(JobType.name == name).one_or_none()
|
job_type = JobType.query.get(type_id)
|
||||||
if not job_type:
|
if not job_type:
|
||||||
raise NotFound
|
raise NotFound
|
||||||
return job_type
|
return job_type
|
||||||
|
@ -153,3 +153,34 @@ def remove_event_slot(event, slot_id):
|
||||||
else:
|
else:
|
||||||
raise NotFound
|
raise NotFound
|
||||||
db.session.commit()
|
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)
|
||||||
|
try:
|
||||||
|
db.session.add(job_slot)
|
||||||
|
db.session.commit()
|
||||||
|
except IntegrityError:
|
||||||
|
raise BadRequest("JobSlot with that type already exists on this EventSlot")
|
||||||
|
|
||||||
|
|
||||||
|
def delete_job_slot(job_slot):
|
||||||
|
db.session.delete(job_slot)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def assign_job(job_slot, user, value):
|
||||||
|
job = Job.query.get((job_slot.id_, user._id))
|
||||||
|
if job:
|
||||||
|
job.value = value
|
||||||
|
else:
|
||||||
|
job = Job(user_=user, value=value, slot_=job_slot)
|
||||||
|
db.session.add(job)
|
||||||
|
db.session.commit()
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlalchemy import UniqueConstraint
|
||||||
|
|
||||||
from flaschengeist.models import ModelSerializeMixin, UtcDateTime
|
from flaschengeist.models import ModelSerializeMixin, UtcDateTime
|
||||||
from flaschengeist.models.user import User
|
from flaschengeist.models.user import User
|
||||||
from flaschengeist.database import db
|
from flaschengeist.database import db
|
||||||
|
@ -18,7 +20,7 @@ class EventType(db.Model, ModelSerializeMixin):
|
||||||
|
|
||||||
class JobType(db.Model, ModelSerializeMixin):
|
class JobType(db.Model, ModelSerializeMixin):
|
||||||
__tablename__ = "job_type"
|
__tablename__ = "job_type"
|
||||||
id_ = db.Column("id", db.Integer, primary_key=True)
|
id: int = db.Column("id", db.Integer, primary_key=True)
|
||||||
name: str = db.Column(db.String(30), nullable=False, unique=True)
|
name: str = db.Column(db.String(30), nullable=False, unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,33 +31,34 @@ class JobType(db.Model, ModelSerializeMixin):
|
||||||
|
|
||||||
class Job(db.Model, ModelSerializeMixin):
|
class Job(db.Model, ModelSerializeMixin):
|
||||||
__tablename__ = "job"
|
__tablename__ = "job"
|
||||||
userid: str = None
|
userid: str = ""
|
||||||
value: float = db.Column(db.Numeric(precision=3, scale=2), nullable=False)
|
value: float = db.Column(db.Numeric(precision=3, scale=2, asdecimal=False), nullable=False)
|
||||||
|
|
||||||
id_ = db.Column("id", db.Integer, primary_key=True)
|
_slot_id = db.Column("slot_id", db.Integer, db.ForeignKey("job_slot.id"), nullable=False, primary_key=True)
|
||||||
_user_id = db.Column("user_id", db.Integer, db.ForeignKey("user.id"), nullable=False)
|
_user_id = db.Column("user_id", db.Integer, db.ForeignKey("user.id"), nullable=False, primary_key=True)
|
||||||
_slot_id = db.Column("slot_id", db.Integer, db.ForeignKey("job_slot.id"), nullable=False)
|
|
||||||
|
|
||||||
user_: User = db.relationship("User")
|
user_: User = db.relationship("User")
|
||||||
slot_ = db.relationship("JobSlot")
|
slot_ = db.relationship("JobSlot")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def userid(self):
|
def userid(self):
|
||||||
return self._user.userid
|
return self.user_.userid
|
||||||
|
|
||||||
|
|
||||||
class JobSlot(db.Model, ModelSerializeMixin):
|
class JobSlot(db.Model, ModelSerializeMixin):
|
||||||
__tablename__ = "job_slot"
|
__tablename__ = "job_slot"
|
||||||
_type_id = db.Column("type_id", db.Integer, db.ForeignKey("job_type.id"))
|
_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"))
|
_event_slot_id = db.Column("event_slot_id", db.Integer, db.ForeignKey("event_slot.id"), nullable=False)
|
||||||
|
|
||||||
id: int = db.Column(db.Integer, primary_key=True)
|
id_: int = db.Column("id", db.Integer, primary_key=True)
|
||||||
type: JobType = db.relationship("JobType")
|
type: JobType = db.relationship("JobType")
|
||||||
users: [Job] = db.relationship("Job", back_populates="slot_")
|
users: [Job] = db.relationship("Job", back_populates="slot_")
|
||||||
required_jobs: float = db.Column(db.Numeric(precision=4, scale=2))
|
required_jobs: float = db.Column(db.Numeric(precision=4, scale=2, asdecimal=False))
|
||||||
|
|
||||||
event_slot_ = db.relationship("EventSlot", back_populates="jobs")
|
event_slot_ = db.relationship("EventSlot", back_populates="jobs")
|
||||||
|
|
||||||
|
__table_args__ = (UniqueConstraint('type_id', 'event_slot_id', name='_type_event_slot_uc'),)
|
||||||
|
|
||||||
|
|
||||||
##########
|
##########
|
||||||
# Events #
|
# Events #
|
||||||
|
|
Loading…
Reference in New Issue