Skip to content

Commit fc6f777

Browse files
Add samplesheetparser/info and samplesheetparser/validate modules (#11604)
* Add samplesheetparser/info and samplesheetparser/validate modules * Address PR review feedback for samplesheetparser modules - Update container engine check to support apptainer - Remove hardcoded --format flag, pass via ext.args instead - Use touch for stub outputs - Use sanitizeOutput in test assertions and regenerate snapshots - Remove redundant test tags - Rename emit to versions_samplesheetparser - Run nf-core modules lint --fix (formatting, licence style, ontologies) * Fix container_links lint failure by removing quay.io prefix * Revert container prefix removal, CI lint requires quay.io/ registry prefix
1 parent ff147c3 commit fc6f777

10 files changed

Lines changed: 545 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
channels:
2+
- conda-forge
3+
- bioconda
4+
- defaults
5+
dependencies:
6+
- bioconda::samplesheet-parser=1.2.0
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
process SAMPLESHEETPARSER_INFO {
2+
tag "$meta.id"
3+
label 'process_single'
4+
5+
conda "${moduleDir}/environment.yml"
6+
container "${ workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container ?
7+
'https://depot.galaxyproject.org/singularity/samplesheet-parser:1.2.0--pyhdfd78af_0' :
8+
'quay.io/biocontainers/samplesheet-parser:1.2.0--pyhdfd78af_0' }"
9+
10+
input:
11+
tuple val(meta), path(samplesheet)
12+
13+
output:
14+
tuple val(meta), path("*.info.json"), emit: json
15+
tuple val("${task.process}"), val('samplesheet-parser'), eval("samplesheet --version | sed 's/samplesheet-parser //'"), topic: versions, emit: versions_samplesheetparser
16+
17+
when:
18+
task.ext.when == null || task.ext.when
19+
20+
script:
21+
def args = task.ext.args ?: ''
22+
def prefix = task.ext.prefix ?: "${meta.id}"
23+
"""
24+
samplesheet info \\
25+
${args} \\
26+
${samplesheet} > ${prefix}.info.json
27+
"""
28+
29+
stub:
30+
def prefix = task.ext.prefix ?: "${meta.id}"
31+
"""
32+
touch ${prefix}.info.json
33+
"""
34+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: samplesheetparser_info
2+
description: |
3+
Display a structured JSON summary of an Illumina SampleSheet.csv (V1 or V2)
4+
including format version, sample count, lanes, index type, read lengths,
5+
adapter sequences, experiment name, and instrument platform.
6+
Useful for logging run metadata or conditional branching in pipelines.
7+
keywords:
8+
- illumina
9+
- samplesheet
10+
- metadata
11+
- bclconvert
12+
- bcl2fastq
13+
- genomics
14+
tools:
15+
- samplesheet-parser:
16+
description: |
17+
Format-agnostic parser for Illumina SampleSheet.csv files.
18+
Supports IEM V1 (bcl2fastq) and BCLConvert V2 with auto-detection,
19+
bidirectional conversion, index validation, and Hamming distance checking.
20+
homepage: https://github.com/chaitanyakasaraneni/samplesheet-parser
21+
documentation: https://illumina-samplesheet.readthedocs.io
22+
tool_dev_url: https://github.com/chaitanyakasaraneni/samplesheet-parser
23+
doi: "10.5281/zenodo.18989694"
24+
licence:
25+
- "Apache-2.0"
26+
identifier: biotools:samplesheet-parser
27+
input:
28+
- - meta:
29+
type: map
30+
description: |
31+
Groovy Map containing sample information.
32+
Use [ id:'run_id' ] to identify the sheet.
33+
- samplesheet:
34+
type: file
35+
description: Illumina SampleSheet.csv (V1 or V2 format, auto-detected)
36+
pattern: "*.{csv,CSV}"
37+
ontologies:
38+
- edam: "http://edamontology.org/format_3752"
39+
output:
40+
json:
41+
- - meta:
42+
type: map
43+
description: Groovy Map containing sample information
44+
- "*.info.json":
45+
type: file
46+
description: |
47+
JSON summary with file, format, sample_count, lanes, index_type,
48+
read_lengths, adapters, experiment_name, and instrument fields.
49+
pattern: "*.info.json"
50+
ontologies:
51+
- edam: http://edamontology.org/format_3464
52+
versions_samplesheetparser:
53+
- - ${task.process}:
54+
type: string
55+
description: The name of the process
56+
- samplesheet-parser:
57+
type: string
58+
description: The name of the tool
59+
- samplesheet --version | sed 's/samplesheet-parser //':
60+
type: eval
61+
description: The expression to obtain the version of the tool
62+
topics:
63+
versions:
64+
- - ${task.process}:
65+
type: string
66+
description: The name of the process
67+
- samplesheet-parser:
68+
type: string
69+
description: The name of the tool
70+
- samplesheet --version | sed 's/samplesheet-parser //':
71+
type: eval
72+
description: The expression to obtain the version of the tool
73+
authors:
74+
- "@chaitanyakasaraneni"
75+
maintainers:
76+
- "@chaitanyakasaraneni"
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
nextflow_process {
2+
3+
name "Test Process SAMPLESHEETPARSER_INFO"
4+
script "../main.nf"
5+
process "SAMPLESHEETPARSER_INFO"
6+
7+
tag "samplesheetparser"
8+
tag "samplesheetparser/info"
9+
tag "modules"
10+
tag "modules_nfcore"
11+
12+
test("V1 sheet - info") {
13+
14+
when {
15+
process {
16+
"""
17+
input[0] = [
18+
[ id: 'test_v1' ],
19+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bcl/flowcell_samplesheet.v1.csv', checkIfExists: true)
20+
]
21+
"""
22+
}
23+
}
24+
25+
then {
26+
assertAll(
27+
{ assert process.success },
28+
{ assert snapshot(sanitizeOutput(process.out)).match() }
29+
)
30+
}
31+
}
32+
33+
test("V2 sheet - info") {
34+
35+
when {
36+
process {
37+
"""
38+
input[0] = [
39+
[ id: 'test_v2' ],
40+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bcl/flowcell_samplesheet.v2.csv', checkIfExists: true)
41+
]
42+
"""
43+
}
44+
}
45+
46+
then {
47+
assertAll(
48+
{ assert process.success },
49+
{ assert snapshot(sanitizeOutput(process.out)).match() }
50+
)
51+
}
52+
}
53+
54+
test("V1 sheet - stub") {
55+
56+
options "-stub"
57+
58+
when {
59+
process {
60+
"""
61+
input[0] = [
62+
[ id: 'test_stub' ],
63+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bcl/flowcell_samplesheet.v1.csv', checkIfExists: true)
64+
]
65+
"""
66+
}
67+
}
68+
69+
then {
70+
assertAll(
71+
{ assert process.success },
72+
{ assert snapshot(sanitizeOutput(process.out)).match() }
73+
)
74+
}
75+
}
76+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"V1 sheet - info": {
3+
"content": [
4+
{
5+
"json": [
6+
[
7+
{
8+
"id": "test_v1"
9+
},
10+
"test_v1.info.json:md5,432ca46ec470fc47c7c5af5d698cbde8"
11+
]
12+
],
13+
"versions_samplesheetparser": [
14+
[
15+
"SAMPLESHEETPARSER_INFO",
16+
"samplesheet-parser",
17+
"1.2.0"
18+
]
19+
]
20+
}
21+
],
22+
"timestamp": "2026-05-13T09:46:13.123399",
23+
"meta": {
24+
"nf-test": "0.9.5",
25+
"nextflow": "25.10.4"
26+
}
27+
},
28+
"V2 sheet - info": {
29+
"content": [
30+
{
31+
"json": [
32+
[
33+
{
34+
"id": "test_v2"
35+
},
36+
"test_v2.info.json:md5,b02a9f7d581f1e14aeeaa8b30a42e8b6"
37+
]
38+
],
39+
"versions_samplesheetparser": [
40+
[
41+
"SAMPLESHEETPARSER_INFO",
42+
"samplesheet-parser",
43+
"1.2.0"
44+
]
45+
]
46+
}
47+
],
48+
"timestamp": "2026-05-13T09:46:25.541946",
49+
"meta": {
50+
"nf-test": "0.9.5",
51+
"nextflow": "25.10.4"
52+
}
53+
},
54+
"V1 sheet - stub": {
55+
"content": [
56+
{
57+
"json": [
58+
[
59+
{
60+
"id": "test_stub"
61+
},
62+
"test_stub.info.json:md5,d41d8cd98f00b204e9800998ecf8427e"
63+
]
64+
],
65+
"versions_samplesheetparser": [
66+
[
67+
"SAMPLESHEETPARSER_INFO",
68+
"samplesheet-parser",
69+
"1.2.0"
70+
]
71+
]
72+
}
73+
],
74+
"timestamp": "2026-05-13T09:46:37.243217",
75+
"meta": {
76+
"nf-test": "0.9.5",
77+
"nextflow": "25.10.4"
78+
}
79+
}
80+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
channels:
2+
- conda-forge
3+
- bioconda
4+
- defaults
5+
dependencies:
6+
- bioconda::samplesheet-parser=1.2.0
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
process SAMPLESHEETPARSER_VALIDATE {
2+
tag "$meta.id"
3+
label 'process_single'
4+
5+
conda "${moduleDir}/environment.yml"
6+
container "${ workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container ?
7+
'https://depot.galaxyproject.org/singularity/samplesheet-parser:1.2.0--pyhdfd78af_0' :
8+
'quay.io/biocontainers/samplesheet-parser:1.2.0--pyhdfd78af_0' }"
9+
10+
input:
11+
tuple val(meta), path(samplesheet)
12+
13+
output:
14+
tuple val(meta), path("*.validation.json"), emit: json
15+
tuple val("${task.process}"), val('samplesheet-parser'), eval("samplesheet --version | sed 's/samplesheet-parser //'"), topic: versions, emit: versions_samplesheetparser
16+
17+
when:
18+
task.ext.when == null || task.ext.when
19+
20+
script:
21+
def args = task.ext.args ?: ''
22+
def prefix = task.ext.prefix ?: "${meta.id}"
23+
"""
24+
samplesheet validate \\
25+
${args} \\
26+
${samplesheet} > ${prefix}.validation.json
27+
"""
28+
29+
stub:
30+
def prefix = task.ext.prefix ?: "${meta.id}"
31+
"""
32+
touch ${prefix}.validation.json
33+
"""
34+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: samplesheetparser_validate
2+
description: |
3+
Validate an Illumina SampleSheet.csv (V1 or V2) for index, adapter, and
4+
structural issues. Format is auto-detected. Exits 0 if valid, 1 if errors
5+
are found — causing the pipeline to fail early with a clear message rather
6+
than discovering demultiplexing problems downstream.
7+
keywords:
8+
- illumina
9+
- samplesheet
10+
- validation
11+
- demultiplexing
12+
- bclconvert
13+
- bcl2fastq
14+
- genomics
15+
tools:
16+
- samplesheet-parser:
17+
description: |
18+
Format-agnostic parser for Illumina SampleSheet.csv files.
19+
Supports IEM V1 (bcl2fastq) and BCLConvert V2 with auto-detection,
20+
bidirectional conversion, index validation, and Hamming distance checking.
21+
homepage: https://github.com/chaitanyakasaraneni/samplesheet-parser
22+
documentation: https://illumina-samplesheet.readthedocs.io
23+
tool_dev_url: https://github.com/chaitanyakasaraneni/samplesheet-parser
24+
doi: "10.5281/zenodo.18989694"
25+
licence:
26+
- "Apache-2.0"
27+
identifier: biotools:samplesheet-parser
28+
input:
29+
- - meta:
30+
type: map
31+
description: |
32+
Groovy Map containing sample information.
33+
Use [ id:'run_id' ] to identify the sheet.
34+
- samplesheet:
35+
type: file
36+
description: Illumina SampleSheet.csv (V1 or V2 format)
37+
pattern: "*.{csv,CSV}"
38+
ontologies:
39+
- edam: "http://edamontology.org/format_3752"
40+
output:
41+
json:
42+
- - meta:
43+
type: map
44+
description: Groovy Map containing sample information
45+
- "*.validation.json":
46+
type: file
47+
description: |
48+
Structured JSON validation report with is_valid, errors, warnings,
49+
and min_hamming_distance fields.
50+
pattern: "*.validation.json"
51+
ontologies:
52+
- edam: http://edamontology.org/format_3464
53+
versions_samplesheetparser:
54+
- - ${task.process}:
55+
type: string
56+
description: The name of the process
57+
- samplesheet-parser:
58+
type: string
59+
description: The name of the tool
60+
- samplesheet --version | sed 's/samplesheet-parser //':
61+
type: eval
62+
description: The expression to obtain the version of the tool
63+
topics:
64+
versions:
65+
- - ${task.process}:
66+
type: string
67+
description: The name of the process
68+
- samplesheet-parser:
69+
type: string
70+
description: The name of the tool
71+
- samplesheet --version | sed 's/samplesheet-parser //':
72+
type: eval
73+
description: The expression to obtain the version of the tool
74+
authors:
75+
- "@chaitanyakasaraneni"
76+
maintainers:
77+
- "@chaitanyakasaraneni"

0 commit comments

Comments
 (0)