changing the ParticipantType to an IntFlag, so multiple roles are possible. Adapting every validation rule (0001, 0003, 0004, 0005), which may be affected by this change. Changing the filter for a participant type to properly include the change. Changing the pier_side rule (0006B), which uses the shipcall and times_terminal. New shipcalls should now be evaluated properly, unless no participant is assigned at all. If the ladder case can occur, the validation rules 0001N+0001O will be added (held back for now).
This commit is contained in:
parent
8e9be1eae5
commit
dc79f05b8b
@ -1,6 +1,6 @@
|
|||||||
from enum import Enum
|
from enum import Enum, IntFlag
|
||||||
|
|
||||||
class ParticipantType(Enum):
|
class ParticipantType(IntFlag):
|
||||||
"""determines the type of a participant"""
|
"""determines the type of a participant"""
|
||||||
NONE = 0
|
NONE = 0
|
||||||
BSMD = 1
|
BSMD = 1
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import datetime
|
import datetime
|
||||||
|
import typing
|
||||||
from BreCal.schemas.model import Shipcall, Ship, Participant, Berth, User, Times
|
from BreCal.schemas.model import Shipcall, Ship, Participant, Berth, User, Times
|
||||||
from BreCal.database.enums import ParticipantType
|
from BreCal.database.enums import ParticipantType
|
||||||
|
|
||||||
@ -110,7 +111,8 @@ class SQLHandler():
|
|||||||
self.initialize_shipcall_participant_list()
|
self.initialize_shipcall_participant_list()
|
||||||
|
|
||||||
# update the 'type' in shipcall_participants_map
|
# update the 'type' in shipcall_participants_map
|
||||||
self.add_participant_type_to_map()
|
# fully deprecated
|
||||||
|
# self.add_participant_type_to_map()
|
||||||
return
|
return
|
||||||
|
|
||||||
def build_full_mysql_df_dict(self, all_schemas):
|
def build_full_mysql_df_dict(self, all_schemas):
|
||||||
@ -143,11 +145,12 @@ class SQLHandler():
|
|||||||
applies a lambda function, where the 'type'-column in the shipcall_participant_map is updated by reading the
|
applies a lambda function, where the 'type'-column in the shipcall_participant_map is updated by reading the
|
||||||
respective data from the participants. Updates the shipcall_participant_map inplace.
|
respective data from the participants. Updates the shipcall_participant_map inplace.
|
||||||
"""
|
"""
|
||||||
spm = self.df_dict["shipcall_participant_map"]
|
raise Exception("deprecated! Overwriting the shipcall_participant_map may cause harm, as a participant with multi-flag might be wrongfully assigned to multiple roles simultaneously.")
|
||||||
participant_df = self.df_dict["participant"]
|
#spm = self.df_dict["shipcall_participant_map"]
|
||||||
|
#participant_df = self.df_dict["participant"]
|
||||||
|
|
||||||
spm.loc[:,"type"] = spm.loc[:].apply(lambda x: set_participant_type(x, participant_df=participant_df),axis=1)
|
#spm.loc[:,"type"] = spm.loc[:].apply(lambda x: set_participant_type(x, participant_df=participant_df),axis=1)
|
||||||
self.df_dict["shipcall_participant_map"] = spm
|
#self.df_dict["shipcall_participant_map"] = spm
|
||||||
return
|
return
|
||||||
|
|
||||||
def get_assigned_participants(self, shipcall)->pd.DataFrame:
|
def get_assigned_participants(self, shipcall)->pd.DataFrame:
|
||||||
@ -159,7 +162,11 @@ class SQLHandler():
|
|||||||
|
|
||||||
def get_assigned_participants_by_type(self, assigned_participants:pd.DataFrame, participant_type:ParticipantType):
|
def get_assigned_participants_by_type(self, assigned_participants:pd.DataFrame, participant_type:ParticipantType):
|
||||||
"""filters a dataframe of assigned_participants by the provided type enumerator"""
|
"""filters a dataframe of assigned_participants by the provided type enumerator"""
|
||||||
assigned_participants_of_type = assigned_participants.loc[assigned_participants["type"]==participant_type.value]
|
if isinstance(participant_type,int):
|
||||||
|
participant_type = ParticipantType(participant_type)
|
||||||
|
|
||||||
|
assigned_participants_of_type = assigned_participants.loc[[participant_type in ParticipantType(int(pt_)) for pt_ in list(assigned_participants["type"].values)]]
|
||||||
|
#assigned_participants_of_type = assigned_participants.loc[assigned_participants["type"]==participant_type.value]
|
||||||
return assigned_participants_of_type
|
return assigned_participants_of_type
|
||||||
|
|
||||||
def check_if_any_participant_of_type_is_unassigned(self, shipcall, *args:list[ParticipantType])->bool:
|
def check_if_any_participant_of_type_is_unassigned(self, shipcall, *args:list[ParticipantType])->bool:
|
||||||
@ -234,8 +241,25 @@ class SQLHandler():
|
|||||||
data = data_model(**data)
|
data = data_model(**data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def filter_df_by_participant_type(self, df, participant_type:typing.Union[int, ParticipantType])->pd.DataFrame:
|
||||||
|
"""
|
||||||
|
As ParticipantTypes are Flag objects, a dataframe's integer might resemble multiple participant types simultaneously.
|
||||||
|
This function allows for more complex filters, as the IntFlag allows more complex queries
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
ParticipantType(6) is 2,4 (2+4 = 6)
|
||||||
|
|
||||||
|
Participant(2) in Participant(6) = True # 6 is both, 2 and 4
|
||||||
|
Participant(1) in Participant(6) = False # 6 is both, 2 and 4, but not 1
|
||||||
|
"""
|
||||||
|
if isinstance(participant_type,int):
|
||||||
|
participant_type = ParticipantType(participant_type)
|
||||||
|
filtered_df = df.loc[[participant_type in ParticipantType(df_pt) for df_pt in list(df["participant_type"].values)]]
|
||||||
|
return filtered_df
|
||||||
|
|
||||||
def get_times_for_participant_type(self, df_times, participant_type:int):
|
def get_times_for_participant_type(self, df_times, participant_type:int):
|
||||||
filtered_series = df_times.loc[df_times["participant_type"]==participant_type]
|
filtered_series = self.filter_df_by_participant_type(df_times, participant_type)
|
||||||
|
#filtered_series = df_times.loc[df_times["participant_type"]==participant_type]
|
||||||
|
|
||||||
if len(filtered_series)==0:
|
if len(filtered_series)==0:
|
||||||
return None
|
return None
|
||||||
@ -299,7 +323,8 @@ class SQLHandler():
|
|||||||
df_times = df_times.loc[~df_times[non_null_column].isnull()] # NOT null filter
|
df_times = df_times.loc[~df_times[non_null_column].isnull()] # NOT null filter
|
||||||
|
|
||||||
# filter by the agency participant_type
|
# filter by the agency participant_type
|
||||||
times_agency = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value]
|
times_agency = self.filter_df_by_participant_type(df_times, ParticipantType.AGENCY.value)
|
||||||
|
#times_agency = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value]
|
||||||
return times_agency
|
return times_agency
|
||||||
|
|
||||||
def filter_df_by_key_value(self, df, key, value)->pd.DataFrame:
|
def filter_df_by_key_value(self, df, key, value)->pd.DataFrame:
|
||||||
|
|||||||
@ -866,18 +866,18 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
|||||||
times_terminal = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.TERMINAL.value)
|
times_terminal = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.TERMINAL.value)
|
||||||
|
|
||||||
# when one of the two values is null, the state is GREEN
|
# when one of the two values is null, the state is GREEN
|
||||||
if (times_agency.pier_side is None) or (times_terminal.pier_side is None):
|
if (shipcall.pier_side is None) or (times_terminal.pier_side is None):
|
||||||
return self.get_no_violation_default_output()
|
return self.get_no_violation_default_output()
|
||||||
|
|
||||||
# when one of the two values is null, the state is GREEN
|
# when one of the two values is null, the state is GREEN
|
||||||
if (pd.isnull(times_agency.pier_side)) or (pd.isnull(times_terminal.pier_side)):
|
if (pd.isnull(shipcall.pier_side)) or (pd.isnull(times_terminal.pier_side)):
|
||||||
return self.get_no_violation_default_output()
|
return self.get_no_violation_default_output()
|
||||||
|
|
||||||
# only incoming shipcalls matter. The other ones are not relevant for the pier_side selection
|
# only incoming shipcalls matter. The other ones are not relevant for the pier_side selection
|
||||||
if shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
if shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||||
return self.get_no_violation_default_output()
|
return self.get_no_violation_default_output()
|
||||||
|
|
||||||
violation_state = bool(times_agency.pier_side)!=bool(times_terminal.pier_side)
|
violation_state = bool(shipcall.pier_side)!=bool(times_terminal.pier_side)
|
||||||
|
|
||||||
if violation_state:
|
if violation_state:
|
||||||
validation_name = "validation_rule_fct_agency_and_terminal_pier_side_disagreement"
|
validation_name = "validation_rule_fct_agency_and_terminal_pier_side_disagreement"
|
||||||
|
|||||||
@ -33,6 +33,10 @@ class ValidationRules(ValidationRuleFunctions):
|
|||||||
|
|
||||||
if len(df_times)==0:
|
if len(df_times)==0:
|
||||||
return (StatusFlags.GREEN.value, [])
|
return (StatusFlags.GREEN.value, [])
|
||||||
|
|
||||||
|
spm = self.sql_handler.df_dict["shipcall_participant_map"]
|
||||||
|
if len(spm.loc[spm["shipcall_id"]==shipcall.id])==0:
|
||||||
|
return (StatusFlags.GREEN.value, [])
|
||||||
|
|
||||||
# filter by shipcall id
|
# filter by shipcall id
|
||||||
df_times = self.sql_handler.get_times_of_shipcall(shipcall)
|
df_times = self.sql_handler.get_times_of_shipcall(shipcall)
|
||||||
|
|||||||
@ -1114,7 +1114,8 @@ def test_validation_rule_fct_agency_and_terminal_pier_side_disagreement__agency_
|
|||||||
vr = build_sql_proxy_connection['vr']
|
vr = build_sql_proxy_connection['vr']
|
||||||
shipcall = get_shipcall_simple()
|
shipcall = get_shipcall_simple()
|
||||||
df_times = get_df_times(shipcall)
|
df_times = get_df_times(shipcall)
|
||||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
shipcall.pier_side = True
|
||||||
|
# df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
||||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "pier_side"] = True
|
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "pier_side"] = True
|
||||||
|
|
||||||
(code, msg) = vr.validation_rule_fct_agency_and_terminal_pier_side_disagreement(shipcall=shipcall, df_times=df_times)
|
(code, msg) = vr.validation_rule_fct_agency_and_terminal_pier_side_disagreement(shipcall=shipcall, df_times=df_times)
|
||||||
@ -1126,7 +1127,8 @@ def test_validation_rule_fct_agency_and_terminal_pier_side_disagreement__agency_
|
|||||||
vr = build_sql_proxy_connection['vr']
|
vr = build_sql_proxy_connection['vr']
|
||||||
shipcall = get_shipcall_simple()
|
shipcall = get_shipcall_simple()
|
||||||
df_times = get_df_times(shipcall)
|
df_times = get_df_times(shipcall)
|
||||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
shipcall.pier_side = True
|
||||||
|
#df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
||||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "pier_side"] = False
|
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "pier_side"] = False
|
||||||
|
|
||||||
(code, msg) = vr.validation_rule_fct_agency_and_terminal_pier_side_disagreement(shipcall=shipcall, df_times=df_times)
|
(code, msg) = vr.validation_rule_fct_agency_and_terminal_pier_side_disagreement(shipcall=shipcall, df_times=df_times)
|
||||||
@ -1138,7 +1140,8 @@ def test_validation_rule_fct_agency_and_terminal_pier_side_disagreement__agency_
|
|||||||
vr = build_sql_proxy_connection['vr']
|
vr = build_sql_proxy_connection['vr']
|
||||||
shipcall = get_shipcall_simple()
|
shipcall = get_shipcall_simple()
|
||||||
df_times = get_df_times(shipcall)
|
df_times = get_df_times(shipcall)
|
||||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
shipcall.pier_side = True
|
||||||
|
# df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
||||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "pier_side"] = None
|
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "pier_side"] = None
|
||||||
|
|
||||||
(code, msg) = vr.validation_rule_fct_agency_and_terminal_pier_side_disagreement(shipcall=shipcall, df_times=df_times)
|
(code, msg) = vr.validation_rule_fct_agency_and_terminal_pier_side_disagreement(shipcall=shipcall, df_times=df_times)
|
||||||
@ -1178,7 +1181,8 @@ def test_validation_rule_fct_agency_and_terminal_pier_side_agreement(build_sql_p
|
|||||||
t2.participant_type = ParticipantType.TERMINAL.value
|
t2.participant_type = ParticipantType.TERMINAL.value
|
||||||
|
|
||||||
# agreement
|
# agreement
|
||||||
t1.pier_side = True
|
shipcall.pier_side = True
|
||||||
|
# t1.pier_side = True
|
||||||
t2.pier_side = True
|
t2.pier_side = True
|
||||||
|
|
||||||
time_objects = [t1, t2]
|
time_objects = [t1, t2]
|
||||||
@ -1209,7 +1213,8 @@ def test_validation_rule_fct_agency_and_terminal_pier_side_disagreement(build_sq
|
|||||||
t2.participant_type = ParticipantType.TERMINAL.value
|
t2.participant_type = ParticipantType.TERMINAL.value
|
||||||
|
|
||||||
# disagreement
|
# disagreement
|
||||||
t1.pier_side = True
|
shipcall.pier_side = True
|
||||||
|
# t1.pier_side = True
|
||||||
t2.pier_side = False
|
t2.pier_side = False
|
||||||
|
|
||||||
time_objects = [t1, t2]
|
time_objects = [t1, t2]
|
||||||
|
|||||||
Reference in New Issue
Block a user