First version of schedule plugin

This commit is contained in:
Ferdinand Thiessen 2020-09-05 22:26:00 +02:00
parent 7f6ff3f001
commit bd657d11b6
5 changed files with 466 additions and 277 deletions

View File

@ -1,289 +1,364 @@
from dateutil import parser
from flask import Blueprint, request, jsonify
from werkzeug.exceptions import BadRequest, NotFound
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 import logger
from datetime import datetime
from flaschengeist.system.models.event import Event, EventKind
import .roles
schedule_bp = Blueprint("schedule", __name__)
schedule = Blueprint("schedule", __name__)
@schedule.route("/schedule/jobs", methods=['POST'])
@login_required(roles=['schedule_read'])
def _getUsers(**kwrags):
logger.debug("/schedule/jobs")
def register():
return schedule_bp
@schedule_bp.route("/schedule/events", methods=['GET'])
@schedule_bp.route("/schedule/events/<int:year>", methods=['GET'])
@schedule_bp.route("/schedule/events/<int:year>/<int:month>", methods=['GET'])
@schedule_bp.route("/schedule/events/<int:year>/<int:month>/<int:day>", methods=['GET'])
@login_required() # roles=['schedule_read'])
def _get_events(year=datetime.now().year, month=None, day=None, **kwrags):
"""Get Event objects for specified date (or month or year),
if nothing set then events for current year are returned
Args:
year (int, optional): year to query, defaults to current year
month (int, optional): month to query (if set)
day (int, optional): day to query events for (if set)
**kwrags: contains at least access_token (see flaschengeist.decorator)
Returns:
JSON list containing events found
Raises:
BadRequest: If date is invalid
"""
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
}
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:
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
end = datetime(year=year, month=month + 1, day=1)
else:
end = datetime(year=year + 1, month=1, day=1)
events = Event.query.filter((begin <= Event.begin), (Event.begin < end))
return jsonify(events)
except ValueError:
raise BadRequest("Invalid date given")
@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
@schedule_bp.route("/schedule/eventKinds", methods=['POST'])
@login_required()
def __new_event_kind(**kwargs):
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
if "name" not in data:
raise BadRequest
event = eventController.create_event_kind(data["name"])
return jsonify({"ok": "ok", "id": event.id})
@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
@schedule_bp.route("/schedule/events", methods=['POST'])
@login_required()
def __new_event(**kwargs):
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
event = eventController.create_event(begin=parser.isoparse(data["begin"]),
end=parser.isoparse(data["end"]),
description=data["description"],
kind=EventKind.query.get(data["kind"]))
return jsonify({"ok": "ok", "id": event.id})
@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
@schedule_bp.route("/schedule/events/<int:id>", methods=["DELETE"])
@login_required()
def __delete_event(id, **kwargs):
if Event.query.filter(Event.id == id).delete() != 1:
raise NotFound
db.session.commit()
return jsonify({'ok': 'ok'})
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
def __edit_event():
...
@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
# 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):
# creditL = getCreditLogger()
# @schedule.route("/user/main")
# @login_required(groups=[USER])
# def _main(**kwargs):
# debug.info("/user/main")
# try:
# if 'accToken' in kwargs:
@ -299,9 +374,9 @@ def _deleteJobRequest(**kwargs):
# return jsonify("error", "something went wrong"), 500
#@schedule.route("/user/addAmount", methods=['POST'])
#@login_required(groups=[USER])
#def _addAmount(**kwargs):
# @schedule.route("/user/addAmount", methods=['POST'])
# @login_required(groups=[USER])
# def _addAmount(**kwargs):
# debug.info("/user/addAmount")
# try:
# if 'accToken' in kwargs:
@ -324,9 +399,9 @@ def _deleteJobRequest(**kwargs):
# return jsonify({"error": "something went wrong"}), 500
#@schedule.route("/user/saveConfig", methods=['POST'])
#@login_required(groups=[USER])
#def _saveConfig(**kwargs):
# @schedule.route("/user/saveConfig", methods=['POST'])
# @login_required(groups=[USER])
# def _saveConfig(**kwargs):
# debug.info("/user/saveConfig")
# try:
# if 'accToken' in kwargs:
@ -345,9 +420,9 @@ def _deleteJobRequest(**kwargs):
# 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):
# @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.

View File

@ -0,0 +1,32 @@
from werkzeug.exceptions import BadRequest
from flaschengeist.system.models.event import EventKind, Event
from sqlalchemy.exc import IntegrityError
from flaschengeist import logger
from flaschengeist.system.database import db
def create_event(begin, kind, end=None, description=None):
try:
event = Event(begin=begin,
end=end,
description=description,
kind=kind)
db.session.add(event)
db.session.commit()
return event
except IntegrityError:
logger.debug("Database error when creating new event", exc_info=True)
raise BadRequest
def create_event_kind(name):
try:
event = EventKind(name=name)
db.session.add(event)
db.session.commit()
return event
except IntegrityError:
logger.debug("IntegrityError: Looks like there is a name collision", exc_info=True)
raise BadRequest("Name already exists")

View File

@ -0,0 +1,82 @@
from ..database import db
class Event(db.Model):
"""Model for an Event"""
__tablename__ = 'event'
id = db.Column(db.Integer, primary_key=True)
begin = db.Column(db.DateTime, nullable=False)
end = db.Column(db.DateTime)
description = db.Column(db.String(240))
kind_id = db.Column(db.Integer, db.ForeignKey('event_kind.id', ondelete="CASCADE"), nullable=False)
kind = db.relationship("EventKind")
slots = db.relationship("EventSlot", back_populates="event", cascade="all, delete")
#notices = db.relationship("EventNotice", back_populates="event")
def serialize(self):
return {
"id": self.id,
"begin": self.begin,
"end": self.end,
"description": self.description,
"kind": self.kind
}
class EventKind(db.Model):
"""Model for an EventKind"""
__tablename__ = "event_kind"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), nullable=False, unique=True)
def serialize(self):
return {
"id": self.id,
"name": self.name
}
class EventSlot(db.Model):
"""Model for an EventSlot"""
__tablename__ = "event_slot"
id = db.Column(db.Integer, primary_key=True)
start = db.Column(db.DateTime)
end = db.Column(db.DateTime)
event_id = db.Column(db.Integer, db.ForeignKey('event.id'), nullable=False)
event = db.relationship("Event", back_populates="slots")
slots = db.relationship("JobSlot", back_populates="event_slot")
def serialize(self):
return {
"id": self.id,
"start": self.start,
"end": self.end,
"event": self.event_id,
}
class JobSlot(db.Model):
__tablename__ = "job_slot"
id = db.Column(db.Integer, primary_key=True)
needed_persons = db.Column(db.Numeric(precision=4, scale=2))
event_slot_id = db.Column(db.Integer, db.ForeignKey('event_slot.id'))
event_slot = db.relationship("EventSlot", back_populates="slots")
kind_id = db.Column(db.Integer, db.ForeignKey('job_kind.id'))
kind = db.relationship("JobKind")
jobs = db.relationship("Job", back_populates="slot")
class Job(db.Model):
__tablename__ = "job"
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.Numeric(precision=3, scale=2))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship("User")
slot_id = db.Column(db.Integer, db.ForeignKey('job_slot.id'))
slot = db.relationship("JobSlot")
class JobKind(db.Model):
__tablename__ = "job_kind"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30))