From f4449c0292bd01e90c6fc95e8b5bdb264bd0ffa4 Mon Sep 17 00:00:00 2001 From: goei <JsXLRu> Date: Mon, 10 Aug 2020 19:54:46 +0200 Subject: [PATCH] TMSS-266: Add (task) type to the TaskTemplate model with observation type as default --- .../tmss/tmssapp/migrations/0001_initial.py | 15 ++++++++++++ .../src/tmss/tmssapp/models/specification.py | 14 ++++++++++- SAS/TMSS/src/tmss/tmssapp/populate.py | 5 +++- .../tmss/tmssapp/serializers/specification.py | 5 +++- .../tmss/tmssapp/viewsets/specification.py | 5 ++++ SAS/TMSS/src/tmss/urls.py | 1 + .../test/t_tmssapp_specification_REST_API.py | 23 ++++++++++++++++++- SAS/TMSS/test/tmss_test_data_django_models.py | 13 ++++++----- SAS/TMSS/test/tmss_test_data_rest.py | 6 ++++- 9 files changed, 76 insertions(+), 11 deletions(-) diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py index b04ecd0b93b..81e15db2fa2 100644 --- a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py +++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py @@ -690,6 +690,16 @@ class Migration(migrations.Migration): ('validation_code_js', models.CharField(help_text='JavaScript code for additional (complex) validation.', max_length=128)), ], ), + migrations.CreateModel( + name='TaskType', + fields=[ + ('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( name='Setting', fields=[ @@ -707,6 +717,11 @@ class Migration(migrations.Migration): model_name='tasktemplate', constraint=models.UniqueConstraint(fields=('name', 'version'), name='TaskTemplate_unique_name_version'), ), + migrations.AddField( + model_name='tasktemplate', + name='type', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.TaskType'), + ), migrations.AddField( model_name='taskschedulingrelationdraft', name='first', diff --git a/SAS/TMSS/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/src/tmss/tmssapp/models/specification.py index 60b95bb0dff..26683b2cde8 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/models/specification.py @@ -187,6 +187,17 @@ class ProjectCategory(AbstractChoice): TEST = "test" +class TaskType(AbstractChoice): + """Defines the model and predefined list of possible TaskType's for Task. + The items in the Choices class below are automagically populated into the database via a data migration.""" + class Choices(Enum): + OBSERVATION = "observation" + PIPELINE = "pipeline" + INGEST = "ingest" + MAINTENANCE = "maintenance" + OTHER = 'other' + + # concrete models class Setting(BasicCommon): @@ -199,7 +210,7 @@ class TaskConnectorType(BasicCommon): datatype = ForeignKey('Datatype', null=False, on_delete=PROTECT) dataformats = ManyToManyField('Dataformat', blank=True) output_of = ForeignKey("TaskTemplate", related_name='output_connector_types', on_delete=CASCADE) - input_of = ForeignKey("TaskTemplate", related_name='inpput_connector_types', on_delete=CASCADE) + input_of = ForeignKey("TaskTemplate", related_name='input_connector_types', on_delete=CASCADE) # @@ -245,6 +256,7 @@ class DefaultSchedulingUnitTemplate(BasicCommon): class TaskTemplate(Template): validation_code_js = CharField(max_length=128, help_text='JavaScript code for additional (complex) validation.') + type = ForeignKey('TaskType', null=False, on_delete=PROTECT) class Meta: # TODO: move up to the abstract base class and replace with django 3.0 UniqueConstraint(... name='%*class)s_unique_name_version) diff --git a/SAS/TMSS/src/tmss/tmssapp/populate.py b/SAS/TMSS/src/tmss/tmssapp/populate.py index c2ebe1653ce..8de5dc5f2d0 100644 --- a/SAS/TMSS/src/tmss/tmssapp/populate.py +++ b/SAS/TMSS/src/tmss/tmssapp/populate.py @@ -37,7 +37,7 @@ def populate_choices(apps, schema_editor): ''' for choice_class in [Role, Datatype, Dataformat, CopyReason, SubtaskState, SubtaskType, StationType, Algorithm, ScheduleMethod, SchedulingRelationPlacement, - Flag, ProjectCategory, PeriodCategory, Quantity]: + Flag, ProjectCategory, PeriodCategory, Quantity, TaskType]: choice_class.objects.bulk_create([choice_class(value=x.value) for x in choice_class.Choices]) def populate_settings(apps, schema_editor): @@ -272,6 +272,7 @@ def _populate_observation_with_stations_schema(): with open(os.path.join(working_dir, "schemas/task-observation-with-stations.json")) as json_file: json_data = json.loads(json_file.read()) task_template_data = {"name": "observation schema", + "type": TaskType.objects.get(value='observation'), "description": 'schema for observations', "version": '0.1', "tags": [], @@ -283,6 +284,7 @@ def _populate_calibrator_addon_schema(): with open(os.path.join(working_dir, "schemas/task-calibrator-addon.json")) as json_file: json_data = json.loads(json_file.read()) task_template_data = {"name": "calibrator schema", + "type": TaskType.objects.get(value='observation'), "description": 'addon schema for calibrator observations', "version": '0.1', "tags": [], @@ -414,6 +416,7 @@ def _populate_preprocessing_schema(): with open(os.path.join(working_dir, "schemas/task-preprocessing.json")) as json_file: json_data = json.loads(json_file.read()) task_template_data = {"name": "preprocessing schema", + "type": TaskType.objects.get(value='pipeline'), "description": 'preprocessing settings', "version": '0.1', "tags": [], diff --git a/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py index f034bc0acc0..0e173fff986 100644 --- a/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py @@ -380,5 +380,8 @@ class TaskSchedulingRelationBlueprintSerializer(RelationalHyperlinkedModelSerial fields = '__all__' - +class TaskTypeSerializer(RelationalHyperlinkedModelSerializer): + class Meta: + model = models.TaskType + fields = '__all__' diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py index aa83ab8741c..4404f40d626 100644 --- a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py @@ -605,3 +605,8 @@ class TaskRelationBlueprintNestedViewSet(LOFARNestedViewSet): return task_relation_draft.related_task_relation_blueprint.all() else: return models.TaskRelationBlueprint.objects.all() + + +class TaskTypeViewSet(LOFARViewSet): + queryset = models.TaskType.objects.all() + serializer_class = serializers.TaskTypeSerializer \ No newline at end of file diff --git a/SAS/TMSS/src/tmss/urls.py b/SAS/TMSS/src/tmss/urls.py index 6f9ea2167eb..35696e914d1 100644 --- a/SAS/TMSS/src/tmss/urls.py +++ b/SAS/TMSS/src/tmss/urls.py @@ -90,6 +90,7 @@ router.register(r'flag', viewsets.FlagViewSet) router.register(r'period_category', viewsets.PeriodCategoryViewSet) router.register(r'project_category', viewsets.ProjectCategoryViewSet) router.register(r'quantity', viewsets.QuantityViewSet) +router.register(r'task_type', viewsets.TaskTypeViewSet) # templates router.register(r'generator_template', viewsets.GeneratorTemplateViewSet) diff --git a/SAS/TMSS/test/t_tmssapp_specification_REST_API.py b/SAS/TMSS/test/t_tmssapp_specification_REST_API.py index bc95e9ccebc..9e8b6ee34df 100755 --- a/SAS/TMSS/test/t_tmssapp_specification_REST_API.py +++ b/SAS/TMSS/test/t_tmssapp_specification_REST_API.py @@ -216,7 +216,7 @@ class TaskTemplateTestCase(unittest.TestCase): test_data = test_data_creator.TaskTemplate() r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data, 201, test_data) url = r_dict['url'] - GET_OK_and_assert_equal_expected_response(self, url + '?format=json', test_data) + GET_OK_and_assert_equal_expected_response(self, url, test_data) def test_task_template_PUT_invalid_raises_error(self): test_data = test_data_creator.TaskTemplate() @@ -269,6 +269,27 @@ class TaskTemplateTestCase(unittest.TestCase): GET_OK_and_assert_equal_expected_response(self, BASE_URL + '/task_template/' + str(id1), test_data_1) GET_OK_and_assert_equal_expected_response(self, BASE_URL + '/task_template/' + str(id2), test_data_2) + def test_task_template_PROTECT_behavior_on_type_choice_deleted(self): + st_test_data = test_data_creator.TaskTemplate() + + # create dependency that is safe to delete (enums are not populated / re-established between tests) + type_data = {'value': 'kickme'} + POST_and_assert_expected_response(self, BASE_URL + '/task_type/', type_data, 201, type_data) + type_url = BASE_URL + '/task_type/kickme/' + + # POST new item and verify + test_data = dict(st_test_data) + test_data['type'] = type_url + url = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data, 201, test_data)['url'] + GET_OK_and_assert_equal_expected_response(self, url, test_data) + + # Try to DELETE dependency, verify that was not successful + # Unfortunately we don't get a nice error in json, but a Django debug page on error 500... + response = requests.delete(type_url, auth=AUTH) + self.assertEqual(500, response.status_code) + self.assertTrue("ProtectedError" in str(response.content)) + GET_OK_and_assert_equal_expected_response(self, type_url, type_data) + class TaskRelationSelectionTemplateTestCase(unittest.TestCase): def test_task_relation_selection_template_list_apiformat(self): diff --git a/SAS/TMSS/test/tmss_test_data_django_models.py b/SAS/TMSS/test/tmss_test_data_django_models.py index 21ee23b0d2e..9ad9f6b8432 100644 --- a/SAS/TMSS/test/tmss_test_data_django_models.py +++ b/SAS/TMSS/test/tmss_test_data_django_models.py @@ -64,12 +64,13 @@ def TaskTemplate_test_data(name="my TaskTemplate", version:str=None) -> dict: if version is None: version = str(uuid.uuid4()) - return {"validation_code_js":"", - "name": name, - "description": 'My TaskTemplate description', - "version": version, - "schema": {"mykey": "my value"}, - "tags": ["TMSS", "TESTING"]} + return {"type": models.TaskType.objects.get(value='observation'), + "validation_code_js":"", + "name": name, + "description": 'My TaskTemplate description', + "version": version, + "schema": {"mykey": "my value"}, + "tags": ["TMSS", "TESTING"]} def TaskRelationSelectionTemplate_test_data(name="my_TaskRelationSelectionTemplate", version:str=None) -> dict: if version is None: diff --git a/SAS/TMSS/test/tmss_test_data_rest.py b/SAS/TMSS/test/tmss_test_data_rest.py index a58cb44b971..765910ff9ab 100644 --- a/SAS/TMSS/test/tmss_test_data_rest.py +++ b/SAS/TMSS/test/tmss_test_data_rest.py @@ -81,15 +81,19 @@ class TMSSRESTTestDataCreator(): "schema": {"mykey": "my value"}, "tags": ["TMSS", "TESTING"]} - def TaskTemplate(self, name="tasktemplate1", version:str=None) -> dict: + def TaskTemplate(self, name="tasktemplate1", task_type_url:str=None, version:str=None) -> dict: if version is None: version = str(uuid.uuid4()) + if task_type_url is None: + task_type_url = self.django_api_url + '/task_type/observation/' + return {"name": name, "description": 'My one observation', "version": version, "schema": {"mykey": "my value"}, "tags": ["TMSS", "TESTING"], + "type": task_type_url, "validation_code_js": "???"} def TaskRelationSelectionTemplate(self, name="taskrelationselectiontemplate1", version:str=None) -> dict: -- GitLab