From bbf14627d5ca5cee3400c6722423202ce1647db3 Mon Sep 17 00:00:00 2001 From: Adam Taylor Date: Wed, 7 Feb 2024 12:53:37 +0000 Subject: [PATCH 1/7] Add method to use custom config file --- main.nf | 1 + modules/histoqc.nf | 7 ++++++- subworkflows/run_histoqc.nf | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/main.nf b/main.nf index dadf6c7..792c4c0 100644 --- a/main.nf +++ b/main.nf @@ -7,6 +7,7 @@ if (params.input) { params.input = file(params.input) } else { exit 1, 'Input sa // Set parameters and defaults params.outDir = './outputs' params.config = 'default' +params.custom_config = false include { NF_HISTOQC } from './workflows/nf_histoqc.nf' // include { COMBINE_RESULTS } from './modules/combine_results.nf' diff --git a/modules/histoqc.nf b/modules/histoqc.nf index bfac2df..f19d79c 100644 --- a/modules/histoqc.nf +++ b/modules/histoqc.nf @@ -8,6 +8,7 @@ process HISTOQC { input: tuple val(meta), path(images) + val configInput output: path "out/results.tsv", emit: results @@ -15,8 +16,12 @@ process HISTOQC { path "*.png", emit: masks script: + // Check if configInput is a file or a string + def configFile = file(configInput).getName().endsWith('.ini')? file(configInput) : params.config + """ - python -m histoqc -c ${params.config} $images -o out + echo "Using config: $configFile" + python -m histoqc -c $configFile $images -o out mv out/$images/*.png . """ } \ No newline at end of file diff --git a/subworkflows/run_histoqc.nf b/subworkflows/run_histoqc.nf index 6962624..fc05e5a 100644 --- a/subworkflows/run_histoqc.nf +++ b/subworkflows/run_histoqc.nf @@ -6,7 +6,8 @@ workflow RUN { images main: - HISTOQC ( images ) + def configInput = params.custom_config && file(params.custom_config).exists() ? file(params.custom_config) : params.config + HISTOQC ( images, configInput ) emit: output = HISTOQC.out.masks From 9ef7d187c8130eab4f4ffa5859dbe7476bffe99e Mon Sep 17 00:00:00 2001 From: Adam Taylor Date: Wed, 7 Feb 2024 12:59:16 +0000 Subject: [PATCH 2/7] Update schema --- nextflow_schema.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 7375c3e..10e29d9 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -29,13 +29,20 @@ "title": "HistoQC options", "type": "object", "fa_icon": "fas fa-solid fa-microscope", - "description": "Define parameters for histoqc", + "description": "Define parameters for HistoQC", "required": ["config"], "properties": { "config": { "type": "string", "default": "default", - "enum": ["default", "ihc", "clinical", "first", "light", "v2.1"] + "enum": ["default", "ihc", "clinical", "first", "light", "v2.1"], + "description": "Name of built-in HistoQC config. Overridden by `custom_config`." + }, + "custom_config": { + "type": "string", + "format": "file-path", + "mimetype": "text/plain", + "description": "Path to a HistoQC config `.ini` file. Overrides `config`" } } } From 6818def735d158aaa086ee8f671683f7adb30e01 Mon Sep 17 00:00:00 2001 From: Adam Taylor Date: Wed, 7 Feb 2024 17:05:27 +0000 Subject: [PATCH 3/7] Reconfigure logic --- modules/histoqc.nf | 8 ++++---- subworkflows/run_histoqc.nf | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/modules/histoqc.nf b/modules/histoqc.nf index f19d79c..1287396 100644 --- a/modules/histoqc.nf +++ b/modules/histoqc.nf @@ -8,7 +8,7 @@ process HISTOQC { input: tuple val(meta), path(images) - val configInput + val ini output: path "out/results.tsv", emit: results @@ -17,11 +17,11 @@ process HISTOQC { script: // Check if configInput is a file or a string - def configFile = file(configInput).getName().endsWith('.ini')? file(configInput) : params.config + //def configFile = file(configInput).getName().endsWith('.ini')? file(configInput) : params.config """ - echo "Using config: $configFile" - python -m histoqc -c $configFile $images -o out + echo "Using config: $ini" + python -m histoqc -c $ini $images -o out mv out/$images/*.png . """ } \ No newline at end of file diff --git a/subworkflows/run_histoqc.nf b/subworkflows/run_histoqc.nf index a6bf184..5fa1119 100644 --- a/subworkflows/run_histoqc.nf +++ b/subworkflows/run_histoqc.nf @@ -7,8 +7,18 @@ workflow RUN { images main: - def configInput = params.custom_config && file(params.custom_config).exists() ? file(params.custom_config) : params.config - params.convert ? CONVERT(images) | HISTOQC : HISTOQC(images) + + def config_ch = params.custom_config ? file(params.custom_config) : params.config + + println "config_ch: $config_ch" + + if (params.convert) { + CONVERT( images ) + HISTOQC( CONVERT.out, config_ch ) + } + else { + HISTOQC ( images, config_ch ) + } emit: output = HISTOQC.out.masks From c06a470f252a7a5182f16b026655cdfeae5c6e1f Mon Sep 17 00:00:00 2001 From: Adam Taylor Date: Wed, 7 Feb 2024 17:27:33 +0000 Subject: [PATCH 4/7] tidy --- main.nf | 5 +++++ modules/histoqc.nf | 3 --- subworkflows/run_histoqc.nf | 2 -- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/main.nf b/main.nf index 57a1592..4e3eb92 100644 --- a/main.nf +++ b/main.nf @@ -12,12 +12,17 @@ if (params.convert) { } } +if ( params.custom_config ) { + if (! file(params.custom_config).exists() ) { error "Custom config file does not exist" } + if (! file(params.custom_config).getName().endsWith(".ini") ) { error "Custom config file is not a .ini file" } +} // Set parameters and defaults params.outDir = './outputs' params.config = 'default' params.custom_config = false + include { NF_HISTOQC } from './workflows/nf_histoqc.nf' // include { COMBINE_RESULTS } from './modules/combine_results.nf' diff --git a/modules/histoqc.nf b/modules/histoqc.nf index 1287396..420842d 100644 --- a/modules/histoqc.nf +++ b/modules/histoqc.nf @@ -16,9 +16,6 @@ process HISTOQC { path "*.png", emit: masks script: - // Check if configInput is a file or a string - //def configFile = file(configInput).getName().endsWith('.ini')? file(configInput) : params.config - """ echo "Using config: $ini" python -m histoqc -c $ini $images -o out diff --git a/subworkflows/run_histoqc.nf b/subworkflows/run_histoqc.nf index 5fa1119..f27e098 100644 --- a/subworkflows/run_histoqc.nf +++ b/subworkflows/run_histoqc.nf @@ -10,8 +10,6 @@ workflow RUN { def config_ch = params.custom_config ? file(params.custom_config) : params.config - println "config_ch: $config_ch" - if (params.convert) { CONVERT( images ) HISTOQC( CONVERT.out, config_ch ) From d904cd9188336d39f65f54827f41f78c94356f0c Mon Sep 17 00:00:00 2001 From: Adam Taylor Date: Fri, 9 Feb 2024 10:02:54 +0000 Subject: [PATCH 5/7] Fixes based on conversation with Brad --- assets/empty.txt | 0 main.nf | 15 ++++++++++----- modules/histoqc.nf | 20 +++++++++++++++++--- subworkflows/run_histoqc.nf | 14 ++++++++------ 4 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 assets/empty.txt diff --git a/assets/empty.txt b/assets/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/main.nf b/main.nf index 4e3eb92..4e36ac5 100644 --- a/main.nf +++ b/main.nf @@ -2,30 +2,35 @@ nextflow.enable.dsl=2 +// Set parameters and defaults + +// Check if the input samplesheet is provided if (params.input) { params.input = file(params.input) } else { exit 1, 'Input samplesheet not specified!' } +// Check requirements for the convert param to be used params.convert = false - if (params.convert) { if (!params.mag || !params.mpp) { error "Both nominal magnification ('mag') and microns per pixel ('mpp' parameters must be provided when 'params.convert' is set to true." } } +// Check requirements for the custom_config param to be used +params.custom_config = null if ( params.custom_config ) { if (! file(params.custom_config).exists() ) { error "Custom config file does not exist" } if (! file(params.custom_config).getName().endsWith(".ini") ) { error "Custom config file is not a .ini file" } } -// Set parameters and defaults +// Set default outdir and config params.outDir = './outputs' params.config = 'default' -params.custom_config = false - +// Import workflow include { NF_HISTOQC } from './workflows/nf_histoqc.nf' -// include { COMBINE_RESULTS } from './modules/combine_results.nf' + +// Run workflow workflow { NF_HISTOQC () } diff --git a/modules/histoqc.nf b/modules/histoqc.nf index 420842d..f8bbf41 100644 --- a/modules/histoqc.nf +++ b/modules/histoqc.nf @@ -8,7 +8,8 @@ process HISTOQC { input: tuple val(meta), path(images) - val ini + val config_string + path custom_config output: path "out/results.tsv", emit: results @@ -17,8 +18,21 @@ process HISTOQC { script: """ - echo "Using config: $ini" - python -m histoqc -c $ini $images -o out + echo $config_string + echo $custom_config + # Add the logic for the if config_string or custom_config here + # Check if custom_config points to "empty.txt" + if [[ "$custom_config" == *empty.txt ]]; then + # If yes, use the config_string + ini="$config_string" + else + # If no, use the path to custom_config + ini=$custom_config + fi + + + echo "Using config: \$ini" + python -m histoqc $images -o out -c \$ini mv out/$images/*.png . """ } \ No newline at end of file diff --git a/subworkflows/run_histoqc.nf b/subworkflows/run_histoqc.nf index f27e098..0c982c0 100644 --- a/subworkflows/run_histoqc.nf +++ b/subworkflows/run_histoqc.nf @@ -8,14 +8,16 @@ workflow RUN { main: - def config_ch = params.custom_config ? file(params.custom_config) : params.config + def custom_config = params.custom_config ? Channel.fromPath(params.custom_config).first() : Channel.fromPath("assets/empty.txt").first() // need to pass a dummy file in the projectDir or similar - if (params.convert) { - CONVERT( images ) - HISTOQC( CONVERT.out, config_ch ) - } + // Scenario 1: Conversion and custom configuration + if (params.convert ) { + CONVERT(images) + HISTOQC(CONVERT.out, params.config, custom_config) + } + // Scenario 3: No conversion, but with custom configuration else { - HISTOQC ( images, config_ch ) + HISTOQC(images, params.config, custom_config) } emit: From 2253565c4800e1722d4988829e66c19ca63b538f Mon Sep 17 00:00:00 2001 From: Adam Taylor Date: Fri, 9 Feb 2024 10:05:03 +0000 Subject: [PATCH 6/7] Cleanup --- modules/histoqc.nf | 3 --- subworkflows/run_histoqc.nf | 5 +++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/histoqc.nf b/modules/histoqc.nf index f8bbf41..5c7799f 100644 --- a/modules/histoqc.nf +++ b/modules/histoqc.nf @@ -18,8 +18,6 @@ process HISTOQC { script: """ - echo $config_string - echo $custom_config # Add the logic for the if config_string or custom_config here # Check if custom_config points to "empty.txt" if [[ "$custom_config" == *empty.txt ]]; then @@ -30,7 +28,6 @@ process HISTOQC { ini=$custom_config fi - echo "Using config: \$ini" python -m histoqc $images -o out -c \$ini mv out/$images/*.png . diff --git a/subworkflows/run_histoqc.nf b/subworkflows/run_histoqc.nf index 0c982c0..561e6fd 100644 --- a/subworkflows/run_histoqc.nf +++ b/subworkflows/run_histoqc.nf @@ -8,14 +8,15 @@ workflow RUN { main: + // If a custom config is passed use it, otherwise pass a dummy file def custom_config = params.custom_config ? Channel.fromPath(params.custom_config).first() : Channel.fromPath("assets/empty.txt").first() // need to pass a dummy file in the projectDir or similar - // Scenario 1: Conversion and custom configuration + // Scenario 1: Conversion if (params.convert ) { CONVERT(images) HISTOQC(CONVERT.out, params.config, custom_config) } - // Scenario 3: No conversion, but with custom configuration + // Scenario 2: No conversion else { HISTOQC(images, params.config, custom_config) } From b9d4ce8aa54d592527b613658939a0a9c78f81c6 Mon Sep 17 00:00:00 2001 From: Adam Taylor Date: Sat, 10 Feb 2024 16:25:01 +0000 Subject: [PATCH 7/7] Update README --- README.md | 64 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 2c7b572..5fad7fc 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,11 @@ Other columns may be provided but are not used by the pipeline. - `input`: Path to a CSV sample sheet. This parameter is required. - `outDir`: Specifies the directory where the output data should be saved. Default is `outputs`. -#### HistoQC options +#### Other options -- `config`: Configuration file used by HistoQC. Must be one of `default`, `ihc`, `clinical`, `first`, `light`, or `v2.1`.Custom config files are not currently supported. +- `config` (string): Name of a built-in configuration used by HistoQC. Must be one of `default`, `ihc`, `clinical`, `first`, `light`, or `v2.1`. Defaults to `default`. +- `custom_config` (path): Path to a HistoQC compatible configuration file. Must have a `.ini` extension. Overrides `config`. +- `convert` (bool): If provided, `vips` is used to [create an OpenSlide compatiable TIFF file](http://www.andrewjanowczyk.com/converting-an-existing-image-into-an-openslide-compatible-format/). Uses [mc2-center/histoqc-openslide-converter](https://github.com/mc2-center/histoqc-openslide-converter). ### Profiles @@ -86,23 +88,45 @@ The container is automatically pulled by NextFlow, but if local use is required > A Nextflow pipeline is implicitly modelled by a direct acyclic graph (DAG). The vertices in the graph represent the pipeline’s processes and operators, while the edges represent the data connections (i.e. channels) between them. ```mermaid -flowchart TD - p0((Channel.fromPath)) - p1([splitCsv]) - p2([map]) - p3[NF_HISTOQC:RUN_HISTOQC:HISTOQC] - p4(( )) - p5(( )) - p6([collect]) - p7[NF_HISTOQC:COLLECT_RESULTS:COLLECT] - p8(( )) - p0 --> p1 - p1 --> p2 - p2 -->|images| p3 - p3 -->|results| p6 - p3 --> p5 - p3 -->|output| p4 - p6 --> p7 - p7 --> p8 +flowchart TB + subgraph " " + v0["Channel.fromPath"] + v3["Channel.fromPath"] + v6["config_string"] + end + subgraph NF_HISTOQC + subgraph RUN + v5([CONVERT]) + v7([HISTOQC]) + v1(( )) + v4(( )) + v9(( )) + v13(( )) + end + subgraph COLLECT + v10([RESULTS]) + v11([TIDY]) + v14([LOGS]) + end + end + subgraph " " + v8["output"] + v12[" "] + v15[" "] + end + v0 --> v1 + v3 --> v4 + v1 --> v5 + v5 --> v7 + v6 --> v7 + v4 --> v7 + v7 --> v8 + v7 --> v9 + v7 --> v13 + v9 --> v10 + v10 --> v11 + v11 --> v12 + v13 --> v14 + v14 --> v15 ```