def set_step_outputs(self, invocation_step, outputs, already_persisted=False): step = invocation_step.workflow_step if invocation_step.output_value: outputs[invocation_step.output_value.workflow_output.output_name] = invocation_step.output_value.value self.outputs[step.id] = outputs if not already_persisted: workflow_outputs_by_name = {wo.output_name: wo for wo in step.workflow_outputs} for output_name, output_object in outputs.items(): if hasattr(output_object, "history_content_type"): invocation_step.add_output(output_name, output_object) else: # Add this non-data, non workflow-output output to the workflow outputs. # This is required for recovering the output in the next scheduling iteration, # and should be replaced with a WorkflowInvocationStepOutputValue ASAP. if not workflow_outputs_by_name.get(output_name) and not output_object == modules.NO_REPLACEMENT: workflow_output = model.WorkflowOutput(step, output_name=output_name) step.workflow_outputs.append(workflow_output) for workflow_output in step.workflow_outputs: output_name = workflow_output.output_name if output_name not in outputs: message = f"Failed to find expected workflow output [{output_name}] in step outputs [{outputs}]" # raise KeyError(message) # Pre-18.01 we would have never even detected this output wasn't configured # and even in 18.01 we don't have a way to tell the user something bad is # happening so I guess we just log a debug message and continue sadly for now. # Once https://github.com/galaxyproject/galaxy/issues/5142 is complete we could # at least tell the user what happened, give them a warning. log.debug(message) continue output = outputs[output_name] self._record_workflow_output( step, workflow_output, output=output, )
def update_workflow_from_dict(self, trans, stored_workflow, workflow_data, from_editor=False): # Put parameters in workflow mode trans.workflow_building_mode = True # Convert incoming workflow data from json if coming from editor data = json.loads(workflow_data) if from_editor else workflow_data # Create new workflow from incoming data workflow = model.Workflow() # Just keep the last name (user can rename later) workflow.name = stored_workflow.name # Assume no errors until we find a step that has some workflow.has_errors = False # Create each step steps = [] # The editor will provide ids for each step that we don't need to save, # but do need to use to make connections steps_by_external_id = {} errors = [] for key, step_dict in data['steps'].iteritems(): is_tool = is_tool_module_type( step_dict[ 'type' ] ) if is_tool and not trans.app.toolbox.has_tool( step_dict['tool_id'], exact=True ): errors.append("Step %s requires tool '%s'." % (step_dict['id'], step_dict['tool_id'])) if errors: raise MissingToolsException(workflow, errors) # First pass to build step objects and populate basic values for step_dict in self.__walk_step_dicts( data ): module, step = self.__module_from_dict( trans, step_dict, secure=from_editor ) # Create the model class for the step steps.append( step ) steps_by_external_id[ step_dict['id' ] ] = step if 'workflow_outputs' in step_dict: for output_name in step_dict['workflow_outputs']: m = model.WorkflowOutput(workflow_step=step, output_name=output_name) trans.sa_session.add(m) if step.tool_errors: # DBTODO Check for conditional inputs here. workflow.has_errors = True # Second pass to deal with connections between steps self.__connect_workflow_steps( steps, steps_by_external_id ) # Order the steps if possible attach_ordered_steps( workflow, steps ) # Connect up workflow.stored_workflow = stored_workflow stored_workflow.latest_workflow = workflow # Persist trans.sa_session.flush() # Return something informative errors = [] if workflow.has_errors: errors.append( "Some steps in this workflow have validation errors" ) if workflow.has_cycles: errors.append( "This workflow contains cycles" ) return workflow, errors
def example_invocation(trans): invocation = model.WorkflowInvocation() workflow = yaml_to_model(TEST_WORKFLOW_YAML) workflow.id = 342 invocation.id = 44 invocation.workflow = workflow # TODO: fix this to use workflow id and eliminate hack. stored_workflow = model.StoredWorkflow() stored_workflow.id = 342 invocation.workflow.stored_workflow = stored_workflow hda = model.HistoryDatasetAssociation(create_dataset=True, sa_session=trans.sa_session) hda.id = 567 invocation.add_input(hda, step=workflow.steps[0]) out_hda = model.HistoryDatasetAssociation(create_dataset=True, sa_session=trans.sa_session) out_hda.id = 563 wf_output = model.WorkflowOutput(workflow.steps[2], label="output_label") invocation.add_output(wf_output, workflow.steps[2], out_hda) return invocation
def _dict_to_workflow_output(workflow_step, as_dict): output = model.WorkflowOutput(workflow_step) for key, value in as_dict.items(): setattr(output, key, value) return output
def tag_outputs(self, trans, id, **kwargs): stored = self.get_stored_workflow(trans, id, check_ownership=False) user = trans.get_user() if stored.user != user: if trans.sa_session.query(model.StoredWorkflowUserShareAssociation) \ .filter_by(user=user, stored_workflow=stored).count() == 0: error("Workflow is not owned by or shared with current user") # Get the latest revision workflow = stored.latest_workflow # It is possible for a workflow to have 0 steps if len(workflow.steps) == 0: error( "Workflow cannot be tagged for outputs because it does not have any steps" ) if workflow.has_cycles: error( "Workflow cannot be tagged for outputs because it contains cycles" ) if workflow.has_errors: error( "Workflow cannot be tagged for outputs because of validation errors in some steps" ) # Build the state for each step errors = {} has_upgrade_messages = False # has_errors is never used # has_errors = False if kwargs: # If kwargs were provided, the states for each step should have # been POSTed for step in workflow.steps: if step.type == 'tool': # Extract just the output flags for this step. p = "%s|otag|" % step.id l = len(p) outputs = [ k[l:] for (k, v) in kwargs.items() if k.startswith(p) ] if step.workflow_outputs: for existing_output in step.workflow_outputs: if existing_output.output_name not in outputs: trans.sa_session.delete(existing_output) else: outputs.remove(existing_output.output_name) for outputname in outputs: m = model.WorkflowOutput(workflow_step_id=int(step.id), output_name=outputname) trans.sa_session.add(m) # Prepare each step trans.sa_session.flush() module_injector = WorkflowModuleInjector(trans) for step in workflow.steps: step.upgrade_messages = {} # Contruct modules module_injector.inject(step) if step.upgrade_messages: has_upgrade_messages = True if step.type == 'tool' or step.type is None: # Error dict if step.tool_errors: errors[step.id] = step.tool_errors # Render the form return trans.fill_template("workflow/tag_outputs.mako", steps=workflow.steps, workflow=stored, has_upgrade_messages=has_upgrade_messages, errors=errors, incoming=kwargs)