Assigning Samples to New Workflows

It is sometimes necessary to assign a sample to a new workflow from within another workflow.

You can do this in the BaseSpace Clarity LIMS Operations Interface, by manually adding the sample to the desired workflow. However, it is also possible to perform this action through the API.

Solution

This example shows how to use the API to automate the addition of samples to a specified workflow, based on a UDF value.

  • The script can be run off any protocol step whose underlying process is configured with Analyte inputs and a single per-input ResultFile output.

  • The process may have any number of per-all-input ResultFile outputs, as they will be ignored by the script.

  • A Result File UDF, named Validate, will control which samples will be added to the specified workflow.

Parameters

The script accepts the following parameters:

-iThe limsid of the process invoking the script (Required)The {processLuid} token

-s

The URI of the step that launches the script (Required)

The {stepURI:v2:http} token (in the form http://<YourIP>/api/v2/steps/<ProtocolStepLimsid>)

-u

The username of the current user (Required)

The {username} token

-p

The password of the current user (Required)

The {password} token

-w

The name of the destination workflow (Required)

The {workflow} token

Step 1: Create Result File UDF

Before the example script can be used, first create the ResultFile's Validate UDF in the Operations Interface. This is a single-line text UDF with preset values of 'Yes' and 'No'.

Assigning_samples_to_workflows_ResultFileUDF.png

Step 2: Create and Configure Process Type

Also in the Operations Interface, create a new process type named Cookbook Workflow Addition.

This process type must:

  • Have Analyte inputs.

  • Have a single per-input ResultFile output.

  • Apply the Validate UDF on its ResultFile outputs.

Step 3: Configure an EPP call on this process type as follows:

bash -c "/opt/gls/groovy/current/bin/groovy -cp /opt/groovy/lib /opt/gls/clarity/customextensions/SwitchingWorkflows.groovy -u {username} -p {password} -s {stepURI:v2:http} -i {processURI:v2:http} -w 'Destination Workflow'"

Step 4: Modify the file paths to suit your server's Groovy installation.

Step 3: Create Protocol

Once the process type is created, in the Clarity LIMS Web Interface, create a protocol named Cookbook Workflow Addition Protocol.

This protocol should have one the protocol step - Cookbook Workflow Addition.

Assigning_samples_to_workflows_Protocol.png

Step 6: Configure EPP

Configure the EPP script to automatically initiate at the end of the Cookbook Workflow Addition step:

Assigning_samples_to_workflows_protEpp.png

Step 7: Create workflows

To finish configuration, create two workflows:

  • Destination Workflow: THis workflow should contain the DNA Initial QC protocol only.

  • Sending Workflow: This workflow should contain the new Cookbook Workflow Addition Protocol.

About the code

Once the script has processed the input parameters and ensured that all the required information is available, we can start processing the samples to determine if they should be assigned to the new workflow.

  1. To begin, we retrieve the process from the API. This gives us access to the input-output maps of the process. These will be used to determine which ResultFiles we will examine.

  2. Next, we retrieve the protocol step action list. This contains a list of the input analytes' URIs and their next steps.

  3. We then search this list for and collect all analyte URIs whose next action has been set to Mark as protocol complete.

    // Retrieve the process
    def process = GLSRestApiUtils.httpGET(processURI, username, password)
             
    // Retrieve the analytes which have been set to complete
    def actionsList = GLSRestApiUtils.httpGET(stepURI + '/actions', username, password)
    def completedAnalyteURIs = actionsList.'next-actions'.'next-action'.findAll { it.@action == COMPLETE }.collect { it.@'artifact-uri' }
  4. Next, we gather the per-input ResultFile input-output maps. We can collect the ResultFile URIs of those related to the analytes who have been marked as complete. NOTE: It is important that we strip any extra state information from the URIs. The URIs found in the next action list do not contain any state information and, when compared against a non-stripped URI, will return 'false'.

  5. Once we have the ResultFile URIs, we can retrieve them with batchGET. It is important that the list contains unique URIs, as the batchGET will fail otherwise.

    // Gather the PerInput ResultFile mappings
    def inputOutputMaps = process.'input-output-map'.findAll { it.'output'[0].@'output-generation-type' == PER_INPUT }
             
    // Determine which ResultFiles to examine and retrieve them
    def resultFilesURIs = inputOutputMaps.findAll { completedAnalyteURIs.contains(GLSRestApiUtils.stripQuery(it.'input'[0].@uri)) }.collect { it.'output'[0].@uri }.unique()
    def resultFiles = GLSRestApiUtils.batchGET(resultFilesURIs, username, password)
  6. After we have retrieved the ResultFiles, we can iterate through the list, adding the parent sample's URI to our list of sample URIs if the ResultFile's Validate UDF is set to Yes. We also increment a counter which will allow us to report to the user how many samples were assigned to the new workflow.

    // Determine which artifacts should be assigned
    List sampleURIs = []
    resultFiles.each {
        if(it.'udf:field'.find { VALIDATION_UDF == it.@name }.value()[0] == YES) {
            sampleURIs.add(it.'sample'[0].@uri)
            switched++
        }
    }
  7. Since we don't assign samples themselves to workflows, we first need to retrieve the samples' derived artifacts. We can do this by iterating through each sample URI, retrieving it, and adding its artifact's URI to a list.

    // Gather the sample artifacts URIs
    List artifactsToAssignURIs = []
    sampleURIs.each {
        Node sample = GLSRestApiUtils.httpGET(it, username, password)
        artifactsToAssignURIs.add(sample.'artifact'[0].@uri)
    }
  8. Before we can add the artifacts to the workflow, we need to determine the destination workflow's URI. By retrieving a list of all the workflows in the system, we can find the one that matches our input workflow name.

    // Retrieve workflow URI
    def workflowList = GLSRestApiUtils.httpGET(baseURI + '/configuration/workflows', username, password)
    String workflowURI = workflowList.find { it.@name == workflow }.@uri
    
  9. Assigning artifacts to workflows requires the posting of a routing command to the routing endpoint.

    • We first generate the required XML by using a Streaming Markup Builder.

    • We then dynamically build our XML by looping inside of the markup declaration. // Create a new routing assignment using the Markup Builder

      def assignmentOrder = builder.bind {
          mkp.xmlDeclaration()
          mkp.declareNamespace(rt: 'http://genologics.com/ri/routing')
          'rt:routing' {
              if(artifactURIsToNewWorkflow.size() != 0) {
                  'assign'('workflow-uri': workflowURI) {
                      artifactURIsToNewWorkflow.each {
                          'artifact'(uri: it)
                      }
                  }
              }
          }
      }
       
      return GLSRestApiUtils.xmlStringToNode(assignmentOrder.toString())
  10. To create our routing command, we pass the workflow URI and the artifact URIs that we wish to assign to the workflow to a method containing the above code. This will generate the required node.

  11. We then perform an httpPOST to the routing endpoint to perform the action.

    // Create and post the assignment
    Node assignmentNode = createAssignmentNode(workflowURI, artifactsToAssignURIs)
    GLSRestApiUtils.httpPOST(assignmentNode, baseURI + '/route/artifacts/', username, password)  
  12. Finally, we define our success message to the user. This will allow us to inform the user of the results of the script.

    // Define the success message to the user
    outputMessage = "Script has completed successfully.${LINE_TERMINATOR}" +
        "${switched} samples were assigned to the '${workflow}' workflow.${LINE_TERMINATOR}" +
        "You can find them queued in the 'DNA Initial QC' protocol."

User Interaction

  1. Assuming samples have been placed in the Switching Workflow, the user proceeds as normal through the protocol step.

  2. In the Record Details screen, the user enters Validate values in the ResultFile UDFs.

  3. The user then proceeds to the Assign Next Steps screen, provides a variety of Next Steps, and completes the protocol step.

  4. A message displays, alerting the user of the execution of a custom script.

  5. When the script completes, a success message displays and the samples are added to the specified workflow.

Assumptions and Notes

  • The attached file is placed on the Clarity LIMS server, in the /opt/gls/clarity/customextensions folder.

  • GLSRestApiUtils.groovy is placed in the Groovy lib folder.

  • The required configuration has been set up, as described in Configuration.

  • The example code is provided for illustrative purposes only. It does not contain sufficient exception handling for use 'as is' in a production environment.

Attachments

SwitchingWorkflows.groovy:

Last updated