From 8c7886399f18b46b747f5c0da9f5e5a7a462be38 Mon Sep 17 00:00:00 2001 From: Matthias Zepper Date: Tue, 24 Sep 2024 20:18:37 +0200 Subject: [PATCH 1/9] First draft, inspired by oncoanalyzer. --- lib/Constants.groovy | 32 +++++++++++++++ lib/ToolTracker.groovy | 92 ++++++++++++++++++++++++++++++++++++++++++ lib/Utililities.groovy | 85 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 lib/Constants.groovy create mode 100644 lib/ToolTracker.groovy create mode 100644 lib/Utililities.groovy diff --git a/lib/Constants.groovy b/lib/Constants.groovy new file mode 100644 index 0000000..164ba3b --- /dev/null +++ b/lib/Constants.groovy @@ -0,0 +1,32 @@ +// Class to hold constants + +class Constants { + + static enum RunMode { + FASTQ, + RUNFOLDER, + } + + // Define all available tools centrally here + + static enum Tool { + FASTQSCREEN, + FASTQC, + MULTIQC, + } + + // Define some default profiles for the tool selection + + static Map ToolProfiles = [ + + // Tool trackers do not need to explicitly define all tools + + DEFAULT: new ToolTracker(tool_selection: [FASTQSCREEN: false, FASTQC: true]), + + FULL: new ToolTracker(tool_selection: [FASTQSCREEN: true, FASTQC: true]), + + MINIMAL: new ToolTracker(tool_selection: [FASTQC: true]), + + ] + +} diff --git a/lib/ToolTracker.groovy b/lib/ToolTracker.groovy new file mode 100644 index 0000000..f15fc49 --- /dev/null +++ b/lib/ToolTracker.groovy @@ -0,0 +1,92 @@ +/* Example usage of the ToolTracker class to define and intersect selections of tools + +def profile1 = new ToolTracker() +profile1['tool1'] = true +profile1['tool2'] = false +profile1['tool3'] = false + +def profile2 = new ToolTracker() +profile2['tool1'] = false +profile2['tool2'] = true + +def andResult = profile1.andOperation(profile2) +def orResult = profile1.orOperation(profile2) + +println "AND Result: ${andResult.tool_selection}" +println "OR Result: ${orResult.tool_selection}" + +*/ + +// ToolTracker class to define and intersect selections of tools + +class ToolTracker { + Map tool_selection = [:] + + // Override getAt method for concise access + Boolean getAt(String tool) { + return tool_selection[tool] + } + + // Override putAt method for concise assignment + void putAt(String tool, Boolean setting) { + tool_selection[tool] = setting + } + + // Method to perform AND operation + public ToolTracker andOperation(ToolTracker other) { + ToolTracker result = new ToolTracker() + this.tool_selection.each { tool, setting -> + if (other.tool_selection.containsKey(tool)) { + result[tool] = setting && other[tool] + } + } + return result + } + + // Method to perform OR operation + public ToolTracker orOperation(ToolTracker other) { + ToolTracker result = new ToolTracker() + this.tool_selection.each { tool, setting -> + if (other.tool_selection.containsKey(tool)) { + result[tool] = setting || other[tool] + } + } + return result + } + + // Method to perform UnionOR operation: Retain entries that exists in either of the ToolTracker instances, but not both + public ToolTracker unionOrOperation(ToolTracker other) { + ToolTracker result = new ToolTracker() + Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() + + allTools.each { tool -> + if (this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { + result[tool] = this[tool] || other[tool] + } else if (this.tool_selection.containsKey(tool) && !other.tool_selection.containsKey(tool)) { + result[tool] = this[tool] + } else if (!this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { + result[tool] = other[tool] + } + } + + return result + } + + // Method to perform exclusiveOR operation: Retain entries that exists in either of the ToolTracker instances, but not both + public ToolTracker exclusiveOrOperation(ToolTracker other) { + ToolTracker result = new ToolTracker() + Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() + + allTools.each { tool -> + if (this.tool_selection.containsKey(tool) && !other.tool_selection.containsKey(tool)) { + result[tool] = this[tool] + } else if (!this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { + result[tool] = other[tool] + } + } + + return result + } + +} + diff --git a/lib/Utililities.groovy b/lib/Utililities.groovy new file mode 100644 index 0000000..2e0f888 --- /dev/null +++ b/lib/Utililities.groovy @@ -0,0 +1,85 @@ +// Class to hold ultility functions + +class Ultilities { + + public static getEnumFromString(s, e) { + try { + return e.valueOf(s.toUpperCase()) + } catch(java.lang.IllegalArgumentException err) { + return null + } + } + + public static getEnumNames(e) { + e + .values() + *.name() + *.toLowerCase() + } + + + public static getAllToolNames() { + Constants.Tool + .values() + *.name() + *.toLowerCase() + } + + + public static getRunMode(run_mode, log) { + def run_mode_enum = Ultilities.getEnumFromString(run_mode, Constants.RunMode) + if (!run_mode_enum) { + def run_modes_str = Ultilities.getEnumNames(Constants.RunMode).join('\n - ') + log.error "Invalid run mode selected: '${run_mode}'. Valid options are:\n - ${run_modes_str}" + Nextflow.exit(1) + } + return run_mode_enum + } + + public static getRunStages(include, exclude, manual_select, log) { + def processes = manual_select ? [] : Constants.Process.values().toList() + def include_list = this.getProcessList(include, log) + def exclude_list = this.getProcessList(exclude, log) + this.checkIncludeExcludeList(include_list, exclude_list, log) + + processes.addAll(include_list) + processes.removeAll(exclude_list) + + return Constants.Process + .values() + .collectEntries { p -> [p.name().toLowerCase(), p in processes] } + } + + public static getProcessList(process_str, log) { + if (!process_str) { + return [] + } + return process_str + .tokenize(',') + .collect { name -> + try { + return Constants.Process.valueOf(name.toUpperCase()) + } catch(java.lang.IllegalArgumentException e) { + def processes_str = Processes.getProcessNames().join('\n - ') + log.error "recieved invalid process: '${name}'. Valid options are:\n - ${processes_str}" + nextflow.Nextflow.exit(1) + } + } + .unique() + } + + public static checkIncludeExcludeList(include_list, exclude_list, log) { + def processes_shared = [*include_list, *exclude_list] + .countBy { it } + .findAll { k, v -> v > 1 } + .keySet() + + if (processes_shared) { + def processes_shared_str = processes_shared.join('\n - ') + def message_base = 'the following processes was found in the include and the exclude list' + log.error "${message_base}:\n - ${processes_shared_str}" + nextflow.Nextflow.exit(1) + } + } + +} From 7f510e339779e347efe02ac2464605e1c5b1cd1d Mon Sep 17 00:00:00 2001 From: Matthias Zepper Date: Wed, 25 Sep 2024 21:13:56 +0200 Subject: [PATCH 2/9] Extend utility functions for profile operations. --- lib/Constants.groovy | 4 +- lib/ToolTracker.groovy | 6 +- lib/Utililities.groovy | 133 +++++++++++++++++++++++++++++------------ 3 files changed, 98 insertions(+), 45 deletions(-) diff --git a/lib/Constants.groovy b/lib/Constants.groovy index 164ba3b..f9acb17 100644 --- a/lib/Constants.groovy +++ b/lib/Constants.groovy @@ -12,12 +12,12 @@ class Constants { static enum Tool { FASTQSCREEN, FASTQC, - MULTIQC, + // MULTIQC, // Turning MultiQC off kind of defeats the purpose of the pipeline } // Define some default profiles for the tool selection - static Map ToolProfiles = [ + static Map ToolProfiles = [ // Tool trackers do not need to explicitly define all tools diff --git a/lib/ToolTracker.groovy b/lib/ToolTracker.groovy index f15fc49..e91d587 100644 --- a/lib/ToolTracker.groovy +++ b/lib/ToolTracker.groovy @@ -54,7 +54,7 @@ class ToolTracker { return result } - // Method to perform UnionOR operation: Retain entries that exists in either of the ToolTracker instances, but not both + // Method to perform UnionOR operation: Retain entries that exists in either of the ToolTracker instances, OR for common entries public ToolTracker unionOrOperation(ToolTracker other) { ToolTracker result = new ToolTracker() Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() @@ -68,11 +68,10 @@ class ToolTracker { result[tool] = other[tool] } } - return result } - // Method to perform exclusiveOR operation: Retain entries that exists in either of the ToolTracker instances, but not both + // Method to perform exclusiveOR (XOR) operation: Retain entries that exists in either of the ToolTracker instances, but not both public ToolTracker exclusiveOrOperation(ToolTracker other) { ToolTracker result = new ToolTracker() Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() @@ -84,7 +83,6 @@ class ToolTracker { result[tool] = other[tool] } } - return result } diff --git a/lib/Utililities.groovy b/lib/Utililities.groovy index 2e0f888..a531459 100644 --- a/lib/Utililities.groovy +++ b/lib/Utililities.groovy @@ -1,8 +1,8 @@ -// Class to hold ultility functions +// Class to hold ultility functions, inspired by nf-core/oncoanalyser class Ultilities { - public static getEnumFromString(s, e) { + public static getEnumFromString(String s, Enum e) { try { return e.valueOf(s.toUpperCase()) } catch(java.lang.IllegalArgumentException err) { @@ -10,7 +10,7 @@ class Ultilities { } } - public static getEnumNames(e) { + public static getEnumNames(Enum e) { e .values() *.name() @@ -19,67 +19,122 @@ class Ultilities { public static getAllToolNames() { - Constants.Tool - .values() - *.name() - *.toLowerCase() + Ultilities.getEnumNames(Constants.Tool) } + // Function to get a valid profile from a ToolProfiles map - public static getRunMode(run_mode, log) { - def run_mode_enum = Ultilities.getEnumFromString(run_mode, Constants.RunMode) - if (!run_mode_enum) { - def run_modes_str = Ultilities.getEnumNames(Constants.RunMode).join('\n - ') - log.error "Invalid run mode selected: '${run_mode}'. Valid options are:\n - ${run_modes_str}" - Nextflow.exit(1) - } - return run_mode_enum - } - - public static getRunStages(include, exclude, manual_select, log) { - def processes = manual_select ? [] : Constants.Process.values().toList() - def include_list = this.getProcessList(include, log) - def exclude_list = this.getProcessList(exclude, log) - this.checkIncludeExcludeList(include_list, exclude_list, log) + public static getProfileFromToolProfiles(String profile, Map toolProfiles, log) { - processes.addAll(include_list) - processes.removeAll(exclude_list) + if (!toolProfiles) { + toolProfiles = Constants.ToolProfiles + } - return Constants.Process - .values() - .collectEntries { p -> [p.name().toLowerCase(), p in processes] } + if (!toolProfiles.containsKey(profile.toUpperCase())) { + def keys = toolProfiles.keySet().toLowerCase().join('\n - ') + log.error "Invalid profile specified: '${profile}'. Valid options are:\n - ${keys}" + nextflow.Nextflow.exit(1) + } + return map[key] } - public static getProcessList(process_str, log) { - if (!process_str) { + // Function to convert a comma-seperated string of tools to a list of Tool enums + + public static getToolList(String tool_str, log) { + if (!tool_str) { return [] } - return process_str + return tool_str .tokenize(',') - .collect { name -> + .collect { token -> try { - return Constants.Process.valueOf(name.toUpperCase()) + return Constants.Tool.valueOf(token.toUpperCase()) } catch(java.lang.IllegalArgumentException e) { - def processes_str = Processes.getProcessNames().join('\n - ') - log.error "recieved invalid process: '${name}'. Valid options are:\n - ${processes_str}" + def all_tools = Ultilities.getAllToolNames().join('\n - ') + log.error "Recieved invalid tool specification: '${token}'. Valid options are:\n - ${all_tools}" nextflow.Nextflow.exit(1) } } .unique() } - public static checkIncludeExcludeList(include_list, exclude_list, log) { - def processes_shared = [*include_list, *exclude_list] + // Function to check if the include and exclude lists have any common elements + + public static checkIncludeExcludeList(List include_list, List exclude_list, log) { + def common_tools = [*include_list, *exclude_list] .countBy { it } .findAll { k, v -> v > 1 } .keySet() - if (processes_shared) { - def processes_shared_str = processes_shared.join('\n - ') - def message_base = 'the following processes was found in the include and the exclude list' + if (common_tools) { + def common_tools_str = common_tools.values().join('\n - ') + def message_base = 'The following tools were found in the include and the exclude lists!' log.error "${message_base}:\n - ${processes_shared_str}" nextflow.Nextflow.exit(1) } } + // Create a ToolTracker from the include and exclude lists + + public static buildToolTracker(List include_tools, List exclude_tools) { + def tool_tracker = new ToolTracker() + include_tools + .each { tool -> + tool_tracker[tool.name()] = true + } + exclude_tools + .each { tool -> + tool_tracker[tool.name()] = false + } + return tool_tracker + } + + public parseAndApplyBooleanOperation(String profileString, Map toolProfiles, log) { + def tokens = profileString.tokenize(' ') + + // A valid string must always consist of an odd number of tokens, e.g. "default" or "default AND minimal" + if (tokens.size() % 2 == 0) { + log.error("Invalid profile operation specified: $profileString") + nextflow.Nextflow.exit(1) + } + + def result = Ultilities.getProfileFromToolProfiles([tokens[0],toolProfiles,log) + + // Sequentially apply the operations in a left to right manner + + for (int i = 1; i < tokens.size(); i += 2) { + def operation = tokens[i].toUpperCase() + def nextProfile = Ultilities.getProfileFromToolProfiles([tokens[i + 1],toolProfiles,log) + + // New Nextflow syntax no longer supports Java-style switch statements + + if (operation == "AND") { + result = result.andOperation(nextProfile) + } else if (operation == "OR") { + result = result.orOperation(nextProfile) + } else if (operation == "UOR") { + result = result.unionOrOperation(nextProfile) + } else if (operation == "XOR") { + result = result.exclusiveOrOperation(nextProfile) + } else { + log.error("Unsupported operation: $operation") + nextflow.Nextflow.exit(1) + } + } + + return result + } + + // Function to convert the run mode string to a RunMode enum (check for validity) + + public static getRunMode(String run_mode, log) { + def run_mode_enum = Ultilities.getEnumFromString(run_mode, Constants.RunMode) + if (!run_mode_enum) { + def run_modes_str = Ultilities.getEnumNames(Constants.RunMode).join('\n - ') + log.error "Invalid run mode selected: '${run_mode}'. Valid options are:\n - ${run_modes_str}" + Nextflow.exit(1) + } + return run_mode_enum + } + } From 7da020823c7b4d0e1e25236028f37e0e3cb68382 Mon Sep 17 00:00:00 2001 From: Matthias Zepper Date: Thu, 26 Sep 2024 15:38:41 +0200 Subject: [PATCH 3/9] Rewrite Boolean algebra of ToolTracker class to retain non-common elements. --- lib/ToolTracker.groovy | 49 ++++++++++++++++++++++++++++-------------- lib/Utililities.groovy | 6 +++--- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/lib/ToolTracker.groovy b/lib/ToolTracker.groovy index e91d587..1cbb6cc 100644 --- a/lib/ToolTracker.groovy +++ b/lib/ToolTracker.groovy @@ -32,30 +32,28 @@ class ToolTracker { tool_selection[tool] = setting } - // Method to perform AND operation + // There is actually no use for non-union methods, since that would eliminate entries from the maps. + + + // Method to perform AND operation: Perform AND common entries and set the rest to false (interpret absence as false) public ToolTracker andOperation(ToolTracker other) { ToolTracker result = new ToolTracker() - this.tool_selection.each { tool, setting -> - if (other.tool_selection.containsKey(tool)) { - result[tool] = setting && other[tool] - } - } - return result - } + Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() - // Method to perform OR operation - public ToolTracker orOperation(ToolTracker other) { - ToolTracker result = new ToolTracker() - this.tool_selection.each { tool, setting -> - if (other.tool_selection.containsKey(tool)) { - result[tool] = setting || other[tool] + allTools.each { tool -> + if (this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { + result[tool] = this[tool] && other[tool] + } else if (this.tool_selection.containsKey(tool) && !other.tool_selection.containsKey(tool)) { + result[tool] = false + } else if (!this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { + result[tool] = false } } return result } // Method to perform UnionOR operation: Retain entries that exists in either of the ToolTracker instances, OR for common entries - public ToolTracker unionOrOperation(ToolTracker other) { + public ToolTracker orOperation(ToolTracker other) { ToolTracker result = new ToolTracker() Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() @@ -72,7 +70,7 @@ class ToolTracker { } // Method to perform exclusiveOR (XOR) operation: Retain entries that exists in either of the ToolTracker instances, but not both - public ToolTracker exclusiveOrOperation(ToolTracker other) { + public ToolTracker xorOperation(ToolTracker other) { ToolTracker result = new ToolTracker() Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() @@ -81,6 +79,25 @@ class ToolTracker { result[tool] = this[tool] } else if (!this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { result[tool] = other[tool] + } else { + result[tool] = false + } + } + return result + } + + // Method to perform xorAND operation: Retain entries that exists in either of the ToolTracker instances, AND conjunction for common entries + public ToolTracker xorAndOperation(ToolTracker other) { + ToolTracker result = new ToolTracker() + Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() + + allTools.each { tool -> + if (this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { + result[tool] = this[tool] && other[tool] + } else if (this.tool_selection.containsKey(tool) && !other.tool_selection.containsKey(tool)) { + result[tool] = this[tool] + } else if (!this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { + result[tool] = other[tool] } } return result diff --git a/lib/Utililities.groovy b/lib/Utililities.groovy index a531459..0394749 100644 --- a/lib/Utililities.groovy +++ b/lib/Utililities.groovy @@ -112,10 +112,10 @@ class Ultilities { result = result.andOperation(nextProfile) } else if (operation == "OR") { result = result.orOperation(nextProfile) - } else if (operation == "UOR") { - result = result.unionOrOperation(nextProfile) + } else if (operation == "XORAND") { + result = result.xorAndOperationOperation(nextProfile) } else if (operation == "XOR") { - result = result.exclusiveOrOperation(nextProfile) + result = result.xorOperation(nextProfile) } else { log.error("Unsupported operation: $operation") nextflow.Nextflow.exit(1) From f12d5f267410940309b55f47c8456f953561bffc Mon Sep 17 00:00:00 2001 From: Matthias Zepper Date: Fri, 27 Sep 2024 16:59:06 +0200 Subject: [PATCH 4/9] Add new pipeline parameters to the schema. --- lib/Constants.groovy | 6 ++++-- nextflow_schema.json | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/Constants.groovy b/lib/Constants.groovy index f9acb17..b32edaf 100644 --- a/lib/Constants.groovy +++ b/lib/Constants.groovy @@ -21,12 +21,14 @@ class Constants { // Tool trackers do not need to explicitly define all tools - DEFAULT: new ToolTracker(tool_selection: [FASTQSCREEN: false, FASTQC: true]), + ALL: Ultilities.buildToolTracker(Constants.Tool.values().toList(), []), - FULL: new ToolTracker(tool_selection: [FASTQSCREEN: true, FASTQC: true]), + DEFAULT: new ToolTracker(tool_selection: [FASTQSCREEN: false, FASTQC: true]), MINIMAL: new ToolTracker(tool_selection: [FASTQC: true]), + NONE: Ultilities.buildToolTracker([], Constants.Tool.values().toList()), + ] } diff --git a/nextflow_schema.json b/nextflow_schema.json index 36308a0..0a4b30c 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema", + "$schema": "https://json-schema.org/draft-07/schema", "$id": "https://raw.githubusercontent.com/nf-core/seqinspector/master/nextflow_schema.json", "title": "nf-core/seqinspector pipeline parameters", "description": "Pipeline to QC your sequences", @@ -43,6 +43,34 @@ } } }, + "tool_selection_options": { + "title": "Tool selection options", + "type": "object", + "description": "Select the quality checks and tools you wish to run.", + "default": "", + "properties": { + "tool_selection": { + "type": "string", + "default": "default", + "description": "Pick and apply profiles to select tools. Choose from the profiles `none`, `minimal`,`default`, `all` and the operations `AND`, `OR`, `XOR` as well as `XORAND`.", + "fa_icon": "fab fa-blackberry", + "pattern": "^\\w+(\\s(AND|OR|XOR|XORAND)\\s\\w+)*$" + }, + "tools_include": { + "type": "string", + "fa_icon": "fas fa-search-plus", + "description": "Comma-seperated list of tools that you wish to run. The selection will be applied after the profiles and added to the final list.", + "pattern": "^(\\w+(,\\s\\w+)*)?$" + }, + "tools_exclude": { + "type": "string", + "fa_icon": "fas fa-search-minus", + "description": "Comma-seperated list of tools that must not run. The selection will be subtracted from the evaluated profiles.", + "pattern": "^(\\w+(,\\s\\w+)*)?$" + } + }, + "required": ["tool_selection"] + }, "reference_genome_options": { "title": "Reference genome options", "type": "object", @@ -280,6 +308,9 @@ { "$ref": "#/definitions/input_output_options" }, + { + "$ref": "#/definitions/tool_selection_options" + }, { "$ref": "#/definitions/reference_genome_options" }, From e008c462712c524b09c6a74f3a0fc11d143d852b Mon Sep 17 00:00:00 2001 From: Matthias Zepper Date: Fri, 27 Sep 2024 19:42:22 +0200 Subject: [PATCH 5/9] Implement tool parameter parsing and conditional application. --- lib/Constants.groovy | 4 +-- lib/ToolTracker.groovy | 4 +-- lib/Utililities.groovy | 4 +-- nextflow_schema.json | 4 +-- .../main.nf | 28 +++++++++++++++++++ workflows/seqinspector.nf | 3 ++ 6 files changed, 39 insertions(+), 8 deletions(-) diff --git a/lib/Constants.groovy b/lib/Constants.groovy index b32edaf..a91d190 100644 --- a/lib/Constants.groovy +++ b/lib/Constants.groovy @@ -21,13 +21,13 @@ class Constants { // Tool trackers do not need to explicitly define all tools - ALL: Ultilities.buildToolTracker(Constants.Tool.values().toList(), []), + ALL: Ultilities.buildToolTracker(Constants.Tool.values().toList(), []), // generate a tool tracker with all tools set to true DEFAULT: new ToolTracker(tool_selection: [FASTQSCREEN: false, FASTQC: true]), MINIMAL: new ToolTracker(tool_selection: [FASTQC: true]), - NONE: Ultilities.buildToolTracker([], Constants.Tool.values().toList()), + NONE: Ultilities.buildToolTracker([], Constants.Tool.values().toList()), // generate a tool tracker with all tools set to false ] diff --git a/lib/ToolTracker.groovy b/lib/ToolTracker.groovy index 1cbb6cc..01b48f9 100644 --- a/lib/ToolTracker.groovy +++ b/lib/ToolTracker.groovy @@ -86,8 +86,8 @@ class ToolTracker { return result } - // Method to perform xorAND operation: Retain entries that exists in either of the ToolTracker instances, AND conjunction for common entries - public ToolTracker xorAndOperation(ToolTracker other) { + // Method to perform inclusiveAND operation: Retain entries that exists in either of the ToolTracker instances, AND conjunction for common entries + public ToolTracker iAndOperation(ToolTracker other) { ToolTracker result = new ToolTracker() Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() diff --git a/lib/Utililities.groovy b/lib/Utililities.groovy index 0394749..47a2100 100644 --- a/lib/Utililities.groovy +++ b/lib/Utililities.groovy @@ -112,8 +112,8 @@ class Ultilities { result = result.andOperation(nextProfile) } else if (operation == "OR") { result = result.orOperation(nextProfile) - } else if (operation == "XORAND") { - result = result.xorAndOperationOperation(nextProfile) + } else if (operation == "IAND") { + result = result.iAndOperationOperation(nextProfile) } else if (operation == "XOR") { result = result.xorOperation(nextProfile) } else { diff --git a/nextflow_schema.json b/nextflow_schema.json index 0a4b30c..b91a2df 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -52,9 +52,9 @@ "tool_selection": { "type": "string", "default": "default", - "description": "Pick and apply profiles to select tools. Choose from the profiles `none`, `minimal`,`default`, `all` and the operations `AND`, `OR`, `XOR` as well as `XORAND`.", + "description": "Pick and apply profiles to select tools. Choose from the profiles `none`, `minimal`,`default`, `all` and the operations `AND`, `OR`, `XOR` as well as `IAND`.", "fa_icon": "fab fa-blackberry", - "pattern": "^\\w+(\\s(AND|OR|XOR|XORAND)\\s\\w+)*$" + "pattern": "^\\w+(\\s(AND|OR|XOR|IAND)\\s\\w+)*$" }, "tools_include": { "type": "string", diff --git a/subworkflows/local/utils_nfcore_seqinspector_pipeline/main.nf b/subworkflows/local/utils_nfcore_seqinspector_pipeline/main.nf index 3c63037..68aa30a 100644 --- a/subworkflows/local/utils_nfcore_seqinspector_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_seqinspector_pipeline/main.nf @@ -77,6 +77,34 @@ workflow PIPELINE_INITIALISATION { // validateInputParameters() // Runs additional validation that is not done by $projectDir/nextflow_schema.json + // + // Process the tool selection parameters to generate the final list of tools (processes) to run + // + def seqinspector_tools = Constants.ToolProfiles["NONE"] // Start with no tools (except MultiQC) + + // Activate the tools that are selected through the applied profiles (typically `default`) + if (params.tool_selection) { + def evaluated_profiles = Utilities.parseAndApplyBooleanOperation(params.tool_selection, Constants.ToolProfiles, log) + seqinspector_tools = seqinspector_tools.orOperation(evaluated_profiles) + } + + if (params.tools_include || params.tools_exclude) { + def tool_include_list = Utilities.getToolList(params.tools_include, log) + def tool_exclude_list = Utilities.getToolList(params.tools_exclude, log) + + // Check for overlapping include and exclude list entries + Utilities.checkIncludeExcludeList(tool_include_list, tool_exclude_list, log) + + // Convert the specified custom tools into a profile + def custom_tools = Utilities.buildToolTracker(tool_include_list, tool_exclude_list) + + // Apply the custom tools to the existing tool profile: orOperation to activate the include_tools + seqinspector_tools = seqinspector_tools.orOperation(custom_tools) + + // Apply the custom tools to the existing tool profile: iAndOperation to deactivate the exclude_tools + seqinspector_tools = seqinspector_tools.iAndOperation(custom_tools) + } + // // Create channel from input file provided through params.input // diff --git a/workflows/seqinspector.nf b/workflows/seqinspector.nf index 1ba00c6..71db3d2 100644 --- a/workflows/seqinspector.nf +++ b/workflows/seqinspector.nf @@ -32,6 +32,8 @@ workflow SEQINSPECTOR { ch_multiqc_extra_files = Channel.empty() ch_multiqc_reports = Channel.empty() + + if (seqinspector_tools['FASTQC']){ // // MODULE: Run FastQC // @@ -40,6 +42,7 @@ workflow SEQINSPECTOR { ) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip) ch_versions = ch_versions.mix(FASTQC.out.versions.first()) + } // // Collate and save software versions From c7debff37215a5e885fa9c21debd194fd7ba30ac Mon Sep 17 00:00:00 2001 From: Matthias Zepper Date: Thu, 17 Oct 2024 15:15:04 +0200 Subject: [PATCH 6/9] Start refactor of tool profile functionality according to draft PR feedback. --- lib/Constants.groovy | 34 ---------- lib/ToolProfile.groovy | 25 +++++++ .../local/utils_seqinspector_profiles/main.nf | 67 ++++++------------- .../local/utils_seqinspector_tools/main.nf | 20 ++++++ .../utils_seqinspector_validation/main.nf | 0 5 files changed, 66 insertions(+), 80 deletions(-) delete mode 100644 lib/Constants.groovy create mode 100644 lib/ToolProfile.groovy rename lib/ToolTracker.groovy => modules/local/utils_seqinspector_profiles/main.nf (60%) create mode 100644 modules/local/utils_seqinspector_tools/main.nf rename lib/Utililities.groovy => modules/local/utils_seqinspector_validation/main.nf (100%) diff --git a/lib/Constants.groovy b/lib/Constants.groovy deleted file mode 100644 index a91d190..0000000 --- a/lib/Constants.groovy +++ /dev/null @@ -1,34 +0,0 @@ -// Class to hold constants - -class Constants { - - static enum RunMode { - FASTQ, - RUNFOLDER, - } - - // Define all available tools centrally here - - static enum Tool { - FASTQSCREEN, - FASTQC, - // MULTIQC, // Turning MultiQC off kind of defeats the purpose of the pipeline - } - - // Define some default profiles for the tool selection - - static Map ToolProfiles = [ - - // Tool trackers do not need to explicitly define all tools - - ALL: Ultilities.buildToolTracker(Constants.Tool.values().toList(), []), // generate a tool tracker with all tools set to true - - DEFAULT: new ToolTracker(tool_selection: [FASTQSCREEN: false, FASTQC: true]), - - MINIMAL: new ToolTracker(tool_selection: [FASTQC: true]), - - NONE: Ultilities.buildToolTracker([], Constants.Tool.values().toList()), // generate a tool tracker with all tools set to false - - ] - -} diff --git a/lib/ToolProfile.groovy b/lib/ToolProfile.groovy new file mode 100644 index 0000000..0857616 --- /dev/null +++ b/lib/ToolProfile.groovy @@ -0,0 +1,25 @@ +// enum for the supported run modes + +static enum RunMode { + FASTQ, + RUNFOLDER, + } + +// enum for the available Tools + +static enum Tool { + FASTQSCREEN, + FASTQC, + // MULTIQC, // Turning MultiQC off kind of defeats the purpose of the pipeline + } + +// ToolProfile class to define and intersect selections of tools + +class ToolProfile { + Set enable + Set disable + Map> tool_arguments = [:] +} + + + diff --git a/lib/ToolTracker.groovy b/modules/local/utils_seqinspector_profiles/main.nf similarity index 60% rename from lib/ToolTracker.groovy rename to modules/local/utils_seqinspector_profiles/main.nf index 01b48f9..541eb8d 100644 --- a/lib/ToolTracker.groovy +++ b/modules/local/utils_seqinspector_profiles/main.nf @@ -1,43 +1,21 @@ -/* Example usage of the ToolTracker class to define and intersect selections of tools - -def profile1 = new ToolTracker() -profile1['tool1'] = true -profile1['tool2'] = false -profile1['tool3'] = false - -def profile2 = new ToolTracker() -profile2['tool1'] = false -profile2['tool2'] = true - -def andResult = profile1.andOperation(profile2) -def orResult = profile1.orOperation(profile2) - -println "AND Result: ${andResult.tool_selection}" -println "OR Result: ${orResult.tool_selection}" +/* +======================================================================================== + CLASSES +======================================================================================== */ -// ToolTracker class to define and intersect selections of tools - -class ToolTracker { - Map tool_selection = [:] - - // Override getAt method for concise access - Boolean getAt(String tool) { - return tool_selection[tool] - } - - // Override putAt method for concise assignment - void putAt(String tool, Boolean setting) { - tool_selection[tool] = setting - } - // There is actually no use for non-union methods, since that would eliminate entries from the maps. +/* +======================================================================================== + FUNCTIONS +======================================================================================== +*/ - // Method to perform AND operation: Perform AND common entries and set the rest to false (interpret absence as false) - public ToolTracker andOperation(ToolTracker other) { - ToolTracker result = new ToolTracker() + // Method to perform AND operation: Perform AND common entries and set the rest to false (interpret absence as false) + public ToolProfile andOperation(ToolProfile other) { + ToolProfile result = new ToolProfile() Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() allTools.each { tool -> @@ -52,9 +30,9 @@ class ToolTracker { return result } - // Method to perform UnionOR operation: Retain entries that exists in either of the ToolTracker instances, OR for common entries - public ToolTracker orOperation(ToolTracker other) { - ToolTracker result = new ToolTracker() + // Method to perform UnionOR operation: Retain entries that exists in either of the ToolProfile instances, OR for common entries + public ToolProfile orOperation(ToolProfile other) { + ToolProfile result = new ToolProfile() Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() allTools.each { tool -> @@ -69,9 +47,9 @@ class ToolTracker { return result } - // Method to perform exclusiveOR (XOR) operation: Retain entries that exists in either of the ToolTracker instances, but not both - public ToolTracker xorOperation(ToolTracker other) { - ToolTracker result = new ToolTracker() + // Method to perform exclusiveOR (XOR) operation: Retain entries that exists in either of the ToolProfile instances, but not both + public ToolProfile xorOperation(ToolProfile other) { + ToolProfile result = new ToolProfile() Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() allTools.each { tool -> @@ -86,9 +64,9 @@ class ToolTracker { return result } - // Method to perform inclusiveAND operation: Retain entries that exists in either of the ToolTracker instances, AND conjunction for common entries - public ToolTracker iAndOperation(ToolTracker other) { - ToolTracker result = new ToolTracker() + // Method to perform inclusiveAND operation: Retain entries that exists in either of the ToolProfile instances, AND conjunction for common entries + public ToolProfile iAndOperation(ToolProfile other) { + ToolProfile result = new ToolProfile() Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() allTools.each { tool -> @@ -102,6 +80,3 @@ class ToolTracker { } return result } - -} - diff --git a/modules/local/utils_seqinspector_tools/main.nf b/modules/local/utils_seqinspector_tools/main.nf new file mode 100644 index 0000000..176a3ca --- /dev/null +++ b/modules/local/utils_seqinspector_tools/main.nf @@ -0,0 +1,20 @@ +/* +======================================================================================== + CLASSES +======================================================================================== +*/ + + +enum SeqinspectorTools { + FASTQC, + FASTQSCREEN, + MULTIQC, + // ... +} + + +/* +======================================================================================== + FUNCTIONS +======================================================================================== +*/ diff --git a/lib/Utililities.groovy b/modules/local/utils_seqinspector_validation/main.nf similarity index 100% rename from lib/Utililities.groovy rename to modules/local/utils_seqinspector_validation/main.nf From 15cbf01c32b4064b07eddaf37b5133e23f5fc179 Mon Sep 17 00:00:00 2001 From: Matthias Zepper Date: Fri, 18 Oct 2024 15:41:56 +0200 Subject: [PATCH 7/9] Refactor validation functions. --- ....groovy => SeqinspectorDataClasses.groovy} | 6 +- .../utils_seqinspector_validation/main.nf | 150 ++++++------------ 2 files changed, 55 insertions(+), 101 deletions(-) rename lib/{ToolProfile.groovy => SeqinspectorDataClasses.groovy} (74%) diff --git a/lib/ToolProfile.groovy b/lib/SeqinspectorDataClasses.groovy similarity index 74% rename from lib/ToolProfile.groovy rename to lib/SeqinspectorDataClasses.groovy index 0857616..19ec11d 100644 --- a/lib/ToolProfile.groovy +++ b/lib/SeqinspectorDataClasses.groovy @@ -5,15 +5,15 @@ static enum RunMode { RUNFOLDER, } -// enum for the available Tools +// enum for the available tools static enum Tool { FASTQSCREEN, FASTQC, - // MULTIQC, // Turning MultiQC off kind of defeats the purpose of the pipeline + MULTIQC, } -// ToolProfile class to define and intersect selections of tools +// ToolProfile class to define and intersect selections of tools and handle extra arguments and settings class ToolProfile { Set enable diff --git a/modules/local/utils_seqinspector_validation/main.nf b/modules/local/utils_seqinspector_validation/main.nf index 47a2100..dac1ca1 100644 --- a/modules/local/utils_seqinspector_validation/main.nf +++ b/modules/local/utils_seqinspector_validation/main.nf @@ -1,46 +1,45 @@ -// Class to hold ultility functions, inspired by nf-core/oncoanalyser +/* +======================================================================================== + FUNCTIONS +======================================================================================== +*/ -class Ultilities { - public static getEnumFromString(String s, Enum e) { - try { - return e.valueOf(s.toUpperCase()) - } catch(java.lang.IllegalArgumentException err) { - return null - } - } - - public static getEnumNames(Enum e) { - e - .values() - *.name() - *.toLowerCase() - } +// Function to convert a string to an enum variant (enums give you a way of saying a value is one of a possible set of values) + /* + String s + Enum e + */ - public static getAllToolNames() { - Ultilities.getEnumNames(Constants.Tool) +def getEnumFromString(s, e) { + try { + return e.valueOf(s.toUpperCase()) + } catch(java.lang.IllegalArgumentException err) { + return null } +} - // Function to get a valid profile from a ToolProfiles map +// Function to return all possible names of an enum + /* + Enum e + */ - public static getProfileFromToolProfiles(String profile, Map toolProfiles, log) { +def getEnumNames(e) { + e + .values() + *.name() + *.toLowerCase() +} - if (!toolProfiles) { - toolProfiles = Constants.ToolProfiles - } - if (!toolProfiles.containsKey(profile.toUpperCase())) { - def keys = toolProfiles.keySet().toLowerCase().join('\n - ') - log.error "Invalid profile specified: '${profile}'. Valid options are:\n - ${keys}" - nextflow.Nextflow.exit(1) - } - return map[key] - } - // Function to convert a comma-seperated string of tools to a list of Tool enums +// Function to convert a comma-seperated string of tools to a list of Tool enums + /* + String tool_str + */ - public static getToolList(String tool_str, log) { +def validateToolList(tool_str, log) { if (!tool_str) { return [] } @@ -48,9 +47,9 @@ class Ultilities { .tokenize(',') .collect { token -> try { - return Constants.Tool.valueOf(token.toUpperCase()) + return SeqinspectorDataClasses.Tool.valueOf(token.toUpperCase()) } catch(java.lang.IllegalArgumentException e) { - def all_tools = Ultilities.getAllToolNames().join('\n - ') + def all_tools = getEnumNames(SeqinspectorDataClasses.Tool).join('\n - ') log.error "Recieved invalid tool specification: '${token}'. Valid options are:\n - ${all_tools}" nextflow.Nextflow.exit(1) } @@ -59,9 +58,13 @@ class Ultilities { } // Function to check if the include and exclude lists have any common elements + /* + List include_list + List exclude_list + */ - public static checkIncludeExcludeList(List include_list, List exclude_list, log) { - def common_tools = [*include_list, *exclude_list] +def checkIncludeExcludeList(include_list, exclude_list, log) { + def common_tools = include_list + exclude_list .countBy { it } .findAll { k, v -> v > 1 } .keySet() @@ -69,72 +72,23 @@ class Ultilities { if (common_tools) { def common_tools_str = common_tools.values().join('\n - ') def message_base = 'The following tools were found in the include and the exclude lists!' - log.error "${message_base}:\n - ${processes_shared_str}" + log.error "${message_base}:\n - ${common_tools_str}" nextflow.Nextflow.exit(1) } - } - - // Create a ToolTracker from the include and exclude lists - - public static buildToolTracker(List include_tools, List exclude_tools) { - def tool_tracker = new ToolTracker() - include_tools - .each { tool -> - tool_tracker[tool.name()] = true - } - exclude_tools - .each { tool -> - tool_tracker[tool.name()] = false - } - return tool_tracker - } - - public parseAndApplyBooleanOperation(String profileString, Map toolProfiles, log) { - def tokens = profileString.tokenize(' ') - - // A valid string must always consist of an odd number of tokens, e.g. "default" or "default AND minimal" - if (tokens.size() % 2 == 0) { - log.error("Invalid profile operation specified: $profileString") - nextflow.Nextflow.exit(1) - } - - def result = Ultilities.getProfileFromToolProfiles([tokens[0],toolProfiles,log) - - // Sequentially apply the operations in a left to right manner - - for (int i = 1; i < tokens.size(); i += 2) { - def operation = tokens[i].toUpperCase() - def nextProfile = Ultilities.getProfileFromToolProfiles([tokens[i + 1],toolProfiles,log) - - // New Nextflow syntax no longer supports Java-style switch statements +} - if (operation == "AND") { - result = result.andOperation(nextProfile) - } else if (operation == "OR") { - result = result.orOperation(nextProfile) - } else if (operation == "IAND") { - result = result.iAndOperationOperation(nextProfile) - } else if (operation == "XOR") { - result = result.xorOperation(nextProfile) - } else { - log.error("Unsupported operation: $operation") - nextflow.Nextflow.exit(1) - } - } - return result - } +// Function to validate the run mode by comparing it to the enumerated values of a constant + /* + String run_mode + */ - // Function to convert the run mode string to a RunMode enum (check for validity) - - public static getRunMode(String run_mode, log) { - def run_mode_enum = Ultilities.getEnumFromString(run_mode, Constants.RunMode) - if (!run_mode_enum) { - def run_modes_str = Ultilities.getEnumNames(Constants.RunMode).join('\n - ') - log.error "Invalid run mode selected: '${run_mode}'. Valid options are:\n - ${run_modes_str}" - Nextflow.exit(1) - } - return run_mode_enum +def validateRunMode(run_mode, log) { + def run_mode_enum = getEnumFromString(run_mode, SeqinspectorDataClasses.RunMode) + if (!run_mode_enum) { + def run_modes_str = getEnumNames(SeqinspectorDataClasses.RunMode).join('\n - ') + log.error "Invalid run mode selected: '${run_mode}'. Valid options are:\n - ${run_modes_str}" + nextflow.Nextflow.exit(1) } - + return run_mode_enum } From 0c3f7cd78b003b034b38763a068faafe8cc5f03b Mon Sep 17 00:00:00 2001 From: Matthias Zepper Date: Mon, 21 Oct 2024 14:48:41 +0200 Subject: [PATCH 8/9] Continue refactor of ToolProfiles. --- .../local/utils_seqinspector_profiles/main.nf | 125 +++++++++--------- .../local/utils_seqinspector_tools/main.nf | 18 +-- 2 files changed, 65 insertions(+), 78 deletions(-) diff --git a/modules/local/utils_seqinspector_profiles/main.nf b/modules/local/utils_seqinspector_profiles/main.nf index 541eb8d..3a41072 100644 --- a/modules/local/utils_seqinspector_profiles/main.nf +++ b/modules/local/utils_seqinspector_profiles/main.nf @@ -1,82 +1,77 @@ - -/* -======================================================================================== - CLASSES -======================================================================================== -*/ - - /* ======================================================================================== FUNCTIONS ======================================================================================== */ +// Function to get a valid profile from a ToolProfiles map + /* + String profileStr, + Map toolProfilesMap + log + */ - // Method to perform AND operation: Perform AND common entries and set the rest to false (interpret absence as false) - public ToolProfile andOperation(ToolProfile other) { - ToolProfile result = new ToolProfile() - Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() +def getProfileFromToolProfiles(profileStr, toolProfilesMap, log) { - allTools.each { tool -> - if (this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { - result[tool] = this[tool] && other[tool] - } else if (this.tool_selection.containsKey(tool) && !other.tool_selection.containsKey(tool)) { - result[tool] = false - } else if (!this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { - result[tool] = false - } + if (!toolProfilesMap) { + toolProfilesMap = SeqinspectorDataClasses.ToolProfiles } - return result - } - // Method to perform UnionOR operation: Retain entries that exists in either of the ToolProfile instances, OR for common entries - public ToolProfile orOperation(ToolProfile other) { - ToolProfile result = new ToolProfile() - Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() - - allTools.each { tool -> - if (this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { - result[tool] = this[tool] || other[tool] - } else if (this.tool_selection.containsKey(tool) && !other.tool_selection.containsKey(tool)) { - result[tool] = this[tool] - } else if (!this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { - result[tool] = other[tool] - } + if (!toolProfilesMap.containsKey(profileStr.toUpperCase())) { + def keys = toolProfilesMap.keySet().toLowerCase().join('\n - ') + log.error "Invalid profile specified: '${profileStr}'. Valid options are:\n - ${keys}" + nextflow.Nextflow.exit(1) } - return result + return toolProfilesMap[profileStr] } - // Method to perform exclusiveOR (XOR) operation: Retain entries that exists in either of the ToolProfile instances, but not both - public ToolProfile xorOperation(ToolProfile other) { - ToolProfile result = new ToolProfile() - Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() - - allTools.each { tool -> - if (this.tool_selection.containsKey(tool) && !other.tool_selection.containsKey(tool)) { - result[tool] = this[tool] - } else if (!this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { - result[tool] = other[tool] - } else { - result[tool] = false - } - } - return result - } - // Method to perform inclusiveAND operation: Retain entries that exists in either of the ToolProfile instances, AND conjunction for common entries - public ToolProfile iAndOperation(ToolProfile other) { - ToolProfile result = new ToolProfile() - Set allTools = this.tool_selection.keySet() + other.tool_selection.keySet() - - allTools.each { tool -> - if (this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { - result[tool] = this[tool] && other[tool] - } else if (this.tool_selection.containsKey(tool) && !other.tool_selection.containsKey(tool)) { - result[tool] = this[tool] - } else if (!this.tool_selection.containsKey(tool) && other.tool_selection.containsKey(tool)) { - result[tool] = other[tool] +// Function to combine two profiles + /* + String profileStr, + Map toolProfilesMap + log + */ + +def combine_profiles(firstProfile, otherProfile, log) { + + // Create a new ToolProfile instance to store the combined results + def combinedProfile = new SeqinspectorDataClasses.ToolProfile( + enable: (firstProfile.enable + otherProfile.enable), + disable: (firstProfile.disable + otherProfile.disable), + tool_arguments: [:] + ) + + // remove possibly disabled tools + combinedProfile.enable.removeAll(combinedProfile.disable) + + + // Combine tool_arguments maps + def allArgs = firstProfile.tool_arguments.keySet() + otherProfile.tool_arguments.keySet() + allArgs.each { tool -> + def firstArgs = firstProfile.tool_arguments[tool] ?: [:] + def otherArgs = otherProfile.tool_arguments[tool] ?: [:] + + // Check for common arguments specified in both profiles for a tool + def commonArgs = firstArgs.keySet().intersect(otherArgs.keySet()) + // if a common setting for a tool is detected, compare the values + if (commonArgs) { + def incompatibleArgs = false + commonArgs.each { arg -> + if(firstArgs[arg] != otherArgs[arg]) { + log.error "Conflicting settings of argument '${arg}' for tool '${tool}'" + incompatibleArgs = true + } + } + if(incompatibleArgs) { + nextflow.Nextflow.exit(1) } } - return result + combinedProfile.tool_arguments[tool] = firstArgs + otherArgs } + + return combinedProfile + + +} + diff --git a/modules/local/utils_seqinspector_tools/main.nf b/modules/local/utils_seqinspector_tools/main.nf index 176a3ca..c5da0f2 100644 --- a/modules/local/utils_seqinspector_tools/main.nf +++ b/modules/local/utils_seqinspector_tools/main.nf @@ -1,20 +1,12 @@ -/* -======================================================================================== - CLASSES -======================================================================================== -*/ - - -enum SeqinspectorTools { - FASTQC, - FASTQSCREEN, - MULTIQC, - // ... -} +include { getEnumNames } from '../utils_seqinspector_validation/main.nf' /* ======================================================================================== FUNCTIONS ======================================================================================== */ + +def getAllToolNames() { + getEnumNames(SeqinspectorDataClasses.Tool) + } From 64cc996e3139e54c91162b6be86c6e6d8532ed3f Mon Sep 17 00:00:00 2001 From: Matthias Zepper Date: Tue, 22 Oct 2024 14:59:18 +0200 Subject: [PATCH 9/9] Modify parameters and continue refactor. --- lib/SeqinspectorDataClasses.groovy | 6 +++--- modules/local/utils_seqinspector_profiles/main.nf | 2 ++ modules/local/utils_seqinspector_tools/main.nf | 2 ++ .../local/utils_seqinspector_validation/main.nf | 3 +++ nextflow.config | 6 ++++++ nextflow_schema.json | 14 ++++++++++---- .../utils_nfcore_seqinspector_pipeline/main.nf | 8 ++++++-- 7 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/SeqinspectorDataClasses.groovy b/lib/SeqinspectorDataClasses.groovy index 19ec11d..35c836c 100644 --- a/lib/SeqinspectorDataClasses.groovy +++ b/lib/SeqinspectorDataClasses.groovy @@ -1,13 +1,13 @@ // enum for the supported run modes -static enum RunMode { +public static enum RunMode { FASTQ, RUNFOLDER, } // enum for the available tools -static enum Tool { +public static enum Tool { FASTQSCREEN, FASTQC, MULTIQC, @@ -15,7 +15,7 @@ static enum Tool { // ToolProfile class to define and intersect selections of tools and handle extra arguments and settings -class ToolProfile { +public class ToolProfile { Set enable Set disable Map> tool_arguments = [:] diff --git a/modules/local/utils_seqinspector_profiles/main.nf b/modules/local/utils_seqinspector_profiles/main.nf index 3a41072..b40f414 100644 --- a/modules/local/utils_seqinspector_profiles/main.nf +++ b/modules/local/utils_seqinspector_profiles/main.nf @@ -1,3 +1,5 @@ +new GroovyShell().evaluate(new File("$projectDir/lib/SeqinspectorDataClasses.groovy")) + /* ======================================================================================== FUNCTIONS diff --git a/modules/local/utils_seqinspector_tools/main.nf b/modules/local/utils_seqinspector_tools/main.nf index c5da0f2..1925c97 100644 --- a/modules/local/utils_seqinspector_tools/main.nf +++ b/modules/local/utils_seqinspector_tools/main.nf @@ -1,6 +1,8 @@ include { getEnumNames } from '../utils_seqinspector_validation/main.nf' +new GroovyShell().evaluate(new File("$projectDir/lib/SeqinspectorDataClasses.groovy")) + /* ======================================================================================== FUNCTIONS diff --git a/modules/local/utils_seqinspector_validation/main.nf b/modules/local/utils_seqinspector_validation/main.nf index dac1ca1..0bbd44c 100644 --- a/modules/local/utils_seqinspector_validation/main.nf +++ b/modules/local/utils_seqinspector_validation/main.nf @@ -1,3 +1,6 @@ + +new GroovyShell().evaluate(new File("$projectDir/lib/SeqinspectorDataClasses.groovy")) + /* ======================================================================================== FUNCTIONS diff --git a/nextflow.config b/nextflow.config index 70e433a..8332f80 100644 --- a/nextflow.config +++ b/nextflow.config @@ -25,6 +25,12 @@ params { max_multiqc_email_size = '25.MB' multiqc_methods_description = null + // Tool selection options + tool_profile = 'default' + tool_profile_custom_path = null + tools_include = null + tools_exclude = null + // Boilerplate options outdir = null publish_dir_mode = 'copy' diff --git a/nextflow_schema.json b/nextflow_schema.json index b91a2df..e71e1b2 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -49,13 +49,20 @@ "description": "Select the quality checks and tools you wish to run.", "default": "", "properties": { - "tool_selection": { + "tool_profile": { "type": "string", "default": "default", - "description": "Pick and apply profiles to select tools. Choose from the profiles `none`, `minimal`,`default`, `all` and the operations `AND`, `OR`, `XOR` as well as `IAND`.", + "description": "Pick and apply preconfigured profiles to select tools.", "fa_icon": "fab fa-blackberry", "pattern": "^\\w+(\\s(AND|OR|XOR|IAND)\\s\\w+)*$" }, + "tool_profile_custom_path": { + "type": "string", + "description": "Path to a custom profile used instead of the preconfigured ones.", + "format": "file-path", + "mimetype": "text/vnd.yaml", + "fa_icon": "fas fa-clipboard-list" + }, "tools_include": { "type": "string", "fa_icon": "fas fa-search-plus", @@ -68,8 +75,7 @@ "description": "Comma-seperated list of tools that must not run. The selection will be subtracted from the evaluated profiles.", "pattern": "^(\\w+(,\\s\\w+)*)?$" } - }, - "required": ["tool_selection"] + } }, "reference_genome_options": { "title": "Reference genome options", diff --git a/subworkflows/local/utils_nfcore_seqinspector_pipeline/main.nf b/subworkflows/local/utils_nfcore_seqinspector_pipeline/main.nf index 68aa30a..e65634d 100644 --- a/subworkflows/local/utils_nfcore_seqinspector_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_seqinspector_pipeline/main.nf @@ -80,13 +80,17 @@ workflow PIPELINE_INITIALISATION { // // Process the tool selection parameters to generate the final list of tools (processes) to run // - def seqinspector_tools = Constants.ToolProfiles["NONE"] // Start with no tools (except MultiQC) + def seqinspector_tools = new SeqinspectorDataClasses.ToolProfile() // Start with no tools (except MultiQC) + + if(path.tool_profile_custom_path){ + + } else { // Activate the tools that are selected through the applied profiles (typically `default`) if (params.tool_selection) { def evaluated_profiles = Utilities.parseAndApplyBooleanOperation(params.tool_selection, Constants.ToolProfiles, log) seqinspector_tools = seqinspector_tools.orOperation(evaluated_profiles) - } + }} if (params.tools_include || params.tools_exclude) { def tool_include_list = Utilities.getToolList(params.tools_include, log)