Skip to content
Snippets Groups Projects
Commit ccefc083 authored by Jörn Künsemöller's avatar Jörn Künsemöller
Browse files

TMSS-255: debugging and adding tests for avoid_twilight constraint

parent 920b83c9
No related branches found
No related tags found
1 merge request!287Resolve TMSS-255
......@@ -76,6 +76,19 @@ def has_manual_scheduler_constraint(scheduling_unit: models.SchedulingUnitBluepr
def can_run_within_timewindow_with_daily_constraints(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime, upper_bound: datetime) -> bool:
"""
Checks whether it is possible to run the scheduling unit /somewhere/ in the given time window, considering the duration of the involved observation.
:return: True if there is at least one possibility to place the scheduling unit in a way that all daily constraints are met over the runtime of the observation, else False.
"""
# todo: use moving window lower_bound to lower_bound + obs duration, return true once window returned true
return can_run_anywhere_within_timewindow_with_daily_constraints(scheduling_unit, lower_bound, upper_bound)
def can_run_anywhere_within_timewindow_with_daily_constraints(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime, upper_bound: datetime) -> bool:
"""
Checks whether it is possible to place the scheduling unit arbitrarily in the given time window, i.e. the daily constraints must be met over the full time window.
:return: True if all daily constraints are met over the entire time window, else False.
"""
'''evaluate the daily contraint'''
constraints = scheduling_unit.draft.scheduling_constraints_doc
if constraints['daily']['require_day'] or constraints['daily']['require_night'] or constraints['daily']['avoid_twilight']:
......@@ -92,7 +105,7 @@ def can_run_within_timewindow_with_daily_constraints(scheduling_unit: models.Sch
# check contraint and return false on first failure
for station in stations:
# get day/night times for bounds
# we could sample in between bounds, but will instead do some checks
# we could sample in between bounds, but will instead do some checks so that bounds are sufficient
if constraints['daily']['require_day'] and lower_bound.date() != upper_bound.date():
logger.info("### SchedulingUnitBlueprint id=%s cannot meet require_day constraint when starting and ending on different days." % scheduling_unit.id)
return False
......@@ -101,11 +114,8 @@ def can_run_within_timewindow_with_daily_constraints(scheduling_unit: models.Sch
if constraints['daily']['require_day']:
for i in range(len(timestamps)):
if timestamps[i] < sun_events['day'][i]['start'] or timestamps[i] > sun_events['day'][i]['end']:
logger.info("### %s not between %s and %s" % (timestamps[i], sun_events['day'][i]['start'], sun_events['day'][i]['end']))
logger.info("### SchedulingUnitBlueprint id=%s does not meet require_day constraint at timestamp=%s" % (scheduling_unit.id, timestamps[i]))
return False
else:
logger.info("### %s between %s and %s" % (timestamps[i], sun_events['day'][i]['start'], sun_events['day'][i]['end'] ))
if constraints['daily']['require_night']:
if sun_events['night'][0]['start'].date() != sun_events['night'][1]['start'].date():
......@@ -125,12 +135,23 @@ def can_run_within_timewindow_with_daily_constraints(scheduling_unit: models.Sch
logger.info("### SchedulingUnitBlueprint id=%s does not meet avoid_twilight constraint." % scheduling_unit.id)
return False
logger.info('### SchedulingUnitBlueprint id=%s meets all daily constraints. Returning True.')
return True
def can_run_within_timewindow_with_time_constraints(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime, upper_bound: datetime) -> bool:
"""
Checks whether it is possible to run the scheduling unit /somewhere/ in the given time window, considering the duration of the involved observation.
:return: True if there is at least one possibility to place the scheduling unit in a way that all time constraints are met over the runtime of the observation, else False.
"""
# todo: use moving window lower_bound to lower_bound + obs duration, return true once window returned true
return can_run_anywhere_within_timewindow_with_time_constraints(scheduling_unit, lower_bound, upper_bound)
def can_run_anywhere_within_timewindow_with_time_constraints(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime, upper_bound: datetime) -> bool:
"""
Checks whether it is possible to place the scheduling unit arbitrarily in the given time window, i.e. the time constraints must be met over the full time window.
:return: True if all time constraints are met over the entire time window, else False.
"""
'''evaluate the time contraint(s)'''
constraints = scheduling_unit.draft.scheduling_constraints_doc
# TODO: TMSS-244 (and more?), evaluate the constraints in constraints['time']
......@@ -200,7 +221,7 @@ def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBluep
next_day = sun_events['day'][1]
next_night = sun_events['night'][1]
if constraints['daily']['require_day']:
# TODO: Do we need to check for observations that are too long and can e.g. only be run in summer?
# TODO: Do we need to check for observations that are too long and can e.g. only be run in summer? -> recursively traverse through days or sth?
if lower_bound + duration > day['end']:
start_time_per_station[station] = next_day['start']
continue
......@@ -221,19 +242,20 @@ def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBluep
continue
if constraints['daily']['avoid_twilight']:
if lower_bound + duration < day['end']:
if lower_bound >= day['start']:
start_time_per_station[station] = lower_bound
continue
start_time_per_station[station] = day['start']
if lower_bound >= day['start'] and lower_bound + duration < day['end']:
# starts and ends in daytime
start_time_per_station[station] = lower_bound
continue
if lower_bound + duration < night['end']:
if lower_bound >= night['start']:
start_time_per_station[station] = lower_bound
continue
start_time_per_station[station] = night['start']
if lower_bound >= night['start'] and lower_bound + duration < night['end']:
# starts and ends in nighttime
start_time_per_station[station] = lower_bound
continue
start_time_per_station[station] = next_day['start']
if lower_bound < day['start'] and lower_bound + duration >= night['end']:
# ends in morning twilight
start_time_per_station[station] = day['start']
continue
# ends in evening twilight
start_time_per_station[station] = night['start']
continue
return max(start_time_per_station.values())
except Exception as e:
......
......@@ -302,7 +302,11 @@ class TestDailyConstraints(unittest.TestCase):
'CS001': {"sunrise": [{"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}, {"start": datetime(2020, 1, 2, 7, 30, 0), "end": datetime(2020, 1, 2, 9, 30, 0)}],
"day": [{"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}, {"start": datetime(2020, 1, 2, 9, 30, 0), "end": datetime(2020, 1, 2, 15, 30, 0)}],
"sunset": [{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)},{"start": datetime(2020, 1, 2, 15, 30, 0), "end": datetime(2020, 1, 2, 17, 30, 0)}],
"night": [{"start": datetime(2019, 12, 31, 17, 30, 0), "end": datetime(2020, 1, 1, 7, 30, 0)}, {"start": datetime(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}]}}
"night": [{"start": datetime(2019, 12, 31, 17, 30, 0), "end": datetime(2020, 1, 1, 7, 30, 0)}, {"start": datetime(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}]},
'DE601': {"sunrise": [{"start": datetime(2020, 1, 1, 7, 45, 0), "end": datetime(2020, 1, 1, 9, 45, 0)}, {"start": datetime(2020, 1, 2, 7, 45, 0), "end": datetime(2020, 1, 2, 9, 45, 0)}],
"day": [{"start": datetime(2020, 1, 1, 9, 45, 0), "end": datetime(2020, 1, 1, 15, 45, 0)}, {"start": datetime(2020, 1, 2, 9, 45, 0), "end": datetime(2020, 1, 2, 15, 45, 0)}],
"sunset": [{"start": datetime(2020, 1, 1, 15, 45, 0), "end": datetime(2020, 1, 1, 17, 45, 0)},{"start": datetime(2020, 1, 2, 15, 45, 0), "end": datetime(2020, 1, 2, 17, 45, 0)}],
"night": [{"start": datetime(2019, 12, 31, 17, 45, 0), "end": datetime(2020, 1, 1, 7, 45, 0)}, {"start": datetime(2020, 1, 1, 17, 45, 0), "end": datetime(2020, 1, 2, 7, 45, 0)}]}}
# constraint checker requests lower and upper bound, so we need some variants for various cases
......@@ -347,6 +351,7 @@ class TestDailyConstraints(unittest.TestCase):
def test_get_earliest_possible_start_time_with_daytime_constraint_returns_day_start(self):
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
self.scheduling_unit_blueprint.save()
self.sunrise_mock.return_value = self.sunrise_data_early_night
timestamp = datetime(2020, 1, 1, 4, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, self.sunrise_data['CS001']['day'][0]['start'])
......@@ -355,6 +360,7 @@ class TestDailyConstraints(unittest.TestCase):
self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['stations'] = ['CS001', 'DE601']
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
self.scheduling_unit_blueprint.save()
self.sunrise_mock.return_value = self.sunrise_data_early_night
timestamp = datetime(2020, 1, 1, 4, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, self.sunrise_data['DE601']['day'][0]['start'])
......@@ -531,7 +537,116 @@ class TestDailyConstraints(unittest.TestCase):
upper_bound = datetime(2020, 1, 1, 23, 0, 0)
self.assertFalse(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound))
# todo: avoid_twilight checks / TMSS-256
# avoid_twilight
def test_get_earliest_possible_start_time_with_twilight_constraint_returns_day_start(self):
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
self.scheduling_unit_blueprint.save()
self.sunrise_mock.return_value = self.sunrise_data_early_night
timestamp = datetime(2020, 1, 1, 9, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, self.sunrise_data['CS001']['day'][0]['start'])
def test_get_earliest_possible_start_time_with_twilight_constraint_returns_day_start_of_latest_station(self):
self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['stations'] = ['CS001', 'DE601']
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
self.scheduling_unit_blueprint.save()
self.sunrise_mock.return_value = self.sunrise_data_early_night
timestamp = datetime(2020, 1, 1, 9, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, self.sunrise_data['DE601']['day'][0]['start'])
def test_get_earliest_possible_start_time_with_twilight_constraint_returns_night_start(self):
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
self.scheduling_unit_blueprint.save()
self.sunrise_mock.return_value = self.sunrise_data
timestamp = datetime(2020, 1, 1, 17, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, self.sunrise_data['CS001']['night'][0]['start'])
def test_get_earliest_possible_start_time_with_twilight_constraint_returns_night_start_of_latest_station(self):
self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['stations'] = ['CS001', 'DE601']
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
self.scheduling_unit_blueprint.save()
self.sunrise_mock.return_value = self.sunrise_data
timestamp = datetime(2020, 1, 1, 17, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, self.sunrise_data['DE601']['night'][0]['start'])
def test_get_earliest_possible_start_time_with_twilight_constraint_returns_timestamp(self):
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
self.scheduling_unit_blueprint.save()
# daytime
timestamp = datetime(2020, 1, 1, 10, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, timestamp)
# late time
timestamp = datetime(2020, 1, 1, 20, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, timestamp)
# early night
self.sunrise_mock.return_value = self.sunrise_data_early_night
timestamp = datetime(2020, 1, 1, 3, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, timestamp)
def test_get_earliest_possible_start_time_with_twilight_constraint_returns_day_or_night_start_when_obs_does_not_fit(self):
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
self.scheduling_unit_blueprint.save()
timestamp = datetime(2020, 1, 1, 15, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, self.sunrise_data['CS001']['night'][0]['start'])
self.sunrise_mock.return_value = self.sunrise_data_early_night
timestamp = datetime(2020, 1, 1, 7, 0, 0)
returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
self.assertEqual(returned_time, self.sunrise_data['CS001']['day'][0]['start'])
def test_can_run_within_timewindow_with_twilight_constraint_returns_true(self):
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'][
'min_distance'] = {} # remove sky constraint
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
self.scheduling_unit_blueprint.save()
self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night
lower_bound = datetime(2020, 1, 1, 10, 0, 0)
upper_bound = datetime(2020, 1, 1, 15, 0, 0)
self.assertTrue(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound))
def test_can_run_within_timewindow_with_twilight_constraint_returns_false_when_in_twilight(self):
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'][
'min_distance'] = {} # remove sky constraint
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
self.scheduling_unit_blueprint.save()
self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night
lower_bound = datetime(2020, 1, 1, 20, 0, 0)
upper_bound = datetime(2020, 1, 1, 23, 0, 0)
self.assertFalse(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound))
def test_can_run_within_timewindow_with_twilight_constraint_returns_false_when_partially_in_twilight(self):
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'][
'min_distance'] = {} # remove sky constraint
self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
self.scheduling_unit_blueprint.save()
self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night
lower_bound = datetime(2020, 1, 1, 10, 0, 0)
upper_bound = datetime(2020, 1, 1, 18, 0, 0)
self.assertFalse(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound))
self.sunrise_mock.return_value = self.sunrise_data_early_night_late_night
lower_bound = datetime(2020, 1, 1, 8, 0, 0)
upper_bound = datetime(2020, 1, 1, 10, 0, 0)
self.assertFalse(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound))
class TestSkyConstraints(unittest.TestCase):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment