changeset 0:59c8bad5f6bc draft default tip

planemo upload for repository https://github.com/workflow4metabolomics/tools-metabolomics/blob/master/tools/kmd_hmdb_data_plot/ commit 7fa454b6a4268b89fe18043e8dd10f30a7b4c7ca
author workflow4metabolomics
date Tue, 29 Aug 2023 09:45:16 +0000
parents
children
files kmd_hmdb_data_plot.xml kmd_hmdb_interrogator.py kmd_hmdb_plot_generator.py macro.xml test-data/get_data_tol_0.01.tsv
diffstat 5 files changed, 863 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kmd_hmdb_data_plot.xml	Tue Aug 29 09:45:16 2023 +0000
@@ -0,0 +1,176 @@
+<tool id="kmd_hmdb_data_plot" name="KMD HMDB Data Plot" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="21.09">
+    <description>
+        retrieves data from KMD HMDB API and produce plot and tsv file
+    </description>
+    <macros>
+        <import>macro.xml</import>
+        <token name="@TOOL_VERSION@">1.0.0</token>
+        <token name="@VERSION_SUFFIX@">0</token>
+    </macros>
+    <edam_topics>
+        <edam_topic>topic_0091</edam_topic>
+    </edam_topics>
+    <edam_operations>
+        <edam_operation>operation_3803</edam_operation>
+    </edam_operations>
+    <requirements>
+        <requirement type="package" version="3.10">python</requirement>
+        <requirement type="package" version="2.0.3">pandas</requirement>
+        <requirement type="package" version="5.15.0">plotly</requirement>
+        <requirement type="package" version="1.0.1">kmd_hmdb_api_client</requirement>
+    </requirements>
+    <command detect_errors="exit_code">
+<![CDATA[
+#if "get_data" in str($what.to_do)
+    python3 '$__tool_directory__/kmd_hmdb_interrogator.py' compound
+        --mz-ratio '$what.mz_ratio'
+        --database '$what.database'
+        --mass-tolerance '$what.mass_tolerance'
+        #for adduct in $what.adducts
+            --adducts '$adduct'
+        #end for
+        --output-path '$output_path'
+#end if
+
+#if $what.to_do == "get_data_and_produce_plot"
+    &&
+#end if
+
+#if "produce_plot" in str($what.to_do)
+    python3 '$__tool_directory__/kmd_hmdb_plot_generator.py' plot
+
+    #if "get_data" in str($what.to_do)
+        --input '$output_path'
+    #else
+        --input '$what.tsv_input'
+    #end if
+
+    #if $what.x_columns
+        #for x in $what.x_columns
+          --x-column '$x'
+        #end for
+    #end if
+
+    #if $what.y_columns
+        #for y in $what.y_columns
+          --y-column '$y'
+        #end for
+    #end if
+
+    #if $what.annotation_columns
+        #for annotation_column in $what.annotation_columns
+            --annotation-column '$annotation_column'
+        #end for
+    #end if
+
+      --output 'out.html'
+
+    && cat 'out.html' > '$output'
+#end if
+]]>
+    </command>
+
+    <inputs>
+        <conditional name="what">
+            <param name="to_do" type="select"
+                label="What to do"
+            >
+                <option value="get_data">Only get data</option>
+                <option value="produce_plot">Only produce plot</option>
+                <option value="get_data_and_produce_plot" selected="true">
+                    Get data from database + Produce Plot
+                </option>
+            </param>
+            <when value="produce_plot">
+                <expand macro="produce_plot_inputs" />
+                <expand macro="not_get_data" />
+            </when>
+            <when value="get_data">
+                <expand macro="get_data_inputs" />
+                <expand macro="not_produce_plot" />
+            </when>
+            <when value="get_data_and_produce_plot">
+                <expand macro="get_data_inputs" />
+                <expand macro="not_produce_plot" />
+            </when>
+        </conditional>
+    </inputs>
+
+    <outputs>
+        <expand macro="get_data_outputs" />
+        <expand macro="produce_plot_outputs" />
+    </outputs>
+
+    <tests>
+        <test>
+            <!-- #1 get_data with tolerance = 0.01 -->
+            <param name="to_do" value="get_data" />
+            <param name="mass_tolerance" value="0.01" />
+            <param name="mz_ratio" value="303.05" />
+            <param name="database" value="hmdb" />
+            <param name="adducts" value="M+H" />
+            <output name="output_path" file="get_data_tol_0.01.tsv" />
+        </test>
+    </tests>
+
+    <help><![CDATA[
+
+This tool includes two utilities.
+One that retrieves data from the KMD HMDB API formated as a tsv file,
+and the other plots those data in a plotly graph.
+
+The default behavior of the plot is to produce a plot of the kmd in
+in function of the nominal mass of the compound.
+
+Compounds are retrieved using a query with default parameters:
+ - mz default to @DEFAULT_MZ@
+ - mz tolerance defaults to @DEFAULT_TOLERENCE@
+ - adduct list defaults to "@DEFAULT_ADDUCT@"
+ - database default to "@DEFAULT_DATABASE@", and possible values are one of:
+
+   - KMD Metabolites
+   - HMDB
+
+Those two utilities are usable independently, or sequentially in galaxy.
+
+Multiple X / Y values in the X/Y column selection can be
+selected to produce a lot of graphs at once.
+
+For example, imagine you have 5 columns in you tsv file,
+and those columns are named: A, B, C, D and F.
+
+If you choose X = [A, C] and Y = [B],
+then you will get two graphs (in one single HTML file) with:
+
+ - f(A) = B
+ - f(C) = B
+
+But if you choose multiple values for both X and Y, you get
+all combinations of columns X and Y. For example, if you
+select X = [A, B] and Y = [C, D, E], then you will get six
+graphs on the same plot:
+
+ - f(A) = C
+ - f(A) = D
+ - f(A) = E
+ - f(B) = C
+ - f(B) = D
+ - f(B) = E
+
+All those graph's traces will be tooglable in the HTML page.
+So don't hesitate to select a lot of parameters for X and Y!
+
+]]>
+    </help>
+    <citations></citations>
+    <creator>
+        <person
+            honorificPrefix="Mx."
+            givenName="Lain"
+            familyName="Pavot"
+            email="lain.pavot@inrae.fr"
+            identifier="https://orcid.org/0009-0007-1841-4358"
+        />
+    </creator>
+
+</tool>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kmd_hmdb_interrogator.py	Tue Aug 29 09:45:16 2023 +0000
@@ -0,0 +1,328 @@
+#!/usr/bin/env python3
+
+import csv
+import operator
+
+import click
+
+import kmd_hmdb_api_client.client
+from kmd_hmdb_api_client.api.default import (
+    api_annotation_get,
+    api_compound_find,
+    api_taxonomy_get,
+)
+
+__version__ = "1.0.0"
+
+
+kmd_hmdb_client = kmd_hmdb_api_client.client.Client(
+    "https://kmd-hmdb-rest-api.metabolomics-chopin.e-metabohub.fr",
+    verify_ssl=False,
+    timeout=500,
+)
+
+find_compound = (
+    lambda *args, **kwargs:
+        api_compound_find.sync(*args, **kwargs, client=kmd_hmdb_client)
+)
+get_taxonomy = (
+    lambda *args, **kwargs:
+        api_taxonomy_get.sync(*args, **kwargs, client=kmd_hmdb_client)
+)
+get_annotation = (
+    lambda *args, **kwargs:
+        api_annotation_get.sync(*args, **kwargs, client=kmd_hmdb_client)
+)
+
+positive_adducts = [
+    "M+H",
+    "M+2H",
+    "M+H+NH4",
+    "M+H+Na",
+    "M+H+K",
+    "M+ACN+2H",
+    "M+2Na",
+    "M+H-2H2O",
+    "M+H-H2O",
+    "M+NH4",
+    "M+Na",
+    "M+CH3OH+H",
+    "M+K",
+    "M+ACN+H",
+    "M+2Na-H",
+    "M+IsoProp+H",
+    "M+ACN+Na",
+    "M+2K+H",
+    "M+DMSO+H",
+    "M+2ACN+H",
+    "2M+H",
+    "2M+NH4",
+    "2M+Na",
+    "2M+K",
+]
+
+negative_adducts = [
+    "M-H",
+    "M-2H",
+    "M-H2O-H",
+    "M+Cl",
+    "M+FA-H",
+    "M+Hac-H",
+    "M-H+HCOONa",
+    "M+Br",
+    "M+TFA-H",
+    "2M-H",
+    "2M+FA-H",
+    "2M+Hac-H",
+]
+
+adduct_choices = positive_adducts + negative_adducts
+
+taxonomy_column_choices = [
+    "class",
+    "kingdom",
+    "molecular_framework",
+    "sub_class",
+    "super_class",
+    "id",
+]
+
+annotation_column_choices = [
+    "adduct",
+    "kendricks_mass",
+    "kendricks_mass_defect",
+    "monisotopic_molecular_weight",
+    "nominal_mass",
+    "polarity",
+    "annotation_id",
+]
+
+compound_column_choices = [
+
+    "database",
+    "metabolite_name",
+    "chemical_formula",
+    "hmdb_id",
+    "inchikey",
+    "compound_id",
+] + annotation_column_choices
+
+
+@click.group()
+def cli():
+    pass
+
+
+@cli.command(help="")
+@click.option(
+    "--version",
+    is_flag=True,
+)
+@click.option(
+    "--mz-ratio",
+    default=[303.05],
+    show_default=True,
+    multiple=True,
+    help="Provide the mz-ratio."
+)
+@click.option(
+    "--database",
+    default=["farid"],
+    show_default=True,
+    multiple=True,
+    help="Provide the database."
+)
+@click.option(
+    "--mass-tolerance",
+    default=10.5,
+    show_default=True,
+    help="Provide the mass-tolerance."
+)
+@click.option(
+    "--adducts",
+    default=["M+H"],
+    type=click.Choice(adduct_choices),
+    multiple=True,
+    show_default=True,
+    show_choices=False,
+    help="Provide the adducts."
+)
+@click.option(
+    "--columns",
+    default=compound_column_choices[:],
+    type=click.Choice(compound_column_choices),
+    multiple=True,
+    show_default=True,
+    show_choices=False,
+    help="Provide the outputed columns."
+)
+@click.option(
+    "--output-path",
+    help="Provide the output path."
+)
+def compound(*args, **kwargs):
+
+    if kwargs.pop("version"):
+        print(__version__)
+        exit(0)
+
+    adducts = kwargs.pop("adducts")
+    polarity = get_polarity(adducts)
+
+    other_kwargs, compound_kwargs = build_kwargs(
+        adducts=adducts,
+        polarity=polarity,
+        **kwargs
+    )
+    columns = other_kwargs["columns"]
+    result = find_compound(**compound_kwargs)
+    result = explode_compounds(
+        result,
+        with_annotations=any(map(
+          columns.__contains__,
+          annotation_column_choices
+        ))
+    )
+    check_columns_in_result(result, columns)
+    output_csv_result(
+        result,
+        columns,
+        other_kwargs.get("output_path"),
+        delimiter="\t",
+    )
+
+
+def explode_compounds(result, with_annotations):
+    if with_annotations:
+        return [{
+            "database": cpd.database,
+            "metabolite_name": cpd.metabolite_name,
+            "chemical_formula": cpd.chemical_formula,
+            "hmdb_id": cpd.hmdb_id,
+            "inchikey": cpd.inchikey,
+            "compound_id": cpd.id,
+            "adduct": annotation.name,
+            "kendricks_mass": annotation.kendricks_mass,
+            "kendricks_mass_defect": annotation.kendricks_mass_defect,
+            "monisotopic_molecular_weight":
+                annotation.monisotopic_molecular_weight,
+            "nominal_mass": annotation.nominal_mass,
+            "polarity": annotation.polarity,
+            "annotation_id": annotation.id,
+            }
+            for cpd in result
+            for annotation in cpd.annotations
+        ]
+    else:
+        return [{
+            "database": cpd.database,
+            "metabolite_name": cpd.metabolite_name,
+            "chemical_formula": cpd.chemical_formula,
+            "hmdb_id": cpd.hmdb_id,
+            "inchikey": cpd.inchikey,
+            "compound_id": cpd.id,
+            }
+            for cpd in result
+        ]
+
+
+@cli.command(help="")
+@click.option(
+    "--id",
+    type=int,
+    help="Provide the wanted annotation's id."
+)
+@click.option(
+    "--columns",
+    default=annotation_column_choices[:],
+    type=click.Choice(annotation_column_choices),
+    multiple=True,
+    show_default=True,
+    show_choices=False,
+    help="Provide the outputed columns."
+)
+@click.option(
+    "--output-path",
+    help="Provide the output path."
+)
+def annotation(*args, **kwargs):
+    result = get_annotation(id=kwargs.pop("id"))
+    result = [result]
+    columns = kwargs["columns"]
+    check_columns_in_result(result, columns)
+    output_csv_result(
+        result,
+        columns,
+        kwargs.get("output_path")
+    )
+
+
+def get_polarity(adducts):
+    if any(map(positive_adducts.__contains__, adducts)):
+        return "positive"
+    if any(map(negative_adducts.__contains__, adducts)):
+        return "negative"
+    # polarity = []
+    # if any(map(positive_adducts.__contains__, adducts)):
+    #     polarity.append("positive")
+    # if any(map(negative_adducts.__contains__, adducts)):
+    #     polarity.append("negative")
+
+
+def build_kwargs(**kwargs):
+    for original, replacement in (
+        ("database", "database_list"),
+        ("polarity", "polarity_list"),
+    ):
+        if original in kwargs:
+            kwargs[replacement] = kwargs.pop(original)
+    other_kwargs = {
+        other_arg: kwargs.pop(other_arg)
+        for other_arg in ("columns", "output_path", "with_annotations")
+        if other_arg in kwargs
+    }
+    return other_kwargs, kwargs
+
+
+def check_columns_in_result(result, columns):
+    if not result:
+        return
+    if not isinstance(result[0], dict):
+        result = [item.to_dict() for item in result]
+    keys = result[0].keys()
+    missing = [
+        column for column in columns
+        if column not in keys
+    ]
+    if missing:
+        if len(missing) == 1:
+            raise ValueError(
+                f"Could not find the column {missing[0]} in the results."
+            )
+        else:
+            raise ValueError(
+                "Could not find any of the columns "
+                + ','.join(missing)
+                + " in the results."
+            )
+
+
+def output_csv_result(result, columns, output_path, **csv_parameters):
+    if not output_path:
+        raise ValueError("Missing output path. Cannot output CSV results.")
+    with open(output_path, mode="w", newline='') as output_file:
+        writer = csv.writer(output_file, **csv_parameters)
+        write_result(result, columns, writer)
+
+
+def write_result(result, columns, writer):
+    getters = list(map(operator.itemgetter, columns))
+    writer.writerow(columns)
+    writer.writerows(
+        (getter(compound) for getter in getters)
+        for compound in result
+    )
+
+
+if __name__ == "__main__":
+    cli()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kmd_hmdb_plot_generator.py	Tue Aug 29 09:45:16 2023 +0000
@@ -0,0 +1,174 @@
+#!/usr/bin/env python3
+
+import csv
+import itertools
+import os
+
+import click
+
+import plotly.express
+import plotly.graph_objects
+
+__version__ = "1.0.0"
+
+
+@click.group()
+def cli():
+    pass
+
+
+@cli.command(help="")
+@click.option(
+    "--version",
+    is_flag=True,
+    default=False,
+)
+@click.option(
+    "--input",
+    default="./test.tsv",
+    help="Provide the mz-ratio."
+)
+@click.option(
+    "--output",
+    default="./test.html",
+    help="Provide the database."
+)
+@click.option(
+    "--x-column",
+    default=["nominal_mass"],
+    multiple=True,
+    help="Provide the column names for the X axis.",
+)
+@click.option(
+    "--y-column",
+    default=["kendricks_mass_defect"],
+    multiple=True,
+    help="Provide the column names for the Y axis.",
+)
+@click.option(
+    "--annotation-column",
+    multiple=True,
+    default=[
+        "metabolite_name",
+        "chemical_formula",
+    ],
+    help="Provide the columns name for the annotation."
+)
+def plot(*args, **kwargs):
+
+    if kwargs.pop("version"):
+        print(__version__)
+        exit(0)
+
+    input_path = kwargs.pop("input")
+    data = read_input(input_path, kwargs)
+    fig = build_fig(*data)
+    build_html_plot(fig, kwargs.get("output"))
+
+
+def read_input(path: str, kwargs: {}):
+    if not os.path.exists(path):
+        raise ValueError(f"The path '{path}' does not exist.")
+    sep = detect_sep(path)
+    with open(path) as csv_file:
+        line_generator = csv.reader(csv_file, delimiter=sep)
+        first_line = next(line_generator)
+        all_lines = list(line_generator)
+        hover_names = (
+            "metabolite_name",
+            "chemical_formula",
+        )
+        annotation_indexes = get_index_of(first_line, hover_names)
+        (
+            x_index,
+            y_index,
+            x_column,
+            y_column,
+        ) = get_indexes_names(
+            first_line,
+            list(kwargs.get("x_column")),
+            list(kwargs.get("y_column")),
+        )
+        x_lists = [[] for i in range(len(x_index))]
+        y_lists = [[] for i in range(len(y_index))]
+        x_column = list(map(first_line.__getitem__, x_index))
+        y_column = list(map(first_line.__getitem__, y_index))
+        trace_names = [
+            f"f({x_column[i]}) = {y_column[i]}"
+            for i in range(len(x_index))
+        ]
+        hover_names = kwargs["annotation_column"]
+        annotation_indexes = [
+            get_index_of(first_line, column)[0]
+            for column in hover_names
+        ]
+        hover_names = list(map(first_line.__getitem__, annotation_indexes))
+        annotations = list()
+        for line in all_lines:
+            for i in range(len(x_index)):
+                x_lists[i].append(float(line[x_index[i]]))
+                y_lists[i].append(float(line[y_index[i]]))
+            annotations.append("<br>".join(
+                f"{hover_names[hover_index]}: {line[index]}"
+                for hover_index, index in enumerate(annotation_indexes)
+            ))
+    return x_lists, y_lists, annotations, trace_names
+
+
+def get_indexes_names(first_line, x_column, y_column):
+    x_column, y_column = map(list, zip(*itertools.product(x_column, y_column)))
+    x_index = get_index_of(first_line, x_column)
+    y_index = get_index_of(first_line, y_column)
+    for i in range(len(x_index))[::-1]:
+        if x_index[i] == y_index[i]:
+            del x_index[i], x_column[i], y_index[i], y_column[i],
+    return (
+        x_index,
+        y_index,
+        x_column,
+        y_column,
+    )
+
+
+def get_index_of(first_line, column):
+    if isinstance(column, (tuple, list)):
+        return [get_index_of(first_line, x)[0] for x in list(column)]
+    try:
+        return [int(column) - 1]
+    except ValueError:
+        return [first_line.index(column)]
+
+
+def build_fig(x_lists, y_lists, annotations, trace_names):
+    fig = plotly.express.scatter()
+    for i in range(len(x_lists)):
+        fig.add_trace(
+            plotly.graph_objects.Scatter(
+                name=trace_names[i],
+                x=x_lists[i],
+                y=y_lists[i],
+                hovertext=annotations,
+                mode="markers",
+            )
+        )
+    return fig
+
+
+def detect_sep(tabular_file: str) -> str:
+    with open(tabular_file, "r") as file:
+        first_line = file.readline()
+    if len(first_line.split(',')) > len(first_line.split('\t')):
+        return ','
+    return '\t'
+
+
+def build_html_plot(fig, output: str):
+    return plotly.offline.plot(
+        fig,
+        filename=output,
+        auto_open=False,
+    )
+
+
+if __name__ == "__main__":
+    cli()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/macro.xml	Tue Aug 29 09:45:16 2023 +0000
@@ -0,0 +1,170 @@
+<macros>
+
+  <token name="@DEFAULT_MZ@">303.05</token>
+  <token name="@DEFAULT_TOLERENCE@">10.0</token>
+  <token name="@DEFAULT_ADDUCT@">M+H</token>
+  <token name="@DEFAULT_DATABASE@">HMDB</token>
+
+  <xml name="get_data_inputs">
+    <param argument="--mz-ratio" type="float" min="1" max="1000" value="@DEFAULT_MZ@"
+      help="
+        The database will give us all compounds with m/z = 
+        m/zRatio±MassTolerance and we will plot those
+        compounds.
+        Choose the mass tolerance according to this information.
+      "
+      label="M/z Ratio"
+    />
+
+    <param argument="--mass-tolerance" type="float" value="@DEFAULT_TOLERENCE@"
+      help="
+        The database will give us all compounds with m/z = 
+        m/zRatio±MassTolerance and we will plot those
+        compounds.
+        Choose the mass tolerance according to this information.
+      "
+      label="Mass Tolerance"
+    />
+
+    <param argument="--database" type="select" value="@DEFAULT_DATABASE@"
+      label="Which database to interrogate"
+      help="
+        Those databases are sub-databases of KMD-HMDB Metabolites
+        and contains compounds with their KMD.
+      "
+    >
+      <option selected="true" value="hmdb">HMDB</option>
+      <option value="farid">KMD Metabolites</option>
+    </param>
+
+    <param argument="--adducts" type="select" multiple="true"
+      label="Adducts"
+      help="Which adducts to retrieve"
+    >
+      <option selected="true" value="M+H">M+H</option>
+      <option value="M+2H">M+2H</option>
+      <option value="M+H+NH4">M+H+NH4</option>
+      <option value="M+H+Na">M+H+Na</option>
+      <option value="M+H+K">M+H+K</option>
+      <option value="M+ACN+2H">M+ACN+2H</option>
+      <option value="M+2Na">M+2Na</option>
+      <option value="M+H-2H2O">M+H-2H2O</option>
+      <option value="M+H-H2O">M+H-H2O</option>
+      <option value="M+NH4">M+NH4</option>
+      <option value="M+Na">M+Na</option>
+      <option value="M+CH3OH+H">M+CH3OH+H</option>
+      <option value="M+K">M+K</option>
+      <option value="M+ACN+H">M+ACN+H</option>
+      <option value="M+2Na-H">M+2Na-H</option>
+      <option value="M+IsoProp+H">M+IsoProp+H</option>
+      <option value="M+ACN+Na">M+ACN+Na</option>
+      <option value="M+2K+H">M+2K+H</option>
+      <option value="M+DMSO+H">M+DMSO+H</option>
+      <option value="M+2ACN+H">M+2ACN+H</option>
+      <option value="2M+H">2M+H</option>
+      <option value="2M+NH4">2M+NH4</option>
+      <option value="2M+Na">2M+Na</option>
+      <option value="2M+K">2M+K</option>
+      <option value="M-H">M-H</option>
+      <option value="M-2H">M-2H</option>
+      <option value="M-H2O-H">M-H2O-H</option>
+      <option value="M+Cl">M+Cl</option>
+      <option value="M+FA-H">M+FA-H</option>
+      <option value="M+Hac-H">M+Hac-H</option>
+      <option value="M-H+HCOONa">M-H+HCOONa</option>
+      <option value="M+Br">M+Br</option>
+      <option value="M+TFA-H">M+TFA-H</option>
+      <option value="2M-H">2M-H</option>
+      <option value="2M+FA-H">2M+FA-H</option>
+      <option value="2M+Hac-H">2M+Hac-H</option>
+    </param>
+  </xml>
+
+  <xml name="produce_plot_inputs">
+    <param name="tsv_input" type="data" format="tsv"
+      help="Tabular file to use to produce the plot."
+      label="A Tabular Input File"
+    />
+    <param
+      name="annotation_columns"
+      type="data_column"
+      data_ref="tsv_input"
+      use_header_names="true"
+      multiple="true"
+      optional="true"
+      help="Select columns to show when a point of the graph is hovered"
+      label="Annotation columns"
+    />
+    <param
+      name="x_columns"
+      type="data_column"
+      data_ref="tsv_input"
+      use_header_names="true"
+      optional="true"
+      multiple="true"
+      help="
+        Select one or multiple column to use as X values
+        to generate the graph. See the help section to better
+        understand the usage of multiple values
+      "
+      label="X Axis"
+    />
+    <param
+      name="y_columns"
+      type="data_column"
+      data_ref="tsv_input"
+      use_header_names="true"
+      optional="true"
+      multiple="true"
+      help="
+        Select one or multiple column to use as Y values
+        to generate the graph. See the help section to better
+        understand the usage of multiple values
+      "
+      label="Y Axis"
+    />
+  </xml>
+
+  <xml name="not_get_data">
+    <param name="mz_ratio" value="unknown" type="hidden" />
+    <param name="mass_tolerance" value="unknown" type="hidden" />
+    <param name="database" value="unknown" type="hidden" />
+    <param name="adducts" value="unknown" type="hidden" />
+  </xml>
+
+  <xml name="not_produce_plot">
+    <param name="tsv_input" value="unknown" type="hidden" />
+    <param name="annotation_columns" type="hidden" />
+    <param name="x_columns" type="hidden" />
+    <param name="y_columns" type="hidden" />
+  </xml>
+
+  <xml name="get_data_outputs">
+    <data name="output_path" format="tsv"
+      label="tsv - ${tool.name} on ${what.mz_ratio}±${what.mass_tolerance} - ${what.database}"
+    >
+      <filter>"get_data" in str(what['to_do'])</filter>
+      <actions>
+        <action name="column_names" type="metadata"
+          default="database,metabolite_name,chemical_formula,hmdb_id,inchikey,compound_id,adduct,kendricks_mass,kendricks_mass_defect,monisotopic_molecular_weight,nominal_mass,polarity,annotation_id"
+        />
+      </actions>
+    </data>
+  </xml>
+
+  <xml name="produce_plot_outputs">
+    <data name="output" format="html"
+      label="html - ${tool.name} on ${
+      ' - '
+      + str($what['mz_ratio'])
+      + '±' + str($what['mass_tolerance'])
+      + ' - ' + str($what['database'])
+      if 'get_data' in str($what['to_do'])
+      else ''' ' ''' + $what.csv_input.name + ''' ' '''
+    }"
+    >
+      <filter>"produce_plot" in str(what['to_do'])</filter>
+    </data>
+  </xml>
+
+</macros>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/get_data_tol_0.01.tsv	Tue Aug 29 09:45:16 2023 +0000
@@ -0,0 +1,15 @@
+database	metabolite_name	chemical_formula	hmdb_id	inchikey	compound_id	adduct	kendricks_mass	kendricks_mass_defect	monisotopic_molecular_weight	nominal_mass	polarity	annotation_id
+hmdb	5-(3',5'-Dihydroxyphenyl)-gamma-valerolactone-O-sulphate-O-methyl	C12H14O7S	HMDB0060031	FXGBBWWEXQWRKV-UHFFFAOYSA-N	193796	M+H	302.715	0.28509	303.053	303.0	positive	3982213
+hmdb	Quercetin	C15H10O7	HMDB0005794	REFJWTPEDVJJIY-UHFFFAOYSA-N	40965	M+H	302.712	0.288457	303.05	303.0	positive	4379351
+hmdb	8-Chloroinosine	C10H11ClN4O5	HMDB0247428	ROPMUQKCJYNROP-UHFFFAOYSA-N	130732	M+H	302.711	0.289311	303.049	303.0	positive	4548699
+hmdb	5-((p-Hydroxybenzylidene)amino)-3-methylisothiazolo(5,4-d)pyrimidine-4,6(5H,7H)-dione	C13H10N4O3S	HMDB0253558	ALZDMJPUQGYCAX-UHFFFAOYSA-N	68215	M+H	302.716	0.283753	303.055	303.0	positive	4993233
+hmdb	2',4',5,7,8-Pentahydroxyisoflavone	C15H10O7	HMDB0033264	LOLNVJIGYUJCIY-UHFFFAOYSA-N	101970	M+H	302.712	0.288457	303.05	303.0	positive	5292330
+hmdb	2-(2-Nitroimidazol-1-yl)-N-(2,2,3,3,3-pentafluoropropyl)acetamide	C8H7F5N4O3	HMDB0251710	JGGDSDPOPRWSCX-UHFFFAOYSA-N	9228	M+H	302.713	0.28728	303.051	303.0	positive	7593628
+hmdb	5,6,7,3',4'-Pentahydroxyisoflavone	C15H10O7	HMDB0041687	BIDDAFIPYBBDES-UHFFFAOYSA-N	134953	M+H	302.712	0.288457	303.05	303.0	positive	8100148
+hmdb	Morin	C15H10O7	HMDB0030796	YXOLAZRVSSWPPT-UHFFFAOYSA-N	141800	M+H	302.712	0.288457	303.05	303.0	positive	8184605
+hmdb	Tricetin	C15H10O7	HMDB0029620	ARSRJFRKVXALTF-UHFFFAOYSA-N	181210	M+H	302.712	0.288457	303.05	303.0	positive	8412749
+hmdb	9-(2,6-Dioxo-3H-purin-9-yl)-3H-purine-2,6-dione	C10H6N8O4	HMDB0257773	LLFQXBCTHVBLEI-UHFFFAOYSA-N	108799	M+H	302.72	0.279918	303.058	303.0	positive	8782069
+hmdb	6-Hydroxyluteolin	C15H10O7	HMDB0036632	VYAKIUWQLHRZGK-UHFFFAOYSA-N	74622	M+H	302.712	0.288457	303.05	303.0	positive	9521790
+hmdb	Pollenin A	C15H10O7	HMDB0303704	ZDOTZEDNGNPOEW-UHFFFAOYSA-N	3105	M+H	302.712	0.288457	303.05	303.0	positive	9722226
+hmdb	5,7,8,3',4'-Pentahydroxyisoflavone	C15H10O7	HMDB0041689	USQGZNXXBDCNQF-UHFFFAOYSA-N	15604	M+H	302.712	0.288457	303.05	303.0	positive	9958013
+hmdb	{2-methoxy-4-[(5-oxooxolan-2-yl)methyl]phenyl}oxidanesulfonic acid	C12H14O7S	HMDB0127769	FYRRHCSCZYSADR-UHFFFAOYSA-N	51990	M+H	302.715	0.285089	303.053	303.0	positive	10166087