diff --git a/SAS/TMSS/backend/services/scheduling/lib/constraints/__init__.py b/SAS/TMSS/backend/services/scheduling/lib/constraints/__init__.py index 85e452ae48330a0ca82348f8dddf3805ce34ae2f..783dd86c11c1187042cc3fa490753338f77aff1a 100644 --- a/SAS/TMSS/backend/services/scheduling/lib/constraints/__init__.py +++ b/SAS/TMSS/backend/services/scheduling/lib/constraints/__init__.py @@ -68,7 +68,7 @@ def filter_scheduling_units_using_constraints(scheduling_units: [models.Scheduli for scheduling_unit in scheduling_units: try: - if scheduling_unit.draft is None or scheduling_unit.draft.scheduling_constraints_template is None: + if scheduling_unit.draft is None or scheduling_unit.scheduling_constraints_template is None: logger.warning("cannot dynamically schedule scheduling_unit id=%s name='%s' because it has not constraints template", scheduling_unit.id, scheduling_unit.name) continue @@ -168,7 +168,7 @@ def sort_scheduling_units_scored_by_constraints(scheduling_units: [models.Schedu def can_run_within_timewindow(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime, upper_bound: datetime) -> bool: '''Check if the given scheduling_unit can run somewhere within the given time window depending on the sub's constrains-template/doc.''' - constraints_template = scheduling_unit.draft.scheduling_constraints_template + constraints_template = scheduling_unit.scheduling_constraints_template # choose appropriate method based on template (strategy pattern), or raise if constraints_template.name == 'constraints' and constraints_template.version == 1: @@ -184,7 +184,7 @@ def can_run_within_timewindow(scheduling_unit: models.SchedulingUnitBlueprint, l def can_run_after(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime) -> bool: '''Check if the given scheduling_unit can run somewhere after the given lowerbound timestamp depending on the sub's constrains-template/doc.''' - constraints_template = scheduling_unit.draft.scheduling_constraints_template + constraints_template = scheduling_unit.scheduling_constraints_template # choose appropriate method based on template (strategy pattern), or raise if constraints_template.name == 'constraints' and constraints_template.version == 1: @@ -201,7 +201,7 @@ def can_run_after(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: def compute_scores(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound:datetime, upper_bound:datetime) -> ScoredSchedulingUnit: '''Compute the "fitness" scores per constraint for the given scheduling_unit at the given starttime depending on the sub's constrains-template/doc.''' - constraints_template = scheduling_unit.draft.scheduling_constraints_template + constraints_template = scheduling_unit.scheduling_constraints_template # choose appropriate method based on template (strategy pattern), or raise if constraints_template.name == 'constraints' and constraints_template.version == 1: @@ -217,7 +217,7 @@ def compute_scores(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime) -> datetime: '''determine the earliest possible start_time for the given scheduling unit, taking into account all its constraints''' - constraints_template = scheduling_unit.draft.scheduling_constraints_template + constraints_template = scheduling_unit.scheduling_constraints_template # choose appropriate method based on template (strategy pattern), or raise if constraints_template.name == 'constraints' and constraints_template.version == 1: @@ -234,7 +234,7 @@ def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBluep def get_min_earliest_possible_start_time(scheduling_units: [models.SchedulingUnitBlueprint], lower_bound: datetime) -> datetime: '''deterimine the earliest possible starttime over all given scheduling units, taking into account all their constraints''' try: - return min(get_earliest_possible_start_time(scheduling_unit, lower_bound) for scheduling_unit in scheduling_units if scheduling_unit.draft.scheduling_constraints_template is not None) + return min(get_earliest_possible_start_time(scheduling_unit, lower_bound) for scheduling_unit in scheduling_units if scheduling_unit.scheduling_constraints_template is not None) except ValueError: return lower_bound diff --git a/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py b/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py index 594c088ecd651b9b9e7982df30a9e88b81526903..7dcb48aef02d519b0aab7fb8bc620a6eb7028a9d 100644 --- a/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py +++ b/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py @@ -61,7 +61,7 @@ def can_run_within_timewindow(scheduling_unit: models.SchedulingUnitBlueprint, l def can_run_after(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime) -> bool: '''Check if the given scheduling_unit can run somewhere after the given lowerbound timestamp depending on the sub's constrains-template/doc.''' - constraints = scheduling_unit.draft.scheduling_constraints_doc + constraints = scheduling_unit.scheduling_constraints_doc if 'before' in constraints['time']: before = parser.parse(constraints['time']['before'], ignoretz=True) return before > lower_bound @@ -74,7 +74,7 @@ __all__ = ['can_run_within_timewindow', 'can_run_after'] def has_manual_scheduler_constraint(scheduling_unit: models.SchedulingUnitBlueprint) -> bool: '''evaluate the scheduler contraint. Should this unit be manually scheduled?''' - constraints = scheduling_unit.draft.scheduling_constraints_doc + constraints = scheduling_unit.scheduling_constraints_doc return constraints.get('scheduler', '') == 'manual' @@ -101,7 +101,7 @@ def can_run_anywhere_within_timewindow_with_daily_constraints(scheduling_unit: m :return: True if all daily constraints are met over the entire time window, else False. """ main_observation_task_name = get_target_observation_task_name_from_requirements_doc(scheduling_unit) - constraints = scheduling_unit.draft.scheduling_constraints_doc + constraints = scheduling_unit.scheduling_constraints_doc if constraints['daily']['require_day'] or constraints['daily']['require_night'] or constraints['daily']['avoid_twilight']: if (upper_bound - lower_bound).days >= 1: @@ -158,7 +158,7 @@ def can_run_within_timewindow_with_time_constraints(scheduling_unit: models.Sche constraints are met over the runtime of the observation, else False. """ main_observation_task_name = get_target_observation_task_name_from_requirements_doc(scheduling_unit) - constraints = scheduling_unit.draft.scheduling_constraints_doc + constraints = scheduling_unit.scheduling_constraints_doc # Check the 'at' constraint and then only check can_run_anywhere for the single possible time window if 'at' in constraints['time']: @@ -189,7 +189,7 @@ def can_run_anywhere_within_timewindow_with_time_constraints(scheduling_unit: mo can_run_with_after = True can_run_between = True can_run_not_between = True - constraints = scheduling_unit.draft.scheduling_constraints_doc + constraints = scheduling_unit.scheduling_constraints_doc # given time window needs to end before constraint if 'before' in constraints['time']: @@ -254,7 +254,7 @@ def can_run_anywhere_within_timewindow_with_sky_constraints(scheduling_unit: mod # TODO: remove this shortcut after demo return True - constraints = scheduling_unit.draft.scheduling_constraints_doc + constraints = scheduling_unit.scheduling_constraints_doc if not "sky" in constraints: return True @@ -312,7 +312,7 @@ def get_target_observation_task_name_from_requirements_doc(scheduling_unit: mode def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime) -> datetime: - constraints = scheduling_unit.draft.scheduling_constraints_doc + constraints = scheduling_unit.scheduling_constraints_doc main_observation_task_name = get_target_observation_task_name_from_requirements_doc(scheduling_unit) duration = timedelta(seconds=scheduling_unit.requirements_doc['tasks'][main_observation_task_name]['specifications_doc']['duration']) @@ -383,7 +383,7 @@ def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBluep def compute_scores(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound:datetime, upper_bound:datetime) -> ScoredSchedulingUnit: '''Compute the "fitness" scores per constraint for the given scheduling_unit at the given starttime depending on the sub's constrains-template/doc.''' - constraints = scheduling_unit.draft.scheduling_constraints_doc + constraints = scheduling_unit.scheduling_constraints_doc # TODO: add compute_scores methods for each type of constraint # TODO: take start_time into account. For example, an LST constraint yields a better score when the starttime is such that the center of the obs is at LST. diff --git a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py index 3b45ac16bd908ccd1a845b0b63876b4c2039b073..5ff4971b7f719615583eaf50ad3aaf5b86d27f92 100644 --- a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py +++ b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py @@ -298,8 +298,7 @@ def get_dynamically_schedulable_scheduling_units() -> [models.SchedulingUnitBlue defined_independend_subtasks = models.Subtask.independent_subtasks().filter(state__value='defined') defined_independend_subtask_ids = defined_independend_subtasks.values('task_blueprints__scheduling_unit_blueprint_id').distinct().all() scheduling_units = models.SchedulingUnitBlueprint.objects.filter(id__in=defined_independend_subtask_ids) \ - .filter(draft__scheduling_constraints_template__isnull=False) \ - .select_related('draft', 'draft__scheduling_constraints_template').all() + .filter(scheduling_constraints_template__isnull=False).all() return [su for su in scheduling_units if su.status == 'schedulable'] diff --git a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py index 82bd9243e1897bd246367eb96ebb97f88dc927a5..77e62b3c4a2b4a9505563747c6f78c5c9fb1eaa9 100755 --- a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py +++ b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py @@ -426,7 +426,7 @@ class TestDailyConstraints(TestCase): # 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.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) @@ -435,7 +435,7 @@ class TestDailyConstraints(TestCase): 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']['station_groups'] = [{'stations': ['CS001', 'DE601']}] - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True + self.scheduling_unit_blueprint.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) @@ -443,28 +443,28 @@ class TestDailyConstraints(TestCase): 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.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.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.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.scheduling_constraints_doc['daily']['require_day'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night @@ -473,7 +473,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['require_day'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night @@ -482,7 +482,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['require_day'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night @@ -498,10 +498,10 @@ class TestDailyConstraints(TestCase): 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'] = {} + self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {} # set constraint to test - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True + self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_day'] = True self.scheduling_unit_blueprint.save() # can run in day @@ -519,7 +519,7 @@ class TestDailyConstraints(TestCase): # 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.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) @@ -527,14 +527,14 @@ class TestDailyConstraints(TestCase): 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']['station_groups'] = [{'stations': ['CS001', 'DE601']}] - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True + self.scheduling_unit_blueprint.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.scheduling_constraints_doc['daily']['require_night'] = True self.scheduling_unit_blueprint.save() # late night @@ -549,7 +549,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['require_night'] = True self.scheduling_unit_blueprint.save() # early night @@ -559,7 +559,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['require_night'] = True self.scheduling_unit_blueprint.save() # early night @@ -581,7 +581,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['require_night'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night @@ -590,7 +590,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['require_night'] = True self.scheduling_unit_blueprint.save() # night-day next day @@ -632,10 +632,10 @@ class TestDailyConstraints(TestCase): 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'] = {} + self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {} # set constraint to test - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True + self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_night'] = True self.scheduling_unit_blueprint.save() # cannot run in day @@ -654,7 +654,7 @@ class TestDailyConstraints(TestCase): # 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.scheduling_constraints_doc['daily']['avoid_twilight'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data_early_night @@ -664,7 +664,7 @@ class TestDailyConstraints(TestCase): 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']['station_groups'] = [{'stations': ['CS001', 'DE601']}] - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True + self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data_early_night @@ -673,7 +673,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['avoid_twilight'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data @@ -683,7 +683,7 @@ class TestDailyConstraints(TestCase): 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']['station_groups'] = [{'stations': ['CS001', 'DE601']}] - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True + self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data @@ -692,7 +692,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['avoid_twilight'] = True self.scheduling_unit_blueprint.save() # daytime @@ -712,7 +712,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['avoid_twilight'] = True self.scheduling_unit_blueprint.save() timestamp = datetime(2020, 1, 1, 15, 0, 0) @@ -725,7 +725,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['avoid_twilight'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night @@ -734,7 +734,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['avoid_twilight'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night @@ -748,7 +748,7 @@ class TestDailyConstraints(TestCase): 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.scheduling_constraints_doc['daily']['avoid_twilight'] = True self.scheduling_unit_blueprint.save() self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night @@ -764,10 +764,10 @@ class TestDailyConstraints(TestCase): 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'] = {} + self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {} # set constraint to test - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True + self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True self.scheduling_unit_blueprint.save() # can run in day @@ -821,14 +821,14 @@ class TestSkyConstraints(unittest.TestCase): # 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.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.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)) @@ -837,14 +837,14 @@ class TestSkyConstraints(unittest.TestCase): # 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.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.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)) @@ -863,28 +863,28 @@ class TestTimeConstraints(TestCase): """ def add_time_at_constraint(self, at_timestamp): - lst_at_constraint = self.scheduling_unit_blueprint.draft.scheduling_constraints_doc + lst_at_constraint = self.scheduling_unit_blueprint.scheduling_constraints_doc lst_at_constraint['time']['at'] = at_timestamp.isoformat() self.scheduling_unit_blueprint.save() def add_time_between_constraint(self, from_timestamp, to_timestamp): - lst_between_constraints = self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["between"] + lst_between_constraints = self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["between"] time_constraint_dict = {"from": from_timestamp.isoformat(), "to": to_timestamp.isoformat()} lst_between_constraints.append(time_constraint_dict) self.scheduling_unit_blueprint.save() def add_time_not_between_constraint(self, from_timestamp, to_timestamp): - lst_between_constraints = self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["not_between"] + lst_between_constraints = self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["not_between"] time_constraint_dict = {"from": from_timestamp.isoformat(), "to": to_timestamp.isoformat()} lst_between_constraints.append(time_constraint_dict) self.scheduling_unit_blueprint.save() def clear_time_constraints(self): - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["between"] = [] - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["not_between"] = [] - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time'].pop('at', None) - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time'].pop("before", None) - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time'].pop('after', None) + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["between"] = [] + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["not_between"] = [] + self.scheduling_unit_blueprint.scheduling_constraints_doc['time'].pop('at', None) + self.scheduling_unit_blueprint.scheduling_constraints_doc['time'].pop("before", None) + self.scheduling_unit_blueprint.scheduling_constraints_doc['time'].pop('after', None) def setUp(self) -> None: # scheduling unit @@ -902,7 +902,7 @@ class TestTimeConstraints(TestCase): # Set datetime constraints before lower_bound self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat() self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) @@ -911,28 +911,28 @@ class TestTimeConstraints(TestCase): # Set datetime constraints equal to lower_bound self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 12, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 12, 0, 0).isoformat() self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) # Set datetime constraints after lower_bound self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) # Set datetime constraints to upper_bound self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 2, 12, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 2, 12, 0, 0).isoformat() self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) # Set datetime constraints after upper_bound self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 2, 13, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 2, 13, 0, 0).isoformat() self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) @@ -941,14 +941,14 @@ class TestTimeConstraints(TestCase): # Set datetime constraints before lower bounds, but with too short window for obs duration self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat() self.assertFalse(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 1, 13, 0, 0))) # Set datetime constraints after lower bounds, and with too little space left in window for obs duration self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 14, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 14, 0, 0).isoformat() self.assertFalse(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 1, 15, 0, 0))) @@ -957,14 +957,14 @@ class TestTimeConstraints(TestCase): # Set datetime constraints before lower bounds, and with sufficient window for obs duration self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat() self.assertTrue(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 1, 14, 0, 0))) # Set datetime constraints after lower bounds, but with sufficient space left in window for obs duration self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() self.assertTrue(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 1, 16, 0, 0))) @@ -975,27 +975,27 @@ class TestTimeConstraints(TestCase): # Set datetime constraints before lower_bound self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 11, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 11, 0, 0).isoformat() self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) # Set datetime constraints equal to lower_bound self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 12, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 12, 0, 0).isoformat() self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) # Set datetime constraints after lower_bound self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) # Set datetime constraints equal to upper_bound self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 12, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 12, 0, 0).isoformat() self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) @@ -1005,7 +1005,7 @@ class TestTimeConstraints(TestCase): # Set datetime constraints after upper_bound self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat() self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) @@ -1014,14 +1014,14 @@ class TestTimeConstraints(TestCase): # Set datetime constraints after upper bound, but with too short window for obs duration self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat() self.assertFalse(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 2, 11, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) # Set datetime constraints after lower bound, and with too little space left in window for obs duration self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() self.assertFalse(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) @@ -1030,14 +1030,14 @@ class TestTimeConstraints(TestCase): # Set datetime constraints after upper bounds, and with sufficient window for obs duration self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat() self.assertTrue(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) # Set datetime constraints after lower bounds, but with sufficient space left in window for obs duration self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 0).isoformat() self.assertTrue(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint, datetime(2020, 1, 1, 12, 0, 0), datetime(2020, 1, 2, 12, 0, 0))) @@ -1318,22 +1318,22 @@ class TestTimeConstraints(TestCase): # Set before and after constraint with sufficient gap to fit observation, and assert True self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 12, 59, 59).isoformat() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 1).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 12, 59, 59).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 1).isoformat() self.assertTrue(self.execute_can_run_within_timewindow_with_time_constraints_of_24hour_boundary()) # set before and after constraint with slightly smaller gap for observation, and assert False self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 0).isoformat() self.assertFalse(self.execute_can_run_within_timewindow_with_time_constraints_of_24hour_boundary()) # set before and after constraint with large gap # then and add additional between and not between constraints until window is blocked # can run 13-8h self.clear_time_constraints() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 8, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 8, 0, 0).isoformat() self.assertTrue(self.execute_can_run_within_timewindow_with_time_constraints_of_24hour_boundary()) # can run 13h-20h @@ -1353,7 +1353,7 @@ class TestTimeConstraints(TestCase): self.assertTrue(self.execute_can_run_within_timewindow_with_time_constraints_of_24hour_boundary()) # move before constraint, can not run anymore - self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 5, 0, 0).isoformat() + self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 5, 0, 0).isoformat() self.assertFalse(self.execute_can_run_within_timewindow_with_time_constraints_of_24hour_boundary()) diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py index e2cb08e37434b494c8b502cde395f9ef10510dcb..0110ee6adaa831b503f1b8b42ea28f0ed7a6d0d2 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.9 on 2021-04-08 14:57 +# Generated by Django 3.0.9 on 2021-04-28 21:14 from django.conf import settings import django.contrib.postgres.fields @@ -617,9 +617,10 @@ class Migration(migrations.Migration): ('ingest_permission_granted_since', models.DateTimeField(help_text='The moment when ingest permission was granted.', null=True)), ('output_pinned', models.BooleanField(default=False, help_text='boolean (default FALSE), which blocks deleting unpinned dataproducts. When toggled ON, backend must pick SUB up for deletion. It also must when dataproducts are unpinned.')), ('results_accepted', models.BooleanField(default=False, help_text='boolean (default NULL), which records whether the results were accepted, allowing the higher-level accounting to be adjusted.')), - ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')), ('piggyback_allowed_tbb', models.BooleanField(help_text='Piggyback key for TBB.', null=True)), ('piggyback_allowed_aartfaac', models.BooleanField(help_text='Piggyback key for AARTFAAC.', null=True)), + ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')), + ('scheduling_constraints_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Scheduling Constraints for this run.', null=True)), ], options={ 'abstract': False, @@ -639,9 +640,9 @@ class Migration(migrations.Migration): ('generator_instance_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Parameter value that generated this run draft (NULLable).', null=True)), ('scheduling_constraints_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Scheduling Constraints for this run.', null=True)), ('ingest_permission_required', models.BooleanField(default=False, help_text='Explicit permission is needed before the task.')), - ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')), ('piggyback_allowed_tbb', models.BooleanField(help_text='Piggyback key for TBB.', null=True)), ('piggyback_allowed_aartfaac', models.BooleanField(help_text='Piggyback key for AARTFAAC.', null=True)), + ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')), ], options={ 'abstract': False, @@ -1251,6 +1252,11 @@ class Migration(migrations.Migration): name='requirements_template', field=models.ForeignKey(help_text='Schema used for requirements_doc (IMMUTABLE).', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.SchedulingUnitTemplate'), ), + migrations.AddField( + model_name='schedulingunitblueprint', + name='scheduling_constraints_template', + field=models.ForeignKey(help_text='Schema used for scheduling_constraints_doc.', null=True, on_delete=django.db.models.deletion.CASCADE, to='tmssapp.SchedulingConstraintsTemplate'), + ), migrations.AddField( model_name='schedulingset', name='generator_source', diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py index b927f609a143033d3169b34b1d4a30bcd7bb3360..ada071a865bdf4f336164fa504e62eb9f7083a87 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py @@ -17,7 +17,7 @@ from django.core.exceptions import ValidationError import datetime from collections import Counter from django.utils.functional import cached_property - +from lofar.sas.tmss.tmss.exceptions import TMSSException # # Mixins @@ -480,6 +480,8 @@ class SchedulingUnitBlueprint(RefreshFromDbInvalidatesCachedPropertiesMixin, Tem SCHEDULED = "scheduled" SCHEDULABLE = "schedulable" + # todo: are many of these fields supposed to be immutable in the database? + # Or are we fine to just not allow most users to change them? requirements_doc = JSONField(help_text='Scheduling and/or quality requirements for this scheduling unit (IMMUTABLE).') do_cancel = BooleanField() ingest_permission_required = BooleanField(default=False, help_text='Explicit permission is needed before the task.') @@ -492,19 +494,40 @@ class SchedulingUnitBlueprint(RefreshFromDbInvalidatesCachedPropertiesMixin, Tem piggyback_allowed_aartfaac = BooleanField(help_text='Piggyback key for AARTFAAC.', null=True) priority_rank = FloatField(null=False, default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.') priority_queue = ForeignKey('PriorityQueueType', null=False, on_delete=PROTECT, default="A", help_text='Priority queue of this scheduling unit. Queues provide a strict ordering between scheduling units.') + scheduling_constraints_doc = JSONField(help_text='Scheduling Constraints for this run.', null=True) + scheduling_constraints_template = ForeignKey('SchedulingConstraintsTemplate', on_delete=CASCADE, null=True, help_text='Schema used for scheduling_constraints_doc.') + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # keep original scheduling constraints to detect changes on save + # Note: we cannot use self.scheduling_constraints_doc here since that causes an infinite loop of update_from_db + if 'scheduling_constraints_doc' in kwargs.keys(): + self.__original_scheduling_constraints_doc = kwargs['scheduling_constraints_doc'] + else: + self.__original_scheduling_constraints_doc = None + self.__original_scheduling_constraints_template_id = self.scheduling_constraints_template_id def save(self, force_insert=False, force_update=False, using=None, update_fields=None): self.annotate_validate_add_defaults_to_doc_using_template('requirements_doc', 'requirements_template') - - # This code only happens if the objects is not in the database yet. self._state.adding is True creating - if self._state.adding and hasattr(self, 'draft'): - self.ingest_permission_required = self.draft.ingest_permission_required - - # Propagate scheduling_unit_draft piggyback values as default for scheduling_unit_blueprint - if self._state.adding and self.piggyback_allowed_tbb is None and hasattr(self, 'draft'): - self.piggyback_allowed_tbb = self.draft.piggyback_allowed_tbb - if self._state.adding and self.piggyback_allowed_aartfaac is None and hasattr(self, 'draft'): - self.piggyback_allowed_aartfaac = self.draft.piggyback_allowed_aartfaac + + if self._state.adding: + # On creation, propagate the following scheduling_unit_draft attributes as default for the new scheduling_unit_blueprint + for copy_field in ['ingest_permission_required', 'piggyback_allowed_tbb', 'piggyback_allowed_aartfaac', + 'scheduling_constraints_doc', 'scheduling_constraints_template']: + if hasattr(self, 'draft'): + setattr(self, copy_field, getattr(self.draft, copy_field)) + else: + # On updates, prevent changing the scheduling constraints doc or template if we are past schedulable state + # todo: This causes a ton of tests to fail, e.g. t_workflow_qaworkflow returns errors 422 + if self.status not in [SchedulingUnitBlueprint.Status.DEFINED.value, SchedulingUnitBlueprint.Status.SCHEDULABLE.value] and \ + ((self.__original_scheduling_constraints_doc is not None and self.scheduling_constraints_doc != self.__original_scheduling_constraints_doc) or + self.scheduling_constraints_template_id != self.__original_scheduling_constraints_template_id): + raise TMSSException('The scheduling constraints of SchedulingUnitBlueprint pk=%s status=%s cannot be updated since it is not in defined or schedulable state.' % (self.pk, self.status)) + + # update the original constraints value for comparison on next save + self.__original_scheduling_constraints_doc = self.scheduling_constraints_doc + self.__original_scheduling_constraints_template_id = self.scheduling_constraints_template_id super().save(force_insert, force_update, using, update_fields) diff --git a/SAS/TMSS/backend/test/t_conversions.py b/SAS/TMSS/backend/test/t_conversions.py index 6a07693cbced93562963ebd79790cf1716c58e0e..ad873499f1271b74fd75cf7da3f59b39fc1cbecf 100755 --- a/SAS/TMSS/backend/test/t_conversions.py +++ b/SAS/TMSS/backend/test/t_conversions.py @@ -288,11 +288,12 @@ class UtilREST(unittest.TestCase): # defaults are CS002 and today self.assertIn('CS002', r_dict.keys()) - # assert day of timestamp matches day of returned rise - expected_date = datetime.date.today() + # assert target sets within 24h after now and rises within 24h before it sets + expected_date = datetime.datetime.utcnow() target_rise = dateutil.parser.parse(r_dict['CS002'][0]['rise']) target_set = dateutil.parser.parse(r_dict['CS002'][0]['set']) - self.assertTrue(expected_date == target_rise.date() or expected_date == target_set.date()) + self.assertTrue(0 < (target_set - expected_date).total_seconds() < 86400) + self.assertTrue(0 < (target_set - target_rise).total_seconds() < 86400) def test_util_target_rise_and_set_considers_stations(self): stations = ['CS005', 'RS305', 'DE609'] diff --git a/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py b/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py index b8d82ead9e47fbab49e00befc8742bedc634eee3..f1218237f3ff7b8ee8a7d70e24c3486d081cf500 100755 --- a/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py +++ b/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py @@ -45,6 +45,7 @@ from django.db.utils import IntegrityError from django.core.exceptions import ValidationError from django.db.models.deletion import ProtectedError from lofar.sas.tmss.tmss.exceptions import SchemaValidationException +from lofar.sas.tmss.tmss.exceptions import TMSSException class GeneratorTemplateTest(unittest.TestCase): def test_GeneratorTemplate_gets_created_with_correct_creation_timestamp(self): @@ -471,7 +472,7 @@ class SchedulingUnitDraftTest(unittest.TestCase): models.SchedulingUnitDraft.objects.create(**test_data) def test_SchedulingUnitDraft_gets_created_with_correct_default_ingest_permission_required(self): - + # setup entry = models.SchedulingUnitDraft.objects.create(**SchedulingUnitDraft_test_data()) #check the auto_ingest on project @@ -749,239 +750,339 @@ class SchedulingUnitBlueprintTest(unittest.TestCase): self.assertEqual(scheduling_unit_blueprint.piggyback_allowed_tbb, scheduling_unit_draft.piggyback_allowed_tbb) self.assertEqual(scheduling_unit_blueprint.piggyback_allowed_aartfaac, scheduling_unit_draft.piggyback_allowed_aartfaac) - -class TaskBlueprintTest(unittest.TestCase): - @classmethod - def setUpClass(cls) -> None: - cls.task_draft = models.TaskDraft.objects.create(**TaskDraft_test_data()) - cls.scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data()) - - def test_TaskBlueprint_gets_created_with_correct_creation_timestamp(self): - - # setup - before = datetime.utcnow() - entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) - - after = datetime.utcnow() - - # assert - self.assertLess(before, entry.created_at) - self.assertGreater(after, entry.created_at) - - def test_TaskBlueprint_update_timestamp_gets_changed_correctly(self): + def test_SchedulingUnitBlueprint_gets_created_with_correct_default_scheduling_constraints(self): # setup - entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) - before = datetime.utcnow() - entry.save() - after = datetime.utcnow() - - # assert - self.assertLess(before, entry.updated_at) - self.assertGreater(after, entry.updated_at) - - def test_TaskBlueprint_prevents_missing_template(self): - - # setup - test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) - test_data['specifications_template'] = None - - # assert - with self.assertRaises(IntegrityError): - models.TaskBlueprint.objects.create(**test_data) - - def test_TaskBlueprint_prevents_missing_draft(self): + constraints = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data(name='constraints')) + scheduling_unit_draft = models.SchedulingUnitDraft.objects.create(**SchedulingUnitDraft_test_data( + scheduling_constraints_doc={'foo': 'baz'}, + scheduling_constraints_template=constraints)) + scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data(draft=scheduling_unit_draft)) - # setup - test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) - test_data['draft'] = None + scheduling_unit_blueprint.refresh_from_db() # assert - with self.assertRaises(IntegrityError): - models.TaskBlueprint.objects.create(**test_data) - - def test_TaskBlueprint_prevents_draft_deletion(self): - # setup - test_data = dict(TaskBlueprint_test_data()) - blueprint = models.TaskBlueprint.objects.create(**test_data) - draft = blueprint.draft - with self.assertRaises(ProtectedError): - draft.delete() + self.assertEqual(scheduling_unit_blueprint.scheduling_constraints_doc, scheduling_unit_draft.scheduling_constraints_doc) + self.assertEqual(scheduling_unit_blueprint.scheduling_constraints_template, scheduling_unit_draft.scheduling_constraints_template) - def test_TaskBlueprint_prevents_missing_scheduling_unit_blueprint(self): + def test_SchedulingUnitBlueprint_prevents_updating_scheduling_constraints_template_if_not_in_correct_state(self): # setup - test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) - test_data['scheduling_unit_blueprint'] = None - - # assert - with self.assertRaises(IntegrityError): - models.TaskBlueprint.objects.create(**test_data) - - def test_TaskBlueprint_predecessors_and_successors_none(self): - task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) - task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) - - self.assertEqual(set(), set(task_blueprint_1.predecessors.all())) - self.assertEqual(set(), set(task_blueprint_2.predecessors.all())) - self.assertEqual(set(), set(task_blueprint_1.successors.all())) - self.assertEqual(set(), set(task_blueprint_2.successors.all())) - - def test_TaskBlueprint_predecessors_and_successors_simple(self): - task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) - task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) + constraints_1 = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data(name='constraints_1')) + constraints_2 = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data(name='constraints_2')) + scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data()) + scheduling_unit_blueprint.scheduling_constraints_template = constraints_1 + scheduling_unit_blueprint.save() + + task_blueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(scheduling_unit_blueprint=scheduling_unit_blueprint)) + subtask = models.Subtask.objects.create(**Subtask_test_data()) + subtask.task_blueprints.set([task_blueprint]) + subtask.state = models.SubtaskState.objects.get(value='error') # the derived SUB status is then also error + subtask.save() - models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_2)) - - self.assertEqual(task_blueprint_1, task_blueprint_2.predecessors.all()[0]) - self.assertEqual(task_blueprint_2, task_blueprint_1.successors.all()[0]) - - def test_TaskBlueprint_predecessors_and_successors_complex(self): - task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()))) - task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint)) - task_blueprint_3: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint)) - task_blueprint_4: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint)) - task_blueprint_5: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint)) - task_blueprint_6: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint)) - - # ST1 ---> ST3 ---> ST4 - # | | - # ST2 - -> ST5 ---> ST6 + scheduling_unit_blueprint.refresh_from_db() - models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_3)) - trb1 = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_2, consumer=task_blueprint_3)) - models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_4)) - models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_5)) - models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_5, consumer=task_blueprint_6)) + # we should be able to modify other fields + scheduling_unit_blueprint.results_accepted = not scheduling_unit_blueprint.results_accepted + scheduling_unit_blueprint.save() - self.assertEqual(set((task_blueprint_1, task_blueprint_2)), set(task_blueprint_3.predecessors.all())) - self.assertEqual(set((task_blueprint_4, task_blueprint_5)), set(task_blueprint_3.successors.all())) - self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_4.predecessors.all())) - self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_5.predecessors.all())) - self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_1.successors.all())) - self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_2.successors.all())) - self.assertEqual(set(), set(task_blueprint_1.predecessors.all())) - self.assertEqual(set(), set(task_blueprint_2.predecessors.all())) - self.assertEqual(set(), set(task_blueprint_4.successors.all())) - self.assertEqual(set((task_blueprint_6,)), set(task_blueprint_5.successors.all())) + # but scheduling constraints should be immutable + with self.assertRaises(TMSSException) as context: + scheduling_unit_blueprint.scheduling_constraints_template = constraints_2 + scheduling_unit_blueprint.save() + self.assertIn('schedulable state', str(context.exception)) -class TaskRelationBlueprintTest(unittest.TestCase): - @classmethod - def setUpClass(cls) -> None: - cls.producer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data()) - cls.consumer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data()) + def test_SchedulingUnitBlueprint_allows_updating_scheduling_constraints_template_if_in_correct_state(self): - def test_TaskRelationBlueprint_gets_created_with_correct_creation_timestamp(self): # setup - before = datetime.utcnow() - entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) + constraints_3 = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data(name='constraints_3')) + constraints_4 = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data(name='constraints_4')) + scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data()) + scheduling_unit_blueprint.scheduling_constraints_template = constraints_3 + scheduling_unit_blueprint.save() - after = datetime.utcnow() + task_blueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(scheduling_unit_blueprint=scheduling_unit_blueprint)) + subtask = models.Subtask.objects.create(**Subtask_test_data()) + subtask.task_blueprints.set([task_blueprint]) + subtask.save() - # assert - self.assertLess(before, entry.created_at) - self.assertGreater(after, entry.created_at) + scheduling_unit_blueprint.refresh_from_db() - def test_TaskRelationBlueprint_update_timestamp_gets_changed_correctly(self): - # setup - entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) - before = datetime.utcnow() - entry.save() - after = datetime.utcnow() + # we can still change the constraints + scheduling_unit_blueprint.scheduling_constraints_template = constraints_4 + scheduling_unit_blueprint.save() - # assert - self.assertLess(before, entry.updated_at) - self.assertGreater(after, entry.updated_at) + def test_SchedulingUnitBlueprint_prevents_updating_scheduling_constraints_doc_if_not_in_correct_state(self): - def test_TaskRelationBlueprint_prevents_missing_selection_template(self): # setup - test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) - test_data['selection_template'] = None + scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data()) + task_blueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(scheduling_unit_blueprint=scheduling_unit_blueprint)) + subtask = models.Subtask.objects.create(**Subtask_test_data()) + subtask.task_blueprints.set([task_blueprint]) + subtask.state = models.SubtaskState.objects.get(value='error') # the derived SUB status is then also error + subtask.save() - # assert - with self.assertRaises(IntegrityError): - models.TaskRelationBlueprint.objects.create(**test_data) + scheduling_unit_blueprint.refresh_from_db() - def test_TaskRelationBlueprint_prevents_missing_draft(self): - # setup - test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) - test_data['draft'] = None + # we should be able to modify other fields + scheduling_unit_blueprint.results_accepted = not scheduling_unit_blueprint.results_accepted + scheduling_unit_blueprint.save() - # assert - with self.assertRaises(IntegrityError): - models.TaskRelationBlueprint.objects.create(**test_data) + # but scheduling constraints should be immutable + with self.assertRaises(TMSSException) as context: + scheduling_unit_blueprint.scheduling_constraints_doc = {'foo': 'matic'} + scheduling_unit_blueprint.save() - def test_TaskRelationBlueprint_prevents_missing_producer(self): - # setup - test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) - test_data['producer'] = None + self.assertIn('schedulable state', str(context.exception)) - # assert - with self.assertRaises(IntegrityError): - models.TaskRelationBlueprint.objects.create(**test_data) + def test_SchedulingUnitBlueprint_allows_updating_scheduling_constraints_doc_if_in_correct_state(self): - def test_TaskRelationBlueprint_prevents_missing_consumer(self): # setup - test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) - test_data['consumer'] = None - - # assert - with self.assertRaises(IntegrityError): - models.TaskRelationBlueprint.objects.create(**test_data) + scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data()) + task_blueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(scheduling_unit_blueprint=scheduling_unit_blueprint)) + subtask = models.Subtask.objects.create(**Subtask_test_data()) + subtask.task_blueprints.set([task_blueprint]) + subtask.save() - def test_TaskRelationBlueprint_prevents_missing_input(self): - # setup - test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) - test_data['input_role'] = None + scheduling_unit_blueprint.refresh_from_db() - # assert - with self.assertRaises(IntegrityError): - models.TaskRelationBlueprint.objects.create(**test_data) + scheduling_unit_blueprint.scheduling_constraints_doc = {'foo': 'matic'} + scheduling_unit_blueprint.save() - def test_TaskRelationBlueprint_prevents_missing_output(self): - # setup - test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) - test_data['output_role'] = None - # assert - with self.assertRaises(IntegrityError): - models.TaskRelationBlueprint.objects.create(**test_data) - - - - -class TestStationTimeLine(unittest.TestCase): - """ - Actually this simple testcase should be in a separate module (t_tmssapp_calculations_django_API.py) - but I was just lazy and spare some overhead and I just 'piggyback' with this module - """ - - def test_StationTimeline_raises_Error_on_duplicate_station_timeline(self): - """ - Test if adding a duplicate station-timestamp combination leads to an Error and so data is not inserted - """ - import datetime - - test_data = {"station_name": "CS001", - "timestamp": datetime.date(2021, 4, 1), - "sunrise_start": datetime.datetime(year=2021, month=4, day=1, hour=6, minute=1, second=0), - "sunrise_end": datetime.datetime(year=2021, month=4, day=1, hour=7, minute=2, second=0), - "sunset_start": datetime.datetime(year=2021, month=4, day=1, hour=20, minute=31, second=0), - "sunset_end": datetime.datetime(year=2021, month=4, day=1, hour=21, minute=33, second=0) } - - models.StationTimeline.objects.create(**test_data) - with self.assertRaises(IntegrityError) as context: - models.StationTimeline.objects.create(**test_data) - self.assertIn('unique_station_time_line', str(context.exception)) - - self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 1) - self.assertEqual(len(models.StationTimeline.objects.all()), 1) - # Add a non-duplicate - test_data["station_name"] = "CS002" - models.StationTimeline.objects.create(**test_data) - self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 2) - self.assertEqual(len(models.StationTimeline.objects.all()), 2) +# class TaskBlueprintTest(unittest.TestCase): +# @classmethod +# def setUpClass(cls) -> None: +# cls.task_draft = models.TaskDraft.objects.create(**TaskDraft_test_data()) +# cls.scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data()) +# +# def test_TaskBlueprint_gets_created_with_correct_creation_timestamp(self): +# +# # setup +# before = datetime.utcnow() +# entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) +# +# after = datetime.utcnow() +# +# # assert +# self.assertLess(before, entry.created_at) +# self.assertGreater(after, entry.created_at) +# +# def test_TaskBlueprint_update_timestamp_gets_changed_correctly(self): +# +# # setup +# entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) +# before = datetime.utcnow() +# entry.save() +# after = datetime.utcnow() +# +# # assert +# self.assertLess(before, entry.updated_at) +# self.assertGreater(after, entry.updated_at) +# +# def test_TaskBlueprint_prevents_missing_template(self): +# +# # setup +# test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) +# test_data['specifications_template'] = None +# +# # assert +# with self.assertRaises(IntegrityError): +# models.TaskBlueprint.objects.create(**test_data) +# +# def test_TaskBlueprint_prevents_missing_draft(self): +# +# # setup +# test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) +# test_data['draft'] = None +# +# # assert +# with self.assertRaises(IntegrityError): +# models.TaskBlueprint.objects.create(**test_data) +# +# def test_TaskBlueprint_prevents_draft_deletion(self): +# # setup +# test_data = dict(TaskBlueprint_test_data()) +# blueprint = models.TaskBlueprint.objects.create(**test_data) +# draft = blueprint.draft +# with self.assertRaises(ProtectedError): +# draft.delete() +# +# def test_TaskBlueprint_prevents_missing_scheduling_unit_blueprint(self): +# +# # setup +# test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) +# test_data['scheduling_unit_blueprint'] = None +# +# # assert +# with self.assertRaises(IntegrityError): +# models.TaskBlueprint.objects.create(**test_data) +# +# def test_TaskBlueprint_predecessors_and_successors_none(self): +# task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) +# task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) +# +# self.assertEqual(set(), set(task_blueprint_1.predecessors.all())) +# self.assertEqual(set(), set(task_blueprint_2.predecessors.all())) +# self.assertEqual(set(), set(task_blueprint_1.successors.all())) +# self.assertEqual(set(), set(task_blueprint_2.successors.all())) +# +# def test_TaskBlueprint_predecessors_and_successors_simple(self): +# task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) +# task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint)) +# +# models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_2)) +# +# self.assertEqual(task_blueprint_1, task_blueprint_2.predecessors.all()[0]) +# self.assertEqual(task_blueprint_2, task_blueprint_1.successors.all()[0]) +# +# def test_TaskBlueprint_predecessors_and_successors_complex(self): +# task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()))) +# task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint)) +# task_blueprint_3: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint)) +# task_blueprint_4: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint)) +# task_blueprint_5: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint)) +# task_blueprint_6: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint)) +# +# # ST1 ---> ST3 ---> ST4 +# # | | +# # ST2 - -> ST5 ---> ST6 +# +# models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_3)) +# trb1 = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_2, consumer=task_blueprint_3)) +# models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_4)) +# models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_5)) +# models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_5, consumer=task_blueprint_6)) +# +# self.assertEqual(set((task_blueprint_1, task_blueprint_2)), set(task_blueprint_3.predecessors.all())) +# self.assertEqual(set((task_blueprint_4, task_blueprint_5)), set(task_blueprint_3.successors.all())) +# self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_4.predecessors.all())) +# self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_5.predecessors.all())) +# self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_1.successors.all())) +# self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_2.successors.all())) +# self.assertEqual(set(), set(task_blueprint_1.predecessors.all())) +# self.assertEqual(set(), set(task_blueprint_2.predecessors.all())) +# self.assertEqual(set(), set(task_blueprint_4.successors.all())) +# self.assertEqual(set((task_blueprint_6,)), set(task_blueprint_5.successors.all())) +# +# +# class TaskRelationBlueprintTest(unittest.TestCase): +# @classmethod +# def setUpClass(cls) -> None: +# cls.producer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data()) +# cls.consumer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data()) +# +# def test_TaskRelationBlueprint_gets_created_with_correct_creation_timestamp(self): +# # setup +# before = datetime.utcnow() +# entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) +# +# after = datetime.utcnow() +# +# # assert +# self.assertLess(before, entry.created_at) +# self.assertGreater(after, entry.created_at) +# +# def test_TaskRelationBlueprint_update_timestamp_gets_changed_correctly(self): +# # setup +# entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) +# before = datetime.utcnow() +# entry.save() +# after = datetime.utcnow() +# +# # assert +# self.assertLess(before, entry.updated_at) +# self.assertGreater(after, entry.updated_at) +# +# def test_TaskRelationBlueprint_prevents_missing_selection_template(self): +# # setup +# test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) +# test_data['selection_template'] = None +# +# # assert +# with self.assertRaises(IntegrityError): +# models.TaskRelationBlueprint.objects.create(**test_data) +# +# def test_TaskRelationBlueprint_prevents_missing_draft(self): +# # setup +# test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) +# test_data['draft'] = None +# +# # assert +# with self.assertRaises(IntegrityError): +# models.TaskRelationBlueprint.objects.create(**test_data) +# +# def test_TaskRelationBlueprint_prevents_missing_producer(self): +# # setup +# test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) +# test_data['producer'] = None +# +# # assert +# with self.assertRaises(IntegrityError): +# models.TaskRelationBlueprint.objects.create(**test_data) +# +# def test_TaskRelationBlueprint_prevents_missing_consumer(self): +# # setup +# test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) +# test_data['consumer'] = None +# +# # assert +# with self.assertRaises(IntegrityError): +# models.TaskRelationBlueprint.objects.create(**test_data) +# +# def test_TaskRelationBlueprint_prevents_missing_input(self): +# # setup +# test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) +# test_data['input_role'] = None +# +# # assert +# with self.assertRaises(IntegrityError): +# models.TaskRelationBlueprint.objects.create(**test_data) +# +# def test_TaskRelationBlueprint_prevents_missing_output(self): +# # setup +# test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer)) +# test_data['output_role'] = None +# +# # assert +# with self.assertRaises(IntegrityError): +# models.TaskRelationBlueprint.objects.create(**test_data) +# +# +# +# +# class TestStationTimeLine(unittest.TestCase): +# """ +# Actually this simple testcase should be in a separate module (t_tmssapp_calculations_django_API.py) +# but I was just lazy and spare some overhead and I just 'piggyback' with this module +# """ +# +# def test_StationTimeline_raises_Error_on_duplicate_station_timeline(self): +# """ +# Test if adding a duplicate station-timestamp combination leads to an Error and so data is not inserted +# """ +# import datetime +# +# test_data = {"station_name": "CS001", +# "timestamp": datetime.date(2021, 4, 1), +# "sunrise_start": datetime.datetime(year=2021, month=4, day=1, hour=6, minute=1, second=0), +# "sunrise_end": datetime.datetime(year=2021, month=4, day=1, hour=7, minute=2, second=0), +# "sunset_start": datetime.datetime(year=2021, month=4, day=1, hour=20, minute=31, second=0), +# "sunset_end": datetime.datetime(year=2021, month=4, day=1, hour=21, minute=33, second=0) } +# +# models.StationTimeline.objects.create(**test_data) +# with self.assertRaises(IntegrityError) as context: +# models.StationTimeline.objects.create(**test_data) +# self.assertIn('unique_station_time_line', str(context.exception)) +# +# self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 1) +# self.assertEqual(len(models.StationTimeline.objects.all()), 1) +# # Add a non-duplicate +# test_data["station_name"] = "CS002" +# models.StationTimeline.objects.create(**test_data) +# self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 2) +# self.assertEqual(len(models.StationTimeline.objects.all()), 2) if __name__ == "__main__": diff --git a/SAS/TMSS/backend/test/tmss_test_data_django_models.py b/SAS/TMSS/backend/test/tmss_test_data_django_models.py index 9b7024f59cb7d6f0f06e429dc72ffb08fd231ef2..538d5d7480920cc1ca4c924914f675e91e4c8892 100644 --- a/SAS/TMSS/backend/test/tmss_test_data_django_models.py +++ b/SAS/TMSS/backend/test/tmss_test_data_django_models.py @@ -191,7 +191,9 @@ def SchedulingSet_test_data(name="my_scheduling_set", project: models.Project=No def SchedulingUnitDraft_test_data(name="my_scheduling_unit_draft", scheduling_set: models.SchedulingSet=None, template: models.SchedulingUnitTemplate=None, requirements_doc: dict=None, - observation_strategy_template: models.SchedulingUnitObservingStrategyTemplate=None) -> dict: + observation_strategy_template: models.SchedulingUnitObservingStrategyTemplate=None, + scheduling_constraints_doc: dict=None, + scheduling_constraints_template: models.SchedulingConstraintsTemplate=None) -> dict: if scheduling_set is None: scheduling_set = models.SchedulingSet.objects.create(**SchedulingSet_test_data()) @@ -201,6 +203,12 @@ def SchedulingUnitDraft_test_data(name="my_scheduling_unit_draft", scheduling_se if requirements_doc is None: requirements_doc = get_default_json_object_for_schema(template.schema) + if scheduling_constraints_template is None: + scheduling_constraints_template = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data()) + + if scheduling_constraints_doc is None: + scheduling_constraints_doc = get_default_json_object_for_schema(scheduling_constraints_template.schema) + return {"name": name, "description": "", "tags": [], @@ -210,7 +218,9 @@ def SchedulingUnitDraft_test_data(name="my_scheduling_unit_draft", scheduling_se "copies": None, "scheduling_set": scheduling_set, "requirements_template": template, - "observation_strategy_template": observation_strategy_template } + "observation_strategy_template": observation_strategy_template, + "scheduling_constraints_template": scheduling_constraints_template, + "scheduling_constraints_doc": scheduling_constraints_doc} def TaskDraft_test_data(name: str=None, specifications_template: models.TaskTemplate=None, specifications_doc: dict=None, scheduling_unit_draft: models.SchedulingUnitDraft=None, output_pinned=False) -> dict: if name is None: @@ -251,6 +261,7 @@ def TaskRelationDraft_test_data(producer: models.TaskDraft = None, consumer: mod "selection_template": models.TaskRelationSelectionTemplate.objects.create(**TaskRelationSelectionTemplate_test_data())} def SchedulingUnitBlueprint_test_data(name=None, requirements_template: models.SchedulingUnitTemplate=None, draft=None, output_pinned=None) -> dict: + if name is None: name = 'my_scheduling_unit_blueprint_' + str(uuid.uuid4()) diff --git a/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py b/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py index 55d8da30199a0d79ac0aa9c43a6d67e465835931..2c3dd34f8f81bd2a256eaa7ffe5164408eb8de34 100644 --- a/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py +++ b/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py @@ -86,11 +86,13 @@ def _call_API_and_assert_expected_response(test_instance, url, call, data, expec for key, value in expected_content.items(): if key not in r_dict.keys(): logger.error('!!! Missing key: %s in %s', key, r_dict.keys()) - test_instance.assertTrue(key in r_dict.keys()) + test_instance.assertIn(key, r_dict.keys()) if isinstance(value, models.Model): value = str(value.pk) value = value.replace(' ', '%20') - test_instance.assertTrue(str(value) in r_dict[key]) + if str(value) not in r_dict[key]: + logger.error('!!! Unexpected value of key=%s: expected=%s got=%s', key, value, r_dict[key]) + test_instance.assertIn(str(value), r_dict[key]) elif type(value) is list: test_instance.assertEqual(sorted(value), sorted(r_dict[key]), msg="lists differ for key=%s"%key) # compare lists independent of ordering elif isinstance(value, datetime.datetime):