diff --git a/modules/msk/annotate_hlahd/environment.yml b/modules/msk/annotate_hlahd/environment.yml new file mode 100644 index 00000000..da913a15 --- /dev/null +++ b/modules/msk/annotate_hlahd/environment.yml @@ -0,0 +1,7 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + - "ANNOTATE_HLAHD=HERE" diff --git a/modules/msk/annotate_hlahd/main.nf b/modules/msk/annotate_hlahd/main.nf new file mode 100644 index 00000000..1b2c2898 --- /dev/null +++ b/modules/msk/annotate_hlahd/main.nf @@ -0,0 +1,50 @@ +process ANNOTATE_HLAHD { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'docker://ghcr.io/mskcc-omics-workflows/hlahd-tools:1.0.0': + 'ghcr.io/mskcc-omics-workflows/hlahd-tools:1.0.0' }" + + input: + tuple val(meta), path(result_dir) + path pgroup_file + + output: + tuple val(meta), path("${prefix}_annotated.tsv"), emit: tsv + tuple val(meta), path("${prefix}_report.html"), emit: report, optional: true + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" + """ + annotate_hlahd \\ + --result_dir ${result_dir} \\ + --sample ${prefix} \\ + --pgroup_file ${pgroup_file} \\ + --outdir . \\ + ${args} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python3 --version | sed 's/Python //') + END_VERSIONS + """ + + stub: + prefix = task.ext.prefix ?: "${meta.id}" + """ + echo -e "locus\\tallele1\\tallele2\\tp_group" > ${prefix}_annotated.tsv + echo "stub report" > ${prefix}_report.html + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: 3.11 + END_VERSIONS + """ +} diff --git a/modules/msk/annotate_hlahd/meta.yml b/modules/msk/annotate_hlahd/meta.yml new file mode 100644 index 00000000..f0a4f2da --- /dev/null +++ b/modules/msk/annotate_hlahd/meta.yml @@ -0,0 +1,64 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json +name: "annotate_hlahd" +description: Annotate HLA-HD class I output with IMGT P-groups and quality flags +keywords: + - hla + - hlahd + - annotation + - immunogenomics +tools: + - "annotate_hlahd": + description: "Post-processes HLA-HD's per-sample final.result.txt/*.est.txt class I output, mapping each allele call to its IMGT P-group and flagging low-confidence calls." + homepage: "https://github.com/mskcc/hla-annotate" + documentation: "https://github.com/mskcc/hla-annotate" + licence: + - "MIT" + identifier: "" +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - result_dir: + type: directory + description: Directory containing HLA-HD's _final.result.txt and _{A,B,C}.est.txt + pattern: "*" + - - pgroup_file: + type: file + description: IMGT wmda/hla_nom_p.txt P-group reference table + pattern: "*.txt" +output: + - tsv: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - ${prefix}_annotated.tsv: + type: file + description: Per-allele annotated TSV with P-group and quality-flag columns + pattern: "*_annotated.tsv" + - report: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - ${prefix}_report.html: + type: file + description: | + Self-contained HTML report for the sample. Optional — pass `--skip_html` + via `task.ext.args` to skip generating it. + pattern: "*_report.html" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + ontologies: + - edam: http://edamontology.org/format_3750 +authors: + - "@johnoooh" +maintainers: + - "@johnoooh" diff --git a/modules/msk/annotate_hlahd/tests/main.nf.test b/modules/msk/annotate_hlahd/tests/main.nf.test new file mode 100644 index 00000000..9ef53ac0 --- /dev/null +++ b/modules/msk/annotate_hlahd/tests/main.nf.test @@ -0,0 +1,115 @@ +nextflow_process { + + name "Test Process ANNOTATE_HLAHD" + script "../main.nf" + process "ANNOTATE_HLAHD" + + tag "modules" + tag "modules_nfcore" + tag "modules_msk" + tag "annotate_hlahd" + + // Test 1: full run with real (synthetic) data. + // The module's result_dir input expects a single directory containing + // _final.result.txt and _{A,B,C}.est.txt; test_data.config + // registers those as individual files, so this test stages them into one + // directory before invoking the process. + test("annotate_hlahd - result_dir - annotated tsv and html") { + + when { + process { + """ + def resultDir = File.createTempDir() + new File(resultDir, "test_sample_final.result.txt") + .bytes = file(params.test_data_mskcc['annotate_hlahd']['final_result_txt'], checkIfExists: true).bytes + new File(resultDir, "test_sample_A.est.txt") + .bytes = file(params.test_data_mskcc['annotate_hlahd']['est_a_txt'], checkIfExists: true).bytes + new File(resultDir, "test_sample_B.est.txt") + .bytes = file(params.test_data_mskcc['annotate_hlahd']['est_b_txt'], checkIfExists: true).bytes + new File(resultDir, "test_sample_C.est.txt") + .bytes = file(params.test_data_mskcc['annotate_hlahd']['est_c_txt'], checkIfExists: true).bytes + + input[0] = [ + [ id:'test_sample' ], + file(resultDir) + ] + input[1] = file(params.test_data_mskcc['annotate_hlahd']['pgroup_file'], checkIfExists: true) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.tsv, + process.out.report, + process.out.versions + ).match() } + ) + } + } + + // Test 2: --skip_html should suppress the (optional) report output. + test("annotate_hlahd - skip_html - no report emitted") { + + config "./nextflow.config" + + when { + process { + """ + def resultDir = File.createTempDir() + new File(resultDir, "test_sample_final.result.txt") + .bytes = file(params.test_data_mskcc['annotate_hlahd']['final_result_txt'], checkIfExists: true).bytes + new File(resultDir, "test_sample_A.est.txt") + .bytes = file(params.test_data_mskcc['annotate_hlahd']['est_a_txt'], checkIfExists: true).bytes + new File(resultDir, "test_sample_B.est.txt") + .bytes = file(params.test_data_mskcc['annotate_hlahd']['est_b_txt'], checkIfExists: true).bytes + new File(resultDir, "test_sample_C.est.txt") + .bytes = file(params.test_data_mskcc['annotate_hlahd']['est_c_txt'], checkIfExists: true).bytes + + input[0] = [ + [ id:'test_sample' ], + file(resultDir) + ] + input[1] = file(params.test_data_mskcc['annotate_hlahd']['pgroup_file'], checkIfExists: true) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert process.out.report == [] }, + { assert snapshot(process.out.tsv, process.out.versions).match() } + ) + } + } + + // Test 3: stub run (no container needed) + test("annotate_hlahd - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ + [ id:'test_sample' ], + file('result_dir') + ] + input[1] = file('hla_nom_p.txt') + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert path(process.out.tsv.get(0).get(1)).exists() }, + { assert path(process.out.report.get(0).get(1)).exists() }, + { assert snapshot(process.out.versions).match() } + ) + } + } +} diff --git a/modules/msk/annotate_hlahd/tests/main.nf.test.snap b/modules/msk/annotate_hlahd/tests/main.nf.test.snap new file mode 100644 index 00000000..e734df81 --- /dev/null +++ b/modules/msk/annotate_hlahd/tests/main.nf.test.snap @@ -0,0 +1,62 @@ +{ + "annotate_hlahd - skip_html - no report emitted": { + "content": [ + [ + [ + { + "id": "test_sample" + }, + "test_sample_annotated.tsv:md5,a8636bc09b81afefdd01e388317b1978" + ] + ], + [ + "versions.yml:md5,7303efd9ae753a785695afaae7bca831" + ] + ], + "timestamp": "2026-08-26T17:30:52.702765", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.6" + } + }, + "annotate_hlahd - stub": { + "content": [ + [ + "versions.yml:md5,e1c7ca2bd9524d224402cef26a5fca25" + ] + ], + "timestamp": "2026-08-26T17:30:57.636612", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.6" + } + }, + "annotate_hlahd - result_dir - annotated tsv and html": { + "content": [ + [ + [ + { + "id": "test_sample" + }, + "test_sample_annotated.tsv:md5,a8636bc09b81afefdd01e388317b1978" + ] + ], + [ + [ + { + "id": "test_sample" + }, + "test_sample_report.html:md5,c78df8f69cf678d42b6aa6fad40d223a" + ] + ], + [ + "versions.yml:md5,7303efd9ae753a785695afaae7bca831" + ] + ], + "timestamp": "2026-08-26T17:30:44.124301", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.6" + } + } +} \ No newline at end of file diff --git a/modules/msk/annotate_hlahd/tests/nextflow.config b/modules/msk/annotate_hlahd/tests/nextflow.config new file mode 100644 index 00000000..52cfe423 --- /dev/null +++ b/modules/msk/annotate_hlahd/tests/nextflow.config @@ -0,0 +1,5 @@ +process { + withName: 'ANNOTATE_HLAHD' { + ext.args = '--skip_html' + } +} diff --git a/modules/msk/annotate_hlahd/tests/tags.yml b/modules/msk/annotate_hlahd/tests/tags.yml new file mode 100644 index 00000000..18559792 --- /dev/null +++ b/modules/msk/annotate_hlahd/tests/tags.yml @@ -0,0 +1,2 @@ +annotate_hlahd: + - "modules/msk/annotate_hlahd/**" diff --git a/tests/config/test_data.config b/tests/config/test_data.config index 347b83d6..f4786b2d 100644 --- a/tests/config/test_data.config +++ b/tests/config/test_data.config @@ -816,5 +816,13 @@ params { test_chr22_collapsed_grouped_bam = "${params.test_data_base_msk}/feature/fgbio_collectduplexseqmetrics/testdata/chr22_collapsed_grouped.bam" test_chr22_collapsed_grouped_bam_bai = "${params.test_data_base_msk}/feature/fgbio_collectduplexseqmetrics/testdata/chr22_collapsed_grouped.bam.bai" } + // NOTE: data lives on the feature/annotate_hlahd branch pending Review Team promotion to an official 'annotate_hlahd' branch. + 'annotate_hlahd' { + final_result_txt = "${params.test_data_base_msk}/feature/annotate_hlahd/annotate_hlahd/test_sample_final.result.txt" + est_a_txt = "${params.test_data_base_msk}/feature/annotate_hlahd/annotate_hlahd/test_sample_A.est.txt" + est_b_txt = "${params.test_data_base_msk}/feature/annotate_hlahd/annotate_hlahd/test_sample_B.est.txt" + est_c_txt = "${params.test_data_base_msk}/feature/annotate_hlahd/annotate_hlahd/test_sample_C.est.txt" + pgroup_file = "${params.test_data_base_msk}/feature/annotate_hlahd/annotate_hlahd/hla_nom_p_demo.txt" + } } }