From 558c25b66588a87afb73a9ee95cf3dae490fe5fd Mon Sep 17 00:00:00 2001 From: Nicolas Vannieuwkerke Date: Wed, 7 Oct 2026 17:11:56 +0200 Subject: [PATCH 1/3] make the output section optional --- CHANGELOG.md | 4 +++ README.md | 2 +- build.gradle | 2 +- .../plugin/config/SamplesheetsConfig.groovy | 6 ++-- .../nfcmgg/plugin/worksheet/Worksheet.groovy | 32 +++++++------------ worksheet-schema.json | 2 +- 6 files changed, 21 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39b32a6..081efd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.2 + +1. Made the `output` section of the worksheet optional. This is especially useful for pipelines that perform quality checking. + ## 0.4.1 1. Removed tag based include filters since that doesn't make sense anymore after splitting samplesheets per tag diff --git a/README.md b/README.md index d59aa36..a9ccaa2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ To use the plugin add the following to your `nextflow.config`: ```groovy plugins { - id 'nf-cmgg@0.4.1' + id 'nf-cmgg@0.4.2' } ``` diff --git a/build.gradle b/build.gradle index 9ecf25b..5686a50 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:5.3.2' } -version = '0.4.1' +version = '0.4.2' nextflowPlugin { nextflowVersion = '25.10.0' diff --git a/src/main/groovy/nfcmgg/plugin/config/SamplesheetsConfig.groovy b/src/main/groovy/nfcmgg/plugin/config/SamplesheetsConfig.groovy index 899f75b..0ca2b9e 100644 --- a/src/main/groovy/nfcmgg/plugin/config/SamplesheetsConfig.groovy +++ b/src/main/groovy/nfcmgg/plugin/config/SamplesheetsConfig.groovy @@ -59,10 +59,10 @@ class SamplesheetsConfig implements ConfigScope { } return worksheetPath }.findAll { sheet -> sheet != null } - } + } ((Path) Nextflow.file(getClass().getResource('/worksheets').toURI().toString())).eachFile { res -> worksheets << res } - } - } + +} diff --git a/src/main/groovy/nfcmgg/plugin/worksheet/Worksheet.groovy b/src/main/groovy/nfcmgg/plugin/worksheet/Worksheet.groovy index 7120975..ece672e 100644 --- a/src/main/groovy/nfcmgg/plugin/worksheet/Worksheet.groovy +++ b/src/main/groovy/nfcmgg/plugin/worksheet/Worksheet.groovy @@ -51,65 +51,55 @@ class Worksheet { idField = worksheetMap.id_field as String final Set inputFields = parsedInput != null ? parsedInput.fields.keySet() : ([] as Set) - WorksheetValues parsedValues = null if (worksheetMap.values != null) { try { - parsedValues = new WorksheetValues(worksheetMap.values as Map, inputFields) + values = new WorksheetValues(worksheetMap.values as Map, inputFields) } catch (WorksheetException e) { errors.record(e.message) } } - values = parsedValues - WorksheetOutput parsedOutput = null - if (worksheetMap.output == null) { - errors.error("Worksheet ${worksheet.name} is missing the required 'output' field") - } else { + if (worksheetMap.output != null) { try { - parsedOutput = new WorksheetOutput(worksheetMap.output as Map) + output = new WorksheetOutput(worksheetMap.output as Map) } catch (WorksheetException e) { errors.record(e.message) } } - output = parsedOutput - WorksheetMetrics parsedMetrics = null if (worksheetMap.metrics != null) { try { - parsedMetrics = new WorksheetMetrics(worksheetMap.metrics as Map) + metrics = new WorksheetMetrics(worksheetMap.metrics as Map) } catch (WorksheetException e) { errors.record(e.message) } } - metrics = parsedMetrics - WorksheetSamplesheets parsedSamplesheets = null if (worksheetMap.samplesheets == null) { errors.error("Worksheet ${worksheet.name} is missing the required 'samplesheets' field") } else { final Set dataFields = [] as Set dataFields.addAll(inputFields) - if (parsedValues != null) { - dataFields.addAll(parsedValues.fields.keySet()) + if (values != null) { + dataFields.addAll(values.fields.keySet()) } - if (parsedOutput != null) { - dataFields.addAll(parsedOutput.fields.keySet()) + if (output != null) { + dataFields.addAll(output.fields.keySet()) } - if (parsedMetrics != null) { - dataFields.addAll(parsedMetrics.fields.keySet()) + if (metrics != null) { + dataFields.addAll(metrics.fields.keySet()) } try { final WorksheetSamplesheetsSettings samplesheetSettings = new WorksheetSamplesheetsSettings( worksheetMap.samplesheets_settings as Map, dataFields ) - parsedSamplesheets = new WorksheetSamplesheets( + samplesheets = new WorksheetSamplesheets( worksheetMap.samplesheets as List, dataFields, samplesheetSettings ) } catch (WorksheetException e) { errors.record(e.message) } } - samplesheets = parsedSamplesheets errors.throwIfAny("Worksheet ${worksheet.name} is invalid") } diff --git a/worksheet-schema.json b/worksheet-schema.json index 0116af9..66799fb 100644 --- a/worksheet-schema.json +++ b/worksheet-schema.json @@ -2,7 +2,7 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "description": "Schema for the worksheet configuration YAML file", "type": "object", - "required": ["name", "id_field", "input", "output", "samplesheets"], + "required": ["name", "id_field", "input", "samplesheets"], "properties": { "name": { "type": "string", From 076034d3755e5364cd9812240bc33f3f41fc9ad2 Mon Sep 17 00:00:00 2001 From: Nicolas Vannieuwkerke Date: Wed, 28 Oct 2026 16:35:29 +0100 Subject: [PATCH 2/3] expand variable access in values block --- CHANGELOG.md | 3 +- README.md | 2 +- build.gradle | 2 +- .../nfcmgg/plugin/worksheet/Worksheet.groovy | 36 ++++++++----------- .../plugin/worksheet/WorksheetValues.groovy | 22 ++++++------ .../worksheets/nfcmgg_preprocessing.yml | 10 +++--- .../plugin/worksheet/WorksheetTest.groovy | 2 +- 7 files changed, 35 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 081efd4..adfaec5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ -## 0.4.2 +## 0.5.0 1. Made the `output` section of the worksheet optional. This is especially useful for pipelines that perform quality checking. +2. It's now possible to use all data fields in the `values` section of the worksheet. The name of the `input` object has also been changed to `data` ## 0.4.1 diff --git a/README.md b/README.md index a9ccaa2..df00739 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ To use the plugin add the following to your `nextflow.config`: ```groovy plugins { - id 'nf-cmgg@0.4.2' + id 'nf-cmgg@0.5.0' } ``` diff --git a/build.gradle b/build.gradle index 5686a50..79f5383 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:5.3.2' } -version = '0.4.2' +version = '0.5.0' nextflowPlugin { nextflowVersion = '25.10.0' diff --git a/src/main/groovy/nfcmgg/plugin/worksheet/Worksheet.groovy b/src/main/groovy/nfcmgg/plugin/worksheet/Worksheet.groovy index ece672e..ff07052 100644 --- a/src/main/groovy/nfcmgg/plugin/worksheet/Worksheet.groovy +++ b/src/main/groovy/nfcmgg/plugin/worksheet/Worksheet.groovy @@ -19,6 +19,8 @@ class Worksheet { final WorksheetMetrics metrics final WorksheetSamplesheets samplesheets + final Set dataFields = [] as Set + Worksheet(Path worksheet) { final WorksheetErrors errors = new WorksheetErrors() final Map worksheetMap = loadWorksheetMap(worksheet) @@ -28,21 +30,20 @@ class Worksheet { } name = worksheetMap.name as String - WorksheetInput parsedInput = null if (worksheetMap.input == null) { errors.error("Worksheet ${worksheet.name} is missing the required 'input' field") } else { try { - parsedInput = new WorksheetInput(worksheetMap.input as Map) + input = new WorksheetInput(worksheetMap.input as Map) + dataFields.addAll(input.fields.keySet()) } catch (WorksheetException e) { errors.record(e.message) } } - input = parsedInput if (worksheetMap.id_field == null) { errors.error("Worksheet ${worksheet.name} is missing the required 'id_field' field") - } else if (parsedInput != null && !parsedInput.hasSource(worksheetMap.id_field.toString())) { + } else if (input != null && !input.hasSource(worksheetMap.id_field.toString())) { errors.error( "Worksheet ${worksheet.name} 'id_field' '${worksheetMap.id_field}' is not defined" + ' as a source or key (when source is missing) in the input block' @@ -50,26 +51,28 @@ class Worksheet { } idField = worksheetMap.id_field as String - final Set inputFields = parsedInput != null ? parsedInput.fields.keySet() : ([] as Set) - if (worksheetMap.values != null) { + if (worksheetMap.output != null) { try { - values = new WorksheetValues(worksheetMap.values as Map, inputFields) + output = new WorksheetOutput(worksheetMap.output as Map) + dataFields.addAll(output.fields.keySet()) } catch (WorksheetException e) { errors.record(e.message) } } - if (worksheetMap.output != null) { + if (worksheetMap.metrics != null) { try { - output = new WorksheetOutput(worksheetMap.output as Map) + metrics = new WorksheetMetrics(worksheetMap.metrics as Map) + dataFields.addAll(metrics.fields.keySet()) } catch (WorksheetException e) { errors.record(e.message) } } - if (worksheetMap.metrics != null) { + if (worksheetMap.values != null) { try { - metrics = new WorksheetMetrics(worksheetMap.metrics as Map) + values = new WorksheetValues(worksheetMap.values as Map, dataFields) + dataFields.addAll(values.fields.keySet()) } catch (WorksheetException e) { errors.record(e.message) } @@ -78,17 +81,6 @@ class Worksheet { if (worksheetMap.samplesheets == null) { errors.error("Worksheet ${worksheet.name} is missing the required 'samplesheets' field") } else { - final Set dataFields = [] as Set - dataFields.addAll(inputFields) - if (values != null) { - dataFields.addAll(values.fields.keySet()) - } - if (output != null) { - dataFields.addAll(output.fields.keySet()) - } - if (metrics != null) { - dataFields.addAll(metrics.fields.keySet()) - } try { final WorksheetSamplesheetsSettings samplesheetSettings = new WorksheetSamplesheetsSettings( worksheetMap.samplesheets_settings as Map, dataFields diff --git a/src/main/groovy/nfcmgg/plugin/worksheet/WorksheetValues.groovy b/src/main/groovy/nfcmgg/plugin/worksheet/WorksheetValues.groovy index e9589a2..b92b6bf 100644 --- a/src/main/groovy/nfcmgg/plugin/worksheet/WorksheetValues.groovy +++ b/src/main/groovy/nfcmgg/plugin/worksheet/WorksheetValues.groovy @@ -22,7 +22,7 @@ class WorksheetValues { */ final Map fields - WorksheetValues(Map values, Set inputFields) { + WorksheetValues(Map values, Set dataFields) { final WorksheetErrors errors = new WorksheetErrors() if (values == null || values.isEmpty()) { errors.error('Worksheet values is missing or empty') @@ -32,15 +32,15 @@ class WorksheetValues { values.each { key, value -> final String fieldName = key as String final Map fieldMap = (value != null ? value as Map : [:]) as Map - parsed[fieldName] = new Field(fieldName, fieldMap, inputFields, errors) + parsed[fieldName] = new Field(fieldName, fieldMap, dataFields, errors) } fields = parsed.asImmutable() errors.throwIfAny('Invalid worksheet values') } - Map convert(Map inputData, Map params) { - Map convertedData = inputData - Binding inputBinding = new Binding([input: inputData, params: params]) + Map convert(Map dataFields, Map params) { + Map convertedData = dataFields + Binding inputBinding = new Binding([data: dataFields, params: params]) GroovyShell inputShell = SafeGroovy.shell(inputBinding) fields.each { key, field -> convertedData[key] = field.define(inputShell) @@ -58,7 +58,7 @@ class WorksheetValues { final Object value final String func - Field(String key, Map fieldMap, Set inputFields, WorksheetErrors errors) { + Field(String key, Map fieldMap, Set dataFields, WorksheetErrors errors) { this.key = key value = fieldMap.containsKey('value') ? fieldMap.value : null func = fieldMap.func != null ? fieldMap.func as String : null @@ -69,7 +69,7 @@ class WorksheetValues { errors.error("Values field '${key}' cannot define both 'value' and 'func'") } if (func != null) { - validateFunc(key, func, inputFields, errors) + validateFunc(key, func, dataFields, errors) } } @@ -82,7 +82,7 @@ class WorksheetValues { } } - private static void validateFunc(String key, String func, Set inputFields, WorksheetErrors errors) { + private static void validateFunc(String key, String func, Set dataFields, WorksheetErrors errors) { try { SafeGroovy.parse(func) } catch (CompilationFailedException e) { @@ -91,10 +91,10 @@ class WorksheetValues { final Matcher matcher = INPUT_FIELD_PATTERN.matcher(func) while (matcher.find()) { final String referenced = matcher.group(1) - if (!inputFields.contains(referenced)) { + if (!dataFields.contains(referenced)) { errors.error( - "Values field '${key}' references unknown input field 'input.${referenced}'. " + - "Available input fields: ${inputFields.sort().join(', ')}" + "Values field '${key}' references unknown data field 'data.${referenced}'. " + + "Available data fields: ${dataFields.sort().join(', ')}" ) } } diff --git a/src/main/resources/worksheets/nfcmgg_preprocessing.yml b/src/main/resources/worksheets/nfcmgg_preprocessing.yml index a038c6c..704062c 100644 --- a/src/main/resources/worksheets/nfcmgg_preprocessing.yml +++ b/src/main/resources/worksheets/nfcmgg_preprocessing.yml @@ -39,15 +39,15 @@ values: strandedness: value: unknown normdup: - func: input.tag.toLowerCase() == "copgt-m" + func: data.tag.toLowerCase() == "copgt-m" nipt: - func: input.tag.toLowerCase() == "cfdnaseq" + func: data.tag.toLowerCase() == "cfdnaseq" binsize: func: | - (input.organism?.toLowerCase() == 'mus musculus' || input.genome?.toLowerCase() == 'mm10') - && ![100, 500].contains(input.binsize) ? 100 : input.binsize + (data.organism?.toLowerCase() == 'mus musculus' || data.genome?.toLowerCase() == 'mm10') + && ![100, 500].contains(data.binsize) ? 100 : data.binsize exomecnv_batch: - func: 'input.library + "_" + input.sex' + func: 'data.library + "_" + data.sex' # Define which output files that should be remembered for samplesheet generation. # Key-value pairs should be used here where the key is the name used by the plugin # All fields can take the following key-value pairs: diff --git a/src/test/groovy/nfcmgg/plugin/worksheet/WorksheetTest.groovy b/src/test/groovy/nfcmgg/plugin/worksheet/WorksheetTest.groovy index ab48353..3906c07 100644 --- a/src/test/groovy/nfcmgg/plugin/worksheet/WorksheetTest.groovy +++ b/src/test/groovy/nfcmgg/plugin/worksheet/WorksheetTest.groovy @@ -77,7 +77,7 @@ samplesheets: then: WorksheetException e = thrown() - e.message.contains("unknown input field 'input.missing'") + e.message.contains("unknown data field 'data.missing'") } void 'missing output pattern aborts the plugin'() { From 6d3c7551baf00b499127d3074fd96ec5318dce07 Mon Sep 17 00:00:00 2001 From: Nicolas Vannieuwkerke Date: Wed, 28 Oct 2026 16:39:40 +0100 Subject: [PATCH 3/3] prek --- .../groovy/nfcmgg/plugin/config/SamplesheetsConfig.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/groovy/nfcmgg/plugin/config/SamplesheetsConfig.groovy b/src/main/groovy/nfcmgg/plugin/config/SamplesheetsConfig.groovy index 0ca2b9e..899f75b 100644 --- a/src/main/groovy/nfcmgg/plugin/config/SamplesheetsConfig.groovy +++ b/src/main/groovy/nfcmgg/plugin/config/SamplesheetsConfig.groovy @@ -59,10 +59,10 @@ class SamplesheetsConfig implements ConfigScope { } return worksheetPath }.findAll { sheet -> sheet != null } - } + } ((Path) Nextflow.file(getClass().getResource('/worksheets').toURI().toString())).eachFile { res -> worksheets << res } - } + } -} + }