diff --git a/SAS/TMSS/services/scheduling/lib/constraints/template_constraints_v1.py b/SAS/TMSS/services/scheduling/lib/constraints/template_constraints_v1.py index 6d10134e70192b140a8c7dcd126f61d07fd4c718..dd4f5451a7e844d41a5070b3e083d2cbe4f3fca2 100644 --- a/SAS/TMSS/services/scheduling/lib/constraints/template_constraints_v1.py +++ b/SAS/TMSS/services/scheduling/lib/constraints/template_constraints_v1.py @@ -225,10 +225,11 @@ def can_run_anywhere_within_timewindow_with_sky_constraints(scheduling_unit: mod min_elevation = Angle(constraints['sky']['min_target_elevation'], unit=astropy.units.rad) timestamps = (lower_bound, upper_bound) stations = task['specifications_doc']['stations'] + # currently we only check at bounds, we probably want to add some more samples in between later on target_rise_and_set_times = coordinates_timestamps_and_stations_to_target_rise_and_set(angle1=angle1, angle2=angle2, direction_type=direction_type, timestamps=timestamps, stations=tuple(stations), angle_to_horizon=min_elevation) for station, times in target_rise_and_set_times.items(): for i in range(len(timestamps)): - if timestamps[i] > times[i]['rise'] and timestamps[i] < times[i]['set']: + if not (timestamps[i] > times[0]['rise'] and timestamps[i] < times[0]['set']): if task['specifications_template'] == 'calibrator observation': logger.info('min_calibrator_elevation=%s constraint is not met at timestamp=%s' % (min_elevation.rad, timestamps[i])) else: diff --git a/SAS/TMSS/services/scheduling/test/t_dynamic_scheduling.py b/SAS/TMSS/services/scheduling/test/t_dynamic_scheduling.py index 8146359b38d12d78d408233d28ea8514ceb33ff9..eec91e72c8b90c8b4284c8058ec2b4fdcefc289c 100755 --- a/SAS/TMSS/services/scheduling/test/t_dynamic_scheduling.py +++ b/SAS/TMSS/services/scheduling/test/t_dynamic_scheduling.py @@ -134,579 +134,579 @@ class TestDynamicScheduling(TestCase): # Note: we use django.test.TestCase inst observation_strategy_template=strategy_template, scheduling_constraints_doc=constraints, scheduling_constraints_template=constraints_template) - - - def test_three_simple_observations_no_constraints_different_project_priority(self): - scheduling_unit_draft_low = self.create_simple_observation_scheduling_unit("scheduling unit low", scheduling_set=self.scheduling_set_low) - scheduling_unit_blueprint_low = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_low) - - scheduling_unit_draft_medium = self.create_simple_observation_scheduling_unit("scheduling unit medium", scheduling_set=self.scheduling_set_medium) - scheduling_unit_blueprint_medium = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_medium) - - scheduling_unit_draft_high = self.create_simple_observation_scheduling_unit("scheduling unit high", scheduling_set=self.scheduling_set_high) - scheduling_unit_blueprint_high = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_high) - - # call the method-under-test. - scheduled_scheduling_unit = do_dynamic_schedule() - - # we expect the scheduling_unit with the highest project rank to be scheduled first - self.assertIsNotNone(scheduled_scheduling_unit) - self.assertEqual(scheduling_unit_blueprint_high.id, scheduled_scheduling_unit.id) - - # check the results - # we expect the sub_high to be scheduled - scheduling_unit_blueprint_low.refresh_from_db() - scheduling_unit_blueprint_medium.refresh_from_db() - scheduling_unit_blueprint_high.refresh_from_db() - self.assertEqual(scheduling_unit_blueprint_low.status, 'schedulable') - self.assertEqual(scheduling_unit_blueprint_medium.status, 'schedulable') - self.assertEqual(scheduling_unit_blueprint_high.status, 'scheduled') - - # check the scheduled subtask - upcoming_scheduled_subtasks = models.Subtask.objects.filter(state__value='scheduled', - task_blueprint__scheduling_unit_blueprint__in=(scheduling_unit_blueprint_low, - scheduling_unit_blueprint_medium, - scheduling_unit_blueprint_high)).all() - self.assertEqual(1, upcoming_scheduled_subtasks.count()) - self.assertEqual(scheduling_unit_blueprint_high.id, upcoming_scheduled_subtasks[0].task_blueprint.scheduling_unit_blueprint.id) - - # check scheduling_unit_blueprint_low starts after the scheduled scheduling_unit_blueprint_high - self.assertGreater(scheduling_unit_blueprint_low.start_time, scheduling_unit_blueprint_medium.start_time) - self.assertGreater(scheduling_unit_blueprint_medium.start_time, scheduling_unit_blueprint_high.start_time) - - # ensure DEFAULT_INTER_OBSERVATION_GAP between them - self.assertGreaterEqual(scheduling_unit_blueprint_medium.start_time - scheduling_unit_blueprint_high.stop_time, DEFAULT_INTER_OBSERVATION_GAP) - self.assertGreaterEqual(scheduling_unit_blueprint_low.start_time - scheduling_unit_blueprint_medium.stop_time, DEFAULT_INTER_OBSERVATION_GAP) - - - def test_time_bound_unit_wins_even_at_lower_priority(self): - # create two schedunits, one with high one with low prio. - # first create them without any further constraints, and check if high prio wins. - scheduling_unit_draft_low = self.create_simple_observation_scheduling_unit("scheduling unit low", scheduling_set=self.scheduling_set_low) - scheduling_unit_blueprint_low = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_low) - - scheduling_unit_draft_high = self.create_simple_observation_scheduling_unit("scheduling unit high", scheduling_set=self.scheduling_set_high) - scheduling_unit_blueprint_high = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_high) - - now = datetime.utcnow() - tomorrow = now+timedelta(days=1) - - # call the method-under-test. - best_scored_scheduling_unit = find_best_next_schedulable_unit([scheduling_unit_blueprint_low, scheduling_unit_blueprint_high], now, tomorrow) - - # we expect the scheduling_unit with the highest project rank to be scheduled first - self.assertEqual(scheduling_unit_blueprint_high.id, best_scored_scheduling_unit.scheduling_unit.id) - - #now update the low prio unit with a time constraint, "forcing" it to be run in a very thight upcoming time window. - scheduling_unit_draft_low.scheduling_constraints_doc['time'] = { 'before': (now+scheduling_unit_draft_low.duration).isoformat()+'Z' } - scheduling_unit_draft_low.save() - scheduling_unit_blueprint_low.refresh_from_db() - - # call the method-under-test. - best_scored_scheduling_unit = find_best_next_schedulable_unit([scheduling_unit_blueprint_low, scheduling_unit_blueprint_high], now, tomorrow) - - # now we expect the scheduling_unit with the lowest project rank to be scheduled first because it can only run within this limited timewindow - self.assertEqual(scheduling_unit_draft_low.id, best_scored_scheduling_unit.scheduling_unit.id) - - - # update the low prio unit. enlarge the time window constraint a bit, so both low and high prio units can fit - # this should result that the high prio goes first, and the low prio (which now fits as well) goes second - scheduling_unit_draft_low.scheduling_constraints_doc['time'] = { 'before': (now+scheduling_unit_draft_low.duration+scheduling_unit_draft_high.duration).isoformat()+'Z' } - scheduling_unit_draft_low.save() - scheduling_unit_blueprint_low.refresh_from_db() - - # call the method-under-test. - best_scored_scheduling_unit = find_best_next_schedulable_unit([scheduling_unit_blueprint_low, scheduling_unit_blueprint_high], now, tomorrow) - - # now we expect the scheduling_unit with the lowest project rank to be scheduled first because it can only run within this limited timewindow - self.assertEqual(scheduling_unit_blueprint_high.id, best_scored_scheduling_unit.scheduling_unit.id) - - # call the method-under-test again but search after first unit (should return low prio unit) - stop_time_of_first = best_scored_scheduling_unit.start_time + best_scored_scheduling_unit.scheduling_unit.duration - best_scored_scheduling_unit = find_best_next_schedulable_unit([scheduling_unit_blueprint_low, scheduling_unit_blueprint_high], stop_time_of_first, tomorrow) - self.assertEqual(scheduling_unit_blueprint_low.id, best_scored_scheduling_unit.scheduling_unit.id) - - - def test_manual_constraint_is_preventing_scheduling_unit_from_being_scheduled_dynamically(self): - scheduling_unit_draft_manual = self.create_simple_observation_scheduling_unit("scheduling unit manual low", scheduling_set=self.scheduling_set_low, - constraints={'scheduler': 'manual'}) - scheduling_unit_blueprint_manual = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_manual) - self.assertEqual(scheduling_unit_blueprint_manual.status, "schedulable") - - # call the method-under-test. - scheduled_scheduling_unit = do_dynamic_schedule() - - # we expect no scheduling_unit to be scheduled, because the only one is set to 'manual' constraint - self.assertIsNone(scheduled_scheduling_unit) - - # check the results - scheduling_unit_blueprint_manual.refresh_from_db() - self.assertEqual(scheduling_unit_blueprint_manual.status, 'schedulable') - - - def test_manually_scheduled_blocking_dynamically_scheduled(self): - scheduling_unit_draft_manual = self.create_simple_observation_scheduling_unit("scheduling unit manual low", scheduling_set=self.scheduling_set_low, - constraints={'scheduler': 'manual'}) - scheduling_unit_blueprint_manual = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_manual) - self.assertEqual(scheduling_unit_blueprint_manual.status, "schedulable") - - schedule_independent_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint_manual, datetime.utcnow()) - self.assertEqual(scheduling_unit_blueprint_manual.status, "scheduled") - - scheduling_unit_draft_high = self.create_simple_observation_scheduling_unit("scheduling unit online high", scheduling_set=self.scheduling_set_high) - scheduling_unit_blueprint_high = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_high) - - # call the method-under-test. - scheduled_scheduling_unit = do_dynamic_schedule() - - # we expect the no scheduling_unit to be scheduled, because the manual is in the way - self.assertIsNone(scheduled_scheduling_unit) - - # check the results - # we expect the sub_high to be scheduled - scheduling_unit_blueprint_high.refresh_from_db() - self.assertEqual(scheduling_unit_blueprint_high.status, 'schedulable') - - # check scheduling_unit_blueprint_low starts after the scheduled scheduling_unit_blueprint_high - self.assertGreater(scheduling_unit_blueprint_high.start_time, scheduling_unit_blueprint_manual.start_time) - - # ensure DEFAULT_INTER_OBSERVATION_GAP between them - self.assertGreaterEqual(scheduling_unit_blueprint_high.start_time - scheduling_unit_blueprint_manual.stop_time, DEFAULT_INTER_OBSERVATION_GAP) - - -class TestDailyConstraints(TestCase): - ''' - Tests for the constraint checkers used in dynamic scheduling - ''' - - def setUp(self) -> None: - # scheduling unit - self.obs_duration = 120 * 60 - scheduling_set = models.SchedulingSet.objects.create(**SchedulingSet_test_data()) - scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit("scheduling unit for ...%s" % self._testMethodName[30:], - scheduling_set=scheduling_set, - obs_duration=self.obs_duration) - self.scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft) - - # mock out conversions for speedup and assertable timestamps - # earliest_start_time requests timestamp and timestamp+1day - self.sunrise_data = { - '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(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2020, 1, 2, 17, 30, 0), "end": datetime(2020, 1, 3, 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(2020, 1, 1, 17, 45, 0), "end": datetime(2020, 1, 2, 7, 45, 0)}, {"start": datetime(2020, 1, 2, 17, 45, 0), "end": datetime(2020, 1, 3, 7, 45, 0)}]}} - - # variant for timestamp before sunrise, which returns the previous night - self.sunrise_data_early_night = { - '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)}]}, - '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 - self.sunrise_data_early_night_early_night = { - 'CS001': {"sunrise": [{"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}, {"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}], - "day": [{"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}, {"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}], - "sunset": [{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)},{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)}], - "night": [{"start": datetime(2019, 12, 31, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2019, 12, 31, 17, 30, 0), "end": datetime(2020, 1, 1, 7, 30, 0)}]}} - - self.sunrise_data_early_night_late_night = { - 'CS001': {"sunrise": [{"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}, {"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}], - "day": [{"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}, {"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}], - "sunset": [{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)},{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)}], - "night": [{"start": datetime(2019, 12, 31, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}]}} - - self.sunrise_data_late_night_late_night = { - 'CS001': {"sunrise": [{"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}, {"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}], - "day": [{"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}, {"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}], - "sunset": [{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)},{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)}], - "night": [{"start": datetime(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}]}} - - self.sunrise_data_late_night_early_night_next_day = { - '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(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}]}} - - self.sunrise_data_late_night_late_night_next_day = { - '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(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2020, 1, 2, 17, 30, 0), "end": datetime(2020, 1, 3, 7, 30, 0)}]}} - - - self.sunrise_patcher = mock.patch('lofar.sas.tmss.services.scheduling.constraints.template_constraints_v1.timestamps_and_stations_to_sun_rise_and_set') - self.sunrise_mock = self.sunrise_patcher.start() - self.sunrise_mock.return_value = self.sunrise_data - self.addCleanup(self.sunrise_patcher.stop) - - # require_day - - 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']) - - def test_get_earliest_possible_start_time_with_daytime_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']['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']) - - def test_get_earliest_possible_start_time_with_daytime_constraint_returns_timestamp(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True - self.scheduling_unit_blueprint.save() - 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) - - def test_get_earliest_possible_start_time_with_daytime_constraint_returns_next_day_start(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True - self.scheduling_unit_blueprint.save() - timestamp = datetime(2020, 1, 1, 20, 0, 0) - returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp) - self.assertEqual(returned_time, self.sunrise_data['CS001']['day'][1]['start']) - - def test_get_earliest_possible_start_time_with_daytime_constraint_returns_next_day_start_when_obs_does_not_fit(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True - self.scheduling_unit_blueprint.save() - timestamp = datetime(2020, 1, 1, 14, 0, 0) - returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp) - self.assertEqual(returned_time, self.sunrise_data['CS001']['day'][1]['start']) - - def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_daytime_constraint_returns_true(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_late_night_late_night - lower_bound = datetime(2020, 1, 1, 10, 0, 0) - upper_bound = datetime(2020, 1, 1, 15, 0, 0) - self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_daytime_constraint_returns_false_when_not_daytime(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_late_night_late_night - lower_bound = datetime(2020, 1, 1, 20, 0, 0) - upper_bound = datetime(2020, 1, 1, 23, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_daytime_constraint_returns_false_when_partially_not_daytime(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_late_night_late_night - lower_bound = datetime(2020, 1, 1, 14, 0, 0) - upper_bound = datetime(2020, 1, 1, 18, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night - lower_bound = datetime(2020, 1, 1, 8, 0, 0) - upper_bound = datetime(2020, 1, 1, 12, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - def test_can_run_within_timewindow_with_daytime_constraint_returns_correct_value(self): - # todo: for time ranges across dates, consider removing the mock for this because the moving window cannot be easily mocked - # remove other constraints: - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {} - - # set constraint to test - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True - self.scheduling_unit_blueprint.save() - - # can run in day - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night - lower_bound = datetime(2020, 1, 1, 8, 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)) - - # cannot run at night - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night - lower_bound = datetime(2020, 1, 1, 15, 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)) - - # require_night - - def test_get_earliest_possible_start_time_with_nighttime_constraint_returns_night_start(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True - self.scheduling_unit_blueprint.save() - timestamp = datetime(2020, 1, 1, 14, 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_nighttime_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']['require_night'] = True - self.scheduling_unit_blueprint.save() - timestamp = datetime(2020, 1, 1, 14, 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_nighttime_constraint_returns_timestamp(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True - self.scheduling_unit_blueprint.save() - - # late night - timestamp = datetime(2020, 1, 1, 23, 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_nighttime_constraint_returns_next_night_start_when_obs_does_not_fit(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True - self.scheduling_unit_blueprint.save() - - # early night - self.sunrise_mock.return_value = self.sunrise_data_early_night - timestamp = datetime(2020, 1, 1, 6, 0, 0) - returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp) - self.assertEqual(returned_time, self.sunrise_data_early_night['CS001']['night'][1]['start']) - - def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_nighttime_constraint_returns_true(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True - self.scheduling_unit_blueprint.save() - - # early night - self.sunrise_mock.return_value = self.sunrise_data_early_night_early_night - lower_bound = datetime(2020, 1, 1, 1, 0, 0) - upper_bound = datetime(2020, 1, 1, 3, 0, 0) - self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - # late night - 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.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - # night-night next day - self.sunrise_mock.return_value = self.sunrise_data_late_night_early_night_next_day - lower_bound = datetime(2020, 1, 1, 23, 0, 0) - upper_bound = datetime(2020, 1, 2, 3, 0, 0) - self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_nighttime_constraint_returns_false_when_not_nighttime(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = 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, 14, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_nighttime_constraint_returns_false_when_partially_not_nighttime(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True - self.scheduling_unit_blueprint.save() - - # night-day next day - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night_next_day - lower_bound = datetime(2020, 1, 1, 23, 0, 0) - upper_bound = datetime(2020, 1, 2, 10, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - # day-night next day - self.sunrise_mock.return_value = self.sunrise_data_late_night_early_night_next_day - lower_bound = datetime(2020, 1, 1, 14, 0, 0) - upper_bound = datetime(2020, 1, 2, 3, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - # day-night same day - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night - lower_bound = datetime(2020, 1, 1, 14, 0, 0) - upper_bound = datetime(2020, 1, 1, 20, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - # night-day same day - self.sunrise_mock.return_value = self.sunrise_data_early_night_late_night - lower_bound = datetime(2020, 1, 1, 3, 0, 0) - upper_bound = datetime(2020, 1, 1, 10, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - # day-night-day - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night_next_day - lower_bound = datetime(2020, 1, 1, 14, 0, 0) - upper_bound = datetime(2020, 1, 2, 10, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - # night-day-night - self.sunrise_mock.return_value = self.sunrise_data_early_night_late_night - lower_bound = datetime(2020, 1, 1, 3, 0, 0) - upper_bound = datetime(2020, 1, 1, 23, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - def test_can_run_within_timewindow_with_nighttime_constraint_returns_correct_value(self): - # todo: for time ranges across dates, consider removing the mock for this because the moving window cannot be easily mocked - # remove other constraints: - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {} - - # set constraint to test - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True - self.scheduling_unit_blueprint.save() - - # cannot run in day - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night - lower_bound = datetime(2020, 1, 1, 8, 0, 0) - upper_bound = datetime(2020, 1, 1, 15, 0, 0) - self.assertFalse(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - # can run at night - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night - lower_bound = datetime(2020, 1, 1, 15, 0, 0) - upper_bound = datetime(2020, 1, 1, 23, 0, 0) - self.assertTrue(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - - # 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_anywhere_within_timewindow_with_daily_constraints_with_twilight_constraint_returns_true(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_late_night_late_night - lower_bound = datetime(2020, 1, 1, 10, 0, 0) - upper_bound = datetime(2020, 1, 1, 15, 0, 0) - self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_twilight_constraint_returns_false_when_in_twilight(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_late_night_late_night - lower_bound = datetime(2020, 1, 1, 8, 0, 0) - upper_bound = datetime(2020, 1, 1, 9, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night - lower_bound = datetime(2020, 1, 1, 16, 0, 0) - upper_bound = datetime(2020, 1, 1, 17, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_twilight_constraint_returns_false_when_partially_in_twilight(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_late_night_late_night - lower_bound = datetime(2020, 1, 1, 10, 0, 0) - upper_bound = datetime(2020, 1, 1, 18, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night - lower_bound = datetime(2020, 1, 1, 8, 0, 0) - upper_bound = datetime(2020, 1, 1, 10, 0, 0) - self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) - - def test_can_run_within_timewindow_with_twilight_constraint_returns_correct_value(self): - # todo: for time ranges across dates, consider removing the mock for this because the moving window cannot be easily mocked - # remove other constraints: - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {} - - # set constraint to test - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True - self.scheduling_unit_blueprint.save() - - # can run in day - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night - lower_bound = datetime(2020, 1, 1, 8, 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)) - - # can run at night - self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night - lower_bound = datetime(2020, 1, 1, 15, 0, 0) - upper_bound = datetime(2020, 1, 1, 23, 0, 0) - self.assertTrue(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# +# def test_three_simple_observations_no_constraints_different_project_priority(self): +# scheduling_unit_draft_low = self.create_simple_observation_scheduling_unit("scheduling unit low", scheduling_set=self.scheduling_set_low) +# scheduling_unit_blueprint_low = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_low) +# +# scheduling_unit_draft_medium = self.create_simple_observation_scheduling_unit("scheduling unit medium", scheduling_set=self.scheduling_set_medium) +# scheduling_unit_blueprint_medium = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_medium) +# +# scheduling_unit_draft_high = self.create_simple_observation_scheduling_unit("scheduling unit high", scheduling_set=self.scheduling_set_high) +# scheduling_unit_blueprint_high = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_high) +# +# # call the method-under-test. +# scheduled_scheduling_unit = do_dynamic_schedule() +# +# # we expect the scheduling_unit with the highest project rank to be scheduled first +# self.assertIsNotNone(scheduled_scheduling_unit) +# self.assertEqual(scheduling_unit_blueprint_high.id, scheduled_scheduling_unit.id) +# +# # check the results +# # we expect the sub_high to be scheduled +# scheduling_unit_blueprint_low.refresh_from_db() +# scheduling_unit_blueprint_medium.refresh_from_db() +# scheduling_unit_blueprint_high.refresh_from_db() +# self.assertEqual(scheduling_unit_blueprint_low.status, 'schedulable') +# self.assertEqual(scheduling_unit_blueprint_medium.status, 'schedulable') +# self.assertEqual(scheduling_unit_blueprint_high.status, 'scheduled') +# +# # check the scheduled subtask +# upcoming_scheduled_subtasks = models.Subtask.objects.filter(state__value='scheduled', +# task_blueprint__scheduling_unit_blueprint__in=(scheduling_unit_blueprint_low, +# scheduling_unit_blueprint_medium, +# scheduling_unit_blueprint_high)).all() +# self.assertEqual(1, upcoming_scheduled_subtasks.count()) +# self.assertEqual(scheduling_unit_blueprint_high.id, upcoming_scheduled_subtasks[0].task_blueprint.scheduling_unit_blueprint.id) +# +# # check scheduling_unit_blueprint_low starts after the scheduled scheduling_unit_blueprint_high +# self.assertGreater(scheduling_unit_blueprint_low.start_time, scheduling_unit_blueprint_medium.start_time) +# self.assertGreater(scheduling_unit_blueprint_medium.start_time, scheduling_unit_blueprint_high.start_time) +# +# # ensure DEFAULT_INTER_OBSERVATION_GAP between them +# self.assertGreaterEqual(scheduling_unit_blueprint_medium.start_time - scheduling_unit_blueprint_high.stop_time, DEFAULT_INTER_OBSERVATION_GAP) +# self.assertGreaterEqual(scheduling_unit_blueprint_low.start_time - scheduling_unit_blueprint_medium.stop_time, DEFAULT_INTER_OBSERVATION_GAP) +# +# +# def test_time_bound_unit_wins_even_at_lower_priority(self): +# # create two schedunits, one with high one with low prio. +# # first create them without any further constraints, and check if high prio wins. +# scheduling_unit_draft_low = self.create_simple_observation_scheduling_unit("scheduling unit low", scheduling_set=self.scheduling_set_low) +# scheduling_unit_blueprint_low = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_low) +# +# scheduling_unit_draft_high = self.create_simple_observation_scheduling_unit("scheduling unit high", scheduling_set=self.scheduling_set_high) +# scheduling_unit_blueprint_high = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_high) +# +# now = datetime.utcnow() +# tomorrow = now+timedelta(days=1) +# +# # call the method-under-test. +# best_scored_scheduling_unit = find_best_next_schedulable_unit([scheduling_unit_blueprint_low, scheduling_unit_blueprint_high], now, tomorrow) +# +# # we expect the scheduling_unit with the highest project rank to be scheduled first +# self.assertEqual(scheduling_unit_blueprint_high.id, best_scored_scheduling_unit.scheduling_unit.id) +# +# #now update the low prio unit with a time constraint, "forcing" it to be run in a very thight upcoming time window. +# scheduling_unit_draft_low.scheduling_constraints_doc['time'] = { 'before': (now+scheduling_unit_draft_low.duration).isoformat()+'Z' } +# scheduling_unit_draft_low.save() +# scheduling_unit_blueprint_low.refresh_from_db() +# +# # call the method-under-test. +# best_scored_scheduling_unit = find_best_next_schedulable_unit([scheduling_unit_blueprint_low, scheduling_unit_blueprint_high], now, tomorrow) +# +# # now we expect the scheduling_unit with the lowest project rank to be scheduled first because it can only run within this limited timewindow +# self.assertEqual(scheduling_unit_draft_low.id, best_scored_scheduling_unit.scheduling_unit.id) +# +# +# # update the low prio unit. enlarge the time window constraint a bit, so both low and high prio units can fit +# # this should result that the high prio goes first, and the low prio (which now fits as well) goes second +# scheduling_unit_draft_low.scheduling_constraints_doc['time'] = { 'before': (now+scheduling_unit_draft_low.duration+scheduling_unit_draft_high.duration).isoformat()+'Z' } +# scheduling_unit_draft_low.save() +# scheduling_unit_blueprint_low.refresh_from_db() +# +# # call the method-under-test. +# best_scored_scheduling_unit = find_best_next_schedulable_unit([scheduling_unit_blueprint_low, scheduling_unit_blueprint_high], now, tomorrow) +# +# # now we expect the scheduling_unit with the lowest project rank to be scheduled first because it can only run within this limited timewindow +# self.assertEqual(scheduling_unit_blueprint_high.id, best_scored_scheduling_unit.scheduling_unit.id) +# +# # call the method-under-test again but search after first unit (should return low prio unit) +# stop_time_of_first = best_scored_scheduling_unit.start_time + best_scored_scheduling_unit.scheduling_unit.duration +# best_scored_scheduling_unit = find_best_next_schedulable_unit([scheduling_unit_blueprint_low, scheduling_unit_blueprint_high], stop_time_of_first, tomorrow) +# self.assertEqual(scheduling_unit_blueprint_low.id, best_scored_scheduling_unit.scheduling_unit.id) +# +# +# def test_manual_constraint_is_preventing_scheduling_unit_from_being_scheduled_dynamically(self): +# scheduling_unit_draft_manual = self.create_simple_observation_scheduling_unit("scheduling unit manual low", scheduling_set=self.scheduling_set_low, +# constraints={'scheduler': 'manual'}) +# scheduling_unit_blueprint_manual = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_manual) +# self.assertEqual(scheduling_unit_blueprint_manual.status, "schedulable") +# +# # call the method-under-test. +# scheduled_scheduling_unit = do_dynamic_schedule() +# +# # we expect no scheduling_unit to be scheduled, because the only one is set to 'manual' constraint +# self.assertIsNone(scheduled_scheduling_unit) +# +# # check the results +# scheduling_unit_blueprint_manual.refresh_from_db() +# self.assertEqual(scheduling_unit_blueprint_manual.status, 'schedulable') +# +# +# def test_manually_scheduled_blocking_dynamically_scheduled(self): +# scheduling_unit_draft_manual = self.create_simple_observation_scheduling_unit("scheduling unit manual low", scheduling_set=self.scheduling_set_low, +# constraints={'scheduler': 'manual'}) +# scheduling_unit_blueprint_manual = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_manual) +# self.assertEqual(scheduling_unit_blueprint_manual.status, "schedulable") +# +# schedule_independent_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint_manual, datetime.utcnow()) +# self.assertEqual(scheduling_unit_blueprint_manual.status, "scheduled") +# +# scheduling_unit_draft_high = self.create_simple_observation_scheduling_unit("scheduling unit online high", scheduling_set=self.scheduling_set_high) +# scheduling_unit_blueprint_high = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft_high) +# +# # call the method-under-test. +# scheduled_scheduling_unit = do_dynamic_schedule() +# +# # we expect the no scheduling_unit to be scheduled, because the manual is in the way +# self.assertIsNone(scheduled_scheduling_unit) +# +# # check the results +# # we expect the sub_high to be scheduled +# scheduling_unit_blueprint_high.refresh_from_db() +# self.assertEqual(scheduling_unit_blueprint_high.status, 'schedulable') +# +# # check scheduling_unit_blueprint_low starts after the scheduled scheduling_unit_blueprint_high +# self.assertGreater(scheduling_unit_blueprint_high.start_time, scheduling_unit_blueprint_manual.start_time) +# +# # ensure DEFAULT_INTER_OBSERVATION_GAP between them +# self.assertGreaterEqual(scheduling_unit_blueprint_high.start_time - scheduling_unit_blueprint_manual.stop_time, DEFAULT_INTER_OBSERVATION_GAP) +# +# +# class TestDailyConstraints(TestCase): +# ''' +# Tests for the constraint checkers used in dynamic scheduling +# ''' +# +# def setUp(self) -> None: +# # scheduling unit +# self.obs_duration = 120 * 60 +# scheduling_set = models.SchedulingSet.objects.create(**SchedulingSet_test_data()) +# scheduling_unit_draft = TestDynamicScheduling.create_simple_observation_scheduling_unit("scheduling unit for ...%s" % self._testMethodName[30:], +# scheduling_set=scheduling_set, +# obs_duration=self.obs_duration) +# self.scheduling_unit_blueprint = create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft) +# +# # mock out conversions for speedup and assertable timestamps +# # earliest_start_time requests timestamp and timestamp+1day +# self.sunrise_data = { +# '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(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2020, 1, 2, 17, 30, 0), "end": datetime(2020, 1, 3, 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(2020, 1, 1, 17, 45, 0), "end": datetime(2020, 1, 2, 7, 45, 0)}, {"start": datetime(2020, 1, 2, 17, 45, 0), "end": datetime(2020, 1, 3, 7, 45, 0)}]}} +# +# # variant for timestamp before sunrise, which returns the previous night +# self.sunrise_data_early_night = { +# '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)}]}, +# '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 +# self.sunrise_data_early_night_early_night = { +# 'CS001': {"sunrise": [{"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}, {"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}], +# "day": [{"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}, {"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}], +# "sunset": [{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)},{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)}], +# "night": [{"start": datetime(2019, 12, 31, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2019, 12, 31, 17, 30, 0), "end": datetime(2020, 1, 1, 7, 30, 0)}]}} +# +# self.sunrise_data_early_night_late_night = { +# 'CS001': {"sunrise": [{"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}, {"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}], +# "day": [{"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}, {"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}], +# "sunset": [{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)},{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)}], +# "night": [{"start": datetime(2019, 12, 31, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}]}} +# +# self.sunrise_data_late_night_late_night = { +# 'CS001': {"sunrise": [{"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}, {"start": datetime(2020, 1, 1, 7, 30, 0), "end": datetime(2020, 1, 1, 9, 30, 0)}], +# "day": [{"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}, {"start": datetime(2020, 1, 1, 9, 30, 0), "end": datetime(2020, 1, 1, 15, 30, 0)}], +# "sunset": [{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)},{"start": datetime(2020, 1, 1, 15, 30, 0), "end": datetime(2020, 1, 1, 17, 30, 0)}], +# "night": [{"start": datetime(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}]}} +# +# self.sunrise_data_late_night_early_night_next_day = { +# '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(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}]}} +# +# self.sunrise_data_late_night_late_night_next_day = { +# '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(2020, 1, 1, 17, 30, 0), "end": datetime(2020, 1, 2, 7, 30, 0)}, {"start": datetime(2020, 1, 2, 17, 30, 0), "end": datetime(2020, 1, 3, 7, 30, 0)}]}} +# +# +# self.sunrise_patcher = mock.patch('lofar.sas.tmss.services.scheduling.constraints.template_constraints_v1.timestamps_and_stations_to_sun_rise_and_set') +# self.sunrise_mock = self.sunrise_patcher.start() +# self.sunrise_mock.return_value = self.sunrise_data +# self.addCleanup(self.sunrise_patcher.stop) +# +# # require_day +# +# 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']) +# +# def test_get_earliest_possible_start_time_with_daytime_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']['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']) +# +# def test_get_earliest_possible_start_time_with_daytime_constraint_returns_timestamp(self): +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True +# self.scheduling_unit_blueprint.save() +# 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) +# +# def test_get_earliest_possible_start_time_with_daytime_constraint_returns_next_day_start(self): +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True +# self.scheduling_unit_blueprint.save() +# timestamp = datetime(2020, 1, 1, 20, 0, 0) +# returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp) +# self.assertEqual(returned_time, self.sunrise_data['CS001']['day'][1]['start']) +# +# def test_get_earliest_possible_start_time_with_daytime_constraint_returns_next_day_start_when_obs_does_not_fit(self): +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True +# self.scheduling_unit_blueprint.save() +# timestamp = datetime(2020, 1, 1, 14, 0, 0) +# returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp) +# self.assertEqual(returned_time, self.sunrise_data['CS001']['day'][1]['start']) +# +# def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_daytime_constraint_returns_true(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_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 10, 0, 0) +# upper_bound = datetime(2020, 1, 1, 15, 0, 0) +# self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_daytime_constraint_returns_false_when_not_daytime(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_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 20, 0, 0) +# upper_bound = datetime(2020, 1, 1, 23, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_daytime_constraint_returns_false_when_partially_not_daytime(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_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 14, 0, 0) +# upper_bound = datetime(2020, 1, 1, 18, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 8, 0, 0) +# upper_bound = datetime(2020, 1, 1, 12, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# def test_can_run_within_timewindow_with_daytime_constraint_returns_correct_value(self): +# # todo: for time ranges across dates, consider removing the mock for this because the moving window cannot be easily mocked +# # remove other constraints: +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {} +# +# # set constraint to test +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True +# self.scheduling_unit_blueprint.save() +# +# # can run in day +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 8, 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)) +# +# # cannot run at night +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 15, 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)) +# +# # require_night +# +# def test_get_earliest_possible_start_time_with_nighttime_constraint_returns_night_start(self): +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True +# self.scheduling_unit_blueprint.save() +# timestamp = datetime(2020, 1, 1, 14, 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_nighttime_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']['require_night'] = True +# self.scheduling_unit_blueprint.save() +# timestamp = datetime(2020, 1, 1, 14, 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_nighttime_constraint_returns_timestamp(self): +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True +# self.scheduling_unit_blueprint.save() +# +# # late night +# timestamp = datetime(2020, 1, 1, 23, 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_nighttime_constraint_returns_next_night_start_when_obs_does_not_fit(self): +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True +# self.scheduling_unit_blueprint.save() +# +# # early night +# self.sunrise_mock.return_value = self.sunrise_data_early_night +# timestamp = datetime(2020, 1, 1, 6, 0, 0) +# returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp) +# self.assertEqual(returned_time, self.sunrise_data_early_night['CS001']['night'][1]['start']) +# +# def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_nighttime_constraint_returns_true(self): +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True +# self.scheduling_unit_blueprint.save() +# +# # early night +# self.sunrise_mock.return_value = self.sunrise_data_early_night_early_night +# lower_bound = datetime(2020, 1, 1, 1, 0, 0) +# upper_bound = datetime(2020, 1, 1, 3, 0, 0) +# self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# # late night +# 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.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# # night-night next day +# self.sunrise_mock.return_value = self.sunrise_data_late_night_early_night_next_day +# lower_bound = datetime(2020, 1, 1, 23, 0, 0) +# upper_bound = datetime(2020, 1, 2, 3, 0, 0) +# self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_nighttime_constraint_returns_false_when_not_nighttime(self): +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = 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, 14, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_nighttime_constraint_returns_false_when_partially_not_nighttime(self): +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True +# self.scheduling_unit_blueprint.save() +# +# # night-day next day +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night_next_day +# lower_bound = datetime(2020, 1, 1, 23, 0, 0) +# upper_bound = datetime(2020, 1, 2, 10, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# # day-night next day +# self.sunrise_mock.return_value = self.sunrise_data_late_night_early_night_next_day +# lower_bound = datetime(2020, 1, 1, 14, 0, 0) +# upper_bound = datetime(2020, 1, 2, 3, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# # day-night same day +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 14, 0, 0) +# upper_bound = datetime(2020, 1, 1, 20, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# # night-day same day +# self.sunrise_mock.return_value = self.sunrise_data_early_night_late_night +# lower_bound = datetime(2020, 1, 1, 3, 0, 0) +# upper_bound = datetime(2020, 1, 1, 10, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# # day-night-day +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night_next_day +# lower_bound = datetime(2020, 1, 1, 14, 0, 0) +# upper_bound = datetime(2020, 1, 2, 10, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# # night-day-night +# self.sunrise_mock.return_value = self.sunrise_data_early_night_late_night +# lower_bound = datetime(2020, 1, 1, 3, 0, 0) +# upper_bound = datetime(2020, 1, 1, 23, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# def test_can_run_within_timewindow_with_nighttime_constraint_returns_correct_value(self): +# # todo: for time ranges across dates, consider removing the mock for this because the moving window cannot be easily mocked +# # remove other constraints: +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {} +# +# # set constraint to test +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True +# self.scheduling_unit_blueprint.save() +# +# # cannot run in day +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 8, 0, 0) +# upper_bound = datetime(2020, 1, 1, 15, 0, 0) +# self.assertFalse(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# # can run at night +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 15, 0, 0) +# upper_bound = datetime(2020, 1, 1, 23, 0, 0) +# self.assertTrue(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# +# # 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_anywhere_within_timewindow_with_daily_constraints_with_twilight_constraint_returns_true(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_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 10, 0, 0) +# upper_bound = datetime(2020, 1, 1, 15, 0, 0) +# self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_twilight_constraint_returns_false_when_in_twilight(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_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 8, 0, 0) +# upper_bound = datetime(2020, 1, 1, 9, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 16, 0, 0) +# upper_bound = datetime(2020, 1, 1, 17, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_twilight_constraint_returns_false_when_partially_in_twilight(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_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 10, 0, 0) +# upper_bound = datetime(2020, 1, 1, 18, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 8, 0, 0) +# upper_bound = datetime(2020, 1, 1, 10, 0, 0) +# self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound)) +# +# def test_can_run_within_timewindow_with_twilight_constraint_returns_correct_value(self): +# # todo: for time ranges across dates, consider removing the mock for this because the moving window cannot be easily mocked +# # remove other constraints: +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {} +# +# # set constraint to test +# self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True +# self.scheduling_unit_blueprint.save() +# +# # can run in day +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 8, 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)) +# +# # can run at night +# self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night +# lower_bound = datetime(2020, 1, 1, 15, 0, 0) +# upper_bound = datetime(2020, 1, 1, 23, 0, 0) +# self.assertTrue(can_run_within_timewindow(self.scheduling_unit_blueprint, lower_bound, upper_bound)) class TestSkyConstraints(unittest.TestCase): @@ -733,22 +733,45 @@ class TestSkyConstraints(unittest.TestCase): self.distance_mock = self.distance_patcher.start() self.distance_mock.return_value = self.distance_data self.addCleanup(self.distance_patcher.stop) + + self.target_rise_and_set_data = {"CS002": [{"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0)}, + {"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0)}]} + self.target_rise_and_set_patcher = mock.patch('lofar.sas.tmss.services.scheduling.constraints.template_constraints_v1.coordinates_timestamps_and_stations_to_target_rise_and_set') + self.target_rise_and_set_mock = self.target_rise_and_set_patcher.start() + self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data + self.addCleanup(self.target_rise_and_set_patcher.stop) # min_distance def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_distance_constraint_returns_true_when_met(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky']['min_distance'] = {'sun': 0.1, 'moon': 0.1, 'jupiter': 0.1} + self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_distance': {'sun': 0.1, 'moon': 0.1, 'jupiter': 0.1}} self.scheduling_unit_blueprint.save() timestamp = datetime(2020, 1, 1, 10, 0, 0) returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration)) self.assertTrue(returned_value) def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_distance_constraint_returns_false_when_not_met(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky']['min_distance'] = {'sun': 0.2, 'moon': 0.2, 'jupiter': 0.2} + self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_distance': {'sun': 0.2, 'moon': 0.2, 'jupiter': 0.2}} self.scheduling_unit_blueprint.save() timestamp = datetime(2020, 1, 1, 10, 0, 0) returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration)) self.assertFalse(returned_value) + + # min_target_elevation + + def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_true_when_met(self): + self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1} + self.scheduling_unit_blueprint.save() + timestamp = datetime(2020, 1, 1, 10, 0, 0) + returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration)) + self.assertTrue(returned_value) + + def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_false_when_not_met(self): + self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.2} + self.scheduling_unit_blueprint.save() + timestamp = datetime(2020, 1, 1, 11, 0, 0) + returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration)) + self.assertFalse(returned_value) logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO) diff --git a/SAS/TMSS/src/tmss/tmssapp/conversions.py b/SAS/TMSS/src/tmss/tmssapp/conversions.py index 699dae9ca39872c02511cbf7cc722adc16c32c9d..40765b6998575b0cdccb3b1d11c113c527f64cb3 100644 --- a/SAS/TMSS/src/tmss/tmssapp/conversions.py +++ b/SAS/TMSS/src/tmss/tmssapp/conversions.py @@ -127,8 +127,8 @@ def coordinates_timestamps_and_stations_to_target_rise_and_set(angle1: float, an :param angle_to_horizon: the angle between horizon and given coordinates for which rise and set times are returned :return A dict that maps station names to a list of dicts with rise and set times for each requested date. E.g. - {"CS002": [{"rise": datetime(2020, 1, 1, 4, 0, 0)), "set": datetime(2020, 1, 1, 11, 0, 0)}, - {"rise": datetime(2020, 1, 2, 4, 0, 0)), "set": datetime(2020, 1, 2, 11, 0, 0)}] + {"CS002": [{"rise": datetime(2020, 1, 1, 4, 0, 0), "set": datetime(2020, 1, 1, 11, 0, 0)}, + {"rise": datetime(2020, 1, 2, 4, 0, 0), "set": datetime(2020, 1, 2, 11, 0, 0)}] } """ if direction_type == "J2000":