diff --git a/SAS/TMSS/backend/services/feedback_handling/lib/feedback_handling.py b/SAS/TMSS/backend/services/feedback_handling/lib/feedback_handling.py index acf4cf888b04acbc6e1716faebb375adc533792d..063290374955b8f32c56a6da8d031da5267a1f55 100644 --- a/SAS/TMSS/backend/services/feedback_handling/lib/feedback_handling.py +++ b/SAS/TMSS/backend/services/feedback_handling/lib/feedback_handling.py @@ -88,7 +88,7 @@ class HybridFeedbackMessageHandler(TMSSEventMessageHandler): specifications_template = self._tmss_client.get_url_as_json_object(subtask['specifications_template']) if specifications_template['type_value'].lower() in ('observation', 'pipeline'): try: - finishing_timestamp = parser.parse(subtask['stop_time'], ignoretz=True) + finishing_timestamp = parser.parse(subtask['scheduled_on_sky_stop_time'], ignoretz=True) except: finishing_timestamp = datetime.utcnow() wait_timeout_timestamp = round_to_second_precision(finishing_timestamp + timedelta(seconds=self._feedback_wait_timeout)) 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 552ce7d7280f6eb5e2a57afe16b073f40a3a24f1..890d56da7af49841a5eb1f4b87923d62f634bc68 100755 --- a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py +++ b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py @@ -1980,7 +1980,7 @@ class TestTriggers(TestCase): self.assertIn(subtask, running_subtasks) # also assert cut-off date is considered - running_subtasks = get_running_observation_subtasks(subtask.stop_time + timedelta(minutes=5)) + running_subtasks = get_running_observation_subtasks(subtask.scheduled_on_sky_stop_time + timedelta(minutes=5)) self.assertNotIn(subtask, running_subtasks) # add a triggered scheduling_unit with higher priority @@ -2047,7 +2047,7 @@ class TestTriggers(TestCase): self.assertIn(subtask, running_subtasks) # also assert cut-off date is considered - running_subtasks = get_running_observation_subtasks(subtask.stop_time + timedelta(minutes=5)) + running_subtasks = get_running_observation_subtasks(subtask.scheduled_on_sky_stop_time + timedelta(minutes=5)) self.assertNotIn(subtask, running_subtasks) # add a triggered scheduling_unit with same trigger priority @@ -2101,7 +2101,7 @@ class TestTriggers(TestCase): self.assertIn(subtask, running_subtasks) # also assert cut-off date is considered - running_subtasks = get_running_observation_subtasks(subtask.stop_time + timedelta(minutes=5)) + running_subtasks = get_running_observation_subtasks(subtask.scheduled_on_sky_stop_time + timedelta(minutes=5)) self.assertNotIn(subtask, running_subtasks) # add a triggered scheduling_unit with higher priority, but a between constraint that can never be met diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py index 2445ff5b65007c70ee2f1d973ac3d9da9d3f0161..f474f4d9b2085130564a7416b6b66c586aa53a6f 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py @@ -310,6 +310,15 @@ class Subtask(BasicCommon, ProjectPropertyMixin, TemplateSchemaMixin): if self.scheduled_on_sky_start_time is None: raise SubtaskSchedulingException("Cannot schedule subtask id=%s when start time is 'None'." % (self.pk, )) + # set actual_process_start_time when subtask goes to STARTED state + if self.state.value == SubtaskState.Choices.STARTED.value and self.__original_state_id == SubtaskState.Choices.STARTING.value: + self.actual_process_start_time = datetime.utcnow() + + # set actual_process_stop_time when subtask goes to FINISHED/ERROR/CANCELLED state + if self.state.value in [SubtaskState.Choices.FINISHED.value, SubtaskState.Choices.ERROR.value or SubtaskState.Choices.CANCELLED.value] \ + and self.actual_process_start_time and not self.actual_process_stop_time: + self.actual_process_stop_time = datetime.utcnow() + # ensure there is and will be exactly one primary subtask per parent task_blueprint # quite a complex check, luckily we have a test for that. nr_of_primary_siblings = 0 if self.task_blueprint is None else self.task_blueprint.subtasks.filter(primary=True).exclude(id=self.id).count() diff --git a/SAS/TMSS/backend/test/t_observation_strategies_specification_and_scheduling_test.py b/SAS/TMSS/backend/test/t_observation_strategies_specification_and_scheduling_test.py index fdedcca0fa2cd67693d5c2e978fa32a7a567f04c..52a33ca03ce5bca8941eb080cd7c6f3f8d3cc86a 100755 --- a/SAS/TMSS/backend/test/t_observation_strategies_specification_and_scheduling_test.py +++ b/SAS/TMSS/backend/test/t_observation_strategies_specification_and_scheduling_test.py @@ -89,8 +89,8 @@ class TestObservationStrategiesSpecificationAndScheduling(unittest.TestCase): self.assertEqual(2 if is_target_obs else 1, int(obs_parset['Observation.nrBeams'])) self.assertEqual('Observation', obs_parset['Observation.processType']) self.assertEqual('Beam Observation', obs_parset['Observation.processSubtype']) - self.assertEqual(parser.parse(obs_subtask['start_time']), parser.parse(obs_parset['Observation.startTime'])) - self.assertEqual(parser.parse(obs_subtask['stop_time']), parser.parse(obs_parset['Observation.stopTime'])) + self.assertEqual(parser.parse(obs_subtask['scheduled_on_sky_start_time']), parser.parse(obs_parset['Observation.startTime'])) + self.assertEqual(parser.parse(obs_subtask['scheduled_on_sky_stop_time']), parser.parse(obs_parset['Observation.stopTime'])) self.assertEqual(200, int(obs_parset['Observation.sampleClock'])) self.assertEqual(244, len(obs_parset['Observation.Beam[0].subbandList'].split(','))) if is_target_obs: @@ -322,8 +322,8 @@ class TestObservationStrategiesSpecificationAndScheduling(unittest.TestCase): self.assertEqual(1, int(obs_parset['Observation.nrBeams'])) self.assertEqual('Observation', obs_parset['Observation.processType']) self.assertEqual('Beam Observation', obs_parset['Observation.processSubtype']) - self.assertEqual(parser.parse(obs_subtask['start_time']), parser.parse(obs_parset['Observation.startTime'])) - self.assertEqual(parser.parse(obs_subtask['stop_time']), parser.parse(obs_parset['Observation.stopTime'])) + self.assertEqual(parser.parse(obs_subtask['scheduled_on_sky_start_time']), parser.parse(obs_parset['Observation.startTime'])) + self.assertEqual(parser.parse(obs_subtask['scheduled_on_sky_stop_time']), parser.parse(obs_parset['Observation.stopTime'])) self.assertEqual(200, int(obs_parset['Observation.sampleClock'])) self.assertEqual(244, len(obs_parset['Observation.Beam[0].subbandList'].split(','))) self.assertEqual(True, strtobool(obs_parset['Observation.DataProducts.Output_CoherentStokes.enabled'])) @@ -408,8 +408,8 @@ class TestObservationStrategiesSpecificationAndScheduling(unittest.TestCase): self.assertEqual(4, int(obs_parset['Observation.nrBeams'])) self.assertEqual('Observation', obs_parset['Observation.processType']) self.assertEqual('Beam Observation', obs_parset['Observation.processSubtype']) - self.assertEqual(parser.parse(obs_subtask['start_time']), parser.parse(obs_parset['Observation.startTime'])) - self.assertEqual(parser.parse(obs_subtask['stop_time']), parser.parse(obs_parset['Observation.stopTime'])) + self.assertEqual(parser.parse(obs_subtask['scheduled_on_sky_start_time']), parser.parse(obs_parset['Observation.startTime'])) + self.assertEqual(parser.parse(obs_subtask['scheduled_on_sky_stop_time']), parser.parse(obs_parset['Observation.stopTime'])) self.assertEqual(200, int(obs_parset['Observation.sampleClock'])) self.assertEqual(122, len(obs_parset['Observation.Beam[0].subbandList'].split(','))) self.assertEqual(True, strtobool(obs_parset['Observation.DataProducts.Output_Correlated.enabled'])) diff --git a/SAS/TMSS/backend/test/t_scheduling.py b/SAS/TMSS/backend/test/t_scheduling.py index f26129da57695cccad2b9a21b0ab0ba8d519b977..7a632197f830f12fa45ee9fdaf6d7d988d39be09 100755 --- a/SAS/TMSS/backend/test/t_scheduling.py +++ b/SAS/TMSS/backend/test/t_scheduling.py @@ -159,7 +159,7 @@ class SchedulingTest(unittest.TestCase): subtask_data = test_data_creator.Subtask(specifications_template_url=subtask_template['url'], specifications_doc=specification_doc, cluster_url=cluster_url, - start_time=datetime.utcnow()+timedelta(minutes=5), + scheduled_on_sky_start_time=datetime.utcnow()+timedelta(minutes=5), task_blueprint_url=task_blueprint['url']) subtask = test_data_creator.post_data_and_get_response_as_json_object(subtask_data, '/subtask/') test_data_creator.post_data_and_get_url(test_data_creator.SubtaskOutput(subtask_url=subtask['url']), '/subtask_output/') @@ -573,7 +573,7 @@ class SchedulingTest(unittest.TestCase): self.assertEqual(1, len(task_blueprint['subtasks'])) subtask = client.get_url_as_json_object(task_blueprint['subtasks'][0]) - client.session.patch(subtask['url'], {'start_time': datetime.utcnow() + timedelta(minutes=5)}) + client.session.patch(subtask['url'], {'scheduled_on_sky_start_time': datetime.utcnow() + timedelta(minutes=5)}) client.set_subtask_status(subtask['id'], 'defined') subtask = client.schedule_subtask(subtask['id']) @@ -676,8 +676,8 @@ class SAPTest(unittest.TestCase): specifications_doc=spec, cluster_url = cluster_url, task_blueprint_url=task_blueprint['url'], - start_time=datetime.utcnow() + timedelta(minutes=5), - stop_time=datetime.utcnow() + timedelta(minutes=15)) + scheduled_on_sky_start_time=datetime.utcnow() + timedelta(minutes=5), + scheduled_on_sky_stop_time=datetime.utcnow() + timedelta(minutes=15)) subtask = test_data_creator.post_data_and_get_response_as_json_object(subtask_data, '/subtask/') subtask_id = subtask['id'] test_data_creator.post_data_and_get_url(test_data_creator.SubtaskOutput(subtask_url=subtask['url']), '/subtask_output/') @@ -784,8 +784,8 @@ class TestWithUC1Specifications(unittest.TestCase): for tb in self.task_blueprints: for subtask in tb.subtasks.all(): # start_time to now (and no stoptime) - subtask.stop_time = None - subtask.start_time = datetime.utcnow() + subtask.scheduled_on_sky_stop_time = None + subtask.scheduled_on_sky_start_time = datetime.utcnow() subtask.save() def _schedule_subtask_with_failure(self, station_reserved): @@ -844,8 +844,8 @@ class TestWithUC1Specifications(unittest.TestCase): for name, times in test_timeschedule.items(): task_blueprint = list(filter(lambda x: x.name == name, self.task_blueprints))[0] for subtask in task_blueprint.subtasks.all(): - subtask.stop_time = datetime.strptime(times[1], DATETIME_FORMAT) - subtask.start_time = datetime.strptime(times[0], DATETIME_FORMAT) + subtask.scheduled_on_sky_stop_time = datetime.strptime(times[1], DATETIME_FORMAT) + subtask.scheduled_on_sky_start_time = datetime.strptime(times[0], DATETIME_FORMAT) subtask.save() set_subtask_state_following_allowed_transitions(subtask, "finished") diff --git a/SAS/TMSS/backend/test/t_tmssapp_scheduling_REST_API.py b/SAS/TMSS/backend/test/t_tmssapp_scheduling_REST_API.py index 719371acc1748494e209ee190c4bd6ebec7a428c..b916f81b3cb15f90d03077915f4f48efdba98da8 100755 --- a/SAS/TMSS/backend/test/t_tmssapp_scheduling_REST_API.py +++ b/SAS/TMSS/backend/test/t_tmssapp_scheduling_REST_API.py @@ -1369,7 +1369,7 @@ class SubtaskQueryTestCase(unittest.TestCase): task_blueprint_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskBlueprint(), '/task_blueprint/') cluster_url = test_data_creator.post_data_and_get_url(test_data_creator.Cluster(name="clusterA"), '/cluster/') - test_data_creator.post_data_and_get_url(test_data_creator.Subtask(start_time=datetime.utcnow(), stop_time=datetime.utcnow(), + test_data_creator.post_data_and_get_url(test_data_creator.Subtask(scheduled_on_sky_start_time=datetime.utcnow(), scheduled_on_sky_stop_time=datetime.utcnow(), cluster_url=cluster_url, task_blueprint_url=task_blueprint_url), '/subtask/') for cluster_name, period_length_in_days in SubtaskQueryTestCase.subtasks_test_data_with_start_stop_time.items(): diff --git a/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py b/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py index a86b7972455faa15f56434dcce4aee2bb0d5e323..9d8602702f50d87dbc191034f61e4c87f8ccd4da 100755 --- a/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py +++ b/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py @@ -2279,8 +2279,8 @@ class SchedulingUnitBlueprintTestCase(unittest.TestCase): Test we can filter on this property, which is explicitly named on the model-specific property filter """ # setup - subtask_1 = models.Subtask.objects.create(**Subtask_test_data(start_time=datetime(2050, 1, 1, 10, 0, 0), stop_time=datetime(2050, 1, 1, 14, 0, 0), task_blueprint=models.TaskBlueprint.objects.create(**TaskBlueprint_test_data()))) - subtask_2 = models.Subtask.objects.create(**Subtask_test_data(start_time=datetime(2050, 1, 5, 10, 0, 0), stop_time=datetime(2050, 1, 5, 14, 0, 0), task_blueprint=models.TaskBlueprint.objects.create(**TaskBlueprint_test_data()))) + subtask_1 = models.Subtask.objects.create(**Subtask_test_data(scheduled_on_sky_start_time=datetime(2050, 1, 1, 10, 0, 0), scheduled_on_sky_stop_time=datetime(2050, 1, 1, 14, 0, 0), task_blueprint=models.TaskBlueprint.objects.create(**TaskBlueprint_test_data()))) + subtask_2 = models.Subtask.objects.create(**Subtask_test_data(scheduled_on_sky_start_time=datetime(2050, 1, 5, 10, 0, 0), scheduled_on_sky_stop_time=datetime(2050, 1, 5, 14, 0, 0), task_blueprint=models.TaskBlueprint.objects.create(**TaskBlueprint_test_data()))) # assert response_1 = GET_and_assert_equal_expected_code(self, BASE_URL + '/scheduling_unit_blueprint/?start_time_after=2050-01-01T9:00:00&stop_time_before=2050-01-01T15:00:00', 200) diff --git a/SAS/TMSS/backend/test/test_environment.py b/SAS/TMSS/backend/test/test_environment.py index 5654fadc6e21ca5938ea96ee29e726e4c8d07a5c..b796c40b407a9aca7312a45378a51483114278de 100644 --- a/SAS/TMSS/backend/test/test_environment.py +++ b/SAS/TMSS/backend/test/test_environment.py @@ -729,7 +729,7 @@ def create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id: int state__value=models.SubtaskState.Choices.DEFINED.value).all(): if self.need_to_handle(subtask): - subtask.start_time = datetime.utcnow() + task_blueprint.relative_start_time + subtask.scheduled_on_sky_start_time = datetime.utcnow() + task_blueprint.relative_start_time while subtask.state.value != models.SubtaskState.Choices.SCHEDULED.value: try: @@ -737,8 +737,8 @@ def create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id: int except SubtaskSchedulingException as e: # try again, a bit later subtask.state = models.SubtaskState.objects.get(value=models.SubtaskState.Choices.DEFINED.value) - update_start_time_and_shift_successors_until_after_stop_time(subtask, subtask.start_time + timedelta(hours=3)) - if subtask.start_time - datetime.utcnow() > timedelta(days=1): + update_start_time_and_shift_successors_until_after_stop_time(subtask, subtask.scheduled_on_sky_start_time + timedelta(hours=3)) + if subtask.scheduled_on_sky_start_time - datetime.utcnow() > timedelta(days=1): raise except models.SchedulingUnitBlueprint.DoesNotExist: pass @@ -848,9 +848,9 @@ def create_scheduling_unit_blueprint_simulator(scheduling_unit_blueprint_id: int subtask.id, subtask.specifications_template.type.value, subtask.state.value, next_state) if next_state == models.SubtaskState.objects.get(value=models.SubtaskState.Choices.STARTED.value): - subtask.start_time = datetime.utcnow() + subtask.scheduled_on_sky_start_time = datetime.utcnow() if next_state == models.SubtaskState.objects.get(value=models.SubtaskState.Choices.FINISHING.value): - subtask.stop_time = datetime.utcnow() + subtask.scheduled_on_sky_stop_time = datetime.utcnow() subtask.state = next_state subtask.save() 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 e445780d6f9c4c990310d0f505bf0268eec1f988..b9e2ca30afd51a9de1ea46dbba762ec31a644b1c 100644 --- a/SAS/TMSS/backend/test/tmss_test_data_django_models.py +++ b/SAS/TMSS/backend/test/tmss_test_data_django_models.py @@ -402,7 +402,7 @@ def SubtaskInput_test_data(subtask: models.Subtask=None, producer: models.Subtas "tags":[]} def Subtask_test_data(task_blueprint: models.TaskBlueprint=None, subtask_template: models.SubtaskTemplate=None, - specifications_doc: dict=None, start_time=None, stop_time=None, cluster=None, state=None, + specifications_doc: dict=None, scheduled_on_sky_start_time=None, scheduled_on_sky_stop_time=None, cluster=None, state=None, raw_feedback=None, primary:bool=True) -> dict: if task_blueprint is None: @@ -415,11 +415,11 @@ def Subtask_test_data(task_blueprint: models.TaskBlueprint=None, subtask_templat specifications_doc = get_default_json_object_for_schema(subtask_template.schema) # Type need to be a datetime object not a str so do not add .isoformat() - if start_time is None: - start_time = datetime.utcnow() + if scheduled_on_sky_start_time is None: + scheduled_on_sky_start_time = datetime.utcnow() - if stop_time is None: - stop_time = datetime.utcnow() + timedelta(minutes=10) + if scheduled_on_sky_stop_time is None: + scheduled_on_sky_stop_time = datetime.utcnow() + timedelta(minutes=10) if cluster is None: cluster = models.Cluster.objects.create(name="dummy cluster", location="downstairs", archive_site=True, tags=[]) @@ -427,8 +427,8 @@ def Subtask_test_data(task_blueprint: models.TaskBlueprint=None, subtask_templat if state is None: state = models.SubtaskState.objects.get(value='defining') - return { "start_time": start_time, - "stop_time": stop_time, + return { "scheduled_on_sky_start_time": scheduled_on_sky_start_time, + "scheduled_on_sky_stop_time": scheduled_on_sky_stop_time, "state": state, "specifications_doc": specifications_doc, "task_blueprint": task_blueprint, diff --git a/SAS/TMSS/backend/test/tmss_test_data_rest.py b/SAS/TMSS/backend/test/tmss_test_data_rest.py index c698612aea453d7f6e18d2e155cc087388b6f29a..e6f6f64311690b3bb7969a561db59520a4e8b8c2 100644 --- a/SAS/TMSS/backend/test/tmss_test_data_rest.py +++ b/SAS/TMSS/backend/test/tmss_test_data_rest.py @@ -645,7 +645,7 @@ class TMSSRESTTestDataCreator(): return self._cluster_url - def Subtask(self, cluster_url=None, task_blueprint_url=None, specifications_template_url=None, specifications_doc=None, state:str="defining", start_time: datetime=None, stop_time: datetime=None, raw_feedback:str =None, primary: bool=True): + def Subtask(self, cluster_url=None, task_blueprint_url=None, specifications_template_url=None, specifications_doc=None, state:str="defining", scheduled_on_sky_start_time: datetime=None, scheduled_on_sky_stop_time: datetime=None, raw_feedback:str =None, primary: bool=True): if cluster_url is None: cluster_url = self.cached_cluster_url @@ -658,20 +658,20 @@ class TMSSRESTTestDataCreator(): if specifications_doc is None: specifications_doc = self.get_response_as_json_object(specifications_template_url+'/default') - if start_time is None: - start_time = datetime.utcnow() + if scheduled_on_sky_start_time is None: + scheduled_on_sky_start_time = datetime.utcnow() - if stop_time is None: - stop_time = start_time + timedelta(minutes=60) + if scheduled_on_sky_stop_time is None: + scheduled_on_sky_stop_time = scheduled_on_sky_start_time + timedelta(minutes=60) - if isinstance(start_time, datetime): - start_time = start_time.isoformat() + if isinstance(scheduled_on_sky_start_time, datetime): + scheduled_on_sky_start_time = scheduled_on_sky_start_time.isoformat() - if isinstance(stop_time, datetime): - stop_time = stop_time.isoformat() + if isinstance(scheduled_on_sky_stop_time, datetime): + scheduled_on_sky_stop_time = scheduled_on_sky_stop_time.isoformat() - return {"start_time": start_time, - "stop_time": stop_time, + return {"scheduled_on_sky_start_time": scheduled_on_sky_start_time, + "scheduled_on_sky_stop_time": scheduled_on_sky_stop_time, "state": self.django_api_url + '/subtask_state/%s' % (state,), "specifications_doc": specifications_doc, "task_blueprint": task_blueprint_url,