From 82309a53d61271d9ae15af7690ca445bb9842754 Mon Sep 17 00:00:00 2001 From: scopesorting Date: Thu, 7 Dec 2023 11:54:37 +0100 Subject: [PATCH] bugfix of 0004A and B, where the tide window may have deviated by few seconds, so the wrong state would be concluded. --- src/server/BreCal/validators/time_logic.py | 17 +++++++++- .../validators/validation_rule_functions.py | 13 +++++++- .../test_validation_rule_functions.py | 32 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/server/BreCal/validators/time_logic.py b/src/server/BreCal/validators/time_logic.py index b80384e..1ee37cc 100644 --- a/src/server/BreCal/validators/time_logic.py +++ b/src/server/BreCal/validators/time_logic.py @@ -2,6 +2,15 @@ import datetime import numpy as np import pandas as pd +def validate_time_exceeds_threshold(value:datetime.datetime, seconds:int=60, minutes:int=60, hours:int=24, days:int=30, months:int=12)->bool: + """returns a boolean when the input value is very distant in the future. The parameters provide the threshold""" + # time difference in seconds. Positive: in the future, Negative: in the past + current_time = datetime.datetime.now() + time_ = (value-current_time).total_seconds() + threshold = seconds*minutes*hours*days*months + print(time_, threshold) + return time_>=threshold + class TimeLogic(): def __init__(self): return @@ -41,10 +50,11 @@ class TimeLogic(): def time_inbetween(self, query_time:datetime.datetime, start_time:datetime.datetime, end_time:datetime.datetime) -> bool: """ checks, whether the query time is inbetween the start & end time. Returns a bool to indicate that. + The function internally rounds seconds and microseconds to zero, so they are ignored in the evaluation. Example: a = datetime.datetime(2017, 5, 16, 8, 21, 10) - b = datetime.datetime(2017, 5, 17, 8, 21, 10) + b = datetime.datetime(2017, 5, 17, 8, 21, 09) c = datetime.datetime(2017, 5, 18, 8, 21, 10) is b between a and c? -> yes. Returns True @@ -55,7 +65,12 @@ class TimeLogic(): assert isinstance(query_time, datetime.datetime) assert isinstance(start_time, datetime.datetime) assert isinstance(end_time, datetime.datetime) + """ + query_time = query_time.replace(second=0, microsecond=0) + start_time = start_time.replace(second=0, microsecond=0) + end_time = end_time.replace(second=0, microsecond=0)""" + print("time_inbetween function! UNVIOLATED?", start_time <= query_time <= end_time, start_time, query_time, end_time) return start_time <= query_time <= end_time def time_inbetween_absolute_delta(self, query_time:datetime.datetime, start_time:datetime.datetime, end_time:datetime.datetime) -> tuple: diff --git a/src/server/BreCal/validators/validation_rule_functions.py b/src/server/BreCal/validators/validation_rule_functions.py index 92bf24e..5dfcf96 100644 --- a/src/server/BreCal/validators/validation_rule_functions.py +++ b/src/server/BreCal/validators/validation_rule_functions.py @@ -3,6 +3,7 @@ import types from BreCal.database.enums import ParticipantType, ShipcallType, ParticipantwiseTimeDelta import numpy as np import pandas as pd +import datetime from BreCal.validators.time_logic import TimeLogic from BreCal.database.enums import StatusFlags #from BreCal.validators.schema_validation import validation_state_and_validation_name @@ -713,7 +714,12 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions): if self.check_is_not_a_time_or_is_none(times_terminal.operations_start) or self.check_is_not_a_time_or_is_none(times_agency.eta_berth): return self.get_no_violation_default_output() - # check, whether the start of operations is AFTER the estimated arrival time + # check, whether the end of operations is BEFORE the estimated arrival time + if isinstance(times_terminal.operations_start, (pd.Timestamp, datetime.datetime)): + times_terminal.operations_start = times_terminal.operations_start.replace(second=0, microsecond=0) + if isinstance(times_agency.eta_berth, (pd.Timestamp, datetime.datetime)): + times_agency.eta_berth = times_agency.eta_berth.replace(second=0, microsecond=0) + violation_state = times_terminal.operations_start < times_agency.eta_berth if violation_state: @@ -749,6 +755,11 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions): return self.get_no_violation_default_output() # check, whether the end of operations is AFTER the estimated departure time + if isinstance(times_terminal.operations_end, (pd.Timestamp, datetime.datetime)): + times_terminal.operations_end = times_terminal.operations_end.replace(second=0, microsecond=0) + if isinstance(times_agency.etd_berth, (pd.Timestamp, datetime.datetime)): + times_agency.etd_berth = times_agency.etd_berth.replace(second=0, microsecond=0) + violation_state = times_terminal.operations_end > times_agency.etd_berth if violation_state: diff --git a/src/server/tests/validators/test_validation_rule_functions.py b/src/server/tests/validators/test_validation_rule_functions.py index 2593e85..c277386 100644 --- a/src/server/tests/validators/test_validation_rule_functions.py +++ b/src/server/tests/validators/test_validation_rule_functions.py @@ -1062,6 +1062,38 @@ def test_validation_rule_fct_etd_time_not_in_tidal_window__etd_outside_tidal_win assert code==StatusFlags.RED, f"state should be 'red', etd_berth takes place after the tidal window" return +def test_validation_rule_fct_tidal_window_is_precisely_eta_or_etd__is_okay(build_sql_proxy_connection): + """0004-A & 0004-B eta/etd is exactly tidal window: no violation""" + vr = build_sql_proxy_connection['vr'] + shipcall = get_shipcall_simple() + df_times = get_df_times(shipcall) + times_agency = vr.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value) + + + # tidal window: [t0 +1min, t0 +1hr) + # etd berth: + t0_time = datetime.datetime.now() + times_agency.eta_berth = t0_time + shipcall.tidal_window_from = t0_time + shipcall.tidal_window_to = t0_time+datetime.timedelta(minutes=3) + + violation_state = not vr.time_logic.time_inbetween( + query_time=times_agency.eta_berth, start_time=shipcall.tidal_window_from, end_time=shipcall.tidal_window_to + ) + assert violation_state==False + + t0_time = datetime.datetime.now() + times_agency.etd_berth = t0_time + shipcall.tidal_window_from = t0_time+datetime.timedelta(minutes=-3) + shipcall.tidal_window_to = t0_time + + violation_state = not vr.time_logic.time_inbetween( + query_time=times_agency.etd_berth, start_time=shipcall.tidal_window_from, end_time=shipcall.tidal_window_to + ) + assert violation_state==False + + return + def test_validation_rule_fct_too_many_identical_eta_times__is_violated_by_too_many_identical_times(build_sql_proxy_connection): """0005-A validation_rule_fct_too_many_identical_eta_times""" vr = build_sql_proxy_connection['vr']