diff --git a/SAS/TMSS/src/tmss/exceptions.py b/SAS/TMSS/src/tmss/exceptions.py index ccdf97362bd9d4e8f9c899f162458948c502ee22..a320dbd527a5a58a0d7274836beb66f9f5387c1c 100644 --- a/SAS/TMSS/src/tmss/exceptions.py +++ b/SAS/TMSS/src/tmss/exceptions.py @@ -8,6 +8,9 @@ class SchemaValidationException(TMSSException): class ConversionException(TMSSException): pass +class BlueprintCreationException(ConversionException): + pass + class SubtaskCreationException(ConversionException): pass diff --git a/SAS/TMSS/src/tmss/tmssapp/tasks.py b/SAS/TMSS/src/tmss/tmssapp/tasks.py index bacb4a73980ed9ef04b051619f697d4deba61478..b5088a39e1121425ca7e3f31ceb0c3946ee6774a 100644 --- a/SAS/TMSS/src/tmss/tmssapp/tasks.py +++ b/SAS/TMSS/src/tmss/tmssapp/tasks.py @@ -1,3 +1,4 @@ +from lofar.sas.tmss.tmss.exceptions import * from lofar.sas.tmss.tmss.tmssapp import models from lofar.sas.tmss.tmss.tmssapp.models.specification import TaskBlueprint, SchedulingUnitBlueprint, TaskDraft, SchedulingRelationPlacement from lofar.sas.tmss.tmss.tmssapp.subtasks import create_and_schedule_subtasks_from_task_blueprint @@ -15,7 +16,7 @@ def create_scheduling_unit_blueprint_from_scheduling_unit_draft(scheduling_unit_ Create a SchedulingUnitBlueprint from the SchedulingUnitDraft :raises Exception if instantiate fails. """ - logger.debug("create_scheduling_unit_blueprint_from_scheduling_unit_draft(scheduling_unit_draft.id=%s)", scheduling_unit_draft.pk) + logger.debug("create_scheduling_unit_blueprint_from_scheduling_unit_draft(scheduling_unit_draft.id=%s name=%s)", scheduling_unit_draft.pk, scheduling_unit_draft.name) # TODO: copy/fill-in the properties from the draft to the blueprint scheduling_unit_blueprint = SchedulingUnitBlueprint.objects.create( @@ -26,76 +27,102 @@ def create_scheduling_unit_blueprint_from_scheduling_unit_draft(scheduling_unit_ draft=scheduling_unit_draft, requirements_template=scheduling_unit_draft.requirements_template) - logger.info("create_scheduling_unit_blueprint_from_scheduling_unit_draft(scheduling_unit_draft.id=%s) created scheduling_unit_blueprint id=%s", scheduling_unit_draft.pk, scheduling_unit_blueprint.pk) + logger.info("create_scheduling_unit_blueprint_from_scheduling_unit_draft(scheduling_unit_draft.id=%s name=%s) created scheduling_unit_blueprint id=%s name=%s", + scheduling_unit_draft.pk, scheduling_unit_draft.name, scheduling_unit_blueprint.pk, scheduling_unit_blueprint.name) return scheduling_unit_blueprint -def create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft: models.SchedulingUnitDraft) -> [TaskDraft]: +def create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft: models.SchedulingUnitDraft) -> models.SchedulingUnitDraft: """ - Generic create-method for tasks draft. Calls the appropriate create method based on the scheduling_unit_blueprint - specifications_template name. + Create all defined task_drafts in the scheduling_unit_draft's requirements_doc, connect them, and return the updated scheduling_unit_draft. """ - list_created_task_object = [] - - try: - list_tasks = scheduling_unit_draft.requirements_doc["tasks"] - logger.info("create_task_drafts_from_scheduling_unit_draft with scheduling_unit_draft.id=%s, nbr_tasks=%d" % - (scheduling_unit_draft.pk, len(list_tasks))) - except KeyError: - logger.info("create_task_drafts_from_scheduling_unit_draft -> NO tasks to process from requirements_doc") - list_tasks = [] - - for task in list_tasks: - task_template_name = task["specifications_template"] - logger.info("task name is '%s', task_template_name '%s'" % (task["name"], task_template_name)) + logger.info("create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft.id=%s, name='%s') ...", scheduling_unit_draft.pk, scheduling_unit_draft.name) + + if len(scheduling_unit_draft.requirements_doc["tasks"]) == 0: + raise BlueprintCreationException("create_task_drafts_from_scheduling_unit_draft: scheduling_unit_draft.id=%s has no tasks defined in its requirements_doc" % (scheduling_unit_draft.pk,)) + + for task_definition in scheduling_unit_draft.requirements_doc["tasks"]: + task_template_name = task_definition["specifications_template"] task_template = models.TaskTemplate.objects.get(name=task_template_name) - task_draft = models.TaskDraft.objects.create( - name=task["name"], - description=task.get("description",""), - tags=task.get("tags",[]), - specifications_doc=task["specifications_doc"], - copy_reason=models.CopyReason.objects.get(value='template'), - copies=None, - scheduling_unit_draft=scheduling_unit_draft, - specifications_template=task_template) - logger.info("task draft with id %s created successfully" % task_draft.id) - list_created_task_object.append(task_draft) - - # Now create task relation - try: - list_task_relations = scheduling_unit_draft.requirements_doc["task_relations"] - logger.info("create_task_drafts_from_scheduling_unit_draft, nbr of task relations=%d" % len(list_task_relations)) - except KeyError: - logger.info("create_task_drafts_from_scheduling_unit_draft -> NO task relations to process from requirements_doc") - list_task_relations = [] - for task_relation in list_task_relations: - task_rel_obj = models.TaskRelationDraft.objects.create( - tags=task_relation.get("tags",[]), - selection_doc=task_relation["selection_doc"], - dataformat=models.Dataformat.objects.get(value=task_relation["dataformat"]), - producer=models.TaskDraft.objects.get(name=task_relation["producer"], scheduling_unit_draft_id=scheduling_unit_draft.id), - consumer=models.TaskDraft.objects.get(name=task_relation["consumer"], scheduling_unit_draft_id=scheduling_unit_draft.id), - input_role=models.TaskConnectorType.objects.get(role=task_relation["input"]["role"], datatype=task_relation["input"]["datatype"]), - output_role=models.TaskConnectorType.objects.get(role=task_relation["output"]["role"], datatype=task_relation["output"]["datatype"]), - selection_template=models.TaskRelationSelectionTemplate.objects.get(name=task_relation["selection_template"])) - logger.info("task relation draft object with id %s created successfully" % task_rel_obj.id) + + if scheduling_unit_draft.task_drafts.filter(name=task_definition["name"], specifications_template=task_template).count() > 0: + logger.warning("create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft.id=%s) skipping creation of task draft because it is already in the scheduling_unit... task_name='%s', task_template_name='%s'", + scheduling_unit_draft.pk, task_definition["name"], task_template_name) + continue + + logger.debug("create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft.id=%s) creating task draft... task_name='%s', task_template_name='%s'", + scheduling_unit_draft.pk, task_definition["name"], task_template_name) + + task_draft = models.TaskDraft.objects.create(name=task_definition["name"], + description=task_definition.get("description",""), + tags=task_definition.get("tags",[]), + specifications_doc=task_definition["specifications_doc"], + copy_reason=models.CopyReason.objects.get(value='template'), + copies=None, + scheduling_unit_draft=scheduling_unit_draft, + specifications_template=task_template) + + logger.info("create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft.id=%s) created task draft id=%s task_name='%s', task_template_name='%s'", + scheduling_unit_draft.pk, task_draft.pk, task_definition["name"], task_template_name) + + # Now create task relations + for task_relation_definition in scheduling_unit_draft.requirements_doc["task_relations"]: + 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(role=task_relation_definition["input"]["role"], datatype=task_relation_definition["input"]["datatype"]) + output_role = models.TaskConnectorType.objects.get(role=task_relation_definition["output"]["role"], datatype=task_relation_definition["output"]["datatype"]) + selection_template = models.TaskRelationSelectionTemplate.objects.get(name=task_relation_definition["selection_template"]) + + if models.TaskRelationDraft.objects.filter(producer=producer_task_draft, + consumer=consumer_task_draft, + dataformat=dataformat, + input_role=input_role, + output_role=output_role, + selection_template=selection_template, + selection_doc=task_relation_definition["selection_doc"]).count() > 0: + logger.warning("create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft.id=%s) skipping creation of task_relation between task draft '%s' and '%s' because it is already in the scheduling_unit...", + scheduling_unit_draft.pk, task_relation_definition["producer"], task_relation_definition["consumer"]) + continue + + task_relation = models.TaskRelationDraft.objects.create(tags=task_relation_definition.get("tags",[]), + selection_doc=task_relation_definition["selection_doc"], + dataformat=dataformat, + producer=producer_task_draft, + consumer=consumer_task_draft, + input_role=input_role, + output_role=output_role, + selection_template=selection_template) + logger.info("create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft.id=%s) created task_relation id=%s between task draft '%s' and '%s", + scheduling_unit_draft.pk, task_relation.pk, task_relation_definition["producer"], task_relation_definition["consumer"]) + # task_scheduling_relation - try: - list_task_scheduling_relations = scheduling_unit_draft.requirements_doc["task_scheduling_relations"] - logger.info("create_task_drafts_from_scheduling_unit_draft, nbr of task scheduling relations=%d" % len(list_task_scheduling_relations)) - except KeyError: - logger.info("create_task_drafts_from_scheduling_unit_draft -> NO tasks scheduling relations to process from requirements_doc") - list_task_scheduling_relations = [] - for task_scheduling_relation in list_task_scheduling_relations: - task_rel_sch_obj = models.TaskSchedulingRelationDraft.objects.create( - placement=models.SchedulingRelationPlacement.objects.get(value=task_scheduling_relation["placement"]), - time_offset=task_scheduling_relation["time_offset"], - first=models.TaskDraft.objects.get(name=task_scheduling_relation["first"], scheduling_unit_draft_id=scheduling_unit_draft.id), - second=models.TaskDraft.objects.get(name=task_scheduling_relation["second"], scheduling_unit_draft_id=scheduling_unit_draft.id)) - logger.info("task scheduling relation draft object with id %s created successfully" % task_rel_sch_obj.id) - - return list_created_task_object + for task_scheduling_relation_definition in scheduling_unit_draft.requirements_doc["task_scheduling_relations"]: + placement = models.SchedulingRelationPlacement.objects.get(value=task_scheduling_relation_definition["placement"]) + time_offset = task_scheduling_relation_definition["time_offset"] + first_task_draft = scheduling_unit_draft.task_drafts.get(name=task_scheduling_relation_definition["first"]) + second_task_draft = scheduling_unit_draft.task_drafts.get(name=task_scheduling_relation_definition["second"]) + + if models.TaskSchedulingRelationDraft.objects.filter(placement=placement, + time_offset=time_offset, + first=first_task_draft, + second=second_task_draft).count() > 0: + logger.warning("create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft.id=%s) skipping creation of task_scheduling_relation between task draft '%s' and '%s' because it is already in the scheduling_unit...", + scheduling_unit_draft.pk, task_scheduling_relation_definition["first"], task_scheduling_relation_definition["second"]) + continue + + task_scheduling_relation = models.TaskSchedulingRelationDraft.objects.create(placement=placement, + time_offset=time_offset, + first=first_task_draft, + second=second_task_draft) + logger.info("create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft.id=%s) created task_scheduling_relation id=%s between task draft '%s' and '%s'", + scheduling_unit_draft.pk, task_scheduling_relation.pk, task_scheduling_relation_definition["first"], task_scheduling_relation_definition["second"]) + + logger.info("create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft.id=%s, name='%s') ... done", scheduling_unit_draft.pk, scheduling_unit_draft.name) + + scheduling_unit_draft.refresh_from_db() + return scheduling_unit_draft def create_task_blueprint_from_task_draft(task_draft: models.TaskDraft) -> models.TaskBlueprint: @@ -178,9 +205,10 @@ def create_task_blueprint_from_task_draft(task_draft: models.TaskDraft) -> model def create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft: models.SchedulingUnitDraft) -> models.SchedulingUnitBlueprint: '''Convenience method: Create the scheduling_unit_blueprint, then create its child task_blueprint(s), then create the task_blueprint's subtasks''' - if scheduling_unit_draft.task_drafts.count() == 0: - # create task_drafts first, so the task_blueprints can be created from the scheduling_unit_blueprint in the next step - create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft) + + # make sure we call create task_drafts first, so the task_blueprints can be created from the scheduling_unit_blueprint in the next step + # already known task_drafts and/or relations are skipped automagically + create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft) scheduling_unit_blueprint = create_scheduling_unit_blueprint_from_scheduling_unit_draft(scheduling_unit_draft) return create_task_blueprints_and_subtasks_from_scheduling_unit_blueprint(scheduling_unit_blueprint) @@ -210,11 +238,16 @@ def create_task_blueprint_and_subtasks_and_schedule_subtasks_from_task_draft(tas def create_task_blueprints_from_scheduling_unit_blueprint(scheduling_unit_blueprint: models.SchedulingUnitBlueprint) -> models.SchedulingUnitBlueprint: '''Convenience method: Create the scheduling_unit_blueprint's task_blueprint(s)''' + + # make sure we call create task_drafts first, so the task_blueprints can be created from the scheduling_unit_blueprint in the next step + # already known task_drafts and/or relations are skipped automagically + create_task_drafts_from_scheduling_unit_draft(scheduling_unit_blueprint.draft) + task_drafts = list(scheduling_unit_blueprint.draft.task_drafts.all()) # sort them in 'data-flow'-order, - # because successors can depend on predecessors, so the first tbp's need to be subtask'd first. - task_drafts.sort(key=cmp_to_key(lambda tbp_a, tbp_b: -1 if tbp_a in tbp_b.predecessors else 1 if tbp_b in tbp_a.predecessors else 0)) + # because successors can depend on predecessors, so the first taskdraft's need to be blueprinted first. + task_drafts.sort(key=cmp_to_key(lambda taskdraft_a, taskdraft_b: -1 if taskdraft_a in taskdraft_b.predecessors else 1 if taskdraft_b in taskdraft_a.predecessors else 0)) # convert task_draft(s) to task_blueprint(s) for task_draft in task_drafts: diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py index 68b9df3a508493dd6c49069bb289c92288abeaef..93ab6971734d1c740e52c75b4c6e75407a5b7dd0 100644 --- a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py @@ -266,11 +266,11 @@ class SchedulingUnitDraftViewSet(LOFARViewSet): headers={'Location': scheduling_unit_blueprint_path}) - @swagger_auto_schema(responses={201: 'The Created Task Draft, see Location in Response header', + @swagger_auto_schema(responses={201: 'The updated scheduling_unit_draft with references to its created task_drafts', 403: 'forbidden'}, operation_description="Create Task Drafts from SchedulingUnitDraft.") @action(methods=['get'], detail=True, url_name="create_task_drafts", name="Create Task Drafts from Requirement doc") - def create_tasks_draft(self, request, pk=None): + def create_task_drafts(self, request, pk=None): scheduling_unit_draft = get_object_or_404(models.SchedulingUnitDraft, pk=pk) create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft)