From a3a92e7416ae0c2cb35bd3b71109422b21d011ee Mon Sep 17 00:00:00 2001 From: goei <JsXLRu> Date: Thu, 22 Oct 2020 17:28:50 +0200 Subject: [PATCH] TMSS-324 Add SIPIdentifier table. Add global_sip as foreignkey to the SubTask, Dataproduct and SAP table. Remove identifier from SAP specifications_doc because its in model now. --- .../tmss/tmssapp/migrations/0001_initial.py | 81 +++++++++++++------ .../src/tmss/tmssapp/models/scheduling.py | 27 +++++++ .../tmss/tmssapp/schemas/sap_template-1.json | 12 --- .../tmss/tmssapp/serializers/scheduling.py | 14 ++++ SAS/TMSS/src/tmss/tmssapp/subtasks.py | 10 ++- .../src/tmss/tmssapp/viewsets/scheduling.py | 6 ++ SAS/TMSS/src/tmss/urls.py | 1 + 7 files changed, 111 insertions(+), 40 deletions(-) diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py index e3ccbb0b1e8..a9eb77f656b 100644 --- a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py +++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.9 on 2020-10-16 10:16 +# Generated by Django 3.0.7 on 2020-10-22 07:24 from django.conf import settings import django.contrib.postgres.fields @@ -62,7 +62,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('rcus', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=128)), ('inputs', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, size=128)), ], @@ -78,7 +78,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('location', models.CharField(help_text='Human-readable location of the cluster.', max_length=128)), ('archive_site', models.BooleanField(default=False, help_text='TRUE if this cluster is an archive site, FALSE if not (f.e. a local cluster, or user-owned cluster).')), ], @@ -94,7 +94,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), ], @@ -117,7 +117,7 @@ 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.')), - ('description', models.CharField(blank=True, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128, primary_key=True, serialize=False)), ('start', models.DateTimeField(help_text='Moment at which the cycle starts, that is, when its projects can run.')), ('stop', models.DateTimeField(help_text='Moment at which the cycle officially ends.')), @@ -186,7 +186,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), ], @@ -215,7 +215,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), ], @@ -344,7 +344,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('capacity', models.BigIntegerField(help_text='Capacity in bytes')), ('directory', models.CharField(help_text='Root directory under which we are allowed to write our data.', max_length=1024)), ], @@ -369,7 +369,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), ('create_function', models.CharField(help_text='Python function to call to execute the generator.', max_length=128)), @@ -393,7 +393,7 @@ 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.')), - ('description', models.CharField(blank=True, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128, primary_key=True, serialize=False)), ('priority_rank', models.FloatField(help_text='Priority of this project w.r.t. other projects. Projects can interrupt observations of lower-priority projects.')), ('trigger_priority', models.IntegerField(default=1000, help_text='Priority of this project w.r.t. triggers.')), @@ -438,7 +438,7 @@ 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.')), - ('description', models.CharField(blank=True, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128, primary_key=True, serialize=False)), ], options={ @@ -475,7 +475,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), ], @@ -500,7 +500,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), ], @@ -525,7 +525,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('generator_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Parameters for the generator (NULLable).', null=True)), ], options={ @@ -540,7 +540,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('requirements_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Scheduling and/or quality requirements for this scheduling unit (IMMUTABLE).')), ('do_cancel', models.BooleanField()), ], @@ -556,7 +556,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('requirements_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Scheduling and/or quality requirements for this run.')), ('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)), @@ -573,7 +573,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('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.')), ], @@ -589,7 +589,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), ], @@ -597,6 +597,20 @@ class Migration(migrations.Migration): 'abstract': False, }, ), + migrations.CreateModel( + name='SIPidentifier', + 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)), + ('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.')), + ('source', models.CharField(help_text='Source name', max_length=128)), + ('unique_identifier', models.CharField(help_text='Unique global identifier.', max_length=255, unique=True)), + ], + options={ + 'abstract': False, + }, + ), migrations.CreateModel( name='StationType', fields=[ @@ -679,7 +693,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), ('queue', models.BooleanField(default=False)), @@ -714,7 +728,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('specifications_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schedulings for this task (IMMUTABLE).')), ('do_cancel', models.BooleanField(help_text='Cancel this task.')), ], @@ -742,7 +756,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('specifications_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Specifications for this task.')), ], options={ @@ -783,7 +797,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), ], @@ -821,7 +835,7 @@ class Migration(migrations.Migration): ('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, help_text='A longer description of this object.', max_length=255)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), ('validation_code_js', models.CharField(blank=True, default='', help_text='JavaScript code for additional (complex) validation.', max_length=128)), @@ -1057,6 +1071,11 @@ class Migration(migrations.Migration): name='created_or_updated_by_user', field=models.ForeignKey(editable=False, help_text='The user who created / updated the subtask.', null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), ), + migrations.AddField( + model_name='subtask', + name='global_sip', + field=models.ForeignKey(editable=False, help_text='The global unique identifier for LTA SIP.', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SIPidentifier'), + ), migrations.AddField( model_name='subtask', name='schedule_method', @@ -1077,6 +1096,10 @@ class Migration(migrations.Migration): name='task_blueprint', field=models.ForeignKey(help_text='Task Blueprint to which this Subtask belongs.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subtasks', to='tmssapp.TaskBlueprint'), ), + migrations.AddIndex( + model_name='sipidentifier', + index=django.contrib.postgres.indexes.GinIndex(fields=['tags'], name='tmssapp_sip_tags_bbce92_gin'), + ), migrations.AddConstraint( model_name='schedulingunittemplate', constraint=models.UniqueConstraint(fields=('name', 'version'), name='schedulingunittemplate_unique_name_version'), @@ -1149,6 +1172,11 @@ class Migration(migrations.Migration): model_name='saptemplate', constraint=models.UniqueConstraint(fields=('name', 'version'), name='saptemplate_unique_name_version'), ), + migrations.AddField( + model_name='sap', + name='global_sip', + field=models.ForeignKey(editable=False, help_text='The global unique identifier for LTA SIP.', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SIPidentifier'), + ), migrations.AddField( model_name='sap', name='specifications_template', @@ -1281,6 +1309,11 @@ class Migration(migrations.Migration): name='feedback_template', field=models.ForeignKey(help_text='Schema used for feedback_doc.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.DataproductFeedbackTemplate'), ), + migrations.AddField( + model_name='dataproduct', + name='global_sip', + field=models.ForeignKey(editable=False, help_text='The global unique identifier for LTA SIP.', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SIPidentifier'), + ), migrations.AddField( model_name='dataproduct', name='producer', @@ -1289,7 +1322,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='dataproduct', name='sap', - field=models.ForeignKey(help_text='SAP this dataproduct was generated out of (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='SAP_dataproducts', to='tmssapp.SAP'), + field=models.ForeignKey(help_text='SAP this dataproduct was generated out of (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='dataproducts', to='tmssapp.SAP'), ), migrations.AddField( model_name='dataproduct', diff --git a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py index a6932d705e3..ddbd8e1f730 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py +++ b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py @@ -23,6 +23,7 @@ from lofar.sas.tmss.client.tmssbuslistener import DEFAULT_TMSS_SUBTASK_NOTIFICAT from lofar.common.util import single_line_with_single_spaces from django.conf import settings from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RADBRPC +import uuid # # I/O # @@ -31,6 +32,19 @@ from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RADBRPC # Choices # + +def generate_unique_identifier_for_SIP_when_needed(model): + """ + Create an Unique Identifier for given model class if not exist (None) + UUID is 16 bytes per definition so have to store as string. + This string should have integer values as defined for SIP validation in SIP xsd schema (IdentifierType) + """ + print("generate_unique_identifier_for_SIP_when_needed for %s %s" % (str(type(model)), model.id)) + if model.id is not None and model.global_sip is None: + unique_id = uuid.uuid4().int + model.global_sip = SIPidentifier.objects.create(unique_identifier=str(unique_id), source="TMSS") + + class SubtaskState(AbstractChoice): """Defines the model and predefined list of possible SubtaskStatusChoice's for Subtask. The items in the Choices class below are automagically populated into the database via a data migration.""" @@ -150,6 +164,7 @@ class Subtask(BasicCommon): # resource_claim = ForeignKey("ResourceClaim", null=False, on_delete=PROTECT) # todo <-- how is this external reference supposed to work? created_or_updated_by_user = ForeignKey(User, null=True, editable=False, on_delete=PROTECT, help_text='The user who created / updated the subtask.') raw_feedback = CharField(null=True, max_length=1048576, help_text='The raw feedback for this Subtask') + global_sip = ForeignKey('SIPidentifier', null=True, editable=False, on_delete=PROTECT, help_text='The global unique identifier for LTA SIP.') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -194,6 +209,7 @@ class Subtask(BasicCommon): creating = self._state.adding # True on create, False on update annotate_validate_add_defaults_to_doc_using_template(self, 'specifications_doc', 'specifications_template') + generate_unique_identifier_for_SIP_when_needed(self) if self.state.value == SubtaskState.Choices.SCHEDULED.value and self.__original_state_id == SubtaskState.Choices.SCHEDULING.value: if self.start_time is None: @@ -288,12 +304,15 @@ class SubtaskOutput(BasicCommon): class SAP(BasicCommon): specifications_doc = JSONField(help_text='SAP properties.') specifications_template = ForeignKey('SAPTemplate', null=False, on_delete=CASCADE, help_text='Schema used for specifications_doc.') + global_sip = ForeignKey('SIPidentifier', null=True, editable=False, on_delete=PROTECT, help_text='The global unique identifier for LTA SIP.') def save(self, force_insert=False, force_update=False, using=None, update_fields=None): annotate_validate_add_defaults_to_doc_using_template(self, 'specifications_doc', 'specifications_template') + generate_unique_identifier_for_SIP_when_needed(self) super().save(force_insert, force_update, using, update_fields) + class Dataproduct(BasicCommon): """ A data product represents an atomic dataset, produced and consumed by subtasks. The consumed dataproducts are those @@ -316,13 +335,16 @@ class Dataproduct(BasicCommon): feedback_doc = JSONField(help_text='Dataproduct properties, as reported by the producing process.') feedback_template = ForeignKey('DataproductFeedbackTemplate', on_delete=PROTECT, help_text='Schema used for feedback_doc.') sap = ForeignKey('SAP', on_delete=PROTECT, null=True, related_name="dataproducts", help_text='SAP this dataproduct was generated out of (NULLable).') + global_sip = ForeignKey('SIPidentifier', editable=False, null=True, on_delete=PROTECT, help_text='The global unique identifier for LTA SIP.') def save(self, force_insert=False, force_update=False, using=None, update_fields=None): annotate_validate_add_defaults_to_doc_using_template(self, 'specifications_doc', 'specifications_template') annotate_validate_add_defaults_to_doc_using_template(self, 'feedback_doc', 'feedback_template') + generate_unique_identifier_for_SIP_when_needed(self) super().save(force_insert, force_update, using, update_fields) + class AntennaSet(NamedCommon): station_type = ForeignKey('StationType', null=False, on_delete=PROTECT) rcus = ArrayField(IntegerField(), size=128, blank=False) @@ -368,3 +390,8 @@ class DataproductHash(BasicCommon): algorithm = ForeignKey('Algorithm', null=False, on_delete=PROTECT, help_text='Algorithm used (MD5, AES256).') hash = CharField(max_length=128, help_text='Hash value.') + +class SIPidentifier(BasicCommon): + source = CharField(max_length=128, help_text='Source name') + # the identifier can not be a BigInteger because its 8 bytes and UUID is by defintion 16 byte, so store as char + unique_identifier = CharField(max_length=255, unique=True, help_text='Unique global identifier.') diff --git a/SAS/TMSS/src/tmss/tmssapp/schemas/sap_template-1.json b/SAS/TMSS/src/tmss/tmssapp/schemas/sap_template-1.json index b4f6eb64f1e..e80661a829d 100644 --- a/SAS/TMSS/src/tmss/tmssapp/schemas/sap_template-1.json +++ b/SAS/TMSS/src/tmss/tmssapp/schemas/sap_template-1.json @@ -5,17 +5,6 @@ "type": "object", "default": {}, "properties": { - "identifiers": { - "type": "object", - "properties": { - "SIP": { - "type": "string", - "default": "" - } - }, - "additionalProperties": false, - "default": {} - }, "measurement_type": { "type": "string", "enum": ["calibrator", "target"], @@ -59,7 +48,6 @@ } }, "required": [ - "identifiers", "name", "pointing", "time", diff --git a/SAS/TMSS/src/tmss/tmssapp/serializers/scheduling.py b/SAS/TMSS/src/tmss/tmssapp/serializers/scheduling.py index 091a2352b42..a2a10449dae 100644 --- a/SAS/TMSS/src/tmss/tmssapp/serializers/scheduling.py +++ b/SAS/TMSS/src/tmss/tmssapp/serializers/scheduling.py @@ -160,3 +160,17 @@ class SAPTemplateSerializer(AbstractTemplateSerializer): class Meta: model = models.SAPTemplate fields = '__all__' + + +class SAPSerializer(RelationalHyperlinkedModelSerializer): + specifications_doc = JSONEditorField(schema_source='specifications_template.schema') + + class Meta: + model = models.SAP + fields = '__all__' + + +class SIPidentifierSerializer(RelationalHyperlinkedModelSerializer): + class Meta: + model = models.SIPidentifier + fields = '__all__' diff --git a/SAS/TMSS/src/tmss/tmssapp/subtasks.py b/SAS/TMSS/src/tmss/tmssapp/subtasks.py index 94e2ce2064b..07d3a41cda1 100644 --- a/SAS/TMSS/src/tmss/tmssapp/subtasks.py +++ b/SAS/TMSS/src/tmss/tmssapp/subtasks.py @@ -556,6 +556,7 @@ def schedule_qafile_subtask(qafile_subtask: Subtask): feedback_template=DataproductFeedbackTemplate.objects.get(name="empty"), sap=None # todo: do we need to point to a SAP here? Of which dataproduct then? ) + qafile_subtask_dataproduct.save() # step 5: set state to SCHEDULED (resulting in the qaservice to pick this subtask up and run it) qafile_subtask.state = SubtaskState.objects.get(value=SubtaskState.Choices.SCHEDULED.value) @@ -608,6 +609,7 @@ def schedule_qaplots_subtask(qaplots_subtask: Subtask): feedback_template=DataproductFeedbackTemplate.objects.get(name="empty"), sap=None # todo: do we need to point to a SAP here? Of which dataproduct then? ) + qaplots_subtask_dataproduct.save() # step 5: set state to SCHEDULED (resulting in the qaservice to pick this subtask up and run it) qaplots_subtask.state = SubtaskState.objects.get(value=SubtaskState.Choices.SCHEDULED.value) @@ -725,7 +727,6 @@ def schedule_observation_subtask(observation_subtask: Subtask): antennafields += [{"station": station, "field": field, "type": antennaset.split('_')[0]} for field in fields] sap = SAP.objects.create(specifications_doc={ "name": "%s_%s" % (observation_subtask.id, pointing['name']), - "identifiers": {}, # todo: TMSS-324 "pointing": pointing['pointing'], "time": {"start_time": observation_subtask.start_time.isoformat(), "duration": (observation_subtask.stop_time - observation_subtask.start_time).total_seconds()}, @@ -735,9 +736,9 @@ def schedule_observation_subtask(observation_subtask: Subtask): } }, specifications_template=SAPTemplate.objects.get(name="SAP")) - + sap.save() for sb_nr in pointing['subbands']: - Dataproduct.objects.create(filename="L%d_SAP%03d_SB%03d_uv.MS" % (observation_subtask.id, sap_nr, sb_nr), + dp = Dataproduct.objects.create(filename="L%d_SAP%03d_SB%03d_uv.MS" % (observation_subtask.id, sap_nr, sb_nr), directory=directory, dataformat=Dataformat.objects.get(value="MeasurementSet"), datatype=Datatype.objects.get(value="visibilities"), # todo: is this correct? @@ -749,7 +750,7 @@ def schedule_observation_subtask(observation_subtask: Subtask): size=0 if sb_nr%10==0 else 1024*1024*1024*sb_nr, expected_size=1024*1024*1024*sb_nr, sap=sap) - + dp.save() # step 4: resource assigner (if possible) _assign_resources(observation_subtask) @@ -829,6 +830,7 @@ def schedule_pipeline_subtask(pipeline_subtask: Subtask): feedback_doc=get_default_json_object_for_schema(dataproduct_feedback_template.schema), feedback_template=dataproduct_feedback_template, sap=input_dp.sap) + output_dp.save() DataproductTransform.objects.create(input=input_dp, output=output_dp, identity=False) output_dps.append(output_dp) diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/scheduling.py b/SAS/TMSS/src/tmss/tmssapp/viewsets/scheduling.py index 601321cf92d..54ed8f42527 100644 --- a/SAS/TMSS/src/tmss/tmssapp/viewsets/scheduling.py +++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/scheduling.py @@ -328,6 +328,12 @@ class SAPViewSet(LOFARViewSet): serializer = serializers.DataproductSerializer(sap.dataproducts, many=True, context={'request': request}) return RestResponse(serializer.data) + class SAPTemplateViewSet(AbstractTemplateViewSet): queryset = models.SAPTemplate.objects.all() serializer_class = serializers.SAPTemplateSerializer + + +class SIPidentifierViewSet(LOFARViewSet): + queryset = models.SIPidentifier.objects.all() + serializer_class = serializers.SIPidentifierSerializer \ No newline at end of file diff --git a/SAS/TMSS/src/tmss/urls.py b/SAS/TMSS/src/tmss/urls.py index 85712c60e16..7d5e08c90cb 100644 --- a/SAS/TMSS/src/tmss/urls.py +++ b/SAS/TMSS/src/tmss/urls.py @@ -195,6 +195,7 @@ router.register(r'dataproduct_hash', viewsets.DataproductHashViewSet) router.register(r'subtask_state_log', viewsets.SubtaskStateLogViewSet) router.register(r'user', viewsets.UserViewSet) router.register(r'sap', viewsets.SAPViewSet) +router.register(r'sip_identifier', viewsets.SIPidentifierViewSet) # --- -- GitLab