Skip to content

Metaspace submit #8170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .github/actions/nf-test-action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ runs:
shell: bash
env:
SENTIEON_LICSRVR_IP: ${{ env.SENTIEON_LICSRVR_IP }}
METASPACE_API_KEY: ${{ env.METASPACE_API_KEY }}
SENTIEON_AUTH_MECH: "GitHub Actions - token"
run: |
NFT_WORKDIR=~ \
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/nf-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ jobs:
if: ${{steps.filter.outputs.filtered_paths != '[]'}}
uses: ./.github/actions/nf-test-action
env:
METASPACE_API_KEY: ${{ secrets.METASPACE_API_KEY }}
SENTIEON_ENCRYPTION_KEY: ${{ secrets.SENTIEON_ENCRYPTION_KEY }}
SENTIEON_LICENSE_MESSAGE: ${{ secrets.SENTIEON_LICENSE_MESSAGE }}
SENTIEON_LICSRVR_IP: ${{ secrets.SENTIEON_LICSRVR_IP }}
Expand Down
8 changes: 8 additions & 0 deletions modules/nf-core/metaspace/submit/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
channels:
- conda-forge
- bioconda
dependencies:
- bioconda::metaspace2020=2.0.9
- conda-forge::python=3.11.11
- conda-forge::pip=25.0.1
- conda-forge::pyyaml=6.0.2
43 changes: 43 additions & 0 deletions modules/nf-core/metaspace/submit/main.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
process METASPACE_SUBMIT {
label 'process_low'

conda "${moduleDir}/environment.yml"
container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/c3/c317f9380b8b631acacad83ab362b2badb42e8782f6bfa03e5befe59f2382283/data':
'community.wave.seqera.io/library/python_pip_metaspace-converter:958b8906de66e072' }"

input:
path imzml
path ibd
path config

output:
path("ds_id.txt") , emit: ds_id
path("versions.yml"), emit: versions

when:
task.ext.when == null || task.ext.when

script:
def metaspaceApi = secrets.METASPACE_API_KEY ?
"export API_KEY=\$(mktemp);echo -n \"${secrets.METASPACE_API_KEY}\" > \$API_KEY; " :
""

"""
$metaspaceApi
"""
template 'submit.py'

stub:

"""
touch ds_id.txt

cat <<-END_VERSIONS > versions.yml
"${task.process}":
python: \$(python --version | cut -d ' ' -f 2)
metaspace: \$(python3 -c 'import metaspace; print(metaspace.__version__)')
yaml: \$(python3 -c 'import yaml; print(yaml.__version__)')
END_VERSIONS
"""
}
44 changes: 44 additions & 0 deletions modules/nf-core/metaspace/submit/meta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json
name: "metaspace_submit"
description: Submit imaging mass spectrometry data to METASPACE for metabolite annotation
keywords:
- imaging
- mass_spectrometry
- metabolomics
- annotation
tools:
- "metaspace2020":
description: "Python package providing programmatic access to the METASPACE platform"
homepage: "https://metaspace2020.readthedocs.io"
documentation: "https://metaspace2020.readthedocs.io"
tool_dev_url: "https://github.com/metaspace2020/metaspace/tree/master/metaspace/python-client"
licence: ["Apache-2.0 license"]
identifier: biotools:metaspace

input:
- - imzml:
type: file
description: Path to .imzML file
- - ibd:
type: file
description: Path to .ibd file
- - config:
type: file
description: Path to .yml file containing dataset configuration and metadata
output:
- ds_id:
- "ds_id.txt":
type: file
description: File containing METASPACE dataset ID
pattern: "ds_id.txt"

- versions:
- "versions.yml":
type: file
description: File containing software versions
pattern: "versions.yml"

authors:
- "@bisho2122"
maintainers:
- "@bisho2122"
91 changes: 91 additions & 0 deletions modules/nf-core/metaspace/submit/templates/submit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env python

import yaml
import metaspace
import platform
import os

with open("${config}") as f:
config = yaml.safe_load(f)

api_key_path = os.getenv('API_KEY')

if api_key_path and os.path.isfile(api_key_path):
with open(api_key_path, "r") as f:
API_key = f.read()
else:
API_key = api_key_path

sm = metaspace.SMInstance(api_key=API_key)

ds_name = config['Dataset_name']

Annot_settings = config['Annotation_settings']

metadata = {k: v for k, v in config.items() if k not in ['User_info','Dataset_name','Annotation_settings']}
metadata = {'Data_Type': 'Imaging MS', **metadata}

dbs = [tuple(item) for item in Annot_settings["Metabolite_database"]]

if len(Annot_settings['Adducts']) == 0:
adduct_list = None
else:
adduct_list = Annot_settings['Adducts']

if len(Annot_settings['Chemical_modifications']) == 0:
chem_modif = None
else:
chem_modif = Annot_settings['Chemical_modifications']

scoring_model_id_map = {
'MSM': 2,
'METASPACE_ML_Animal': 3,
'METASPACE_ML_Plant': 4
}

scoring_model_id = scoring_model_id_map[Annot_settings['Analysis_version']]

# Submit dataset

ds_id = sm.submit_dataset(
imzml_fn = "${imzml}",
ibd_fn = "${ibd}",
name = ds_name,
metadata = metadata,
is_public = False,
databases = dbs,
adducts = adduct_list,
chem_mods = chem_modif,
scoring_model = scoring_model_id,
ppm = float(Annot_settings['ppm'])
)

with open("ds_id.txt", 'w') as f:
f.write(ds_id)

# Versions
versions = {"${task.process}": {"python": platform.python_version(),
"metaspace": metaspace.__version__,
"yaml": yaml.__version__}}

def format_yaml_like(data: dict, indent: int = 0) -> str:
"""Formats a dictionary to a YAML-like string.

Args:
data (dict): The dictionary to format.
indent (int): The current indentation level.

Returns:
str: A string formatted as YAML.
"""
yaml_str = ""
for key, value in data.items():
spaces = " " * indent
if isinstance(value, dict):
yaml_str += f"{spaces}{key}:\\n{format_yaml_like(value, indent + 1)}"
else:
yaml_str += f"{spaces}{key}: {value}\\n"
return yaml_str

with open("versions.yml", "w") as f:
f.write(format_yaml_like(versions))
52 changes: 52 additions & 0 deletions modules/nf-core/metaspace/submit/tests/main.nf.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
nextflow_process {

name "Test Process METASPACE_SUBMIT"
script "../main.nf"
process "METASPACE_SUBMIT"
config "./nextflow.config"

tag "modules"
tag "modules_nfcore"
tag "metaspace"
tag "metaspace/submit"

test("metaspace_submit_test") {

when {
process {
"""
input[0] = file(params.test_data_base_path + 'test_metaspace_submit/test.imzML', checkIfExists: true)
input[1] = file(params.test_data_base_path + 'test_metaspace_submit/test.ibd', checkIfExists: true)
input[2] = file(params.test_data_base_path + 'test_metaspace_submit/ds_config.yml')
"""
}
}

then {
assertAll(
{ assert process.success }
)
}
}
test("metaspace_submit - stub") {
tag "versions_test"

options '-stub'
when {
process {
"""
input[0] = file(params.test_data_base_path + 'test_metaspace_submit/test.imzML', checkIfExists: true)
input[1] = file(params.test_data_base_path + 'test_metaspace_submit/test.ibd', checkIfExists: true)
input[2] = file(params.test_data_base_path + 'test_metaspace_submit/ds_config.yml')
"""
}
}
then {
assertAll(
{ assert process.success },
{ assert snapshot(process.out).match() }
)
}
}

}
25 changes: 25 additions & 0 deletions modules/nf-core/metaspace/submit/tests/main.nf.test.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"metaspace_submit - stub": {
"content": [
{
"0": [
"ds_id.txt:md5,d41d8cd98f00b204e9800998ecf8427e"
],
"1": [
"versions.yml:md5,db6244919f766cddc2cd20502db9403d"
],
"ds_id": [
"ds_id.txt:md5,d41d8cd98f00b204e9800998ecf8427e"
],
"versions": [
"versions.yml:md5,db6244919f766cddc2cd20502db9403d"
]
}
],
"meta": {
"nf-test": "0.9.2",
"nextflow": "24.10.5"
},
"timestamp": "2025-03-31T21:55:03.550137605"
}
}
11 changes: 11 additions & 0 deletions modules/nf-core/metaspace/submit/tests/nextflow.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
env {
// NOTE This is how nf-core/metaspace/submit users will use METASPACE submission in real world use
API_KEY = "$METASPACE_API_KEY"

// NOTE This is how nf-core/metaspace/submit users will test out METASPACE submission with a user API key
// nextflow secrets set METASPACE_API_KEY "USER_API_KEY"
Comment on lines +2 to +6
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? Which of these would be the real use case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the confusion. I think secrets has to be defined by the user using nextflow secrets set METASPACE_API_KEY "USER_API_KEY". I was trying to mimic the setup from sentieon modules (https://github.com/nf-core/modules/blob/9fe7867a4f012db581d7bd560228ded1074eb0e5/modules/nf-core/sentieon/dedup/tests/nextflow.config).

So it can be tested locally without the need to use nextflow secrets by just export api key as environment variable. Or it could be set by nextflow secrets. Both should work.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// NOTE This is how nf-core/metaspace/submit users will use METASPACE submission in real world use
API_KEY = "$METASPACE_API_KEY"
// NOTE This is how nf-core/metaspace/submit users will test out METASPACE submission with a user API key
// nextflow secrets set METASPACE_API_KEY "USER_API_KEY"
API_KEY = "$METASPACE_API_KEY"
// NOTE This is how nf-core/metaspace/submit users will set their user API key locally
// nextflow secrets set METASPACE_API_KEY "USER_API_KEY"

@SPPearce Is this more clear ?

}

params {
test_data_base_path = "https://raw.githubusercontent.com/nf-core/test-datasets/spatialmetabo/"
}
Loading