6. Using Expressions in L7|ESP

6.1. L7|ESP Expression Language

As a platform, L7|ESP provides building blocks for creating complete laboratory Workflows. The Workflow, Protocol, Pipeline, and Task builders sufficiently capture a wide range of lab procedures. However, there are cases where another level of "programming" may be desired, either to more tightly couple disparate aspects of a Workflow, or to provide dynamic content, driven by choices the user makes while running a Workflow or Pipeline.

The L7|ESP Expression Language is an embedded programming language that offers deep access to L7|ESP. The Expression language has a few main uses within L7|ESP:

  • Expressions can be used to perform simple computations in Worksheets.

  • Expressions can be used to generate default values and selection lists for Worksheets.

  • Expressions can be used to pull values from one Protocol's Worksheet and populate cells in another Worksheet.

  • Expressions allow parameterization of Tasks in the Pipeline Engine.

  • Expressions define patterns for filenames for automatically registering files.

  • Expressions are used in workflow routing (workflow chain transitions) for calculating whether a sample should "move" to the next workflow(s) in the chain.

Using the Expression Language is simple: when the language is supported, just include an Expression delimited by double curly-brackets:

{{ 1 + 1 }}

This expression will put the result of 1 + 1 in place of the expression.

The Expression Language is based on the Python programming language, though it is limited to single-line statements. Most valid Python "one-liners" can be used as L7|ESP Expressions. The only exceptions are expressions with syntax errors or expressions that fail the Expression Language's security check.

Note

The Expression Language uses a combined white-list/black-list strategy to manage expression security. In general, any built-in command that would allow access to the underlying interpreter is forbidden, including import statements. To allow common function calls, L7|ESP includes a white-list of API functions that are accessible from the Expression language. White-listed functions include common Python functions and custom functions for interacting with L7|ESP.

Under the hood, L7|ESP parses each Expression, and inspects its syntax tree to ensure that no restricted operations are attempted.

Expressions can return scalar values or lists:

# Will return a scalar value: 2
{{ 1 + 1 }}

# Will return the list: [0, 1, 2, 3, ..., 9]
{{ list(range(10)) }}

Lists are especially useful for generating drop-down lists for columns in Protocol Worksheets. For example, the following Expression will generate a list of well labels for a 96-well plate:

{{ ['%s%d' % (row, col) for row in 'ABCDEFGH' for col in range(1, 13)] }}

API calls and global variables within Expressions provide additional access to L7|ESP. Some API calls and variables are always available to Expressions, while others are context-specific. For example, the param() API call provides access to Parameter Groups, and their values and can be used in any L7|ESP Expression:

{{ param('Group Name', 'Key') }}

The param API call significantly extends the ability to create dynamic, portable content for L7|ESP.

Within specific contexts, variables provide more access to related content. For instance, the following expression prints the active protocol name in the LIMS context:

{{ protocol_name }}

For details on using Expressions in specific contexts, see the following sections:

  • Expressions can be used to define patterns for registering files in Tasks.

  • Expression dependencies use Expressions and Expression Labels to pass values between Tasks.

  • Expressions are used to set up data links in Workflows.

  • Expressions can be used as Default Values and Grouping Values when creating Protocols.

6.2. Expression API

To provide access to information and also support complex operations (such as "one liners"), L7|ESP supports an expanding collection of API calls that are available from the Expression Language.

Note

In addition to these API calls, the L7 team can assist users in developing their own Expression Language API calls. Contact an L7 representative for details.

6.2.1. BioBuilds API

BioBuilds is the L7-supported packaging of over 100 bioinformatics tools in a safe, mutually compatible manner, with all applications linked against the same external dependencies (BLAS, ATLAS, libc, etc.). If a user has BioBuilds in their system with the appropriate BioBuilds Param Group setup, they can use the BioBuilds API function to reference the bioinformatics tools.

The BioBuilds Param Group is a key-value store with a minimum of two entries, as JSON:

{
    "current": "biobuilds-1.0",
    "biobuilds-1.0": "/data/ESP/biobuilds-1.0"
}

Where the value given by "current" references another key in the Param Group and the value for all other keys is the installation path to the version of BioBuilds indicated by the key. Note that the key naming other than "current" is arbitrary but typically follows biobuilds-<version>.

bb(tool: str, version: str = None) str

Return the path to a BioBuilds tool

Parameters:
  • tool -- The BioBuilds tool

  • version -- The BioBuilds version. If unspecified, the "current" BioBuilds version will be used.

Returns:

String path to the application

Examples:

{{ bb('bwa') }} -> '/data/ESP/biobuilds-1.1/bin/bwa'
{{ bb('bwa', 'biobuilds-1.0') }} -> '/data/ESP/biobuilds-1.0/bin/bwa'

6.2.2. Config API

The Config API provides access to L7 configuration items.

config(configuration_name: str, path: Optional[str] = None) Any

Get a Configuration, as stored in the Config app.

Parameters:
  • configuration_name -- the name of the Configuration to return

  • path -- an optional configuration path to return. If None, the full object is returned.

Returns:

The configuration object (JSON) or value from path or None if the configuration or path are invalid.

Examples:

{{ config('Spectramax Assays') }} -> {..} # Full Spectramax Assays configuration returned.
{{ config('Spectramax Assays', 'Picogreen.standards.Standard Set 1')[0]['conc'] }} -> 100.000
{{ config('Spectramax Assays', 'Picogreen.standards.Standard Set 1.0.conc) }} -> 100.000
{{ config('No Such Config') }} -> None
{{ config('Bad Config', 'Bad Key') }} -> None
{{ config('Good Config', 'Bad Key') }} -> None
ds(datasource_name: str) Any

Return a user-supplied data source

Parameters:

datasource_name -- The name of the data source to access.

Returns:

The loaded data source given by datasource_name, usually a Python list or dictionary.

Examples

Given an illumina_adapters entry in the lab7.conf data sources configuration point referencing a file with contents:

next: ['AGTTC', 'CTAGT']
tru: ['GTGAG', 'TGACA']

The expression:

{{ds('illumina_adapters')['next'][0]}}

returns: 'AGTTC'

6.2.3. File API

The File API provides basic file name and path manipulation utilities.

file_path(uuid: str, ignore_deleted: bool = True) str

Get the full path to a File from a UUID.

Parameters:
  • uuid -- UUID for an ESP File

  • ignore_deleted -- If True, archived Files will be considered missing.

Returns:

str - the full path to the File

Examples:

{{ file_path('577d0a13-2e20-45ac-89b9-c88f0c136ca5') }} -> /path/to/file.txt
basename(path: str) str

Wrapper for os.path.basename

Parameters:

path -- A path to a file

Returns:

The final component of path

Examples:

{{ basename('/data/experiments/e1/reads.fastq') }} -> 'reads.fastq'
dirname(path: str) str

Wrapper for os.path.dirname

Parameters:

path -- A path to a file

Returns:

The directory component of path

Examples:

{{ dirname('/data/experiments/e1/reads.fastq') }} -> '/data/experiments/e1'
file_ext(file_path: str) str

Get the extension for a file path.

Parameters:

file_path -- The path to a file

Returns:

The extension of the file, including the .

Examples:

{{ file_ext('reads.fastq') }} -> '.fastq'
remove_ext(file_path: str, ext: str = None) str

Remove the extension from a filename.

Parameters:
  • file_path -- The path to a file

  • ext -- an optional extension to remove (leading . optional)

Returns:

file_path with the extension removed

Examples:

{{ remove_ext('reads.fastq') }} -> 'reads'
{{ remove_ext('reads.fastq', 'fastq') }} -> 'reads'
{{ remove_ext('reads.fastq', '.fastq)' }} -> 'reads'
{{ remove_ext('reads.fastq.gz') }} -> 'reads.fastq'
{{ remove_ext('reads.fastq.gz', 'fastq') }} -> 'reads.fastq.gz'

Note

If ext is None (default), then any existing extension will be removed; if not, extension will be removed only if it matches ext.

replace_ext(file_path: str, new_ext: str, old_ext: str = None) str

Replace extension of file specified by file_path with new_ext.

Parameters:
  • file_path -- The path to modify

  • new_ext -- The new extension to use (leading . optional)

  • old_ext -- Optional - the old extension to replace (leading . optional)

Returns:

file_path with the extension replaced new_ext

Examples:

{{ replace_ext('reads.fastq', 'sam') }} -> 'reads.sam'
{{ replace_ext('reads.fastq', '.sam') }} -> 'reads.sam'
{{ replace_ext('reads.fastq', 'sam', 'fastq') }} -> 'reads.sam'
{{ replace_ext('reads.fastq.gz', 'sam') }} -> 'reads.fastq.sam'
{{ replace_ext('reads.fastq.gz', 'sam', 'fastq') }} -> 'reads.fastq.gz'
{{ replace_ext('reads.fastq.gz', 'sam', 'fastq.gz') }} -> 'reads.sam'

Note

If old_ext is None (default), then any existing extension (defined as the substring after the last . in file_path) will be replaced; if not, replacement will only occur if the current extension matches old_ext.

6.2.4. Illumina API

The Illumina API simplifies working with Illumina instruments and Workflows. Currently, nearly all of the values returned by these functions are driven by configuration files, and are therefore very flexible and dynamic. Use of these functions requires the appropriate configuration to be set up first.

Illumina Kit Adapters

Illumina Kit Adapters are stored in a JSON or YAML file with one top-level entry per kit. API calls defined below provide access via the expression language to kit names, adapter names, and sequence lists.

The file is defined in the configuration and must reference a valid Data Source, e.g.:

illumina:
  adapter_datasource: illumina_adapters

datasources:
  illumina_adapters:
   url: file://$LAB7DATA/reference/illumina/indexes/illumina-adapter-sequences.json
   type: application/json

Each kit entry contains entries for each class of adapters supported by the kit. In general, the format for each kit is:

{
    "Illumina Kit Name": {
        "Individual Adapter": "adapter_sequence",
        "Adapter Class 1": [
            ["adapter_name", "adapter_sequence"]
        ],
        "Adapter Class 2": {
            "Adapter Subclass 2.1": [
                ["adapter_name", "adapter_sequence"]
            ],
            "Adapter Subclass 2.2": [
                ["adapter_name", "adapter_sequence"]
            ]
        }
    },
    "Unsupported Kit": "unsupported"
}

Most amplicon and DNA kits have the same basic structure:

"TruSight Amplicon Panels": {
    "Index 1 (i7) Adapters": [
        ["A701", "ATCACGAC"]
    ],
    "Index 2 (i5) Adapter": {
        "MiSeq, HiSeq 2000/2500": [
            ["A501", "TGAACCTT"]
        ],
        "MiniSeq, NextSeq, HiSeq 3000/4000": [
            ["A501", " AAGGTTCA"]
        ]
    }
}

Names are taken directly from the Illumina Adapter Sequences reference and other Vendor reference material to maintain consistency and simplify debugging. Hence, the plural for i7 and singular for i5 and the use of instrument names for selecting i5 forward/reverse complement variants.

RNA kits are more complicated and have many additional adapters. When present, the tend to use a mix of "Adapter Class 1" and "Individual Sequence" entries.

If a kit is present in the Illumina Adapter Sequences reference document but is not encoded fully in the JSON file, it has a single attribute, "unsupported".

Instruments

The "instruments" data source is a YAML or JSON file that consists of a key-value dictionary of known lab instruments, Illumina and otherwise. The key is the name of the instrument as known by the lab. The value is dictionary of configuration values. The exact values depend on the instrument class. Implementations are free to add any additional keys that would be of benefit to their implementation. ESP built-in Illumina support requires at least:

  • kit_version - Sequencing kit version (for novaseq).

  • vendor - should be illumina

  • type - should be one of novaseq, miseq, iseq, hiseq, or nextseq (case-insensitive)

  • serial - instrument serial number

  • runsheet_path - Path for generated run sheets. May contain variables such as:

    • {worksheet_name} - LIMS worksheet name

    • {today} - today's date as a string

    • {start_timestamp} - timestamp of instrument start

    • {assay_name} - Usually the kit name(s) used for library prep

    • {assay_number} - The resolved run number

    • {assay_serial} - The resolved run name

  • output_path - Path where instrument output files are (eventually) written.

Optional keys that some built-in functionality recognizes:

  • read_1_cycles - default parameter for read 1 cycles

  • read_2_cycles - default parameter for read 2 cycles

  • index_cycles - default parameter for read 2 cycles

Examples:

My NovaSeq:
  kit_version: '1.5'
  vendor: illumina
  type: novaseq
  serial: 5678
  read_1_cycles: 150
  read_2_cycles: 150
  index_cycles: 8
  path: /path/to/instrument_folder/{{runname}}/SampleSheet.csv
  output_path: /path/to/instrument_folder

See also the espclient.instrument_support.illumina2 module of the ESP Python client.

Functions

configured_ilmn_instruments(types: Optional[Union[str, list[str]]] = None) list[str]

Get a list of configured Illumina instruments

Parameters:

types -- Optional argument declaring type(s) of instrument to return, e.g. 'NextSeq'

Returns:

A list of configured Illumina instrument names.

Examples:

{{ configured_ilmn_instruments() }} --> ['MS000001', 'NextSeq 10231']
{{ configured_ilmn_instruments('MiSeq') }} --> ['MS000001']
{{ configured_ilmn_instruments('NextSeq') }} --> ['NextSeq 10231']

Note

The list of instruments searched is defined in the "instruments" data source.

ilmn_adapter_names(kit, inst=None, position='i7') list[str]

Fetch adapter names.

General accessor for adapter names regardless of kit type. See also: ilmn_i5_names, ilmn_i5_names, and ilmn_primer_names, and ilmn_adapters.

Parameters:
  • kit -- Name of the kit

  • inst -- Instrument type. Used for determining i5 index orientation.

  • position -- Optional adapter position (i.e. i5 or i7)

Returns:

List of adapter names

Examples:

# equivalent to ilmn_primer_names('TruSeq Diverse 27F 16s')
{{ ilmn_adapter_names('TruSeq Diverse 27F 16s') }} --> ['1', ...]

# equivalent to ilmn_i7_names('TruSeq DNA-RNA')
{{ ilmn_adapter_names('TruSeq DNA-RNA') }} --> ['A001', ...]

# equivalent to ilmn_i5_names('TruSeq DNA-RNA')
{{ ilmn_adapter_names('TruSeq DNA-RNA', position='i5') }} --> ['D501', ...]

# []
{{ ilmn_adapter_names('TruSeq Diverse 27F 16s', position="i5") }} --> []
ilmn_adapter_seq(kit: str, names: Union[str, list[str]], inst: str = None, position: str = 'i7') Optional[str]

Like ilmn_i[57]_seq but accesses all adapter types.

Parameters:
  • kit -- Name of a configured Illumina kit

  • names -- Name or list of names of an adapter within kit

  • inst -- Name of instrument type

  • position -- Optional adapter position (i.e. i5 or i7)

Returns:

The index sequence, or None if the adapter cannot be found

Examples:

# equivalent to ilmn_i7_seq('TruSeq DNA-RNA', 'A001')
{{ ilmn_adapter_seq('TruSeq DNA-RNA', 'A001') }} --> 'ATCACG'

# equivalent to ilmn_primer_seq('TruSeq Diverse 27F 16s', '1)
{{ ilmn_adapter_seq('TruSeq Diverse 27F 16s', '1') }} --> 'TTGAC'

# Equivalent to ilmn_i5_seq('TruSeq DNA-RNA', 'D501')
{{ ilmn_adapter_seq('TruSeq DNA-RNA', 'D501', position='i5') }} --> 'TATAGCCT'
ilmn_adapter_seqs(kit: str, inst: str = None, position: str = 'i7') list[str]

Fetch adapter sequences.

General accessor for adapter sequences regardless of kit type. See also: ilmn_i5_seqs, ilmn_i5_seqs, and ilmn_primer_seqs, and ilmn_adapters.

Parameters:
  • kit -- Name of the kit

  • inst -- Instrument type. Used for determining i5 index orientation.

  • position -- Optional adapter position (i.e. i5 or i7)

Returns:

List of adapter sequences

Examples:

# equivalent to ilmn_primer_seqs('TruSeq Diverse 27F 16s')
{{ ilmn_adapter_names('TruSeq Diverse 27F 16s') }} --> ['TTTGAC', ...]

# equivalent to ilmn_i7_seqs('TruSeq DNA-RNA')
{{ ilmn_adapter_seqs('TruSeq DNA-RNA') }} --> ['ATCACG', ...]

# equivalent to ilmn_i5_seqs('TruSeq DNA-RNA')
{{ ilmn_adapter_seqs('TruSeq DNA-RNA', position='i5') }} --> ['TATAGCCT', ...]

# []
{{ ilmn_adapter_seqs('TruSeq Diverse 27F 16s', position="i5") }} --> []
ilmn_adapters(kit: str, inst: str = None, position: str = 'i7', context: str = None) list[tuple[str, str]]

Return adapters based on the kit type.

Primer kits can accept "i7" and this will return the primers for "i7" and empty lists for "i5". This allows primer lists to be used in protocols that support dual indexes.

Parameters:
  • kit -- Name of the kit

  • inst -- Instrument type. Used for determining i5 index orientation.

  • position -- Optional adapter position (i.e. i5 or i7)

  • context -- Used for when the adapter type is "dual indexes" to determine whether to return sequences or names.

Returns:

List of adapters, as for ilmn_i7_adapters

Examples:

# equivalent to ilmn_primers('TruSeq Diverse 27F 16s')
{{ ilmn_adapters('TruSeq Diverse 27F 16s') }} --> [['1', 'TTGAC'], ...]

# equivalent to ilmn_i7_adapters('TruSeq DNA-RNA')
{{ ilmn_adapters('TruSeq DNA-RNA') }} --> [['A001', 'ATCACG'], ...]

# equivalent to ilmn_i5_adapters('TruSeq DNA-RNA')
{{ ilmn_adapters('TruSeq DNA-RNA', position='i5') }} --> [['D501', 'TATAGCCT'], ...]

# returns [] since there are no i5 adapters for TruSeq Diverse 27F 16s.
{{ ilmn_adapters('TruSeq Diverse 27F 16s', position="i5") }} --> []
ilmn_i5_adapters(kit: str, inst: str = None) list[tuple[str, str]]

Fetch a list of i5 adapters for a given kit

Parameters:
  • kit -- The name of the kit to get the adapters for

  • inst -- The Illumina instrument class (defaults to MiSeq-orientation I5) The value of "inst" can be one of these keys, or any of "MiSeq", "HiSeq 2000", "HiSeq 2500", "MiniSeq", "NextSeq", or "HiSeq 4000". The value dictates the orientation of the returned sequences, based on the standard dual-indexed sequencing workflow for the instrument.

Returns: The i5 adapters for a given kit as a list of

two-element lists. Each two-element list contains the adapter name and the adapter index sequence in that order. Note that the data source entry corresponding to kit must be of type "index adapters" to be included in this list. Also, if inst is none, NextSeq-style i5 index orientation is assumed.

Raises:

ValueError -- If the kit type does not support i5 adapters

Examples:

{{ ilmn_i5_adapters('TruSeq DNA-RNA') }} --> [['D501', 'TATAGCCT'], ...]
ilmn_i5_names(kit: str, inst: str = None) list[str]

Fetch i5 adapter names for a given kit

Parameters:
  • kit -- Name of the Illumina kit

  • inst -- The Illumina instrument class, or None. (defaults to NextSeq-orientation I5) The value of "inst" can be one of these keys, or any of "MiSeq", "HiSeq 2000", "HiSeq 2500", "MiniSeq", "NextSeq", or "HiSeq 4000". The value dictates the orientation of the returned sequences, based on the standard dual-indexed sequencing workflow for the instrument.

Returns:

A list of i5 adapter names for a kit.

Examples:

{{ ilmn_i5_names('TruSeq DNA-RNA') }} --> ['D501', 'D502', ...]
ilmn_i5_seq(kit: str, names: Union[str, list[str]], inst: str = None) str

Fetch the i5 sequence for a given kit and adapter, accounting for instrument.

Parameters:
  • kit -- Name of the Illumina kit

  • names -- Name of the i5 adapter within the kit

  • inst -- Name of an Illumina instrument (defaults to NextSeq-orientation I5) The value of "inst" can be one of these keys, or any of "MiSeq", "HiSeq 2000", "HiSeq 2500", "MiniSeq", "NextSeq", or "HiSeq 4000". The value dictates the orientation of the returned sequences, based on the standard dual-indexed sequencing workflow for the instrument.

Returns:

The i5 adapter sequence

Examples:

{{ ilmn_i7_seq('TruSeq DNA-RNA', 'A001') }} --> 'ATCACG'
ilmn_i5_seqs(kit: str, inst: str = None) list[str]

Fetch a list of i5 adapter sequences for a kit

Parameters:
  • kit -- Name of the Illumina kit

  • inst -- The Illumina instrument class, or None. (defaults to NextSeq-orientation I5) The value of "inst" can be one of these keys, or any of "MiSeq", "HiSeq 2000", "HiSeq 2500", "MiniSeq", "NextSeq", or "HiSeq 4000". The value dictates the orientation of the returned sequences, based on the standard dual-indexed sequencing workflow for the instrument.

Returns:

A list of adapter index sequences for a kit.

Examples:

{{ ilmn_i5_seqs('TruSeq DNA-RNA') }} --> ['TATAGCCT', 'ATAGAGGC', ...]
ilmn_i7_adapters(kit: str, inst=None) list[tuple[str, str]]

Fetch a list of i7 adapters for a given kit

Parameters:
  • kit -- The name of the kit to get the adapters for

  • inst -- ignored

Returns: The i7 adapters for a given kit as a list of

two-element lists. Each two-element list contains the adapter name and the adapter index sequence in that order. Note that the data source entry corresponding to kit must be of type "index adapters" to be included in this list.

Examples:

{{ilmn_i7_adapters('TruSeq DNA-RNA')}} --> [['A001', 'ATCACG'], ...]
ilmn_i7_names(kit: str) list[str]

Fetch i7 adapter names for a given kit

Parameters:

kit -- Name of the Illumina kit

Returns:

A list of i7 adapter names for a kit.

Examples:

{{ ilmn_i7_names('TruSeq DNA-RNA') }} --> ['A001', 'A002', ...]
ilmn_i7_seq(kit: str, name: str) str
Parameters:
  • kit -- Name of the Illumina kit

  • name -- Name of the I5 or I7 adapter within the kit

Returns:

The I5 or I7 adapter sequence

Examples:

{{ ilmn_i7_seq('TruSeq DNA-RNA', 'A001') }} --> 'ATCACG'
ilmn_i7_seqs(kit: str) list[str]

Fetch a list of i7 adapter sequences for a kit

Parameters:

kit -- Name of the Illumina kit

Returns:

A list of adapter index sequences for a kit.

Examples:

{{ ilmn_i7_seqs('TruSeq DNA-RNA') }} --> ['ATCACG', 'CGATGT', ...]
ilmn_instruments() list[str]

List known Illumina instrument types

Returns:

A list of known Illumina instrument types.

Examples:

{{ ilmn_instruments() }} --> ['NextSeq', 'HiSeq 2000', 'HiSeq 2500', 'MiniSeq', 'MiSeq', 'HiSeq 4000']
ilmn_kit_details(kit_name: str) dict[str, Any]

Return configuration details for the named kit

Parameters:

kit_name -- Name of a configured Illumina kit

Returns:

Dictionary of configuration information for the kit

Examples:

{{ ilmn_kit_details('KAPA Dual Indexed Adapter Kit')["_type"] }} --> "dual indexed adapters"

Note

Valid keys depend on the the kit configuration, but typically available are:

  • _type - The type of kit, normally one of primers, dual index adapters, or index adapters.

  • uses_i5_index - True if the kit uses an i5 adapter/index, False otherwise.

  • _source - URL used as the source of the primers.

  • Index 1 (i7) Adapters - a list of length-2 lists. The first element of each list is the adapter name. The second element of each list is the adapter sequence.

  • Index 2 (i5) Adapter - a dictionary with two keys:

    • MiSeq, HiSeq 2000/2500 - a list in the same shape as Index 1, but for the MiSeq orientation of i5 adapters.

    • MiniSeq, NextSeq, HiSeq 3000/4000 - a list in the same shape as Index 1, but for the NextSeq orientation of i5 adapters.

ilmn_kits(types: Sequence[str] = ('index adapters', 'primers', 'dual index adapters'), include_custom: bool = True) list[str]

Get a list of kits matching the input parameters.

Parameters:
  • types -- A Sequence of strings declaring the types of kits to include. Defaults to ['index adapters', 'primers'].

  • include_custom -- Boolean. If true the value "Custom" will be included in the return list of kits.

Returns:

A list of known kits of the specified type(s).

Examples:

# a list of configured kit names: ['TruSeq DNA-RNA', 'TruSeq Diverse 27F 16s', ...]
{{ ilmn_kits() }} --> ['TruSeq DNA-RNA', ..., 'Custom']

# a list of kits of type index adapters.
{{ ilmn_kits(['index adapters']) }} --> ['TruSeq DNA-RNA', ..., 'Custom']

# a list of kits of type primers.
{{ ilmn_kits(['primers']) }} --> ['TruSeq Diverse 27F 16s', ..., 'Custom']

# exclude custom kits
{{ ilmn_kits(['primers'], include_custom=False) }} --> ['TruSeq Diverse 27F 16s', ...]
ilmn_primer_names(kit: str) list[str]

Fetch a list of primer names for a kit

Parameters:

kit -- The name of the kit

Returns:

The list of primer names for a kit, where the kit type is "primers". In L7|ESP, Illumina "primer" kits are kits with only one index per read.

Examples:

{{ ilmn_primer_names('TruSeq Diverse 27F 16s') }} --> ['1', '2', ...[]
ilmn_primer_seq(kit: str, name: str) str

Fetch a primer adapter sequence

Parameters:
  • kit -- The name of the kit

  • name -- The name of the adapter within the kit

Returns:

An index sequence for a given kit and primer name.

Examples:

{{ ilmn_primer_seq('TruSeq Diverse 27F 16s', '2') }} --> 'TGTCAC'
ilmn_primer_seqs(kit: str) list[str]

Fetch a list of primer sequences for a kit

Parameters:

kit -- The name of the kit

Returns:

A list of index sequences for a kit of type primers.

Examples:

{{ ilmn_primer_seqs('TruSeq Diverse 27F 16s') }} --> ['TTGAC', 'TGTCAC', ...]
ilmn_primers(kit: str, inst: str = None) list[tuple[str, str]]

Fetch a list of adapter names and sequences for a "primer" kit (!dual indexed)

Parameters:
  • kit -- The name of the kit

  • inst -- Unused

Returns:

The list of primers for a kit, where the kit type is "primers". In L7|ESP, Illumina "primer" kits are kits with only one index per read.

Examples:

{{ ilmn_primers('TruSeq Diverse 27F 16s') }} --> [['1', 'TTGAC'], ...]
ilmn_seqs_for_indexid(kit: str, indexid: str) list[str]

A generalized version of some of the other adapter lookups.

For _type == 'primers' returns the list [i7 index, 'NA']. For _type == 'index adapters', returns the list [i7 index]. For _type == 'dual index adapters', returns the list [i7 index, i5 index (NextSeq orientation)].

Parameters:
  • kit -- Name of a configured Illumina kit

  • indexid -- Index ID

Examples:

{{ ilmn_seqs_for_indexid("KAPA Dual Indexed Adapter Kit", "A1") }} --> ['TATAGCCT', 'CGAGTAAT']

Note

Both "index adapters" and "dual indexed adapters" are used for paired-index sequencing. The "index adapters" type should be used when the i7 and i5 indexes are contained in separate source tubes and are specified with separate identifiers. The "dual index adapters" type should be used for cases where a single well or tube contains both an i7 and i5 adapter, and a single identifier is used for that particular combination of I7/I5 sequences. Single-index kits should always use _type="primers".

6.2.5. LIMS API

The LIMS API can be used for data linking, default column values, or when directly entering values in a Worksheet.

Variables

The LIMS API provides access to a number of useful context variables:

Variable:

agent - Object representing the current user. Direct access to agent is deprecated. Use the current_user expression instead.

Variable:

sample - Sample JSON, a dict with at least keys name and uuid

{{sample['name']}}

Would give a value similar to: "ESP000001".

Variable:

protocol_name - Name of the current Protocol

Variable:

sample_index - The current row index (0 for the first row, etc.)

Variable:

sample_sheet_uuid - The UUID of the current Worksheet

Variable:

sample_sheet_name - The name of the current Worksheet

Variable:

sample_to_row - Dictionary that maps from Entity UUID to a particular row number in the Worksheet

Variable:

workflow_uuid - The UUID of the current Workflow

Variable:

workflow_name - The name of the current Workflow

Functions

chain_info(hops: Union[str, int, list[int]] = 'anyhop') dict

Return a ChainInfo object containing Workflow Chain data for the current row.

Parameters:

hops -- the "hops" to grab information for. For instance, if looking for information about the previous node in the experiment chain, "hops" would be -1. To fetch all hops in the chain, use "anyhop" (the default).

Returns:

A dictionary of int -> ChainInfo objects, where the key is the "hop" and the value is a ChainInfo object containing Workflow Chain data for the current row. A hop of 0 is the current node. Currently, this function will only return upstream Workflow Chain Node information since there are not normally downstream Workflow Chain Node in the Workflow Chain Instance at the point in time where the expression is evaluated.

Properties of the returned object:

  • source_sample_uuid - the Sample UUID of the current row in the Worksheet

  • source_sample_name - the Sample name of the current row in the Worksheet

  • source_experiment_uuid - the Experiment UUID of the current row in the Worksheet

  • source_experiment_name - the Experiment name of the current row in the Worksheet

  • experiment_uuid - the "target" Experiment UUID. For instance, for hops=-1, it is the experiment_uuid of the previous Experiment in the Workflow Chain.

  • experiment_name - the "target" Experiment name. For instance, for hops=-1, it is the experiment_uuid of the previous Experiment in the Workflow Chain.

  • def_name - Workflow Chain Definition name

  • def_uuid - Workflow Chain Definition UUID

  • instance_name - Workflow Chain Instance name

  • instance_uuid - Workflow Chain Instance UUID

  • source_node_name - source Workflow Chain Node name. Source Workflow Chain Node is the "query root" Workflow Chain Node.

  • source_node_uuid - source Workflow Chain Node UUID

  • node_name - upstream Workflow Chain Node name

  • node_uuid - upstream Workflow Chain Node UUID

  • wf_name - upstream Workflow Definition name

  • wf_uuid - upstream Workflow Definition UUID

  • hops - how many hops upstream this Workflow Chain Node is from the source Workflow Chain Node

Note

This expression is only safe for use in the LIMS context.

Examples

Given a Workflow Chain for "My Chain":

My Chain:
  chain:
    - Node A:
        workflow: WF A
        to: Node B
    - Node B:
        workflow: WF B
        to: Node C
    - Node C:
        workflow: WF C

And that Workflow "WF B" has a Protocol of type "Sample Protocol", suppose a User has submitted Sample "S1" in an Experiment "My Experiment" and then progressed the Sample "S1" through to Workflow Chain Node "Node C". In the process, the user created Sample "S2" as a child of Sample "S1". Furthermore, the individual Experiments at each Workflow Chain Node were named "My Experiment - A", "My Experiment - B", and "My Experiment - C". Then the following Expressions, evaluated in the context of a Worksheet "WF C" would evaluate as noted:

{{ chain_info()[-1].hops }} --> -1

{{ chain_info()[-1].source_sample_name }} --> 'S2'

{{ chain_info()[-1].source_experiment_name }} --> 'My Experiment - C'

{{ chain_info()[-1].experiment_name }} --> 'My Experiment - B'

{{ chain_info()[-1].def_name }} --> 'My Chain'

{{ chain_info()[-1].instance_name }} --> 'My Experiment'

{{ chain_info()[-1].source_node_name }} --> 'Node C'

{{ chain_info()[-1].node_name }} --> 'Node B'

{{ chain_info()[-1].wf_name }} --> 'WF B'

{{ chain_info()[-2].hops }} --> -2

{{ chain_info()[-2].source_sample_name }} --> 'S2'

{{ chain_info()[-2].source_experiment_name }} --> 'My Experiment - C'

{{ chain_info()[-2].experiment_name }} --> 'My Experiment - A'

{{ chain_info()[-2].def_name }} --> 'My Chain'

{{ chain_info()[-2].instance_name }} --> 'My Experiment'

{{ chain_info()[-2].source_node_name }} --> 'Node C'

{{ chain_info()[-2].node_name }} --> 'Node A'

{{ chain_info()[-2].wf_name }} --> 'WF A'

# Will raise a Key Error.
{{ chain_info(-1)[-2].wf_name }}
cell(column: str, protocol: Optional[str] = None, generation: int = 0) str

Retrieve a value from a LIMS cell.

Parameters:
  • column -- The column containing the desired values

  • protocol -- Name of the Protocol containing the column. If protocol is None, the current protocol is used.

  • generation -- Where in the sample-lineage to look for a value. 0 is "the uuid of the Entity in the current protocol" -1 is the parent, -2 the grandparent, etc. Values > 0 are not supported at this time. generation should only be used when grabbing data from a Protocol that is not present in the current Workflow.

Returns:

The associated cell value, as a string.

Examples:

# Returned from the active protocol.
{{ cell('Concentration') }} --> '35'

# Returned from the "QC" protocol, either in the same workflow,
# or from another workflow run on the same Entity.
{{ cell('Concentration', 'QC') }} --> '35'

# Returned from the "QC" protocol from a workflow run on the
# parent of the current Entity
{{ cell('Concentration', 'QC', -1) }} --> '35'

Note

The function prefers Protocol + column combinations in the current Workflow first, regardless of the generation setting. If a matching Protocol and column can't be found in the current Workflow, other Workflows will be searched, using generation to determine the correct sample id.

The lookup across other Workflows will return the most recent, non-null value recorded in the specified generation.

Using the returned value in non-string context requires explicit type-casting, as in: {{ int(cell('Concentration'))/35 }}

This function used to be called column_value. The older reference is still supported for backwards compatibility, but cell is the preferred name for the function as of L7|ESP 2.3.0.

Deprecated Aliases: column_value

column_value_for_uuid(uuid: Union[str, list[str]], column: str, protocol: str, generation: int = 0) Union[str, list[str]]

Fetch a LIMS cell value outside of LIMS context.

Parameters:
  • uuid -- The UUID of the Entity to pull data for.

  • column -- The name of the column to pull data from.

  • protocol -- The name of the Protocol to pull data from.

  • generation -- Which Entity "generation" should have the requested LIMS value, where 0 = current Entity, -1 = parent Entity, -2 = grandparent, etc.

Returns:

Associated column value, as a string or None (uuid=str), or list of strings, possibly with null values (uuid=list[str]). None is used for Entities lacking the requests field. If the Entity lacks the requested field, None is returned for that Entity, If multiple matches are found, returns the most recently updated non-null value.

Note

The difference between cell and column_value_for_uuid: The former should be used for Entities which are submitted to the worksheet whereas column_value_for_uuid should be used to pull values from an arbitrary Entity UUID. For instance, if a drop-down in a workflow allows you to select a Entity based on some criteria (e.g.: EntityType), and you need another column to fetch values for a particular Protocol for the selected Entity.

column_values(column: str, protocol: str = None) list

Fetch all values from a LIMS field within the Worksheet.

Parameters:
  • column -- The name of the column to pull data from.

  • protocol -- The name of the Protocol to pull data from. If protocol is unspecified, default to the Protocol that contains the function call.

Returns:

Associated column values, as a list of strings.

Examples:

# Returned from active protocol
{{ column_values('Concentration') }} --> ["35", "42", ...]

# Returned from the "QC" protocol in the same workflow
{{ column_values('Concentration', 'QC') }} --> ["35", "42" ...]

 # Returns the lowest concentration
{{ min([float(x) for x in column_values('Concentration', 'QC')]) }}

Note

This function only returns values from Protocols and columns within the Worksheet being processed.

experiment_name(self) str

Get the current Entity's Experiment (WorkflowInstance) name

Returns:

Name of the Experiment the current Entity is part of

Examples:

{{ experiment_name() }} --> 'Experiment 01'
experiment_uuid(self) str

Get the current Entity's Experiment (WorkflowInstance) uuid.

Returns:

UUID of the Experiment the current Entity is part of

Examples:

{{ experiment_uuid() }} --> 'e6a1e079-dde1-4f3c-935d-fd6f3774f459'
group_values(protocol: str, column: str) list

Get the value of a column for all Entities in a single group of a grouped Protocol

Parameters:
  • protocol -- The name of the Protocol to get values from, relative to the current Workflow. If protocol is None, the current Protocol is used.

  • column -- The column containing the desired values

Returns:

Column values for all the Entities in the current row's group.

Examples:

{{ group_values('Plate Prep', 'Wells') }} --> ['A1', 'B12', 'C9']

Note

This function is used to get the individual values for each Entity in an Entity Group from a column in the current Protocol (or another Protocol in the same Workflow).

Return the column values for all Entities in the Entity Group as a list. Values are added to the list based on the Entity order, so two lists of values can be zipped together to get multiple values for each Entity.

is_node_selected(protocol: str = None, column: str = None, choices: Union[str, list[str]] = None, include_failed: bool = None) bool

Workflow Chain Transition Rule function that returns True if the option(s) selected by the user matches the target Workflow Chain Node name

Parameters:
  • protocol -- In conjunction with column, where to grab the "Next Workflow" selection(s) from. Defaults to the final Protocol in the Workflow. Uses fixed ID.

  • column -- The column to grab the "Next Workflow" selection(s) from. If unspecified, will try to use "next step", "next workflow", and "next action" in turn (case-insensitive). Uses fixed ID.

  • choices -- The option or options selected by the user for downstream transition. If choices is specified, it overrides protocol and column. Uses Workflow Chain Node name, NOT fixed ID.

  • include_failed -- If True, failed samples are considered for the transition. If False, failed samples are specifically excluded. A value of None triggers the default behavior - if the transition strategy is 'resubmit', failed samples are considered, otherwise they are not.

Returns:

True if the target Workflow Chain Node was selected by the user and False otherwise.

Examples

Given the same Workflow Chain configuration:

My Chain:
  chain:
    X:
      to:
        Y:
          if: "{{ is_node_selected() }}"
        Z:
          if: "{{ is_node_selected() }}"

With various values of the "Next Action" column from "Last Protocol"

# single-select picklist.
{{ cell("Next Action", "Last Protocol") }} --> 'Y'

# evaluated in the context of the "X->Y" transition.
{{ is_node_selected(choices=cell('Next Action', 'Last Protocol')) }} --> True

# evaluated in the context of the "X->Z" transition.
{{ is_node_selected(choices=cell('Next Action', 'Last Protocol')) }} --> False

# single-select picklist.
{{ cell("Next Action", "Last Protocol") }} --> 'Z'

# evaluated in the context of the "X->Y" transition.
{{ is_node_selected(choices=cell('Next Action', 'Last Protocol')) }} --> False

# evaluated in the context of the "X->Z" transition.
{{ is_node_selected(choices=cell('Next Action', 'Last Protocol')) }} --> True


# multi-select picklist.
{{ cell("Next Action", "Last Protocol") }} --> ['Y', 'Z']

# evaluated in the context of the "X->Y" transition.
{{ is_node_selected(choices=cell('Next Action', 'Last Protocol')) }} --> True

# evaluated in the context of the "X->Z" transition.
{{ is_node_selected(choices=cell('Next Action', 'Last Protocol')) }} --> True

# multi-select picklist.
{{ cell("Next Action", "Last Protocol") }} --> []

# evaluated in the context of the "X->Y" transition.
{{ is_node_selected(choices=cell('Next Action', 'Last Protocol')) }} --> False

# evaluated in the context of the "X->Z" transition.
{{ is_node_selected(choices=cell('Next Action', 'Last Protocol')) }} --> False

# Shorthand. Only works if protocol and column are in the current workflow.
{{ is_node_selected('Last Protocol', 'Next Action') }}

# Shortest shorthand. Will look for "Next Action", "Next Workflow" or "Next Step"
# column in the last protocol of the workflow.
{{ is_node_selected() }}

Note

This function is only useful in the context of transitioning a Workflow Chain (the transition "rule" expression). In every other context, the returned value will be False.

This function pairs well with drop-down/multi-select columns using the next_node_choices() Expression function to create the options list.

When choices is a string, this function will first attempt to JSON-decode the string in the event it is a JSON-encoded list.

mes_ref(field: str = None, protocol: str = None, workflow: str = None, step: str = None, member: str = None) str

Get a label for a numbered process element

Parameters:
  • field -- The barcode of the field to reference. If field is unspecified, specifically return a Protocol reference.

  • protocol -- The barcode of the Protocol to reference. If protocol is unspecified, default to the Protocol that contains the function call.

  • workflow -- The barcode of the Workflow to reference. If workflow is unspecified, default to the Workflow that contains the function call.

  • step -- The name of the step to reference. If step is unspecified, specifically return a Protocol reference.

  • member -- The barcode of the WorkflowChain node to reference. If member is unspecified, default to the node that contains the function call.

Returns:

Reference as a string in the form 'ELEMENT_NUMBER NAME'.

Examples:

# Returned field from active protocol.
{{ mes_ref('Concentration') }} --> "1.2.2.1 Concentration"

# Returned field from the "QC" protocol in the active workflow.
{{ mes_ref('Concentration', 'QC') }} --> "1.1.3.1 Concentration"

# Returned the "QC" protocol from the active workflow.
{{ mes_ref(protocol='QC') }} --> "1.1 QC"

Note

The function prefers field, protocol, or (protocol + field) combinations in the current Workflow.

nearest_upstream_entity(upstream_node: str) Optional[list[str]]

Find the Entity or Entities from the upstream Workflow Chain Node connected to the current sample

Parameters:

upstream_node -- Fixed ID for the upstream Workflow Chain Node.

Returns:

List of UUIDs for the upstream Entity

Note

This function only makes sense in the context of Workflow Chains. The nearest upstream Entity is the Entity from which the current row is derived, traced through upstream Workflow Chain Transitions.

Examples:

{{ nearest_upstream_sample('Library Pooling') }} --> ['uuid', ...]

Deprecated Aliases: nearest_upstream_sample

next_node_choices(drop: Optional[str] = 'Finished', exclude: Optional[str] = None) list

Returns a list of potential choices for the next Workflow Chain Node in the Workflow Chain.

The next choice is based on the active Workflow Chain for a given sample.

May only be used in the context of LIMS.

Parameters:
  • drop -- A label that, if selected, means the sample will not be routed to any further Workflows. Default value is "Finished". If None, only valid downstream Workflow Chain Node names will be available for selection.

  • exclude -- A list of Workflow Chain Node choices to exclude from the drop-down. Useful in situations where a Workflow Chain Transition is present for logical clarity but will never be directly "followed" (such as split/merge chains: a -> b, a -> c, b -> d; c -> d).

Returns:

The list of Workflow Chain Nodes (names, not fixed IDs) downstream from the current Workflow for the current Entity row in the Worksheet, lexicographically sorted.

Examples:

Give a **Workflow Chain** configuration of:

My Chain:
    chain:
        X:
            to:
                Y:
                    if: "{{ True }}"
                Z:
                    if: "{{ False }}"

{{ next_node_choices() }} --> ['Finished', 'Y', 'Z']

{{ next_node_choices(drop=None) }} --> ['Y', 'Z']

{{ next_node_choices(drop='Stop Processing') }} --> ['Stop Processing', 'Y', 'Z']

Note

The returned list of options will use the Workflow Chain Node names, not the Workflow name.

This function makes certain assumptions about the context in which it is called. Generally, it should only be used in Protocols that use the same sample set as the last Protocol in a Workflow.

The next choice is based on the active Workflow Chain for a given Entity. May only be used in the context of LIMS

next_node_choices_ht(drop: Optional[str] = 'Finished', exclude: Optional[str] = None) list

Returns a list of potential choices for the next Workflow Chain Node in the Workflow Chain.

The next choice is based on the active Workflow Chain for a given sample.

May only be used in the context of LIMS.

Parameters:
  • drop -- A label that, if selected, means the sample will not be routed to any further Workflows. Default value is "Finished". If None, only valid downstream Workflow Chain Node names will be available for selection.

  • exclude -- A list of Workflow Chain Node choices to exclude from the drop-down. Useful in situations where a Workflow Chain Transition is present for logical clarity but will never be directly "followed" (such as split/merge chains: a -> b, a -> c, b -> d; c -> d).

Returns:

The list of Workflow Chain Nodes (names, not fixed IDs) downstream from the current Workflow for the current Entity row in the Worksheet, lexicographically sorted.

Examples:

Give a **Workflow Chain** configuration of:

My Chain:
    chain:
        X:
            to:
                Y:
                    if: "{{ True }}"
                Z:
                    if: "{{ False }}"

{{ next_node_choices_ht() }} --> ['Finished', 'Y', 'Z']

{{ next_node_choices_ht(drop=None) }} --> ['Y', 'Z']

{{ next_node_choices_ht(drop='Stop Processing') }} --> ['Stop Processing', 'Y', 'Z']

Note

The returned list of options will use the Workflow Chain Node names, not the Workflow name.

This function makes certain assumptions about the context in which it is called. Generally, it should only be used in Protocols that use the same sample set as the last Protocol in a Workflow.

The next choice is based on the active Workflow Chain for a given Entity. May only be used in the context of LIMS

This function is supposed to be a drop-in replacement for next_node_choices, but with better memory, CPU, and time utilization characteristics for high throughput (ht) operations. However, it is provided as an experimental expression at this time. If it proves sufficiently stable and performant, it will replace the original next_node_choices with an alias for next_node_choices_ht for backwards compatibility. So it is safe to try this expression and, if it is working for you, continuing to use the expression indefinitely.

project_name(self) str

Project name associated with LIMS row

Returns:

Name of the Project the current Sample/Experiment is submitted to.

Examples::

{{ project_name() }} --> 'Project 01'

row_count(self) int
Returns:

The number of rows in the active protocol

Examples:

{{ row_count() }} --> 96
tagged_mes_ref(tags: list) str

Get a label for an upstream numbered process element by tag list.

Parameters:

tags -- The list of tags. An element must match all tags to match.

Returns:

Reference as a string in the form 'ELEMENT_NUMBER NAME'.

Examples:

# assuming the illumina kit field was the first field in the first protocol.
{{ tagged_mes_ref(['esp:illumina', 'esp:kit']) }} --> "1.1.1.1 Kit"

Note

Steps are untaggable so they cannot be referenced by this function.

TaggedMESReference.api_func(tags: list[str]) str

Return a reference string to a field, protocol, or workflow.

tagged_value(tags: list, generation: Union[str, int] = 0, sample_uuid: Union[str, list[str]] = None) Union[str, list[str]]

Get values by tag

Parameters:
  • tags -- The list of tags. A column must match all tags to match.

  • generation -- Where in the sample-lineage to look for a value. 0 is "the uuid in the current protocol," -1 is the parent, -2 the grandparent, etc. Any value supported by sample_generation may be used here.

  • sample_uuid -- sample UUID(s) to use. If None, assumed to be self.ctx['sample']['uuid']. If a list of UUIDs is provided, the result will be a list with corresponding results for each UUID.

Returns:

Corresponding column value, as a string.

Examples:

# assuming the Illumina kit value was set to iTru for this sample most recently.
{{ tagged_value(['esp:illumina', 'esp:kit']) }} --> "iTru"

Note

tagged_value will look at LIMS values and entity custom fields. It is intended to be used in situations where a value may come from different "upstream" sources depending on the exact history of the entity.

value_grouped_by(protocol: str, column: str, group_by: list) list

Fetch list of values from the active LIMS Worksheet using custom grouping.

Parameters:
  • protocol -- Name of the Protocol that contains column

  • column -- Name of the column within protocol that contains the values to be grouped

  • group_by -- list of (protocol name, column name) tuples that define the columns to group by.

Returns:

A list of lists based on multiply and arbitrarily grouped column values.

Examples:

# would return uuid as grouped by protocol1.column1 (primary) and
# protocol2.column2 (secondary). the number of lists returned is equal
# to the length of the cartesian product of grouping column values.
{{ value_grouped_by('protocol', 'uuid', [('protocol1', 'column1'), ('protocol2', 'column2')]) }}

# returns [['uuid1', 'uuid2', ...], ['uuidn', 'uuidm', ...], ...]
# Where the # of innert lists is equal to the number of "Well Group" values
# in the "Sample Setup" sheet.
{{ value_grouped_by('uuid', [('Sample Setup', 'Well Group')]) }}

Note

Can only be used for Protocols in the same Worksheet as the expression. Can only be used for Protocols where the sample set size of all grouping columns and the value column is the same.

chain_cell(protocol: Union[str, list[str]], column: str, nodes: Union[str, list[str]] = None, hops: Union[int, list[int], str] = None, all_values: bool = False) Union[str, list[str]]

Return a value from the Workflow Chain.

For values within the same Protocol or Workflow, use cell. If fetching the most-recently-set value for an Entity for a particular Protocol and column is sufficient, use cell or tagged_value. Use chain_cell only if you must guarantee strict provenance of the source value. Furthermore, chain_cell can currently only be used to fetch values across Workflow Chain boundaries where the Entity in the source Workflow Chain Node is the same Entity in the target Workflow Chain Node or when there's a direct ancestry relationship between them (parent-child, grandparent-grandchild, etc.).

Parameters:
  • protocol -- name(s) of the upstream Protocol(s) to look in for column.

  • column -- name of the column

  • nodes -- name(s) of the Workflow Chain Node to fetch the value from. The fetched value will be the closest Workflow Chain Node up the realized Workflow Chain with matching name (unless hops is specified). "anynode" is a valid value to ignore Workflow Chain Node entirely.

  • hops -- upstream "hops" to consider. The current Workflow Chain Node is hop == 0, One Workflow Chain Node "up" from the current node is -1, etc. "anyhop" is a valid value to ignore hops entirely.

  • all_values -- If True, returns a list of values that match the criteria. If False, returns the value from the most recent Workflow Chain Node that matches the other criteria.

Returns:

A string (all_values=False) or list of strings (all_values=True) of values.

Return type:

Union[str, list[str]]

Examples:

# In this example, there is a Workflow Chain with a Workflow Chain Node "My Node" that has been visited three times so far.
# The last time it was visited was the Workflow Chain Node immediately before the current Workflow Chain Instance.
# It was visited in a loop three times in a row.
{{ chain_cell("My Protocol", "My Column", "My Node", "anyhop", True) }} --> ["first", "second", "third"]

{{ chain_cell("My Protocol", "My Column", "My Node", "anyhop", False) }} --> "third"

{{ chain_cell("My Protocol", "My Column", "My Node", -1) }} --> "third"

{{ chain_cell("My Protocol", "My Column", "My Node", -3) }} --> "first"
chain_cell_ht(protocol: Union[str, list[str]], column: Union[str, list[str]], nodes: Union[str, list[str]] = None, hops: Union[int, list[int], str] = None, all_values: bool = False) Union[str, list[str]]

Return a value from the Workflow Chain.

For values within the same Protocol or Workflow, use cell. If fetching the most-recently-set value for an Entity for a particular Protocol and column is sufficient, use cell or tagged_value. Use chain_cell only if you must guarantee strict provenance of the source value. Furthermore, chain_cell can currently only be used to fetch values across Workflow Chain boundaries where the Entity in the source Workflow Chain Node is the same Entity in the target Workflow Chain Node or when there's a direct ancestry relationship between them (parent-child, grandparent-grandchild, etc.).

Parameters:
  • protocol -- name(s) of the upstream Protocol(s) to look in for column.

  • column -- name of the column

  • nodes -- name(s) of the Workflow Chain Node to fetch the value from. The fetched value will be the closest Workflow Chain Node up the realized Workflow Chain with matching name (unless hops is specified). "anynode" is a valid value to ignore Workflow Chain Node entirely.

  • hops -- upstream "hops" to consider. The current Workflow Chain Node is hop == 0, One Workflow Chain Node "up" from the current node is -1, etc. "anyhop" is a valid value to ignore hops entirely.

  • all_values -- If True, returns a list of values that match the criteria. If False, returns the value from the most recent Workflow Chain Node that matches the other criteria.

Returns:

A string (all_values=False) or list of strings (all_values=True) of values.

Return type:

Union[str, list[str]]

Examples:

# In this example, there is a Workflow Chain with a Workflow Chain Node "My Node" that has been visited three times so far.
# The last time it was visited was the Workflow Chain Node immediately before the current Workflow Chain Instance.
# It was visited in a loop three times in a row.
{{ chain_cell("My Protocol", "My Column", "My Node", "anyhop", True) }} --> ["first", "second", "third"]

{{ chain_cell("My Protocol", "My Column", "My Node", "anyhop", False) }} --> "third"

{{ chain_cell("My Protocol", "My Column", "My Node", -1) }} --> "third"

{{ chain_cell("My Protocol", "My Column", "My Node", -3) }} --> "first"
hhmm_add(first: str, second: str) str

Compute a time addition

Parameters:
  • first -- The first time, in HH:MM format.

  • second -- The second time, in HH:MM format.

Returns:

The sum of the first and second times as a HHMM formatted string.

Examples:

{{ hhmm_add('13:05', '01:05') }} --> "1410"

Note

This function should only be used when it is certain you won't wrap the time to a new day. i.e. adding 2 hours to 23:00 is not supported.

hhmm_diff(first: str, second: str) int

Compute a difference in times

Parameters:
  • first -- The first time, in HH:MM format.

  • second -- The second time, in HH:MM format.

Returns:

The difference between the first and second times, in seconds

Examples:

{{ hhmm_diff('13:05', '13:10') }} --> 300

Note

The times are assumed to occur on the same day.

plate1536() list[str]

Return a list of well names for a 1536-well plate.

Convenience wrapper for: plate_wells(32, 48)

plate24() list[str]

Return a list of well names for a 24-well plate.

Convenience wrapper for: plate_wells(4, 6)

plate384() list[str]

Return a list of well names for a 384-well plate.

Convenience wrapper for: plate_wells(16, 24)

plate96() list[str]

Return a list of well names for a 96-well plate.

Convenience wrapper for: plate_wells(8, 12)

plate_wells(rows: int, cols: int) list[str]

Enumerate a list of the well names for a plate with n rows and m columns.

Parameters:
  • rows -- The number of rows in the plate

  • cols -- The number of columns in the plate

Returns:

A list of well labels for the row-by-column plate.

Examples:

{{ plate_wells(8, 12) }} --> ['A01', 'A02', ... 'H11', 'H12']

Note

plate_wells and the other plate expressions are useful for building plate manifests for plates that are created and consumed within a Workflow, and are therefore not tracked long-term.

Q_(value: ~typing.Union[str, ~pint.quantity.build_quantity_class.<locals>.Quantity, ~numbers.Number], units: ~typing.Optional[~typing.Union[~pint.util.UnitsContainer, str, ~pint.quantity.build_quantity_class.<locals>.Quantity]] = None) Quantity

Construct a Quantity.

Parameters:
  • value -- The quantity "value"

  • units -- The quantity units

Examples:

{{ Q_('35 ul') }} --> 35 <Unit('microliter')>

{{ Q_(35, 'ul') }} --> 35 <Unit('microliter')>

{{ Q_(35, Q_('50 ul')) }} --> 35 <Unit('microliter')>

6.2.6. Param Group API

The Param Group API provides access to parameters stored in Param Groups.

param(groupname: str, param: str) str

Lookup a parameter from a Param Group.

Parameters:
  • groupname -- the name of the Param Group

  • param -- the parameter name in the Param Group

Returns:

the value assigned to the key in the Param Group

Examples:

{{ param("References", "Current Human") }} --> 'hg38.fa'
params(groupname: str) dict

Fetch all parameters of a Param Group

Parameters:

groupname -- the name of the Param Group

Returns:

the dictionary of key-value pairs in the Param Group.

Examples:

{{ params('References') }} --> {"Current Human": "hg38.fa"}

6.2.7. Resource API

The Resource API provides general resource access functionality.

In L7|ESP, nearly everything is a Resource, including but not limited to:

  • Applets

  • Container Types

  • Containers

  • Entities (samples, assets, etc.)

  • Entity Classes

  • Entity Types

  • Experiments (Workflow Instances and Workflow Chain Instances)

  • Files

  • Inventory Item Types

  • Inventory Items

  • LIMS Worksheets

  • ParamGroup

  • Pipeline Instances

  • Pipelines

  • Projects

  • Protocols

  • Reports

  • Tasks

  • Workflow Chains

  • Workflows

resource_dict(resource_uuid: str = None, resource_name: str = None, cls: str = None) dict

Return the full dict for a Resource given its UUID or name.

Parameters:
  • resource_uuid -- The UUID of the Resource

  • resource_name -- The name of the Resource

  • cls -- The name of the Resource Class. If none, any Resource type matching the resource name will be returned. Only used in conjunction with resource_name. (Since ESP 3.0.0)

Returns:

The JSON representation of the resource

Examples:

{{resource_dict(sample['sample_type_uuid'])}} -> {
    "name": "Generic sample",
    "uuid": "77777777-7777-4014-b701-000000000001",
    "desc": "Types for samples with no other classification",
    "meta": {"lab7_id_sequence": ["ESP SEQUENCE"]}
}

Note

Either resource_uuid or resource_name is required. If both are specified, resource_name is ignored.

session_timestamp(format_: str = None) str

Provide the current UTC timestamp, with optional custom formatting

If no format is provided, result is ISO-8601-formatted. The timestamp is generated when the DB session is created so multiple calls will return the same value if they are made using the same session.

Parameters:

format -- Optional format string. Default is ISO-8601-formatted. All standard Python format codes are supported.

Returns:

The formatted date and time, current as of the time of the call.

Examples:

{{ session_timestamp() }} --> '2019-01-03T12:59:28.818039Z'
{{ session_timestamp('%Y-%m-%d') }} --> '2019-01-03'
{{ session_timestamp('%D') }} --> '01/03/19'
current_datetime(format_: str = None) str

Provide the current time, with optional custom formatting

If no format is provided, result is ISO-8601-formatted.

Parameters:

format -- Optional format string. Default is ISO-8601-formatted. All standard Python format codes are supported.

Returns:

The formatted date and time, current as of the time of the call.

Examples:

{{ current_datetime() }} --> '2019-01-03T12:59:28.818039'
{{ current_datetime('%Y-%m-%d') }} --> '2019-01-03'
{{ current_datetime('%D') }} --> '01/03/19'
json_dumps(obj)

Convert an object into a JSON string (access to Python's json.dumps function).

Parameters:

obj -- Object to dump

Returns:

The JSON serialization of obj.

Examples:

{{ json_dumps({'a': 'b'}) }} --> '{"a": "b"}'
json_loads(s)

Parse a JSON string to object (access to Python's json.loads function).

Parameters:

s -- The string to load

Returns:

The JSON object from the provided string.

Examples:

{{json_loads(param('group1', 'key1'))}} --> {'a': 'b'}

Note

This can be used to parse JSON from a Param Group value, as in the above example.

Extracts the UUID from a resource link field value. If the value is null, empty string, etc., returns an empty string instead.

6.2.8. Entity API

The Entity API provides access to functionality, specific to working with Entities.

entity_children(entity_uuid: str, flatten: bool = False) Union[list[dict[str, Any]], list[tuple[dict[str, Any], str]]]

Fetch an Entity's children

Parameters:
  • entity_uuid -- UUID of the parent Entity

  • flatten (bool) -- If True, result is returned as [{entity_dict}, ...]. Otherwise, as [[{entity_dict}, 'begat'], ...]. For backwards compatibility, flatten is False by default.

Returns:

List of Entity children. The returned structure is identical to that returned by entity_parents, but for the Entity's children.

Examples:

{{ entity_children(sample['uuid']) }} --> [[{'uuid': '1abfadd7-5ed4-490c-96fb-a1e74b7b59aa', ...}, 'begat'], ...]

Note

This is a shortcut for entity_generation(1, entity_uuid, False, flatten)

Deprecated Aliases: sample_children

entity_children_uuids(entity_uuid: str) list

Fetch the UUIDs of an Entity's children.

Parameters:

entity_uuid -- UUID of the parent Entity

Returns:

Child Entity UUIDs as a list of strings.

Examples:

{{ entity_children_uuids(sample['uuid']) }} --> ['1abfadd7-5ed4-490c-96fb-a1e74b7b59aa', ...]

Note

This is a shortcut for entity_generation(1, uuid, True, True)

Deprecated Aliases: sample_children_uuids

entity_exists(entity_name: str) bool

Determine whether the given Entity exists by name.

Parameters:

entity_name -- Name of the Entity to check for existence

Returns:

Returns True if the named Entity exists, using exact matching for the Entity name.

Note

This function triggers one query per unique name, so should be used with care.

Deprecated Aliases: sample_exists

entity_generation(generation: Union[str, int], sample_uuid: Union[list[str], str] = None, uuids_only: bool = False, flatten: bool = True, traverse_labels: tuple = ('begat',), entity_uuids: Union[list[str], str] = None) Union[list[str], list[dict], list[tuple[dict, str]], list[list[str]], list[list[dict]], list[list[tuple[dict, str]]]]

Fetch a generation of Entities relative to an input Entity

Parameters:
  • generation -- The generation to fetch relative the provided or implied Entity UUID. (See note and examples).

  • sample_uuid -- Alias for entity_uuids. Will be removed in a future release. Use kwarg-based UUIDs instead, as: entity_generation(-1, entity_uuids="UUID here")

  • uuids_only -- If False, a list of dict representations of the Entity are returned. If True, a list of UUIDs is returned.

  • flatten -- If True (default) and uuids_only is False, list is returned as [{entitydict}, ...]. If False and uuids_only is False, list is returned as [[{sampledict}, 'begat'], ...]. flatten should normally be True and is provided for backwards compatibility with entity_parents and entity_children.

  • entity_uuids -- The UUID of the generation=0 entity. If unspecified, the expression context is searched in order for entity['uuid'], entity_uuid, sample['uuid'], sample_uuid. If these are not found, an error is raised.

Returns:

The return value depends on the call arguments as follows

entity_uuids

flatten

uuids_only

Return Type

str

True

list[str]

str

True

False

list[dict]

list[str]

False

False

list[tuple[dict, str]]

list[str]

True

list[list[str]]

list[str]

True

False

list[list[dict]]

list[str]

False

False

list[list[tuple[dict, str]]]

The generation parameter controls exactly which generation(s) are returned. 0 means get the value from for the provided entity_uuid, -1 means get it for the parent of the provided UUID, -2 the grandparent, 1 the child, 2 the grandchild, and so on.

Generation may also be a string, in which case it follows the extended regex format ((all|closest|furthest)(up|down))?[^,]+(,[^,]+)*. For instance:

  • "allup:Specimen" will return all ancestors of the current entity of type Specimen,

  • "closestup:Specimen" will return the closest ancestor of type Specimen,

  • "furthestup:Specimen" will return the most distant ancestors of type Specimen.

  • "Specimen" is equivalent to "closestup:Specimen",

  • and multiple types may be comma-delimited, as "Library,Illumina Library" which would find the nearest ancestors of type Library or Illumina Library.

Note

Multiple Entities may be returned if they fulfill the criteria.

For instance, consider a Entity hierarchy created by making two Illumina libraries, normalizing them, then pooling them:

Library 1
└── Library 3
    └── Pool 1
Library 2
└── Library 4
    └── Pool 1

Then a search on Pool 1 for non-0 generation returns as follows:

  • generation=-1: Library 3 and Library 4

  • generation=-2: Library 1 and Library 2

  • generation="nearest:Illumina Library": same as generation=-1

  • generation="furthest:Illumina Library": same as generation=-2

  • generation="all:Illumina Library": Libraries 1-4.

Now suppose a scientist has made libraries 5 and 6 with normalized libraries 7 and 8. Then they create Pool 2 by combining libraries 7 and 8 with pool 1, as:

Library 1
└── Library 3
    └── Pool 1
        └── Pool 2
Library 2
└── Library 4
    └── Pool 1
        └── Pool 2
Library 5
└── Library 7
    └── Pool 2
Library 6
└── Library 8
    └── Pool 2

Then a search on Pool 2 for non-0 generation returns as follows:

  • generation=-1: Libraries 7 and 8 and Pool 1

  • generation=-2: Libraries 3 - 6. (3 and 4 via Pool 1)

  • generation="nearest:Illumina Library": Libraries 3, 4, 7, and 8 (i.e. all normalized libraries)

  • generation="furthest:Illumina Library": Libraries 1, 2, 5, and 6 (i.e. all unnormalized libraries)

  • generation="all:Illumina Library": Libraries 1-8

Note

This function uses a recursive query and will pre-cache the results for all Entity in a Worksheet if the sample_sets and sample_set_index keys are in the context and the entity_uuid is unspecified or the entity_uuid is equal to the sample['uuid'] or sample_uuid context variables. Otherwise, one query will be executed per row of the Worksheet, although the resulting value will be cached.

Examples:

# fetch the parent(s) of entity in "sample['uuid']" context variable
# or "entity_uuid" context variable.
{{ entity_generation(-1) }}
--> [{"generation": -1, name: "SAM000001", "uuid": <parent uuid>, ...}, ...]

# fetch the parent(s) of entity given by <uuid>.
{{ entity_generation(-1, '<uuid>') }}
--> [{"generation": -1, name: "SAM000001", "uuid": <parent uuid>, ...}, ...]

# fetch the grandparent(s) of entity given by <uuid>.
{{ entity_generation(-2, '<uuid>') }}
--> [{"generation": -2, name: "SAM900001", "uuid": <grandparent uuid>, ...}, ...]

# fetch the child/children of entity given by <uuid>.
{{ entity_generation(1, '<uuid>') }}
--> [{"generation": 1, name: "SAM000009", "uuid": <child uuid>, ...}, ...]

# fetch the grandchild/grandchildren of entity given by <uuid>.
{{ entity_generation(2, '<uuid>') }}
--> [{"generation": 2, name: "SAM000013", "uuid": <grandchild uuid>, ...}, ...]

# fetch the parent entity UUIDs.
{{ entity_generation(1, uuids_only=True) }}
--> ['UUID', ...]

# fetch the child entities, unflattened.
{{ entity_generation(1, flatten=False) }}
--> [[{entity dict}, 'begat'], ...]

# Fetch the nearest ``Illumina Library`` ancestors.
{{ entity_generation('Illumina Library', '<pool uuid>') }}
--> [{"generation": 1, name: "SAM000013", "uuid": <parent uuid>, ...}, ...]

# equivalent to above, but explicitly calling out closestup.
{{ entity_generation('closestup:Illumina Library', '<pool uuid>') }}
--> [{"generation": 1, name: "SAM000013", "uuid": <parent uuid>, ...}, ...]

# Fetch the most distant ``Illumina Library` ancestors.
{{ entity_generation('furthestup:Illumina Library', '<pool uuid>') }}
-->[{"generation": 1, name: "SAM000013", "uuid": <parent uuid>, ...}, ...]

Deprecated Aliases: sample_generation

entity_parent_uuids(entity_uuid: str) list

Fetch the UUIDs of an Entity's parents.

Parameters:

entity_uuid -- UUID of the child Entity

Returns:

Parent Entity UUIDs as a list of strings.

Examples:

{{ entity_parent_uuids(sample['uuid']) }} --> ['1abfadd7-5ed4-490c-96fb-a1e74b7b59aa', ...]

Note

This is a shortcut for entity_generation(-1, uuid, True, True)

Deprecated Aliases: sample_parent_uuids

entity_parents(entity_uuid: str, flatten: bool = False) Union[list[dict[str, Any]], list[tuple[dict[str, Any], str]]]

Fetch an Entity's parents.

Parameters:
  • entity_uuid -- UUID of the child Entity

  • flatten -- (bool) If True, result is returned as [{entity_dict}, ...]. Otherwise, as [[{entity_dict}, 'begat'], ...]. For backwards compatibility, flatten is False by default.

Returns:

Parent Entities, given an Entity UUID. Parents are returned as a list of entries, in which each entry is a list of two values. The first value is the parent Entity dictionary, and the second value can be ignored.

Examples:

{{ entity_parents(sample['uuid']) }} --> [[{'uuid': '1abfadd7-5ed4-490c-96fb-a1e74b7b59aa', ...}, 'begat'], ...]

Note

This is a shortcut for entity_generation(-1, entity_uuid, False, flatten)

Deprecated Aliases: sample_parents

entity_type_id(sample_type_def_uuid: str = None, sample_uuid: str = None, entity_type_def_uuid: str = None, entity_uuid: str = None) str

Return the human-readable fixed-id for the Entity Type.

Parameters:
  • sample_type_def_uuid -- Alias for entity_type_def_uuid. Deprecated.

  • sample_uuid -- Alias for entity_uuid. Deprecated.

  • entity_type_def_uuid -- UUID of the Entity Type for which to retrieve the ID. Optional. Defaults to the definition UUID for the provided Entity corresponding to the provided sample_uuid.

  • entity_uuid -- UUID of the Entity for which to retrieve the Entity Type ID. Only used if sample_type_def_uuid is not provided. In LIMS context, defaults to the sample_uuid of the row being evaluated.

Returns:

The human-readable fixed ID of the Entity Type for the specified definition UUID or Entity UUID.

Examples:

{{ entity_type_id() }} --> 'Illumina Library' # in LIMS context.
{{ entity_type_id(entity_uuid=sample['uuid']) }} --> 'Illumina Library' # in LIMS context.
{{ entity_type_id(entity_type_def_uuid=sample['def_uuid']) }} --> 'Illumina Library' # in LIMS context
Note:

Introduced in 2.4.1.

Deprecated Aliases: sample_type_id

entity_type_name(sample_type_def_uuid=None, sample_uuid=None, entity_type_def_uuid: str = None, entity_uuid: str = None) str

Get the Entity Type name of the Entity

Parameters:
  • sample_type_def_uuid -- Alias for entity_type_def_uuid. Deprecated.

  • sample_uuid -- Alias for entity_uuid. Deprecated.

  • entity_type_def_uuid -- UUID of the Entity Type for which to retrieve the name. Optional. Defaults to the definition UUID for the provided Entity corresponding to the provided sample_uuid. Alias: sample_type_def_uuid.

  • entity_uuid -- UUID of the Entity for which to retrieve the Entity Type name. Only used if sample_type_def_uuid is not provided. In LIMS context, defaults to the UUID of the entity for the row being evaluated. In particular, the context is searched in order for: entity, entity_uuid, sample, sample_uuid. If entity or sample are found in the context, they are assumed to be a dictionary with a key uuid.

Returns:

The name of the Entity Type for the specified definition UUID or Entity UUID.

Examples:

{{ entity_type_name() }} --> "Illumina Library" # in LIMS context.
{{ entity_type_name(entity_uuid=sample['uuid']) }} --> "Illumina Library" # in LIMS context.
{{ entity_type_name(entity_type_def_uuid=sample['def_uuid']) }} --> "Illumina Library" # in LIMS context

Note

In LIMS context, it is generally more efficient to call entity_type_name() for the current row than to call entity_value('entity_type_name'), though both work. In other contexts, it depends on what other values are required, and the most efficient call may be to sample_generation to fetch the full sample dictionaries.

Deprecated Aliases: sample_type_name

entity_types(tags: list = None, namesonly: bool = True) Union[list[str], list[dict[str, Any]]]

Return a list of Entity Types in the system

Parameters:
  • tags -- (List[str]) List of tags to match

  • namesonly -- (bool) If True (default), only Entity Type names are returned. Otherwise, a list of full Entity Type dict is returned.

Returns:

A list of Entity Types in the system. If tags is supplied, the returned types must match at least one of the tags.

Examples:

entity_types() --> ['Generic sample', 'Illumina Library', ...]
entity_types(namesonly=False) --> [{'name': 'Generic sample', 'uuid': ...}, ...]
entity_types(['orderable']) --> ['Sanger Sequencing Sample', ...] # only returns **Entity Types** tagged with the "orderable" tag.

Deprecated Aliases: sample_types

entity_value(varname: str, sample_uuid: Union[list[str], str] = None, generation: Union[str, int] = 0, index: int = -1, entity_uuids: Union[list[str], str] = None) Union[list[str], str]

Return the value of an Entity field

Parameters:
  • varname -- The variable to return. See note for details.

  • sample_uuid -- alias for uuids for backwards compatibility. This argument is deprecated and will be removed in a future release. When it is removed, entity_uuids will become the second positional parameter. Callers should reference entity_uuids by keyword for future-proofing implementations. (i.e. entity_value('name', entity_uuids="..."))

  • generation -- Any value that can be passed to sample_generation can be used here.

  • index -- If multiple samples are present at the specified generation, which of the samples (by index) to use. When the generations is fetched, the are ordered by creation date, so 0 would be the earliest-created sample and -1 would be the most recently created sample at the given generation.

  • entity_uuids -- The UUID(s) of the generation=0 entity. If unspecified, the expression context is searched in order for entity['uuid'], entity_uuid, sample['uuid'], sample_uuid. If these are not found, an error is raised. If entity_uuids is a list of UUID strings, a list of values will be returned in the same length and order as the input.

Returns:

A value or list of values, depending on the shape of entity_uuids.

Note

The varname can be any custom field fixed ID. It can also be one of:

  • name - Entity name

  • desc - Entity description

  • url - (API) URL for the Entity

  • meta - meta dict for the Entity

  • uuid - Entity uuid

  • cls - Resource Class

  • deleted - true if the Entity is archived, false otherwise

  • created_at - created timestamp

  • updated_at - last modified timestamp

  • owner - name of the current Entity owner (normally the Entity creator)

  • entity_type_uuid - UUID of the Entity's Entity Type

  • sample_type_uuid - alias of entity_type_uuid for backwards compatibility

  • entity_type - UUID of the Entity's Entity Type (alias of entity_type_uuid for backwards compatibility)

  • sample_type - alias of entity_type for backwards compatibility

  • entity_type_name - Name of the Entity's Entity Type

  • sample_type_name - alias of entity_type_name for backwards compatibility

  • in_workflow_instance - whether this Entity has ever been in a workflow

  • barcode - the Entity's barcode value.

Examples:

# barcode of current sample. Assumes LIMS context.
{{ entity_value('barcode') }}
--> "sample barcode"

# Return value of "I7 Index" custom field for this sample (LIMS context)
{{ entity_value('I7 Index') }}
--> "ACGTACAT"

# fetch the value of the I7 Index field for the nearest Illumina Library ancestor. LIMS context.
{{ entity_value('I7 Index', generation='closestup:Illumina Library') }}
--> "ACGTACAT"

# fetch the value of the I7 Index field for the nearest Illumina Library ancestor for the provided sample uuids.
entity_value('I7 Index', entity_uuids=['uuid1', 'uuid2', ...], generation='closestup:Illumina Library')
--> [{"generation": 1, name: "SAM000009", "uuid": <child uuid>, ...}, ...]

Deprecated Aliases: sample_value

hid_for_uuid(uuid: str, sequence: str = 'lab7_sample_auto_id_seq', fmt: str = '%d', name: str = None, format_style: str = 'old') str

Generate a human ID for a UUID/Sequence combination. If one already exists, return it.

Parameters:
  • uuid -- UUID for which to generate a unique sequence value

  • sequence_name -- Name of DB Sequence to use for generating sequences

  • fmt -- A %-style Python format string used for formatting the returned value.

  • name -- Provide a namespace for this hid. This allows for several distinct hid's to be generated for the same provided UUID.

  • format_style -- "old" (default) or "format". For old-style formatting, % formatting is used. For "format", it uses string.format. When using format strings, the next value is available via positional specifiers or via the value variable.

Returns:

A unique sequence. Multiple calls to hid_for_uuid for the same uuid + sequence_name + name combination will return the same value.

Examples:

 {{ str(hid_for_uuid(sample_sheet_uuid, 'lab7_sample_auto_id_seq'))
     + '_' + str(hid_for_uuid(sample_sheet_uuid, 'lab7_sample_auto_id_seq') }}
     --> "1234567_1234567"

 {{ str(hid_for_uuid(sample_sheet_uuid, 'lab7_sample_auto_id_seq', 'name1'))
     + '_'
     + str(hid_for_uuid(sample_sheet_uuid, 'lab7_sample_auto_id_seq', 'name1')
     + str(hid_for_uuid(sample_sheet_uuid, 'lab7_sample_auto_id_seq', 'name2') }}
   --> "1234568_1234568_1234569"

{{ str(hid_for_uuid(
    sample_sheet_uuid,
    'lab7_sample_auto_idd_seq',
    fmt='ABC%dEFG')) }}
"ABC1234567EFG"

Note

The difference between hid_for_uuid and sequence_value is the latter will return a new value for each new invocation, whereas hid_for_uuid only returns a new value if the resource identified by the UUID does not have one yet. This can be used to do things like generate a unique run sheet number, for instance.

list_entities(sample_type: str, filters: Optional[dict[str, Any]] = None, displayField: str = 'name') list

Returns a list of Entity names for a particular type of Entity

Parameters:
  • sample_type -- Name of the Entity Type for which to list Entities

  • filters -- Additional filters to apply to the list. Valid filters: any filters accepted by /api/entities queries. Additionally, any custom field ID is valid.

  • displayField -- Entity field name to use for display. Maybe be one of name, desc, barcode, uuid, or a custom field ID.

Returns:

List of Entity names matching the Entity Type.

Examples:

{{ list_entities('Generic sample') }} --> ['ESP000001', ...]
{{ list_entities('Instrument', filters={'instrument_type': 'Centrifuge'}, displayField='My Field') --> ['Centrifuge 1', ...]

Note

The complete list of valid filter keys supported by list_entities is:

  • entity_type (alias: sample_type) - exact match to Entity Type name

  • entity_type.like (alias: sample_type.like) - case-insensitive wildcard match to Entity Type name

  • entity_types (alias: sample_types) - list of Entity Type names (exact match)

  • entity_type_uuids (alias: sample_type_uuids) - list of Entity Type UUIDs

  • entity_class (alias: workflowable_resource_class) - exact match to Entity Class name

  • entity_class.like (alias: workflowable_resource_class.like) - case-insensitive wildcard match to Entity Class name

  • entity_class_uuid (alias: workflowable_resource_class_uuid) - Entity Class UUID

  • uuid - Entity UUID

  • uuids - list of Entity UUIDs

  • name - exact match to Entity name

  • names - exact match to list of Entity names

  • name.like - case-insensitive wildcard match to Entity name

  • desc - exact match to Entity description

  • desc.like - case-insensitive wildcard match to Entity description

  • barcode - exact match to Entity barcode

  • barcode.like - case-insensitive wildcard match to Entity barcode

  • barcodes - exact match to list of Entity barcodes

  • cls - match Entity cls attribute

  • clses - match list of Entity cls attribute

  • created_before - match before this date, up to the date.

  • created_after - match after this date, down to the date

  • owner - exact match to name of Entity creator

  • owner.like - case-insensitive wildcard match to name of Entity creator

  • tags - case-insensitive exact-match to any of tags in the list of tags

  • alltags - case-insensitive exact-match to all tags in the list of tags

  • username - Same as owner

  • username.like - same as owner.like

Deprecated Aliases: list_samples

sequence_value(sequence: str = 'lab7_sample_auto_id_seq', fmt='%d', format_style: str = 'old') str

Generate a formatted string from the next value in a user-defined Sequence.

The default result is simply str(next_value()).

Parameters:
  • sequence_name -- The name of the (DB) Sequence from which to generate values

  • fmt -- A Python format-string specifier.

  • format_style -- "old" (default) or "format". For old-style formatting, % formatting is used. For "format", it uses string.format. When using format strings, the next value is available via positional specifiers or via the value variable (see examples).

Returns:

The next value in the named Sequence of values.

Examples:

{{ sequence_value('lab7_sample_auto_id_seq') }} --> '25'
{{ sequence_value('lab7_sample_auto_id_seq, 'ABC_%d') }} --> 'ABC_26'
{{ sequence_value('lab7_sample_auto_id_seq, 'ABC_{}') }} --> 'ABC_27'
{{ sequence_value('lab7_sample_auto_id_seq, 'ABC_{value}') }} --> 'ABC_28'

Note

By default, the max value of a sequence as returned by L7|ESP is 2^63-1, or about 9.2 quintillion. (9.2*10^18).

6.2.9. User API

The User API supports operations related to L7|ESP Users. This API currently adds a single function to the expression language.

current_user(include_email: bool = True) str

Returns the name of the current User

Parameters:

include_email -- (bool) whether to include the current User's e-mail in the return value (default: True) returned.

Returns:

The current User's name as a string, either "user['name']" or "user['name'] (user['email'])" depending on the value of include_email.

Examples:

{{ current_user() }} --> "system admin (admin@localhost)"
{{ current_user(false) }} --> "system admin"
user_names(include_email: bool = True) list

Return a list of the User names in the system.

Parameters:

include_email -- whether to include email in the output (default: True)

Returns:

A list of all users in the system, including their e-mails.

Examples:

{{ user_names() }} --> ['system admin (admin@localhost)']
{{ user_names(False) }} --> ['system admin']
workgroup_user_names(workgroup: str = None) list

Return a list of the User names in the system tied to a particular Workgroup.

Parameters:

workgroup -- (String) Name of the Workgroup to filter on. Optional. If unspecified, all Users will be returned.

Returns:

A list of Users that belong to the specified Workgroup.

Examples:

{{ workgroup_user_names() }} --> ['User 1', 'User 2', ...] # Equivalent to `user_names()`
{{ workgroup_user_names('QA') }} --> ['QA User 1', 'QA User 2'] # Only Users belonging to the "QA" Workgroup.

6.2.10. Location API

The Location API supports operations related to L7|ESP containers/locations.

formatted_location(value: Union[str, dict[str, Any]]) str

Expression call that returns a "pretty" LIMS Location cell value (container name: <slot>[,...])

Parameters:

value -- The Location value to format, as a Python object or a JSON string.

Returns:

A string showing the Container and slot names that contain the row under consideration.

Examples:

{{ formatted_location(cell('Location', 'Matrix 2D Details')) }} --> 'Plate X: A01'

Note

The rendered value looks similar or identical to that used by the native Location renderer column, but can be used in circumstances that the Location may be exported to external software to avoid exposing the internal JSON structure of how Locations are stored.

location_name(value: Union[str, dict[str, Any]], container: int = 0) str

Expression call that returns the name of a Location

Parameters:
  • value -- The Location value to format, as a Python object or a JSON string

  • container -- The Container index to use. The underlying Container information format allows for associating multiple Containers with a single value, so Container is the index of the stored Container. Normally, this will be 0. If the value is -1, all associated Container names will be returned in a comma-separated list.

Returns:

The name of the Container for the Location

Examples:

{{ location_name(cell('Location', 'Matrix 2D Details')) }} --> 'Plate X'
location_path(location: Union[str, dict[str, Any]], levels: Union[str, list[int]] = '*') str

Return the Container path given a particular location field value.

Parameters:
  • location -- A Location cell value

  • levels -- One of "*" (all levels) or a list of levels e.g. [-1], [0, -1], where "0" is the immediate location of the sample.

Returns:

The Container path given a particular location field value.

Note

Container path will be in the format container:slot -> container:slot -> ...

Examples:

# Examples assume a sample in slot A-A of Cryobox 1, which is in Rack position A1 of Rack 3,
# which is in shelf 2 of freezer 4, and given a **Location** column named ``Location``
# which has the ``Cryobox 1 (A-A)`` location:

{{ container_path(cell('Location')) }} --> 'freezer f:shelf 2 -> Rack 3:A1 -> Cryo Box 1:A-A'
{{ container_path(cell('Location'), [0, -1]) }} --> 'Rack 3:A1 -> Cryo Box 1:A-A'
{{ container_path(cell('Location'), [-2]) }} --> 'freezer f:shelf 2'

Deprecated Aliases: container_path

location_slot(value: Union[str, dict[str, Any]], container: int = 0, slot: int = 0) str

Expression call that returns the "slot" for a Location

Parameters:
  • value -- The Location value to format, as a Python object or a JSON string

  • container -- The container index to use. If the value is -1, the requested slot from all associated containers are returned as a comma-separated list. This will normally be 0.

  • slot -- The slot index to use. If the value if -1, all slots from the requested container(s) are returned as a comma-separated list. This will be 0 for common use-cases, such as a 96-well plate.

Examples:

{{ location_slot(cell('Location', 'Matrix 2D Details')) }} --> 'A01'

6.2.11. Inventory API

The inventory API supports operations related to inventory management, ordering, customers, etc.

customers(self) list

Get the list of known Customers.

Returns:

A list of all Customer names in the system. If one of the current User's Workgroup names matches a Customer name, that name will be first in the list. Otherwise, the names are lexicographically ordered.

Note

This call will attempt to order the list so that the first Customer corresponds to a Workgroup the current User is in.

Deprecated Aliases: customer_list

item_status_options(self) list

Return valid inventory item status options for this ESP Configuration.

Returns:

The list of valid options, as read from the "statuses" key of the "inventory" configuration object. If the configuration or key is not found, returns the default status list: ['Quarantined', 'Recalled', 'Missing', 'Pending Verification', 'Verified']

item_type_vendors(item_type: Union[str, Any] = None, names_only: bool = True) Union[list[str], list[dict[str, Any]]]

Get list of valid vendors for an item type

Parameters:
  • item_type -- The item type to get the vendors for. If not supplied, the context is examined for sample_type_uuid in the sample context object. May be an item type UUID, name, or ItemType.

  • names_only -- Whether to return only names or a full vendor object.

Returns:

A list of valid vendor names (names_only=True) or vendor dictionaries (names_only=False) for the specified item type.

service_types(names_only: bool = True) list

Get the list of Service Types

Parameters:

names_only -- If true, only Service Type names are returned. If false, additional Service Type data is returned.

Returns:

A list of all Service Types in the system, either a list of names (names_only=True), or a list of JSON dictionaries with additional information, such as the base price, the price type, the scale price, the scale units, and any customer-specific pricing.

6.2.12. Project API

The project API supports operations related to projects.

project_list(tags: list = None, namesonly: bool = True) Union[list[str], list[dict]]

Fetch a list of projects

Parameters:
  • tags -- List of tags for filtering the project list

  • namesonly -- Whether to return only names or the project list.

Returns:

A list of projects or project names.

Examples:

# name of every project in the system.
project_list() --> ['Project 1', 'Project 2', ...]

# only projects tagged with study1
project_list(['study1']) --> ['Project 739', 'Project 740']

# full object dictionaries.
project_list(namesonly=False) --> [{'name': 'Project 1', ...}, ...]

6.3. Expression Language Restrictions

For security purposes, the L7|ESP Expression Language only supports a subset of Python language features and standard library calls.

The following is a list of Python language features that are not allowed in L7|ESP Expression:

  • Any multi-statement command or block construct (e.g., if, for, while, try, etc). If the user cannot form a task on one line in Python, it will not be accepted.

  • Generator expressions

  • Class (class) and function definitions (def)

  • Decorators

  • return statements

  • Assignment (=)

  • Explicit Assertions and exceptions

  • Code execution, including backquote statements and exec calls

  • Module manipulation, including from and import

  • Keywords typically used in block scoping such as global and with

The following is a list of standard library calls and system-level objects that are not allowed in an L7|ESP Expression:

  • __import__

  • reload

  • compile

  • eval

  • execfile

  • globals

  • locals

  • vars

  • callable

  • isinstance

  • issubclass

  • super

  • type

  • id

  • dir

  • delattr

  • setattr

  • getattr

  • hasattr

  • classmethod

  • property

  • staticmethod

  • KeyboardInterrupt

  • SystemExit

  • buffer

  • memoryview

  • file

  • open

  • input

  • raw_input

  • apply

  • intern

  • print