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:
scopesorting 2023-11-30 15:53:42 +01:00 committed by Daniel Schick
parent efff2fdf82
commit a0785d012c
5 changed files with 52 additions and 18 deletions

View File

@ -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"""
NONE = 0
BSMD = 1

View File

@ -1,6 +1,7 @@
import numpy as np
import pandas as pd
import datetime
import typing
from BreCal.schemas.model import Shipcall, Ship, Participant, Berth, User, Times
from BreCal.database.enums import ParticipantType
@ -110,7 +111,8 @@ class SQLHandler():
self.initialize_shipcall_participant_list()
# update the 'type' in shipcall_participants_map
self.add_participant_type_to_map()
# fully deprecated
# self.add_participant_type_to_map()
return
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
respective data from the participants. Updates the shipcall_participant_map inplace.
"""
spm = self.df_dict["shipcall_participant_map"]
participant_df = self.df_dict["participant"]
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.")
#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)
self.df_dict["shipcall_participant_map"] = spm
#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
return
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):
"""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
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)
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):
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:
return None
@ -299,7 +323,8 @@ class SQLHandler():
df_times = df_times.loc[~df_times[non_null_column].isnull()] # NOT null filter
# 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
def filter_df_by_key_value(self, df, key, value)->pd.DataFrame:

View File

@ -866,18 +866,18 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
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
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()
# 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()
# 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]:
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:
validation_name = "validation_rule_fct_agency_and_terminal_pier_side_disagreement"

View File

@ -33,6 +33,10 @@ class ValidationRules(ValidationRuleFunctions):
if len(df_times)==0:
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
df_times = self.sql_handler.get_times_of_shipcall(shipcall)

View File

@ -1114,7 +1114,8 @@ def test_validation_rule_fct_agency_and_terminal_pier_side_disagreement__agency_
vr = build_sql_proxy_connection['vr']
shipcall = get_shipcall_simple()
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
(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']
shipcall = get_shipcall_simple()
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
(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']
shipcall = get_shipcall_simple()
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
(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
# agreement
t1.pier_side = True
shipcall.pier_side = True
# t1.pier_side = True
t2.pier_side = True
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
# disagreement
t1.pier_side = True
shipcall.pier_side = True
# t1.pier_side = True
t2.pier_side = False
time_objects = [t1, t2]