From a8481d29a183ff5cd656b515d27073cc2eaca584 Mon Sep 17 00:00:00 2001 From: Jorrit Schaap <schaap@astron.nl> Date: Fri, 4 Dec 2020 10:29:11 +0100 Subject: [PATCH] TMSS-320: use UniqueConstraint to ensure that no duplicate blueprints are created --- .../tmss/tmssapp/migrations/0001_initial.py | 6 ++- .../src/tmss/tmssapp/models/specification.py | 6 ++- SAS/TMSS/src/tmss/tmssapp/tasks.py | 51 +++++++++++-------- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py index 65b22672362..3ef33b5ef6b 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-12-04 09:03 +# Generated by Django 3.0.9 on 2020-12-04 09:22 from django.conf import settings import django.contrib.postgres.fields @@ -1377,6 +1377,10 @@ class Migration(migrations.Migration): model_name='taskblueprint', constraint=models.UniqueConstraint(fields=('name', 'scheduling_unit_blueprint'), name='TaskBlueprint_unique_name_in_scheduling_unit'), ), + migrations.AddConstraint( + model_name='taskblueprint', + constraint=models.UniqueConstraint(fields=('draft', 'scheduling_unit_blueprint'), name='TaskBlueprint_unique_from_task_draft_in_scheduling_unit'), + ), migrations.AddConstraint( model_name='subtasktemplate', constraint=models.UniqueConstraint(fields=('name', 'version'), name='subtasktemplate_unique_name_version'), diff --git a/SAS/TMSS/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/src/tmss/tmssapp/models/specification.py index f743f4ab36e..247020bd672 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/models/specification.py @@ -900,8 +900,10 @@ class TaskBlueprint(NamedCommon): scheduling_unit_blueprint = ForeignKey('SchedulingUnitBlueprint', related_name='task_blueprints', on_delete=CASCADE, help_text='Scheduling Unit Blueprint to which this task belongs.') class Meta: - # ensure there are no tasks with duplicate names within one scheduling_unit - constraints = [UniqueConstraint(fields=['name', 'scheduling_unit_blueprint'], name='TaskBlueprint_unique_name_in_scheduling_unit')] + # ensure there are no tasks with duplicate names within one scheduling_unit, + # and ensure there are no taskblueprints created from the same taskdraft within this scheduling_unit_blueprint + constraints = [UniqueConstraint(fields=['name', 'scheduling_unit_blueprint'], name='TaskBlueprint_unique_name_in_scheduling_unit'), + UniqueConstraint(fields=['draft', 'scheduling_unit_blueprint'], name='TaskBlueprint_unique_from_task_draft_in_scheduling_unit')] 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') diff --git a/SAS/TMSS/src/tmss/tmssapp/tasks.py b/SAS/TMSS/src/tmss/tmssapp/tasks.py index 30d26fbc129..10d7b4dfcf4 100644 --- a/SAS/TMSS/src/tmss/tmssapp/tasks.py +++ b/SAS/TMSS/src/tmss/tmssapp/tasks.py @@ -236,16 +236,23 @@ def create_task_blueprint_from_task_draft(task_draft: models.TaskDraft) -> model if scheduling_unit_blueprint is None: scheduling_unit_blueprint = create_scheduling_unit_blueprint_from_scheduling_unit_draft(task_draft.scheduling_unit_draft) - task_blueprint = TaskBlueprint.objects.create( - description=task_draft.description, - name=task_draft.name, - do_cancel=False, - draft=task_draft, - scheduling_unit_blueprint=scheduling_unit_blueprint, - specifications_doc=task_draft.specifications_doc, - specifications_template=task_draft.specifications_template) - - logger.info("created task_blueprint id=%s from task_draft id=%s", task_blueprint.pk, task_draft.pk) + try: + task_blueprint = TaskBlueprint.objects.create( + description=task_draft.description, + name=task_draft.name, + do_cancel=False, + draft=task_draft, + scheduling_unit_blueprint=scheduling_unit_blueprint, + specifications_doc=task_draft.specifications_doc, + specifications_template=task_draft.specifications_template) + + logger.info("created task_blueprint id=%s from task_draft id=%s", task_blueprint.pk, task_draft.pk) + except IntegrityError as e: + if 'TaskBlueprint_unique_name_in_scheduling_unit' in str(e) or 'TaskBlueprint_unique_from_task_draft_in_scheduling_unit' in str(e): + logger.info("task_blueprint from task_draft id=%s already exists in scheduling_unit_blueprint id=%s name='%s'", + task_draft.pk, scheduling_unit_blueprint.id, scheduling_unit_blueprint.name) + else: + raise # now that we have a task_blueprint, its time to refresh the task_draft so we get the non-cached fields task_draft.refresh_from_db() @@ -260,10 +267,6 @@ def create_task_blueprint_from_task_draft(task_draft: models.TaskDraft) -> model for producing_task_blueprint in task_relation_draft.producer.task_blueprints.all(): for consuming_task_blueprint in task_relation_draft.consumer.task_blueprints.all(): try: - # do nothing if task_relation_blueprint already exists... - models.TaskRelationBlueprint.objects.get(producer_id=producing_task_blueprint.id, consumer_id=consuming_task_blueprint.id) - except models.TaskRelationBlueprint.DoesNotExist: - # ...'else' create it. task_relation_blueprint = models.TaskRelationBlueprint.objects.create(draft=task_relation_draft, input_role=task_relation_draft.input_role, output_role=task_relation_draft.output_role, @@ -273,7 +276,14 @@ def create_task_blueprint_from_task_draft(task_draft: models.TaskDraft) -> model selection_template=task_relation_draft.selection_template, dataformat=task_relation_draft.dataformat) logger.info("created task_relation_blueprint id=%s which connects task_blueprints producer_id=%s and consumer_id=%s", - task_relation_blueprint.pk, producing_task_blueprint.pk, consuming_task_blueprint.pk,) + task_relation_blueprint.pk, producing_task_blueprint.pk, consuming_task_blueprint.pk) + except IntegrityError as e: + if 'TaskRelationBlueprint_unique_relation' in str(e): + logger.info("task_relation_blueprint with producer_id=%s and consumer_id=%s already exists", + producing_task_blueprint.pk, consuming_task_blueprint.pk) + else: + raise + # Do the same 'trick' for Task Scheduling Relation Draft to Blueprint task_draft_scheduling_relations = list(task_draft.first_scheduling_relation.all()) + list(task_draft.second_scheduling_relation.all()) @@ -281,17 +291,18 @@ def create_task_blueprint_from_task_draft(task_draft: models.TaskDraft) -> model for first_task_blueprint in task_scheduling_relation_draft.first.task_blueprints.all(): for second_task_blueprint in task_scheduling_relation_draft.second.task_blueprints.all(): try: - # do nothing if task_scheduling_relation_blueprint already exists... - models.TaskSchedulingRelationBlueprint.objects.get(first_id=first_task_blueprint.id, - second_id=second_task_blueprint.id) - except models.TaskSchedulingRelationBlueprint.DoesNotExist: - # ...'else' create it. task_scheduling_relation_blueprint = models.TaskSchedulingRelationBlueprint.objects.create(first=first_task_blueprint, second=second_task_blueprint, time_offset=task_scheduling_relation_draft.time_offset, placement=task_scheduling_relation_draft.placement) logger.info("created task_scheduling_relation_blueprint id=%s which connects task_blueprints first_id=%s and second_id=%s, placement=%s time_offset=%s[sec]", task_scheduling_relation_blueprint.pk, first_task_blueprint.pk, second_task_blueprint.pk, task_scheduling_relation_draft.placement, task_scheduling_relation_draft.time_offset) + except IntegrityError as e: + if 'TaskSchedulingRelationBlueprint_unique_relation' in str(e): + logger.info("task_scheduling_relation_blueprint with producer_id=%s and consumer_id=%s already exists", + producing_task_blueprint.pk, consuming_task_blueprint.pk) + else: + raise return task_blueprint -- GitLab