diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py index 345ac8b1059bfe82c357a7f20de5984587837081..8ccf852658bfb3c020f90d43753e86874ecd2192 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py @@ -512,7 +512,7 @@ class SchedulingUnitBlueprint(RefreshFromDbInvalidatesCachedPropertiesMixin, Tem # 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 getattr(self, copy_field) is None and hasattr(self, 'draft'): + if hasattr(self, 'draft'): setattr(self, copy_field, getattr(self.draft, copy_field)) else: # On updates, prevent changing the scheduling contraints doc or template if we are past schedulable state 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..43866f04310a4407d2a41e5520bc1aad5472976f 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): @@ -749,6 +750,59 @@ 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) + def test_SchedulingUnitBlueprint_gets_created_with_correct_default_scheduling_constraints(self): + + # setup + 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)) + + scheduling_unit_blueprint.refresh_from_db() + + # assert + 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_SchedulingUnitBlueprint_prevents_updating_scheduling_constraints_template_if_not_in_correct_state(self): + + # setup + 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_constraints_template= constraints_1)) + 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() + + scheduling_unit_blueprint.refresh_from_db() + + with self.assertRaises(TMSSException) as context: + scheduling_unit_blueprint.scheduling_constraints_template = models.SchedulingConstraintsTemplate.objects.get(name="constraints_2") + scheduling_unit_blueprint.save() + + self.assertIn('schedulable state', str(context.exception)) + + def test_SchedulingUnitBlueprint_prevents_updating_scheduling_constraints_doc_if_not_in_correct_state(self): + + # setup + 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() + + scheduling_unit_blueprint.refresh_from_db() + + with self.assertRaises(TMSSException) as context: + scheduling_unit_blueprint.scheduling_constraints_doc = {'foo': 'matic'} + scheduling_unit_blueprint.save() + + self.assertIn('schedulable state', str(context.exception)) + class TaskBlueprintTest(unittest.TestCase): @classmethod 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..9a2f1fd8d352e767d05b4f7e8434803b03225628 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: @@ -250,7 +260,10 @@ def TaskRelationDraft_test_data(producer: models.TaskDraft = None, consumer: mod "output_role": models.TaskConnectorType.objects.create(**TaskConnectorType_test_data()), "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: +def SchedulingUnitBlueprint_test_data(name=None, requirements_template: models.SchedulingUnitTemplate=None, draft=None, output_pinned=None, + scheduling_constraints_doc: dict=None, + scheduling_constraints_template: models.SchedulingConstraintsTemplate=None) -> dict: + if name is None: name = 'my_scheduling_unit_blueprint_' + str(uuid.uuid4()) @@ -263,6 +276,12 @@ def SchedulingUnitBlueprint_test_data(name=None, requirements_template: models.S if output_pinned is None: output_pinned = False + 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": [], @@ -270,7 +289,9 @@ def SchedulingUnitBlueprint_test_data(name=None, requirements_template: models.S "requirements_template": requirements_template, "do_cancel": False, "draft": draft, - "output_pinned": output_pinned} + "output_pinned": output_pinned, + "scheduling_constraints_template": scheduling_constraints_template, + "scheduling_constraints_doc": scheduling_constraints_doc} def TaskBlueprint_test_data(name: str=None, task_draft: models.TaskDraft = None, scheduling_unit_blueprint: models.SchedulingUnitBlueprint = None, specifications_template: models.TaskTemplate=None, specifications_doc: dict=None, output_pinned=False) -> dict: if name is None: