setting up a local mysql database and running the API locally, which requires slight adaptations. Implementing input validation for POST requests of shipcalls and adapting enumerators, as well as data models.
This commit is contained in:
parent
fcb889d2bc
commit
b32b466f74
@ -3,7 +3,8 @@ from webargs.flaskparser import parser
|
|||||||
from marshmallow import Schema, fields, ValidationError
|
from marshmallow import Schema, fields, ValidationError
|
||||||
from ..schemas import model
|
from ..schemas import model
|
||||||
from .. import impl
|
from .. import impl
|
||||||
from ..services.auth_guard import auth_guard
|
from ..services.auth_guard import auth_guard, check_jwt
|
||||||
|
from BreCal.validators.input_validation import check_if_user_is_bsmd_type, check_if_user_data_has_valid_ship_id, check_if_user_data_has_valid_berth_id, check_if_user_data_has_valid_participant_id
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
@ -40,6 +41,26 @@ def PostShipcalls():
|
|||||||
try:
|
try:
|
||||||
content = request.get_json(force=True)
|
content = request.get_json(force=True)
|
||||||
loadedModel = model.ShipcallSchema().load(data=content, many=False, partial=True)
|
loadedModel = model.ShipcallSchema().load(data=content, many=False, partial=True)
|
||||||
|
|
||||||
|
# read the user data from the JWT token (set when login is performed)
|
||||||
|
user_data = check_jwt()
|
||||||
|
|
||||||
|
# check, whether the user belongs to a participant, which is of type ParticipantType.BSMD
|
||||||
|
# as ParticipantType is an IntFlag, a user belonging to multiple groups is properly evaluated.
|
||||||
|
is_bsmd = check_if_user_is_bsmd_type(user_data)
|
||||||
|
if not is_bsmd:
|
||||||
|
raise ValidationError(f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}")
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logging.log(20, loadedModel)
|
||||||
|
logging.log(20, "metz development")
|
||||||
|
"""
|
||||||
|
# loadedModel ...
|
||||||
|
valid_ship_id = check_if_user_data_has_valid_ship_id(ship_id)
|
||||||
|
valid_berth_id = check_if_user_data_has_valid_berth_id(berth_id)
|
||||||
|
valid_participant_id = check_if_user_data_has_valid_participant_id(participant_id)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
except ValidationError as ex:
|
except ValidationError as ex:
|
||||||
logging.error(ex)
|
logging.error(ex)
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from enum import IntEnum, Enum, IntFlag
|
|||||||
|
|
||||||
class ParticipantType(IntFlag):
|
class ParticipantType(IntFlag):
|
||||||
"""determines the type of a participant"""
|
"""determines the type of a participant"""
|
||||||
NONE = 0
|
undefined = 0
|
||||||
BSMD = 1
|
BSMD = 1
|
||||||
TERMINAL = 2
|
TERMINAL = 2
|
||||||
PILOT = 4
|
PILOT = 4
|
||||||
@ -13,10 +13,15 @@ class ParticipantType(IntFlag):
|
|||||||
|
|
||||||
class ShipcallType(IntEnum):
|
class ShipcallType(IntEnum):
|
||||||
"""determines the type of a shipcall, as this changes the applicable validation rules"""
|
"""determines the type of a shipcall, as this changes the applicable validation rules"""
|
||||||
|
undefined = 0
|
||||||
INCOMING = 1
|
INCOMING = 1
|
||||||
OUTGOING = 2
|
OUTGOING = 2
|
||||||
SHIFTING = 3
|
SHIFTING = 3
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _missing_(cls, value):
|
||||||
|
return cls.undefined
|
||||||
|
|
||||||
class ParticipantwiseTimeDelta():
|
class ParticipantwiseTimeDelta():
|
||||||
"""stores the time delta for every participant, which triggers the validation rules in the rule set '0001'"""
|
"""stores the time delta for every participant, which triggers the validation rules in the rule set '0001'"""
|
||||||
AGENCY = 1200.0 # 20 h * 60 min/h = 1200 min
|
AGENCY = 1200.0 # 20 h * 60 min/h = 1200 min
|
||||||
@ -42,10 +47,23 @@ class PierSide(IntEnum):
|
|||||||
"""These enumerators determine the pier side of a shipcall."""
|
"""These enumerators determine the pier side of a shipcall."""
|
||||||
PORTSIDE = 0 # Port/Backbord
|
PORTSIDE = 0 # Port/Backbord
|
||||||
STARBOARD_SIDE = 1 # Starboard / Steuerbord
|
STARBOARD_SIDE = 1 # Starboard / Steuerbord
|
||||||
|
|
||||||
class NotificationType(IntFlag):
|
class NotificationType(IntFlag):
|
||||||
"""determines the method by which a notification is distributed to users. Flagging allows selecting multiple notification types."""
|
"""determines the method by which a notification is distributed to users. Flagging allows selecting multiple notification types."""
|
||||||
UNDEFINED = 0
|
UNDEFINED = 0
|
||||||
EMAIL = 1
|
EMAIL = 1
|
||||||
POPUP = 2
|
POPUP = 2
|
||||||
MESSENGER = 4
|
MESSENGER = 4
|
||||||
|
|
||||||
|
class ParticipantFlag(IntFlag):
|
||||||
|
"""
|
||||||
|
| 1 | If this flag is set on a shipcall record with participant type Agency (8),
|
||||||
|
all participants of type BSMD (1) may edit the record.
|
||||||
|
"""
|
||||||
|
undefined = 0
|
||||||
|
BSMD = 1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _missing_(cls, value):
|
||||||
|
return cls.undefined
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import numpy as np
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import datetime
|
import datetime
|
||||||
import typing
|
import typing
|
||||||
from BreCal.schemas.model import Shipcall, Ship, Participant, Berth, User, Times
|
from BreCal.schemas.model import Shipcall, Ship, Participant, Berth, User, Times, ShipcallParticipantMap
|
||||||
from BreCal.database.enums import ParticipantType
|
from BreCal.database.enums import ParticipantType
|
||||||
|
|
||||||
def pandas_series_to_data_model():
|
def pandas_series_to_data_model():
|
||||||
@ -50,7 +50,8 @@ class SQLHandler():
|
|||||||
'ship'->BreCal.schemas.model.Ship object
|
'ship'->BreCal.schemas.model.Ship object
|
||||||
"""
|
"""
|
||||||
self.str_to_model_dict = {
|
self.str_to_model_dict = {
|
||||||
"shipcall":Shipcall, "ship":Ship, "participant":Participant, "berth":Berth, "user":User, "times":Times
|
"shipcall":Shipcall, "ship":Ship, "participant":Participant, "berth":Berth, "user":User, "times":Times,
|
||||||
|
"shipcall_participant_map":ShipcallParticipantMap
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -70,12 +71,16 @@ class SQLHandler():
|
|||||||
data = [{k:v for k,v in zip(column_names, dat)} for dat in data]
|
data = [{k:v for k,v in zip(column_names, dat)} for dat in data]
|
||||||
|
|
||||||
# 4.) build a dataframe from the respective data models (which ensures the correct data type)
|
# 4.) build a dataframe from the respective data models (which ensures the correct data type)
|
||||||
|
df = self.build_df_from_data_and_name(data, table_name)
|
||||||
|
return df
|
||||||
|
|
||||||
|
def build_df_from_data_and_name(self, data, table_name):
|
||||||
data_model = self.str_to_model_dict.get(table_name)
|
data_model = self.str_to_model_dict.get(table_name)
|
||||||
if data_model is not None:
|
if data_model is not None:
|
||||||
df = pd.DataFrame([data_model(**dat) for dat in data])
|
df = pd.DataFrame([data_model(**dat) for dat in data], columns=list(data_model.__annotations__.keys()))
|
||||||
else:
|
else:
|
||||||
df = pd.DataFrame([dat for dat in data])
|
df = pd.DataFrame([dat for dat in data])
|
||||||
return df
|
return df
|
||||||
|
|
||||||
def mysql_to_df(self, query, table_name):
|
def mysql_to_df(self, query, table_name):
|
||||||
"""provide an arbitrary sql query that should be read from a mysql server {sql_connection}. returns a pandas DataFrame with the obtained data"""
|
"""provide an arbitrary sql query that should be read from a mysql server {sql_connection}. returns a pandas DataFrame with the obtained data"""
|
||||||
@ -94,11 +99,7 @@ class SQLHandler():
|
|||||||
|
|
||||||
# 4.) build a dataframe from the respective data models (which ensures the correct data type)
|
# 4.) build a dataframe from the respective data models (which ensures the correct data type)
|
||||||
data_model = self.str_to_model_dict.get(table_name)
|
data_model = self.str_to_model_dict.get(table_name)
|
||||||
if data_model is not None:
|
df = self.build_df_from_data_and_name(data, table_name)
|
||||||
df = pd.DataFrame([data_model(**dat) for dat in data])
|
|
||||||
else:
|
|
||||||
df = pd.DataFrame([dat for dat in data])
|
|
||||||
|
|
||||||
if 'id' in df.columns:
|
if 'id' in df.columns:
|
||||||
df = df.set_index('id', inplace=False) # avoid inplace updates, so the raw sql remains unchanged
|
df = df.set_index('id', inplace=False) # avoid inplace updates, so the raw sql remains unchanged
|
||||||
return df
|
return df
|
||||||
|
|||||||
@ -60,8 +60,13 @@ def PostShipcalls(schemaModel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
:param schemaModel: The deserialized dict of the request
|
:param schemaModel: The deserialized dict of the request
|
||||||
|
e.g.,
|
||||||
|
{
|
||||||
|
'ship_id': 1, 'type': 1, 'eta': datetime.datetime(2023, 7, 23, 7, 18, 19),
|
||||||
|
'voyage': '43B', 'tug_required': False, 'pilot_required': True, 'flags': 0,
|
||||||
|
'pier_side': False, 'bunkering': True, 'recommended_tugs': 2, 'type_value': 1, 'evaluation_value': 0}
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: Validate the upload data
|
# TODO: Validate the upload data
|
||||||
|
|
||||||
# This creates a *new* entry
|
# This creates a *new* entry
|
||||||
@ -133,7 +138,6 @@ def PostShipcalls(schemaModel):
|
|||||||
# if not full_id_existances:
|
# if not full_id_existances:
|
||||||
# pooledConnection.close()
|
# pooledConnection.close()
|
||||||
# return json.dumps({"message" : "call failed. missing mandatory keywords."}), 500, {'Content-Type': 'application/json; charset=utf-8'}
|
# return json.dumps({"message" : "call failed. missing mandatory keywords."}), 500, {'Content-Type': 'application/json; charset=utf-8'}
|
||||||
|
|
||||||
commands.execute(query, schemaModel)
|
commands.execute(query, schemaModel)
|
||||||
new_id = commands.execute_scalar("select last_insert_id()")
|
new_id = commands.execute_scalar("select last_insert_id()")
|
||||||
|
|
||||||
|
|||||||
@ -7,11 +7,11 @@ import sys
|
|||||||
|
|
||||||
config_path = None
|
config_path = None
|
||||||
|
|
||||||
def initPool(instancePath):
|
def initPool(instancePath, connection_filename="connection_data_devel.json"):
|
||||||
try:
|
try:
|
||||||
global config_path
|
global config_path
|
||||||
if(config_path == None):
|
if(config_path == None):
|
||||||
config_path = os.path.join(instancePath,'../../../secure/connection_data_devel.json');
|
config_path = os.path.join(instancePath,f'../../../../secure/{connection_filename}') #connection_data_devel.json');
|
||||||
|
|
||||||
print (config_path)
|
print (config_path)
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,9 @@ from typing import List
|
|||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
from BreCal.validators.time_logic import validate_time_exceeds_threshold
|
from BreCal.validators.time_logic import validate_time_exceeds_threshold
|
||||||
|
from BreCal.database.enums import ParticipantType, ParticipantFlag
|
||||||
|
|
||||||
|
# from BreCal. ... import check_if_user_is_bsmd_type
|
||||||
|
|
||||||
def obj_dict(obj):
|
def obj_dict(obj):
|
||||||
if isinstance(obj, datetime.datetime):
|
if isinstance(obj, datetime.datetime):
|
||||||
@ -54,6 +57,7 @@ class NotificationType(IntEnum):
|
|||||||
undefined = 0
|
undefined = 0
|
||||||
email = 1
|
email = 1
|
||||||
push = 2
|
push = 2
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _missing_(cls, value):
|
def _missing_(cls, value):
|
||||||
return cls.undefined
|
return cls.undefined
|
||||||
@ -143,12 +147,34 @@ class Participant(Schema):
|
|||||||
street: str
|
street: str
|
||||||
postal_code: str
|
postal_code: str
|
||||||
city: str
|
city: str
|
||||||
type: int
|
type: int # fields.Enum(ParticipantType ...)
|
||||||
flags: int
|
flags: int
|
||||||
created: datetime
|
created: datetime
|
||||||
modified: datetime
|
modified: datetime
|
||||||
deleted: bool
|
deleted: bool
|
||||||
|
|
||||||
|
@validates("type")
|
||||||
|
def validate_type(self, value):
|
||||||
|
# e.g., when an IntFlag has the values 1,2,4; the maximum valid value is 7
|
||||||
|
max_int = sum([int(val) for val in list(ParticipantType._value2member_map_.values())])
|
||||||
|
min_int = 0
|
||||||
|
|
||||||
|
valid_type = 0 <= value < max_int
|
||||||
|
if not valid_type:
|
||||||
|
raise ValidationError(f"the provided integer is not supported for default behaviour of the ParticipantType IntFlag. Your choice: {value}. Supported values are: 0 <= value {max_int}")
|
||||||
|
|
||||||
|
|
||||||
|
@validates("flags")
|
||||||
|
def validate_type(self, value):
|
||||||
|
# e.g., when an IntFlag has the values 1,2,4; the maximum valid value is 7
|
||||||
|
max_int = sum([int(val) for val in list(ParticipantFlag._value2member_map_.values())])
|
||||||
|
min_int = 0
|
||||||
|
|
||||||
|
valid_type = 0 <= value < max_int
|
||||||
|
if not valid_type:
|
||||||
|
raise ValidationError(f"the provided integer is not supported for default behaviour of the ParticipantFlag IntFlag. Your choice: {value}. Supported values are: 0 <= value {max_int}")
|
||||||
|
|
||||||
|
|
||||||
class ParticipantList(Participant):
|
class ParticipantList(Participant):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -163,7 +189,8 @@ class ShipcallSchema(Schema):
|
|||||||
|
|
||||||
id = fields.Integer()
|
id = fields.Integer()
|
||||||
ship_id = fields.Integer()
|
ship_id = fields.Integer()
|
||||||
type = fields.Integer()
|
#type = fields.Enum(ShipcallType, default=ShipcallType.undefined) # type = fields.Integer() # make enum: shipcall type. add validator
|
||||||
|
type = fields.Integer() # make enum: shipcall type. add validator # type = fields.Enum(ShipcallType, default=ShipcallType.undefined) # type = fields.Integer() # make enum: shipcall type. add validator
|
||||||
eta = fields.DateTime(metadata={'required':False}, allow_none=True)
|
eta = fields.DateTime(metadata={'required':False}, allow_none=True)
|
||||||
voyage = fields.String(allow_none=True, metadata={'Required':False}, validate=[validate.Length(max=16)]) # Solving: RemovedInMarshmallow4Warning: Passing field metadata as keyword arguments is deprecated. Use the explicit `metadata=...` argument instead. Additional metadata: {'Required': False}
|
voyage = fields.String(allow_none=True, metadata={'Required':False}, validate=[validate.Length(max=16)]) # Solving: RemovedInMarshmallow4Warning: Passing field metadata as keyword arguments is deprecated. Use the explicit `metadata=...` argument instead. Additional metadata: {'Required': False}
|
||||||
etd = fields.DateTime(metadata={'required':False}, allow_none=True)
|
etd = fields.DateTime(metadata={'required':False}, allow_none=True)
|
||||||
@ -196,15 +223,23 @@ class ShipcallSchema(Schema):
|
|||||||
@post_load
|
@post_load
|
||||||
def make_shipcall(self, data, **kwargs):
|
def make_shipcall(self, data, **kwargs):
|
||||||
if 'type' in data:
|
if 'type' in data:
|
||||||
data['type_value'] = data['type'].value
|
data['type_value'] = int(data['type'])
|
||||||
else:
|
else:
|
||||||
data['type_value'] = ShipcallType.undefined
|
data['type_value'] = int(ShipcallType.undefined)
|
||||||
if 'evaluation' in data:
|
if 'evaluation' in data:
|
||||||
if data['evaluation']:
|
if data['evaluation']:
|
||||||
data['evaluation_value'] = data['evaluation'].value
|
data['evaluation_value'] = int(data['evaluation'])
|
||||||
else:
|
else:
|
||||||
data['evaluation_value'] = EvaluationType.undefined
|
data['evaluation_value'] = int(EvaluationType.undefined)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@validates("type")
|
||||||
|
def validate_type(self, value):
|
||||||
|
valid_shipcall_type = int(value) in [item.value for item in ShipcallType]
|
||||||
|
|
||||||
|
if not valid_shipcall_type:
|
||||||
|
raise ValidationError(f"the provided type is not a valid shipcall type.")
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Participant_Assignment:
|
class Participant_Assignment:
|
||||||
@ -321,7 +356,7 @@ class TimesSchema(Schema):
|
|||||||
berth_info = fields.String(metadata={'required':False}, allow_none=True, validate=[validate.Length(max=256)])
|
berth_info = fields.String(metadata={'required':False}, allow_none=True, validate=[validate.Length(max=256)])
|
||||||
pier_side = fields.Bool(metadata={'required':False}, allow_none = True)
|
pier_side = fields.Bool(metadata={'required':False}, allow_none = True)
|
||||||
shipcall_id = fields.Integer(metadata={'required':True})
|
shipcall_id = fields.Integer(metadata={'required':True})
|
||||||
participant_type = fields.Integer(metadata={'required':False}, allow_none=True)
|
participant_type = fields.Enum(ParticipantType, metadata={'required':False}, allow_none=True, default=ParticipantType.undefined) #fields.Integer(metadata={'required':False}, allow_none=True)
|
||||||
ata = fields.DateTime(metadata={'required':False}, allow_none=True)
|
ata = fields.DateTime(metadata={'required':False}, allow_none=True)
|
||||||
atd = fields.DateTime(metadata={'required':False}, allow_none=True)
|
atd = fields.DateTime(metadata={'required':False}, allow_none=True)
|
||||||
eta_interval_end = fields.DateTime(metadata={'required':False}, allow_none=True)
|
eta_interval_end = fields.DateTime(metadata={'required':False}, allow_none=True)
|
||||||
@ -463,3 +498,22 @@ class Shipcalls(Shipcall):
|
|||||||
|
|
||||||
class TimesList(Times):
|
class TimesList(Times):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ShipcallParticipantMap:
|
||||||
|
id: int
|
||||||
|
shipcall_id: int
|
||||||
|
participant_id: int
|
||||||
|
type : ShipcallType
|
||||||
|
created: datetime
|
||||||
|
modified: datetime
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"shipcall_id": self.shipcall_id,
|
||||||
|
"participant_id": self.participant_id,
|
||||||
|
"type": self.type.name,
|
||||||
|
"created": self.created.isoformat() if self.created else "",
|
||||||
|
"modified": self.modified.isoformat() if self.modified else "",
|
||||||
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ def check_jwt():
|
|||||||
if not token:
|
if not token:
|
||||||
raise Exception('Missing access token')
|
raise Exception('Missing access token')
|
||||||
jwt = token.split('Bearer ')[1]
|
jwt = token.split('Bearer ')[1]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return decode_jwt(jwt)
|
return decode_jwt(jwt)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@ -7,11 +7,26 @@ def create_api_key():
|
|||||||
return secrets.token_urlsafe(16)
|
return secrets.token_urlsafe(16)
|
||||||
|
|
||||||
def generate_jwt(payload, lifetime=None):
|
def generate_jwt(payload, lifetime=None):
|
||||||
|
"""
|
||||||
|
creates an encoded token, which is based on the 'SECRET_KEY' environment variable. The environment variable
|
||||||
|
is set when the .wsgi application is started or can theoretically be set on system-level.
|
||||||
|
|
||||||
|
args:
|
||||||
|
payload:
|
||||||
|
json-dictionary with key:value pairs.
|
||||||
|
|
||||||
|
lifetime:
|
||||||
|
When a 'lifetime' (integer) is provided, the payload will be extended by an expiration key 'exp', which is
|
||||||
|
valid for the next {lifetime} minutes.
|
||||||
|
|
||||||
|
returns: token, a JWT-encoded string
|
||||||
|
"""
|
||||||
if lifetime:
|
if lifetime:
|
||||||
payload['exp'] = (datetime.datetime.now() + datetime.timedelta(minutes=lifetime)).timestamp()
|
payload['exp'] = (datetime.datetime.now() + datetime.timedelta(minutes=lifetime)).timestamp()
|
||||||
return jwt.encode(payload, os.environ.get('SECRET_KEY'), algorithm="HS256")
|
return jwt.encode(payload, os.environ.get('SECRET_KEY'), algorithm="HS256")
|
||||||
|
|
||||||
def decode_jwt(token):
|
def decode_jwt(token):
|
||||||
|
"""this function reverts the {generate_jwt} function. An encoded JWT token is decoded into a JSON dictionary."""
|
||||||
return jwt.decode(token, os.environ.get('SECRET_KEY'), algorithms=["HS256"])
|
return jwt.decode(token, os.environ.get('SECRET_KEY'), algorithms=["HS256"])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -7,21 +7,21 @@ def get_shipcall_simple():
|
|||||||
# only used for the stub
|
# only used for the stub
|
||||||
base_time = datetime.datetime.now()
|
base_time = datetime.datetime.now()
|
||||||
|
|
||||||
shipcall_id = generate_uuid1_int()
|
shipcall_id = 124 # generate_uuid1_int()
|
||||||
ship_id = generate_uuid1_int()
|
ship_id = 5 # generate_uuid1_int()
|
||||||
|
|
||||||
eta = base_time+datetime.timedelta(hours=3, minutes=12)
|
eta = base_time+datetime.timedelta(hours=3, minutes=12)
|
||||||
role_type = 1
|
role_type = 1
|
||||||
voyage = "987654321"
|
voyage = "987654321"
|
||||||
etd = base_time+datetime.timedelta(hours=6, minutes=12) # should never be before eta
|
etd = base_time+datetime.timedelta(hours=6, minutes=12) # should never be before eta
|
||||||
|
|
||||||
arrival_berth_id = generate_uuid1_int()
|
arrival_berth_id = 140 #generate_uuid1_int()
|
||||||
departure_berth_id = generate_uuid1_int()
|
departure_berth_id = 140 #generate_uuid1_int()
|
||||||
|
|
||||||
tug_required = False
|
tug_required = False
|
||||||
pilot_required = False
|
pilot_required = False
|
||||||
|
|
||||||
flags = 0 # #TODO_shipcall_flags. What is meant here? What should be tested?
|
flags = 0 # #TODO_shipcall_flags. What is meant here? What should be tested?
|
||||||
pier_side = False # whether a ship will be fixated on the pier side. en: pier side, de: Anlegestelle. From 'BremenCalling_Datenmodell.xlsx': gedreht/ungedreht
|
pier_side = False # whether a ship will be fixated on the pier side. en: pier side, de: Anlegestelle. From 'BremenCalling_Datenmodell.xlsx': gedreht/ungedreht
|
||||||
bunkering = False # #TODO_bunkering_unclear
|
bunkering = False # #TODO_bunkering_unclear
|
||||||
replenishing_terminal = False # en: replenishing terminal, de: Nachfüll-Liegeplatz
|
replenishing_terminal = False # en: replenishing terminal, de: Nachfüll-Liegeplatz
|
||||||
@ -38,11 +38,13 @@ def get_shipcall_simple():
|
|||||||
anchored = False
|
anchored = False
|
||||||
moored_lock = False # de: 'Festmacherschleuse', en: 'moored lock'
|
moored_lock = False # de: 'Festmacherschleuse', en: 'moored lock'
|
||||||
canceled = False
|
canceled = False
|
||||||
|
|
||||||
|
time_ref_point = 0
|
||||||
|
|
||||||
evaluation = None
|
evaluation = None
|
||||||
evaluation_message = ""
|
evaluation_message = ""
|
||||||
evaluation_time = None
|
evaluation_time = datetime.datetime.now()
|
||||||
evaluation_notifications_sent = None
|
evaluation_notifications_sent = False
|
||||||
created = datetime.datetime.now()
|
created = datetime.datetime.now()
|
||||||
modified = created+datetime.timedelta(seconds=10)
|
modified = created+datetime.timedelta(seconds=10)
|
||||||
|
|
||||||
@ -76,6 +78,7 @@ def get_shipcall_simple():
|
|||||||
evaluation_message,
|
evaluation_message,
|
||||||
evaluation_time,
|
evaluation_time,
|
||||||
evaluation_notifications_sent,
|
evaluation_notifications_sent,
|
||||||
|
time_ref_point,
|
||||||
created,
|
created,
|
||||||
modified,
|
modified,
|
||||||
participants,
|
participants,
|
||||||
|
|||||||
@ -1,8 +1,91 @@
|
|||||||
|
|
||||||
####################################### InputValidation #######################################
|
####################################### InputValidation #######################################
|
||||||
|
|
||||||
|
import json
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from BreCal.schemas.model import Ship, Shipcall, Berth, User, Participant
|
from BreCal.schemas.model import Ship, Shipcall, Berth, User, Participant
|
||||||
|
from BreCal.impl.participant import GetParticipant
|
||||||
|
from BreCal.impl.ships import GetShips
|
||||||
|
from BreCal.impl.berths import GetBerths
|
||||||
|
|
||||||
|
from BreCal.database.enums import ParticipantType
|
||||||
|
|
||||||
|
def get_participant_id_dictionary():
|
||||||
|
# get all participants
|
||||||
|
response,status_code,header = GetParticipant(options={})
|
||||||
|
|
||||||
|
# build a dictionary of id:item pairs, so one can select the respective participant
|
||||||
|
participants = json.loads(response)
|
||||||
|
participants = {items.get("id"):items for items in participants}
|
||||||
|
return participants
|
||||||
|
|
||||||
|
def get_ship_id_dictionary():
|
||||||
|
# get all ships
|
||||||
|
response,status_code,header = GetShips(token=None)
|
||||||
|
|
||||||
|
# build a dictionary of id:item pairs, so one can select the respective participant
|
||||||
|
ships = json.loads(response)
|
||||||
|
ships = {items.get("id"):items for items in ships}
|
||||||
|
return ships
|
||||||
|
|
||||||
|
def get_berth_id_dictionary():
|
||||||
|
# get all berths
|
||||||
|
response,status_code,header = GetBerths(token=None)
|
||||||
|
|
||||||
|
# build a dictionary of id:item pairs, so one can select the respective participant
|
||||||
|
berths = json.loads(response)
|
||||||
|
berths = {items.get("id"):items for items in berths}
|
||||||
|
return berths
|
||||||
|
|
||||||
|
def check_if_user_is_bsmd_type(user_data:dict)->bool:
|
||||||
|
"""
|
||||||
|
given a dictionary of user data, determine the respective participant id and read, whether
|
||||||
|
that participant is a .BSMD-type
|
||||||
|
|
||||||
|
Note: ParticipantType is an IntFlag.
|
||||||
|
Hence, ParticipantType(1) is ParticipantType.BSMD,
|
||||||
|
and ParticipantType(7) is [ParticipantType.BSMD, ParticipantType.TERMINAL, ParticipantType.PILOT]
|
||||||
|
|
||||||
|
both would return 'True'
|
||||||
|
|
||||||
|
returns: boolean. Whether the participant id is a .BSMD type element
|
||||||
|
"""
|
||||||
|
# user_data = decode token
|
||||||
|
participant_id = user_data.get("participant_id")
|
||||||
|
|
||||||
|
# build a dictionary of id:item pairs, so one can select the respective participant
|
||||||
|
participants = get_participant_id_dictionary()
|
||||||
|
participant = participants.get(participant_id,{})
|
||||||
|
|
||||||
|
# boolean check: is the participant of type .BSMD?
|
||||||
|
is_bsmd = ParticipantType.BSMD in ParticipantType(participant.get("type",0))
|
||||||
|
return is_bsmd
|
||||||
|
|
||||||
|
def check_if_user_data_has_valid_ship_id(ship_id):
|
||||||
|
# build a dictionary of id:item pairs, so one can select the respective participant
|
||||||
|
ships = get_ship_id_dictionary()
|
||||||
|
|
||||||
|
# boolean check
|
||||||
|
ship_id_is_valid = ship_id in list(ships.keys())
|
||||||
|
return ship_id_is_valid
|
||||||
|
|
||||||
|
def check_if_user_data_has_valid_berth_id(berth_id):
|
||||||
|
# build a dictionary of id:item pairs, so one can select the respective participant
|
||||||
|
berths = get_berth_id_dictionary()
|
||||||
|
|
||||||
|
# boolean check
|
||||||
|
berth_id_is_valid = berth_id in list(berths.keys())
|
||||||
|
return berth_id_is_valid
|
||||||
|
|
||||||
|
def check_if_user_data_has_valid_participant_id(participant_id):
|
||||||
|
# build a dictionary of id:item pairs, so one can select the respective participant
|
||||||
|
participants = get_participant_id_dictionary()
|
||||||
|
|
||||||
|
# boolean check
|
||||||
|
participant_id_is_valid = participant_id in list(participants.keys())
|
||||||
|
return participant_id_is_valid
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InputValidation():
|
class InputValidation():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|||||||
@ -8,6 +8,7 @@ webargs==6.1.1
|
|||||||
Werkzeug==1.0.1
|
Werkzeug==1.0.1
|
||||||
pydapper[mysql-connector-python]
|
pydapper[mysql-connector-python]
|
||||||
marshmallow-dataclass
|
marshmallow-dataclass
|
||||||
|
marshmallow-enum
|
||||||
bcrypt
|
bcrypt
|
||||||
pyjwt
|
pyjwt
|
||||||
flask-jwt-extended
|
flask-jwt-extended
|
||||||
@ -20,4 +21,4 @@ pytest
|
|||||||
pytest-cov
|
pytest-cov
|
||||||
coverage
|
coverage
|
||||||
|
|
||||||
../server/.
|
-e ../server/.
|
||||||
|
|||||||
@ -8,7 +8,7 @@ def test_create_app():
|
|||||||
import sys
|
import sys
|
||||||
from BreCal import get_project_root
|
from BreCal import get_project_root
|
||||||
|
|
||||||
project_root = get_project_root("brecal")
|
project_root = os.path.join(os.path.expanduser("~"), "brecal")
|
||||||
lib_location = os.path.join(project_root, "src", "server")
|
lib_location = os.path.join(project_root, "src", "server")
|
||||||
sys.path.append(lib_location)
|
sys.path.append(lib_location)
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,17 @@ from BreCal.stubs.df_times import get_df_times, random_time_perturbation, get_df
|
|||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def build_sql_proxy_connection():
|
def build_sql_proxy_connection():
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
conn_from_pool = mysql.connector.connect(**{'host':'localhost', 'port':3306, 'user':'root', 'password':'HalloWach_2323XXL!!', 'pool_name':'brecal_pool', 'pool_size':20, 'database':'bremen_calling', 'autocommit': True})
|
import os
|
||||||
|
import json
|
||||||
|
connection_data_path = os.path.join(os.path.expanduser("~"),"secure","connection_data_local.json")
|
||||||
|
assert os.path.exists(connection_data_path)
|
||||||
|
|
||||||
|
with open(connection_data_path, "r") as jr:
|
||||||
|
connection_data = json.load(jr)
|
||||||
|
connection_data = {k:v for k,v in connection_data.items() if k in ["host", "port", "user", "password", "pool_size", "pool_name", "database"]}
|
||||||
|
|
||||||
|
conn_from_pool = mysql.connector.connect(**connection_data)
|
||||||
|
#conn_from_pool = mysql.connector.connect(**{'host':'localhost', 'port':3306, 'user':'root', 'password':'HalloWach_2323XXL!!', 'pool_name':'brecal_pool', 'pool_size':20, 'database':'bremen_calling_local', 'autocommit': True})
|
||||||
sql_handler = SQLHandler(sql_connection=conn_from_pool, read_all=True)
|
sql_handler = SQLHandler(sql_connection=conn_from_pool, read_all=True)
|
||||||
vr = ValidationRules(sql_handler)
|
vr = ValidationRules(sql_handler)
|
||||||
return locals()
|
return locals()
|
||||||
|
|||||||
Reference in New Issue
Block a user