Skip to content

Commit a189b32

Browse files
new module: pharmcat/phenotyper (nf-core#10116)
* add new module pharmcat/phenotyper * fixed linting for pharmcat/phenotyper * Fix Harshil alignment and removed args from the stub
1 parent c5a0412 commit a189b32

6 files changed

Lines changed: 383 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
channels:
3+
- conda-forge
4+
- bioconda
5+
dependencies:
6+
- bioconda::pharmcat3=3.1.1
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
process PHARMCAT_PHENOTYPER {
2+
tag "$meta.id"
3+
label 'process_single'
4+
5+
conda "${moduleDir}/environment.yml"
6+
container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container
7+
? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/2b/2b27c134f2226e65c3be9687fdcd6dfb5eebb7998bf1ad89ff396c914fe6d81a/data'
8+
: 'community.wave.seqera.io/library/pharmcat3:3.1.1--876b7152770ba008'}"
9+
10+
input:
11+
tuple val(meta), path(match_json), path(outside_match_tsv)
12+
13+
output:
14+
tuple val(meta), path("*.phenotype.json") , emit: phenotyper_json
15+
tuple val("${task.process}"), val('pharmcat'), eval("pharmcat --version | cut -f2 -d ' '"), topic: versions , emit: versions_pharmcat
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}.pharmcat"
23+
24+
def pheno_input = match_json ? " --phenotyper-input ${match_json} " : ""
25+
def outside_pheno_input = outside_match_tsv ? " --phenotyper-outside-call-file ${outside_match_tsv} " : ""
26+
27+
"""
28+
pharmcat \\
29+
--base-filename ${prefix} \\
30+
--output-dir . \\
31+
-phenotyper \\
32+
${pheno_input} \\
33+
${outside_pheno_input} \\
34+
${args}
35+
"""
36+
37+
stub:
38+
def prefix = task.ext.prefix ?: "${meta.id}.pharmcat"
39+
"""
40+
touch ${prefix}.phenotype.json
41+
"""
42+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: "pharmcat_phenotyper"
2+
description: |
3+
The PharmCAT Phenotyper is a core module of the Pharmacogenomics Clinical Annotation Tool
4+
that translates patient diplotypes into specific, actionable metabolizer phenotypes
5+
(e.g., Poor Metabolizer). It operates by analyzing the JSON output from the Named
6+
Allele Matcher, mapping these results to established clinical guidelines (such as
7+
CPIC) to predict drug response.
8+
keywords:
9+
- vcf
10+
- pharmcat
11+
- phenotyper
12+
- PGx
13+
tools:
14+
- "pharmcat":
15+
description: |
16+
"PharmCAT (Pharmacogenomics Clinical Annotation Tool) is a bioinformatics
17+
tool that analyzes genetic variants to predict drug response and tailor medical
18+
treatment to an individual patient’s genetic profile."
19+
homepage: "https://pharmcat.clinpgx.org/"
20+
documentation: "https://pharmcat.clinpgx.org/"
21+
tool_dev_url: "https://github.com/PharmGKB/PharmCAT"
22+
doi: "10.1002/cpt.928"
23+
licence:
24+
- "MPL-2.0"
25+
identifier: ""
26+
input:
27+
- - meta:
28+
type: map
29+
description: |
30+
Groovy Map containing sample information
31+
e.g. `[ id:'test', name:'test_sample' ]`
32+
- match_json:
33+
type: file
34+
description: The Json output from the matcher module of pharmcat
35+
pattern: "*.json"
36+
ontologies:
37+
- edam: http://edamontology.org/format_3464
38+
- outside_match_tsv:
39+
type: file
40+
description: Tab seperated file containing diplotypes calls from other
41+
callers
42+
pattern: "*.tsv"
43+
ontologies:
44+
- edam: http://edamontology.org/format_3475
45+
output:
46+
phenotyper_json:
47+
- - meta:
48+
type: map
49+
description: |
50+
Groovy Map containing sample information
51+
e.g. `[ id:'test', name:'test_sample' ]`
52+
- "*.phenotype.json":
53+
type: file
54+
description: Json output from the phenotyper module of PharmCAT
55+
pattern: "*.phenotype.json"
56+
ontologies:
57+
- edam: http://edamontology.org/format_3464
58+
versions_pharmcat:
59+
- - ${task.process}:
60+
type: string
61+
description: The name of the process
62+
- pharmcat:
63+
type: string
64+
description: The name of the tool
65+
- pharmcat --version | cut -f2 -d ' ':
66+
type: eval
67+
description: The expression to obtain the version of the tool
68+
topics:
69+
versions:
70+
- - ${task.process}:
71+
type: string
72+
description: The name of the process
73+
- pharmcat:
74+
type: string
75+
description: The name of the tool
76+
- pharmcat --version | cut -f2 -d ' ':
77+
type: eval
78+
description: The expression to obtain the version of the tool
79+
authors:
80+
- "@ramsainanduri"
81+
maintainers:
82+
- "@ramsainanduri"
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
nextflow_process {
2+
3+
name "Test Process PHARMCAT_PHENOTYPER"
4+
script "../main.nf"
5+
process "PHARMCAT_PHENOTYPER"
6+
7+
tag "modules"
8+
tag "modules_nfcore"
9+
tag "pharmcat"
10+
tag "pharmcat/phenotyper"
11+
tag "pharmcat/matcher"
12+
13+
config "./nextflow.config"
14+
15+
16+
setup {
17+
run("PHARMCAT_MATCHER") {
18+
script "../../matcher/main.nf"
19+
process {
20+
"""
21+
input[0] = Channel.fromList([
22+
tuple([id: 'PharmCAT'], // meta map
23+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/pharmcat/test.chr22.single.sample.vcf.gz', checkIfExists: true),
24+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/pharmcat/test.chr22.single.sample.vcf.gz.tbi', checkIfExists: true)),
25+
])
26+
input[1] = Channel.of([])
27+
"""
28+
}
29+
}
30+
}
31+
32+
test("Phenotyper with pharmcat matcher JSON") {
33+
34+
when {
35+
36+
params {
37+
module_args = ''
38+
}
39+
40+
process {
41+
"""
42+
input[0] = PHARMCAT_MATCHER.out.matcher_json.collect {
43+
meta, match_json -> match_json
44+
}.map {
45+
match_json -> [
46+
[ id: 'test'],
47+
match_json,
48+
[]
49+
]
50+
}
51+
"""
52+
}
53+
}
54+
55+
then {
56+
assertAll(
57+
{ assert process.success },
58+
{ assert snapshot(
59+
file(process.out.phenotyper_json[0][1]).name,
60+
process.out.findAll { key, val -> key.startsWith("versions") }
61+
).match() }
62+
)
63+
}
64+
}
65+
66+
test("Phenotyper with outside calls JSON") {
67+
68+
when {
69+
params {
70+
module_args = ''
71+
}
72+
73+
process {
74+
"""
75+
input[0] = [
76+
[ id:'test' ], // meta map
77+
[],
78+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/pharmcat/test.chr22.outsideCall.tsv', checkIfExists: true),
79+
]
80+
"""
81+
}
82+
}
83+
84+
then {
85+
assertAll(
86+
{ assert process.success },
87+
{ assert snapshot(
88+
file(process.out.phenotyper_json[0][1]).name,
89+
process.out.findAll { key, val -> key.startsWith("versions") }
90+
).match() }
91+
)
92+
}
93+
}
94+
95+
test("Phenotyper with pharmcat matcher + outside calls JSON") {
96+
97+
when {
98+
params {
99+
module_args = ''
100+
}
101+
102+
process {
103+
"""
104+
input[0] = PHARMCAT_MATCHER.out.matcher_json.collect {
105+
meta, match_json -> match_json
106+
}.map {
107+
match_json -> [
108+
[ id: 'test'],
109+
match_json,
110+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/pharmcat/test.chr22.outsideCall.tsv', checkIfExists: true)
111+
]
112+
}
113+
"""
114+
}
115+
}
116+
117+
then {
118+
assertAll(
119+
{ assert process.success },
120+
{ assert snapshot(
121+
file(process.out.phenotyper_json[0][1]).name,
122+
process.out.findAll { key, val -> key.startsWith("versions") }
123+
).match() }
124+
)
125+
}
126+
}
127+
128+
129+
test("Phenotyper - stub") {
130+
131+
options "-stub"
132+
133+
when {
134+
params {
135+
module_args = ''
136+
}
137+
138+
process {
139+
"""
140+
input[0] = PHARMCAT_MATCHER.out.matcher_json.collect {
141+
meta, match_json -> match_json
142+
}.map {
143+
match_json -> [
144+
[ id: 'test'],
145+
match_json,
146+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/pharmcat/test.chr22.outsideCall.tsv', checkIfExists: true)
147+
]
148+
}
149+
"""
150+
}
151+
}
152+
153+
then {
154+
assertAll(
155+
{ assert process.success },
156+
{ assert snapshot(
157+
sanitizeOutput(process.out)
158+
).match() }
159+
)
160+
}
161+
}
162+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{
2+
"Phenotyper with outside calls JSON": {
3+
"content": [
4+
"test.pharmcat.phenotype.json",
5+
{
6+
"versions_pharmcat": [
7+
[
8+
"PHARMCAT_PHENOTYPER",
9+
"pharmcat",
10+
"3.1.1"
11+
]
12+
]
13+
}
14+
],
15+
"timestamp": "2026-02-19T20:47:46.244471569",
16+
"meta": {
17+
"nf-test": "0.9.4",
18+
"nextflow": "25.10.4"
19+
}
20+
},
21+
"Phenotyper with pharmcat matcher + outside calls JSON": {
22+
"content": [
23+
"test.pharmcat.phenotype.json",
24+
{
25+
"versions_pharmcat": [
26+
[
27+
"PHARMCAT_PHENOTYPER",
28+
"pharmcat",
29+
"3.1.1"
30+
]
31+
]
32+
}
33+
],
34+
"timestamp": "2026-02-19T20:44:35.22835293",
35+
"meta": {
36+
"nf-test": "0.9.4",
37+
"nextflow": "25.10.4"
38+
}
39+
},
40+
"Phenotyper - stub": {
41+
"content": [
42+
{
43+
"phenotyper_json": [
44+
[
45+
{
46+
"id": "test"
47+
},
48+
"test.pharmcat.phenotype.json:md5,d41d8cd98f00b204e9800998ecf8427e"
49+
]
50+
],
51+
"versions_pharmcat": [
52+
[
53+
"PHARMCAT_PHENOTYPER",
54+
"pharmcat",
55+
"3.1.1"
56+
]
57+
]
58+
}
59+
],
60+
"timestamp": "2026-02-19T20:40:10.461209314",
61+
"meta": {
62+
"nf-test": "0.9.4",
63+
"nextflow": "25.10.4"
64+
}
65+
},
66+
"Phenotyper with pharmcat matcher JSON": {
67+
"content": [
68+
"test.pharmcat.phenotype.json",
69+
{
70+
"versions_pharmcat": [
71+
[
72+
"PHARMCAT_PHENOTYPER",
73+
"pharmcat",
74+
"3.1.1"
75+
]
76+
]
77+
}
78+
],
79+
"timestamp": "2026-02-19T20:39:27.090574299",
80+
"meta": {
81+
"nf-test": "0.9.4",
82+
"nextflow": "25.10.4"
83+
}
84+
}
85+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
process {
2+
withName: PHARMCAT_MATCHER {
3+
ext.args = params.module_args
4+
ext.prefix = { "${meta.id}.pharmcat" }
5+
}
6+
}

0 commit comments

Comments
 (0)