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 a89b16fc3d51aaf571122805d7ccde6d3ae46213..2fffaacce2860830ce8cf931ccb535535ae69121 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-03-22 10:34 +# Generated by Django 3.0.9 on 2021-03-23 17:08 from django.conf import settings import django.contrib.postgres.fields @@ -360,6 +360,15 @@ class Migration(migrations.Migration): 'abstract': False, }, ), + migrations.CreateModel( + name='IOType', + fields=[ + ('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)), + ], + options={ + 'abstract': False, + }, + ), migrations.CreateModel( name='PeriodCategory', fields=[ @@ -464,13 +473,13 @@ class Migration(migrations.Migration): name='ReservationStrategyTemplate', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('tags',django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)), + ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)), ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')), ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')), ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)), ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)), - ('template', django.contrib.postgres.fields.jsonb.JSONField(help_text='JSON-data compliant with the JSON-schema in the scheduling_unit_template. This observation strategy template like a predefined recipe with all the correct settings, and defines which parameters the user can alter.')), + ('template', django.contrib.postgres.fields.jsonb.JSONField(help_text='JSON-data compliant with the JSON-schema in the reservation_template. This reservation strategy template like a predefined recipe with all the correct settings, and defines which parameters the user can alter.')), ], options={ 'abstract': False, @@ -790,7 +799,6 @@ class Migration(migrations.Migration): ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)), ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')), ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')), - ('output', models.BooleanField(help_text='True if this connector describes an output, False if this connector describes an input')), ], options={ 'abstract': False, @@ -1011,6 +1019,11 @@ class Migration(migrations.Migration): name='datatype', field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Datatype'), ), + migrations.AddField( + model_name='taskconnectortype', + name='iotype', + field=models.ForeignKey(help_text='Is this connector an input or output', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.IOType'), + ), migrations.AddField( model_name='taskconnectortype', name='role', @@ -1235,12 +1248,12 @@ class Migration(migrations.Migration): migrations.AddField( model_name='projectquotaarchivelocation', name='project_quota', - field=models.ForeignKey(help_text='Project to which this quota belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='project_quota', to='tmssapp.ProjectQuota'), + field=models.ForeignKey(help_text='The ProjectQuota for this archive location', on_delete=django.db.models.deletion.PROTECT, related_name='project_quota_archive_location', to='tmssapp.ProjectQuota'), ), migrations.AddField( model_name='projectquota', name='project', - field=models.ForeignKey(help_text='Project to which this quota belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='quota', to='tmssapp.Project'), + field=models.ForeignKey(help_text='Project to wich this quota belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='quota', to='tmssapp.Project'), ), migrations.AddField( model_name='projectquota', diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py index 2ebdaf12e1bb2f89b818da644773373ba697da99..be5b44b7cbfaa585cb2ca08cd6b7e9dbfd4daa60 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py @@ -55,6 +55,15 @@ class Role(AbstractChoice): ANY = "any" +class IOType(AbstractChoice): + """Defines the model and predefined list of possible IOType's for TaskConnectorType. + The items in the Choises class below are automagically populated into the database via a data migration.""" + class Choices(Enum): + INPUT = "input" + OUTPUT = "output" + # maybe we can add an IN_PLACE="in_place" option in the future, but for now it's not needed. + + class Datatype(AbstractChoice): """Defines the model and predefined list of possible Datatype's for TaskConnectorType. The items in the Choises class below are automagically populated into the database via a data migration.""" @@ -163,7 +172,7 @@ class TaskConnectorType(BasicCommon): datatype = ForeignKey('Datatype', null=False, on_delete=PROTECT) dataformats = ManyToManyField('Dataformat', blank=True) task_template = ForeignKey("TaskTemplate", related_name='output_connector_types', null=False, on_delete=CASCADE) - output = BooleanField(null=False, help_text="True if this connector describes an output, False if this connector describes an input") + iotype = ForeignKey('IOType', null=False, on_delete=PROTECT, help_text="Is this connector an input or output") # diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/populate.py b/SAS/TMSS/backend/src/tmss/tmssapp/populate.py index 43d73566a0ff3de217ac61dba88354a548f5a0b4..4d274999457d157af50a67241c65a213a791a2bb 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/populate.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/populate.py @@ -40,7 +40,7 @@ def populate_choices(apps, schema_editor): each 'choice'type in Role, Datatype, Dataformat, CopyReason :return: None ''' - choice_classes = [Role, Datatype, Dataformat, CopyReason, + choice_classes = [Role, IOType, Datatype, Dataformat, CopyReason, SubtaskState, SubtaskType, StationType, Algorithm, SchedulingRelationPlacement, Flag, ProjectCategory, PeriodCategory, Quantity, TaskType, ProjectRole] @@ -359,35 +359,35 @@ def populate_connectors(): TaskConnectorType.objects.create(role=Role.objects.get(value=Role.Choices.CORRELATOR.value), datatype=Datatype.objects.get(value=Datatype.Choices.VISIBILITIES.value), task_template=TaskTemplate.objects.get(name='calibrator observation'), - output=True) + iotype=IOType.objects.get(value=IOType.Choices.OUTPUT.value)) # target observation TaskConnectorType.objects.create(role=Role.objects.get(value=Role.Choices.CORRELATOR.value), datatype=Datatype.objects.get(value=Datatype.Choices.VISIBILITIES.value), task_template=TaskTemplate.objects.get(name='target observation'), - output=True) + iotype=IOType.objects.get(value=IOType.Choices.OUTPUT.value)) # preprocessing pipeline TaskConnectorType.objects.create(role=Role.objects.get(value=Role.Choices.ANY.value), datatype=Datatype.objects.get(value=Datatype.Choices.VISIBILITIES.value), task_template=TaskTemplate.objects.get(name='preprocessing pipeline'), - output=False) + iotype=IOType.objects.get(value=IOType.Choices.INPUT.value)) TaskConnectorType.objects.create(role=Role.objects.get(value=Role.Choices.ANY.value), datatype=Datatype.objects.get(value=Datatype.Choices.VISIBILITIES.value), task_template=TaskTemplate.objects.get(name='preprocessing pipeline'), - output=True) + iotype=IOType.objects.get(value=IOType.Choices.OUTPUT.value)) # ingest TaskConnectorType.objects.create(role=Role.objects.get(value=Role.Choices.ANY.value), datatype=Datatype.objects.get(value=Datatype.Choices.VISIBILITIES.value), task_template=TaskTemplate.objects.get(name='ingest'), - output=False) + iotype=IOType.objects.get(value=IOType.Choices.INPUT.value)) TaskConnectorType.objects.create(role=Role.objects.get(value=Role.Choices.ANY.value), datatype=Datatype.objects.get(value=Datatype.Choices.TIME_SERIES.value), task_template=TaskTemplate.objects.get(name='ingest'), - output=False) + iotype=IOType.objects.get(value=IOType.Choices.INPUT.value)) def populate_permissions(): diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py index 47086104958108a4cc364a1c07c84c200d909d64..8e21947208819f013ba1c7d23bda3586cd774f91 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py @@ -102,6 +102,11 @@ class RoleSerializer(serializers.ModelSerializer): model = models.Role fields = '__all__' +class IOTypeSerializer(serializers.ModelSerializer): + class Meta: + model = models.IOType + fields = '__all__' + class SchedulingRelationPlacementSerializer(serializers.ModelSerializer): class Meta: model = models.SchedulingRelationPlacement diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py index 0752147c4e54e0d5f9caa67c16ecc374a8adf7a0..e6d9c06ebe4e38f60a459788c6d16f41569b237c 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py @@ -179,8 +179,8 @@ def create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft: models. producer_task_draft = scheduling_unit_draft.task_drafts.get(name=task_relation_definition["producer"]) consumer_task_draft = scheduling_unit_draft.task_drafts.get(name=task_relation_definition["consumer"]) dataformat = models.Dataformat.objects.get(value=task_relation_definition["dataformat"]) - input_role = models.TaskConnectorType.objects.get(task_template=consumer_task_draft.specifications_template, role=task_relation_definition["input"]["role"], datatype=task_relation_definition["input"]["datatype"], output=False) - output_role = models.TaskConnectorType.objects.get(task_template=producer_task_draft.specifications_template, role=task_relation_definition["output"]["role"], datatype=task_relation_definition["output"]["datatype"], output=True) + input_role = models.TaskConnectorType.objects.get(task_template=consumer_task_draft.specifications_template, role=task_relation_definition["input"]["role"], datatype=task_relation_definition["input"]["datatype"], iotype=models.IOType.objects.get(value=models.IOType.Choices.INPUT.value)) + output_role = models.TaskConnectorType.objects.get(task_template=producer_task_draft.specifications_template, role=task_relation_definition["output"]["role"], datatype=task_relation_definition["output"]["datatype"], iotype=models.IOType.objects.get(value=models.IOType.Choices.OUTPUT.value)) selection_template = models.TaskRelationSelectionTemplate.objects.get(name=task_relation_definition["selection_template"]) try: diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py index d53ace784b028f01ba199a80e067090526a66a41..620742eaa77f9aedd8400e88f862121fcb2e2dbf 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py @@ -280,6 +280,11 @@ class RoleViewSet(LOFARViewSet): serializer_class = serializers.RoleSerializer +class IOTypeViewSet(LOFARViewSet): + queryset = models.IOType.objects.all() + serializer_class = serializers.IOTypeSerializer + + class SchedulingRelationPlacement(LOFARViewSet): queryset = models.SchedulingRelationPlacement.objects.all() serializer_class = serializers.SchedulingRelationPlacementSerializer diff --git a/SAS/TMSS/backend/src/tmss/urls.py b/SAS/TMSS/backend/src/tmss/urls.py index 66e58162725f917a20c5020e6492ad6f39bed7d0..afe222f05f2ef50547b85a34cd591755dbd77c40 100644 --- a/SAS/TMSS/backend/src/tmss/urls.py +++ b/SAS/TMSS/backend/src/tmss/urls.py @@ -117,6 +117,7 @@ router.register(r'tags', viewsets.TagsViewSet) # choices router.register(r'role', viewsets.RoleViewSet) +router.register(r'iotype', viewsets.IOTypeViewSet) router.register(r'datatype', viewsets.DatatypeViewSet) router.register(r'dataformat', viewsets.DataformatViewSet) router.register(r'copy_reason', viewsets.CopyReasonViewSet) 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 2a6c110bc7fd857e9706c061d9b7c237b924cd4a..08c549f734feed11c0cda5fe64edd974297cb0af 100644 --- a/SAS/TMSS/backend/test/tmss_test_data_django_models.py +++ b/SAS/TMSS/backend/test/tmss_test_data_django_models.py @@ -107,7 +107,7 @@ def TaskConnectorType_test_data() -> dict: return {"role": models.Role.objects.get(value='calibrator'), "datatype": models.Datatype.objects.get(value='instrument model'), "task_template": models.TaskTemplate.objects.create(**TaskTemplate_test_data()), - "output": True, + "iotype": models.IOType.objects.get(value=models.IOType.Choices.OUTPUT.value), "tags": []} def Cycle_test_data() -> dict: diff --git a/SAS/TMSS/backend/test/tmss_test_data_rest.py b/SAS/TMSS/backend/test/tmss_test_data_rest.py index 340b365afe6f116d668e92ba6f31256cd64bb523..759885c6f84320b6f452ade940b1db2bfe8e4eb5 100644 --- a/SAS/TMSS/backend/test/tmss_test_data_rest.py +++ b/SAS/TMSS/backend/test/tmss_test_data_rest.py @@ -221,7 +221,7 @@ class TMSSRESTTestDataCreator(): return self._task_relation_selection_template_url - def TaskConnectorType(self, role="correlator", output=True, task_template_url=None): + def TaskConnectorType(self, role="correlator", iotype="output", task_template_url=None): if task_template_url is None: task_template_url = self.cached_task_template_url @@ -229,7 +229,7 @@ class TMSSRESTTestDataCreator(): "datatype": self.django_api_url + '/datatype/image', "dataformats": [self.django_api_url + '/dataformat/Beamformed'], "task_template": task_template_url, - "output": output, + "iotype": self.django_api_url + '/iotype/%s'%iotype, "tags": []} @@ -431,10 +431,10 @@ class TMSSRESTTestDataCreator(): selection_doc = self.get_response_as_json_object(template_url+'/default') if input_role_url is None: - input_role_url = self.post_data_and_get_url(self.TaskConnectorType(output=False), '/task_connector_type/') + input_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="input"), '/task_connector_type/') if output_role_url is None: - output_role_url = self.post_data_and_get_url(self.TaskConnectorType(output=True), '/task_connector_type/') + output_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="output"), '/task_connector_type/') return {"tags": [], "selection_doc": selection_doc, @@ -530,10 +530,10 @@ class TMSSRESTTestDataCreator(): selection_doc = self.get_response_as_json_object(template_url+'/default') if input_role_url is None: - input_role_url = self.post_data_and_get_url(self.TaskConnectorType(output=False), '/task_connector_type/') + input_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="input"), '/task_connector_type/') if output_role_url is None: - output_role_url = self.post_data_and_get_url(self.TaskConnectorType(output=True), '/task_connector_type/') + output_role_url = self.post_data_and_get_url(self.TaskConnectorType(iotype="output"), '/task_connector_type/') # test data return {"tags": [],