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 97e17b9d3d22b21f1f3ead469c0508152a8630f8..e943509d30488872845ca114d50fd871f4ba28f1 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-08-05 10:16 +# Generated by Django 3.0.9 on 2021-08-24 11:37 from django.conf import settings import django.contrib.auth.models @@ -756,6 +756,7 @@ class Migration(migrations.Migration): ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')), ('start_time', models.DateTimeField(help_text='Start this subtask at the specified time (NULLable).', null=True)), ('stop_time', models.DateTimeField(help_text='Stop this subtask at the specified time (NULLable).', null=True)), + ('primary', models.BooleanField(default=False, help_text='TRUE if this is the one-and-only primary subtask in a parent TaskBlueprint.')), ('specifications_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Final specifications, as input for the controller.')), ('raw_feedback', models.CharField(help_text='The raw feedback for this Subtask', max_length=1048576, null=True)), ], diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py index 94bbdd24af774ea1327a206e98535a130e542bf9..21c49937bef6aea170ba377492143d83a032001e 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py @@ -148,6 +148,7 @@ class Subtask(BasicCommon, ProjectPropertyMixin, TemplateSchemaMixin): start_time = DateTimeField(null=True, help_text='Start this subtask at the specified time (NULLable).') stop_time = DateTimeField(null=True, help_text='Stop this subtask at the specified time (NULLable).') state = ForeignKey('SubtaskState', null=False, on_delete=PROTECT, related_name='task_states', help_text='Subtask state (see Subtask State Machine).') + primary = BooleanField(default=False, help_text='TRUE if this is the one-and-only primary subtask in a parent TaskBlueprint.') specifications_doc = JSONField(help_text='Final specifications, as input for the controller.') task_blueprints = ManyToManyField('TaskBlueprint', related_name='subtasks', blank=True, help_text='Task Blueprint to which this Subtask belongs.') specifications_template = ForeignKey('SubtaskTemplate', null=False, on_delete=PROTECT, help_text='Schema used for specifications_doc.') @@ -158,6 +159,10 @@ class Subtask(BasicCommon, ProjectPropertyMixin, TemplateSchemaMixin): global_identifier = OneToOneField('SIPidentifier', null=False, editable=False, on_delete=PROTECT, help_text='The global unique identifier for LTA SIP.') path_to_project = 'task_blueprints__scheduling_unit_blueprint__draft__scheduling_set__project' + # TODO: find a way to enforce that only one subtask per TaskBlueprint has primary==True. The ManyToManyField makes that dificult. RawSQL? CheckConstraint and Q? + # class Meta(BasicCommon.Meta): + # constraints = [UniqueConstraint(fields=['primary', 'task_blueprints'], name='subtask_unique_primary_subtask')] + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/scheduling.py b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/scheduling.py index 01ca8544b68aa4c5280f9d9f5b5feced84f0822b..9aea1d21fd13dc200117f53d9dba45d2ebd07bdf 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/scheduling.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/scheduling.py @@ -83,6 +83,7 @@ class SubtaskSerializer(DynamicRelationalHyperlinkedModelSerializer): subtask_type = serializers.StringRelatedField(source='specifications_template.type', label='subtask_type', read_only=True, help_text='The subtask type as defined in the specifications template, provided here to safe an addition lookup.') specifications_doc = JSONEditorField(schema_source='specifications_template.schema') duration = FloatDurationField(read_only=True) + primary = serializers.BooleanField(read_only=True) #primary field is and should only be set by tmss django server upon subtask creation input_dataproducts = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='dataproduct-detail') output_dataproducts = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='dataproduct-detail') diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py index 9c581d92f9e8c2f68195dc39db763e5b5d9dc913..05eab32c6709a745defcdf6f9a34c37fdf39bc15 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py @@ -467,6 +467,7 @@ def create_observation_control_subtask_from_task_blueprint(task_blueprint: TaskB #"task_blueprint": task_blueprint, # ManyToMany, so use set()! "specifications_template": subtask_template, "tags": [], + "primary": True, "cluster": Cluster.objects.get(name=cluster_name) } @@ -551,6 +552,7 @@ def create_qafile_subtask_from_observation_subtask(observation_subtask: Subtask) #"task_blueprint": observation_subtask.task_blueprint, # ManyToMany, use set() "specifications_template": qafile_subtask_template, "specifications_doc": qafile_subtask_spec, + "primary": False, "cluster": observation_subtask.cluster} qafile_subtask = Subtask.objects.create(**qafile_subtask_data) qafile_subtask.task_blueprints.set(observation_subtask.task_blueprints.all()) @@ -632,6 +634,7 @@ def create_qaplots_subtask_from_qafile_subtask(qafile_subtask: Subtask) -> Subta #"task_blueprint": qafile_subtask.task_blueprint, "specifications_template": qaplots_subtask_template, "specifications_doc": qaplots_subtask_spec_doc, + "primary": False, "cluster": qafile_subtask.cluster} qaplots_subtask = Subtask.objects.create(**qaplots_subtask_data) qaplots_subtask.task_blueprints.set(qafile_subtask.task_blueprints.all()) @@ -684,6 +687,7 @@ def create_pipeline_subtask_from_task_blueprint(task_blueprint: TaskBlueprint, s #"task_blueprint": task_blueprint, # ManyToMany, so use set()! "specifications_template": subtask_template, "specifications_doc": subtask_specs, + "primary": True, "cluster": Cluster.objects.get(name=cluster_name) } subtask = Subtask.objects.create(**subtask_data) subtask.task_blueprints.set([task_blueprint]) @@ -741,6 +745,7 @@ def create_ingest_subtask_from_task_blueprint(task_blueprint: TaskBlueprint) -> #"task_blueprint": task_blueprint, # ManyToMany, so use set()! "specifications_template": subtask_template, "specifications_doc": subtask_specs, + "primary": True, "cluster": Cluster.objects.get(name=cluster_name)} subtask = Subtask.objects.create(**subtask_data) subtask.task_blueprints.set([task_blueprint]) @@ -782,6 +787,7 @@ def create_cleanup_subtask_from_task_blueprint(task_blueprint: TaskBlueprint) -> "state": SubtaskState.objects.get(value=SubtaskState.Choices.DEFINING.value), "specifications_template": subtask_template, "specifications_doc": subtask_specs, + "primary": True, "cluster": Cluster.objects.get(name=cluster_name)} subtask = Subtask.objects.create(**subtask_data) subtask.task_blueprints.set([task_blueprint])