add schema
This commit is contained in:
parent
0d230e3930
commit
e486b98a2c
541
lib/NfcoreSchema.groovy
Normal file
541
lib/NfcoreSchema.groovy
Normal file
@ -0,0 +1,541 @@
|
|||||||
|
//
|
||||||
|
// This file holds several functions used to perform JSON parameter validation, help and summary rendering for the nf-core pipeline template.
|
||||||
|
//
|
||||||
|
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 nf-core
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import org.everit.json.schema.Schema
|
||||||
|
import org.everit.json.schema.loader.SchemaLoader
|
||||||
|
import org.everit.json.schema.ValidationException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.json.JSONTokener
|
||||||
|
import org.json.JSONArray
|
||||||
|
import groovy.json.JsonSlurper
|
||||||
|
import groovy.json.JsonBuilder
|
||||||
|
|
||||||
|
class NfcoreSchema {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Resolve Schema path relative to main workflow directory
|
||||||
|
//
|
||||||
|
public static String getSchemaPath(workflow, schema_filename='nextflow_schema.json') {
|
||||||
|
return "${workflow.projectDir}/${schema_filename}"
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function to loop over all parameters defined in schema and check
|
||||||
|
// whether the given parameters adhere to the specifications
|
||||||
|
//
|
||||||
|
/* groovylint-disable-next-line UnusedPrivateMethodParameter */
|
||||||
|
public static void validateParameters(workflow, params, log, schema_filename='nextflow_schema.json') {
|
||||||
|
def has_error = false
|
||||||
|
//=====================================================================//
|
||||||
|
// Check for nextflow core params and unexpected params
|
||||||
|
def json = new File(getSchemaPath(workflow, schema_filename=schema_filename)).text
|
||||||
|
def Map schemaParams = (Map) new JsonSlurper().parseText(json).get('definitions')
|
||||||
|
def nf_params = [
|
||||||
|
// Options for base `nextflow` command
|
||||||
|
'bg',
|
||||||
|
'c',
|
||||||
|
'C',
|
||||||
|
'config',
|
||||||
|
'd',
|
||||||
|
'D',
|
||||||
|
'dockerize',
|
||||||
|
'h',
|
||||||
|
'log',
|
||||||
|
'q',
|
||||||
|
'quiet',
|
||||||
|
'syslog',
|
||||||
|
'v',
|
||||||
|
'version',
|
||||||
|
|
||||||
|
// Options for `nextflow run` command
|
||||||
|
'ansi',
|
||||||
|
'ansi-log',
|
||||||
|
'bg',
|
||||||
|
'bucket-dir',
|
||||||
|
'c',
|
||||||
|
'cache',
|
||||||
|
'config',
|
||||||
|
'dsl2',
|
||||||
|
'dump-channels',
|
||||||
|
'dump-hashes',
|
||||||
|
'E',
|
||||||
|
'entry',
|
||||||
|
'latest',
|
||||||
|
'lib',
|
||||||
|
'main-script',
|
||||||
|
'N',
|
||||||
|
'name',
|
||||||
|
'offline',
|
||||||
|
'params-file',
|
||||||
|
'pi',
|
||||||
|
'plugins',
|
||||||
|
'poll-interval',
|
||||||
|
'pool-size',
|
||||||
|
'profile',
|
||||||
|
'ps',
|
||||||
|
'qs',
|
||||||
|
'queue-size',
|
||||||
|
'r',
|
||||||
|
'resume',
|
||||||
|
'revision',
|
||||||
|
'stdin',
|
||||||
|
'stub',
|
||||||
|
'stub-run',
|
||||||
|
'test',
|
||||||
|
'w',
|
||||||
|
'with-charliecloud',
|
||||||
|
'with-conda',
|
||||||
|
'with-dag',
|
||||||
|
'with-docker',
|
||||||
|
'with-mpi',
|
||||||
|
'with-notification',
|
||||||
|
'with-podman',
|
||||||
|
'with-report',
|
||||||
|
'with-singularity',
|
||||||
|
'with-timeline',
|
||||||
|
'with-tower',
|
||||||
|
'with-trace',
|
||||||
|
'with-weblog',
|
||||||
|
'without-docker',
|
||||||
|
'without-podman',
|
||||||
|
'work-dir'
|
||||||
|
]
|
||||||
|
def unexpectedParams = []
|
||||||
|
|
||||||
|
// Collect expected parameters from the schema
|
||||||
|
def expectedParams = []
|
||||||
|
for (group in schemaParams) {
|
||||||
|
for (p in group.value['properties']) {
|
||||||
|
expectedParams.push(p.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (specifiedParam in params.keySet()) {
|
||||||
|
// nextflow params
|
||||||
|
if (nf_params.contains(specifiedParam)) {
|
||||||
|
log.error "ERROR: You used a core Nextflow option with two hyphens: '--${specifiedParam}'. Please resubmit with '-${specifiedParam}'"
|
||||||
|
has_error = true
|
||||||
|
}
|
||||||
|
// unexpected params
|
||||||
|
def params_ignore = params.schema_ignore_params.split(',') + 'schema_ignore_params'
|
||||||
|
def expectedParamsLowerCase = expectedParams.collect{ it.replace("-", "").toLowerCase() }
|
||||||
|
def specifiedParamLowerCase = specifiedParam.replace("-", "").toLowerCase()
|
||||||
|
def isCamelCaseBug = (specifiedParam.contains("-") && !expectedParams.contains(specifiedParam) && expectedParamsLowerCase.contains(specifiedParamLowerCase))
|
||||||
|
if (!expectedParams.contains(specifiedParam) && !params_ignore.contains(specifiedParam) && !isCamelCaseBug) {
|
||||||
|
// Temporarily remove camelCase/camel-case params #1035
|
||||||
|
def unexpectedParamsLowerCase = unexpectedParams.collect{ it.replace("-", "").toLowerCase()}
|
||||||
|
if (!unexpectedParamsLowerCase.contains(specifiedParamLowerCase)){
|
||||||
|
unexpectedParams.push(specifiedParam)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=====================================================================//
|
||||||
|
// Validate parameters against the schema
|
||||||
|
InputStream input_stream = new File(getSchemaPath(workflow, schema_filename=schema_filename)).newInputStream()
|
||||||
|
JSONObject raw_schema = new JSONObject(new JSONTokener(input_stream))
|
||||||
|
|
||||||
|
// Remove anything that's in params.schema_ignore_params
|
||||||
|
raw_schema = removeIgnoredParams(raw_schema, params)
|
||||||
|
|
||||||
|
Schema schema = SchemaLoader.load(raw_schema)
|
||||||
|
|
||||||
|
// Clean the parameters
|
||||||
|
def cleanedParams = cleanParameters(params)
|
||||||
|
|
||||||
|
// Convert to JSONObject
|
||||||
|
def jsonParams = new JsonBuilder(cleanedParams)
|
||||||
|
JSONObject params_json = new JSONObject(jsonParams.toString())
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
try {
|
||||||
|
schema.validate(params_json)
|
||||||
|
} catch (ValidationException e) {
|
||||||
|
println ''
|
||||||
|
log.error 'ERROR: Validation of pipeline parameters failed!'
|
||||||
|
JSONObject exceptionJSON = e.toJSON()
|
||||||
|
printExceptions(exceptionJSON, params_json, log)
|
||||||
|
println ''
|
||||||
|
has_error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for unexpected parameters
|
||||||
|
if (unexpectedParams.size() > 0) {
|
||||||
|
Map colors = NfcoreTemplate.logColours(params.monochrome_logs)
|
||||||
|
println ''
|
||||||
|
def warn_msg = 'Found unexpected parameters:'
|
||||||
|
for (unexpectedParam in unexpectedParams) {
|
||||||
|
warn_msg = warn_msg + "\n* --${unexpectedParam}: ${params[unexpectedParam].toString()}"
|
||||||
|
}
|
||||||
|
log.warn warn_msg
|
||||||
|
log.info "- ${colors.dim}Ignore this warning: params.schema_ignore_params = \"${unexpectedParams.join(',')}\" ${colors.reset}"
|
||||||
|
println ''
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_error) {
|
||||||
|
System.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Beautify parameters for --help
|
||||||
|
//
|
||||||
|
public static String paramsHelp(workflow, params, command, schema_filename='nextflow_schema.json') {
|
||||||
|
Map colors = NfcoreTemplate.logColours(params.monochrome_logs)
|
||||||
|
Integer num_hidden = 0
|
||||||
|
String output = ''
|
||||||
|
output += 'Typical pipeline command:\n\n'
|
||||||
|
output += " ${colors.cyan}${command}${colors.reset}\n\n"
|
||||||
|
Map params_map = paramsLoad(getSchemaPath(workflow, schema_filename=schema_filename))
|
||||||
|
Integer max_chars = paramsMaxChars(params_map) + 1
|
||||||
|
Integer desc_indent = max_chars + 14
|
||||||
|
Integer dec_linewidth = 160 - desc_indent
|
||||||
|
for (group in params_map.keySet()) {
|
||||||
|
Integer num_params = 0
|
||||||
|
String group_output = colors.underlined + colors.bold + group + colors.reset + '\n'
|
||||||
|
def group_params = params_map.get(group) // This gets the parameters of that particular group
|
||||||
|
for (param in group_params.keySet()) {
|
||||||
|
if (group_params.get(param).hidden && !params.show_hidden_params) {
|
||||||
|
num_hidden += 1
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
def type = '[' + group_params.get(param).type + ']'
|
||||||
|
def description = group_params.get(param).description
|
||||||
|
def defaultValue = group_params.get(param).default ? " [default: " + group_params.get(param).default.toString() + "]" : ''
|
||||||
|
def description_default = description + colors.dim + defaultValue + colors.reset
|
||||||
|
// Wrap long description texts
|
||||||
|
// Loosely based on https://dzone.com/articles/groovy-plain-text-word-wrap
|
||||||
|
if (description_default.length() > dec_linewidth){
|
||||||
|
List olines = []
|
||||||
|
String oline = "" // " " * indent
|
||||||
|
description_default.split(" ").each() { wrd ->
|
||||||
|
if ((oline.size() + wrd.size()) <= dec_linewidth) {
|
||||||
|
oline += wrd + " "
|
||||||
|
} else {
|
||||||
|
olines += oline
|
||||||
|
oline = wrd + " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
olines += oline
|
||||||
|
description_default = olines.join("\n" + " " * desc_indent)
|
||||||
|
}
|
||||||
|
group_output += " --" + param.padRight(max_chars) + colors.dim + type.padRight(10) + colors.reset + description_default + '\n'
|
||||||
|
num_params += 1
|
||||||
|
}
|
||||||
|
group_output += '\n'
|
||||||
|
if (num_params > 0){
|
||||||
|
output += group_output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (num_hidden > 0){
|
||||||
|
output += colors.dim + "!! Hiding $num_hidden params, use --show_hidden_params to show them !!\n" + colors.reset
|
||||||
|
}
|
||||||
|
output += NfcoreTemplate.dashedLine(params.monochrome_logs)
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Groovy Map summarising parameters/workflow options used by the pipeline
|
||||||
|
//
|
||||||
|
public static LinkedHashMap paramsSummaryMap(workflow, params, schema_filename='nextflow_schema.json') {
|
||||||
|
// Get a selection of core Nextflow workflow options
|
||||||
|
def Map workflow_summary = [:]
|
||||||
|
if (workflow.revision) {
|
||||||
|
workflow_summary['revision'] = workflow.revision
|
||||||
|
}
|
||||||
|
workflow_summary['runName'] = workflow.runName
|
||||||
|
if (workflow.containerEngine) {
|
||||||
|
workflow_summary['containerEngine'] = workflow.containerEngine
|
||||||
|
}
|
||||||
|
if (workflow.container) {
|
||||||
|
workflow_summary['container'] = workflow.container
|
||||||
|
}
|
||||||
|
workflow_summary['launchDir'] = workflow.launchDir
|
||||||
|
workflow_summary['workDir'] = workflow.workDir
|
||||||
|
workflow_summary['projectDir'] = workflow.projectDir
|
||||||
|
workflow_summary['userName'] = workflow.userName
|
||||||
|
workflow_summary['profile'] = workflow.profile
|
||||||
|
workflow_summary['configFiles'] = workflow.configFiles.join(', ')
|
||||||
|
|
||||||
|
// Get pipeline parameters defined in JSON Schema
|
||||||
|
def Map params_summary = [:]
|
||||||
|
def blacklist = ['hostnames']
|
||||||
|
def params_map = paramsLoad(getSchemaPath(workflow, schema_filename=schema_filename))
|
||||||
|
for (group in params_map.keySet()) {
|
||||||
|
def sub_params = new LinkedHashMap()
|
||||||
|
def group_params = params_map.get(group) // This gets the parameters of that particular group
|
||||||
|
for (param in group_params.keySet()) {
|
||||||
|
if (params.containsKey(param) && !blacklist.contains(param)) {
|
||||||
|
def params_value = params.get(param)
|
||||||
|
def schema_value = group_params.get(param).default
|
||||||
|
def param_type = group_params.get(param).type
|
||||||
|
if (schema_value != null) {
|
||||||
|
if (param_type == 'string') {
|
||||||
|
if (schema_value.contains('$projectDir') || schema_value.contains('${projectDir}')) {
|
||||||
|
def sub_string = schema_value.replace('\$projectDir', '')
|
||||||
|
sub_string = sub_string.replace('\${projectDir}', '')
|
||||||
|
if (params_value.contains(sub_string)) {
|
||||||
|
schema_value = params_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (schema_value.contains('$params.outdir') || schema_value.contains('${params.outdir}')) {
|
||||||
|
def sub_string = schema_value.replace('\$params.outdir', '')
|
||||||
|
sub_string = sub_string.replace('\${params.outdir}', '')
|
||||||
|
if ("${params.outdir}${sub_string}" == params_value) {
|
||||||
|
schema_value = params_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a default in the schema, and this isn't it
|
||||||
|
if (schema_value != null && params_value != schema_value) {
|
||||||
|
sub_params.put(param, params_value)
|
||||||
|
}
|
||||||
|
// No default in the schema, and this isn't empty
|
||||||
|
else if (schema_value == null && params_value != "" && params_value != null && params_value != false) {
|
||||||
|
sub_params.put(param, params_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params_summary.put(group, sub_params)
|
||||||
|
}
|
||||||
|
return [ 'Core Nextflow options' : workflow_summary ] << params_summary
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Beautify parameters for summary and return as string
|
||||||
|
//
|
||||||
|
public static String paramsSummaryLog(workflow, params) {
|
||||||
|
Map colors = NfcoreTemplate.logColours(params.monochrome_logs)
|
||||||
|
String output = ''
|
||||||
|
def params_map = paramsSummaryMap(workflow, params)
|
||||||
|
def max_chars = paramsMaxChars(params_map)
|
||||||
|
for (group in params_map.keySet()) {
|
||||||
|
def group_params = params_map.get(group) // This gets the parameters of that particular group
|
||||||
|
if (group_params) {
|
||||||
|
output += colors.bold + group + colors.reset + '\n'
|
||||||
|
for (param in group_params.keySet()) {
|
||||||
|
output += " " + colors.blue + param.padRight(max_chars) + ": " + colors.green + group_params.get(param) + colors.reset + '\n'
|
||||||
|
}
|
||||||
|
output += '\n'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output += "!! Only displaying parameters that differ from the pipeline defaults !!\n"
|
||||||
|
output += NfcoreTemplate.dashedLine(params.monochrome_logs)
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Loop over nested exceptions and print the causingException
|
||||||
|
//
|
||||||
|
private static void printExceptions(ex_json, params_json, log) {
|
||||||
|
def causingExceptions = ex_json['causingExceptions']
|
||||||
|
if (causingExceptions.length() == 0) {
|
||||||
|
def m = ex_json['message'] =~ /required key \[([^\]]+)\] not found/
|
||||||
|
// Missing required param
|
||||||
|
if (m.matches()) {
|
||||||
|
log.error "* Missing required parameter: --${m[0][1]}"
|
||||||
|
}
|
||||||
|
// Other base-level error
|
||||||
|
else if (ex_json['pointerToViolation'] == '#') {
|
||||||
|
log.error "* ${ex_json['message']}"
|
||||||
|
}
|
||||||
|
// Error with specific param
|
||||||
|
else {
|
||||||
|
def param = ex_json['pointerToViolation'] - ~/^#\//
|
||||||
|
def param_val = params_json[param].toString()
|
||||||
|
log.error "* --${param}: ${ex_json['message']} (${param_val})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ex in causingExceptions) {
|
||||||
|
printExceptions(ex, params_json, log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Remove an element from a JSONArray
|
||||||
|
//
|
||||||
|
private static JSONArray removeElement(json_array, element) {
|
||||||
|
def list = []
|
||||||
|
int len = json_array.length()
|
||||||
|
for (int i=0;i<len;i++){
|
||||||
|
list.add(json_array.get(i).toString())
|
||||||
|
}
|
||||||
|
list.remove(element)
|
||||||
|
JSONArray jsArray = new JSONArray(list)
|
||||||
|
return jsArray
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Remove ignored parameters
|
||||||
|
//
|
||||||
|
private static JSONObject removeIgnoredParams(raw_schema, params) {
|
||||||
|
// Remove anything that's in params.schema_ignore_params
|
||||||
|
params.schema_ignore_params.split(',').each{ ignore_param ->
|
||||||
|
if(raw_schema.keySet().contains('definitions')){
|
||||||
|
raw_schema.definitions.each { definition ->
|
||||||
|
for (key in definition.keySet()){
|
||||||
|
if (definition[key].get("properties").keySet().contains(ignore_param)){
|
||||||
|
// Remove the param to ignore
|
||||||
|
definition[key].get("properties").remove(ignore_param)
|
||||||
|
// If the param was required, change this
|
||||||
|
if (definition[key].has("required")) {
|
||||||
|
def cleaned_required = removeElement(definition[key].required, ignore_param)
|
||||||
|
definition[key].put("required", cleaned_required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(raw_schema.keySet().contains('properties') && raw_schema.get('properties').keySet().contains(ignore_param)) {
|
||||||
|
raw_schema.get("properties").remove(ignore_param)
|
||||||
|
}
|
||||||
|
if(raw_schema.keySet().contains('required') && raw_schema.required.contains(ignore_param)) {
|
||||||
|
def cleaned_required = removeElement(raw_schema.required, ignore_param)
|
||||||
|
raw_schema.put("required", cleaned_required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return raw_schema
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Clean and check parameters relative to Nextflow native classes
|
||||||
|
//
|
||||||
|
private static Map cleanParameters(params) {
|
||||||
|
def new_params = params.getClass().newInstance(params)
|
||||||
|
for (p in params) {
|
||||||
|
// remove anything evaluating to false
|
||||||
|
if (!p['value']) {
|
||||||
|
new_params.remove(p.key)
|
||||||
|
}
|
||||||
|
// Cast MemoryUnit to String
|
||||||
|
if (p['value'].getClass() == nextflow.util.MemoryUnit) {
|
||||||
|
new_params.replace(p.key, p['value'].toString())
|
||||||
|
}
|
||||||
|
// Cast Duration to String
|
||||||
|
if (p['value'].getClass() == nextflow.util.Duration) {
|
||||||
|
new_params.replace(p.key, p['value'].toString().replaceFirst(/d(?!\S)/, "day"))
|
||||||
|
}
|
||||||
|
// Cast LinkedHashMap to String
|
||||||
|
if (p['value'].getClass() == LinkedHashMap) {
|
||||||
|
new_params.replace(p.key, p['value'].toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new_params
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// This function tries to read a JSON params file
|
||||||
|
//
|
||||||
|
private static LinkedHashMap paramsLoad(String json_schema) {
|
||||||
|
def params_map = new LinkedHashMap()
|
||||||
|
try {
|
||||||
|
params_map = paramsRead(json_schema)
|
||||||
|
} catch (Exception e) {
|
||||||
|
println "Could not read parameters settings from JSON. $e"
|
||||||
|
params_map = new LinkedHashMap()
|
||||||
|
}
|
||||||
|
return params_map
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Method to actually read in JSON file using Groovy.
|
||||||
|
// Group (as Key), values are all parameters
|
||||||
|
// - Parameter1 as Key, Description as Value
|
||||||
|
// - Parameter2 as Key, Description as Value
|
||||||
|
// ....
|
||||||
|
// Group
|
||||||
|
// -
|
||||||
|
private static LinkedHashMap paramsRead(String json_schema) throws Exception {
|
||||||
|
def json = new File(json_schema).text
|
||||||
|
def Map schema_definitions = (Map) new JsonSlurper().parseText(json).get('definitions')
|
||||||
|
def Map schema_properties = (Map) new JsonSlurper().parseText(json).get('properties')
|
||||||
|
/* Tree looks like this in nf-core schema
|
||||||
|
* definitions <- this is what the first get('definitions') gets us
|
||||||
|
group 1
|
||||||
|
title
|
||||||
|
description
|
||||||
|
properties
|
||||||
|
parameter 1
|
||||||
|
type
|
||||||
|
description
|
||||||
|
parameter 2
|
||||||
|
type
|
||||||
|
description
|
||||||
|
group 2
|
||||||
|
title
|
||||||
|
description
|
||||||
|
properties
|
||||||
|
parameter 1
|
||||||
|
type
|
||||||
|
description
|
||||||
|
* properties <- parameters can also be ungrouped, outside of definitions
|
||||||
|
parameter 1
|
||||||
|
type
|
||||||
|
description
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Grouped params
|
||||||
|
def params_map = new LinkedHashMap()
|
||||||
|
schema_definitions.each { key, val ->
|
||||||
|
def Map group = schema_definitions."$key".properties // Gets the property object of the group
|
||||||
|
def title = schema_definitions."$key".title
|
||||||
|
def sub_params = new LinkedHashMap()
|
||||||
|
group.each { innerkey, value ->
|
||||||
|
sub_params.put(innerkey, value)
|
||||||
|
}
|
||||||
|
params_map.put(title, sub_params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ungrouped params
|
||||||
|
def ungrouped_params = new LinkedHashMap()
|
||||||
|
schema_properties.each { innerkey, value ->
|
||||||
|
ungrouped_params.put(innerkey, value)
|
||||||
|
}
|
||||||
|
params_map.put("Other parameters", ungrouped_params)
|
||||||
|
|
||||||
|
return params_map
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get maximum number of characters across all parameter names
|
||||||
|
//
|
||||||
|
private static Integer paramsMaxChars(params_map) {
|
||||||
|
Integer max_chars = 0
|
||||||
|
for (group in params_map.keySet()) {
|
||||||
|
def group_params = params_map.get(group) // This gets the parameters of that particular group
|
||||||
|
for (param in group_params.keySet()) {
|
||||||
|
if (param.size() > max_chars) {
|
||||||
|
max_chars = param.size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max_chars
|
||||||
|
}
|
||||||
|
}
|
||||||
321
lib/NfcoreTemplate.groovy
Normal file
321
lib/NfcoreTemplate.groovy
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
//
|
||||||
|
// This file holds several functions used within the nf-core pipeline template.
|
||||||
|
//
|
||||||
|
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 nf-core
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
import org.yaml.snakeyaml.Yaml
|
||||||
|
|
||||||
|
class NfcoreTemplate {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check AWS Batch related parameters have been specified correctly
|
||||||
|
//
|
||||||
|
public static void awsBatch(workflow, params) {
|
||||||
|
if (workflow.profile.contains('awsbatch')) {
|
||||||
|
// Check params.awsqueue and params.awsregion have been set if running on AWSBatch
|
||||||
|
assert (params.awsqueue && params.awsregion) : "Specify correct --awsqueue and --awsregion parameters on AWSBatch!"
|
||||||
|
// Check outdir paths to be S3 buckets if running on AWSBatch
|
||||||
|
assert params.outdir.startsWith('s3:') : "Outdir not on S3 - specify S3 Bucket to run on AWSBatch!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check params.hostnames
|
||||||
|
//
|
||||||
|
public static void hostName(workflow, params, log) {
|
||||||
|
Map colors = logColours(params.monochrome_logs)
|
||||||
|
if (params.hostnames) {
|
||||||
|
try {
|
||||||
|
def hostname = "hostname".execute().text.trim()
|
||||||
|
params.hostnames.each { prof, hnames ->
|
||||||
|
hnames.each { hname ->
|
||||||
|
if (hostname.contains(hname) && !workflow.profile.contains(prof)) {
|
||||||
|
log.info "=${colors.yellow}====================================================${colors.reset}=\n" +
|
||||||
|
"${colors.yellow}WARN: You are running with `-profile $workflow.profile`\n" +
|
||||||
|
" but your machine hostname is ${colors.white}'$hostname'${colors.reset}.\n" +
|
||||||
|
" ${colors.yellow_bold}Please use `-profile $prof${colors.reset}`\n" +
|
||||||
|
"=${colors.yellow}====================================================${colors.reset}="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn "[$workflow.manifest.name] Could not determine 'hostname' - skipping check. Reason: ${e.message}."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Construct and send completion email
|
||||||
|
//
|
||||||
|
public static void email(workflow, params, summary_params, projectDir, log, multiqc_report=[], fail_mapped_reads=[:]) {
|
||||||
|
|
||||||
|
// Set up the e-mail variables
|
||||||
|
def subject = "[$workflow.manifest.name] Successful: $workflow.runName"
|
||||||
|
if (fail_mapped_reads.size() > 0) {
|
||||||
|
subject = "[$workflow.manifest.name] Partially successful (${fail_mapped_reads.size()} skipped): $workflow.runName"
|
||||||
|
}
|
||||||
|
if (!workflow.success) {
|
||||||
|
subject = "[$workflow.manifest.name] FAILED: $workflow.runName"
|
||||||
|
}
|
||||||
|
|
||||||
|
def summary = [:]
|
||||||
|
for (group in summary_params.keySet()) {
|
||||||
|
summary << summary_params[group]
|
||||||
|
}
|
||||||
|
|
||||||
|
def misc_fields = [:]
|
||||||
|
misc_fields['Date Started'] = workflow.start
|
||||||
|
misc_fields['Date Completed'] = workflow.complete
|
||||||
|
misc_fields['Pipeline script file path'] = workflow.scriptFile
|
||||||
|
misc_fields['Pipeline script hash ID'] = workflow.scriptId
|
||||||
|
if (workflow.repository) misc_fields['Pipeline repository Git URL'] = workflow.repository
|
||||||
|
if (workflow.commitId) misc_fields['Pipeline repository Git Commit'] = workflow.commitId
|
||||||
|
if (workflow.revision) misc_fields['Pipeline Git branch/tag'] = workflow.revision
|
||||||
|
misc_fields['Nextflow Version'] = workflow.nextflow.version
|
||||||
|
misc_fields['Nextflow Build'] = workflow.nextflow.build
|
||||||
|
misc_fields['Nextflow Compile Timestamp'] = workflow.nextflow.timestamp
|
||||||
|
|
||||||
|
def email_fields = [:]
|
||||||
|
email_fields['version'] = workflow.manifest.version
|
||||||
|
email_fields['runName'] = workflow.runName
|
||||||
|
email_fields['success'] = workflow.success
|
||||||
|
email_fields['dateComplete'] = workflow.complete
|
||||||
|
email_fields['duration'] = workflow.duration
|
||||||
|
email_fields['exitStatus'] = workflow.exitStatus
|
||||||
|
email_fields['errorMessage'] = (workflow.errorMessage ?: 'None')
|
||||||
|
email_fields['errorReport'] = (workflow.errorReport ?: 'None')
|
||||||
|
email_fields['commandLine'] = workflow.commandLine
|
||||||
|
email_fields['projectDir'] = workflow.projectDir
|
||||||
|
email_fields['summary'] = summary << misc_fields
|
||||||
|
email_fields['fail_mapped_reads'] = fail_mapped_reads.keySet()
|
||||||
|
email_fields['min_mapped_reads'] = params.min_mapped_reads
|
||||||
|
|
||||||
|
// On success try attach the multiqc report
|
||||||
|
def mqc_report = null
|
||||||
|
try {
|
||||||
|
if (workflow.success && !params.skip_multiqc) {
|
||||||
|
mqc_report = multiqc_report.getVal()
|
||||||
|
if (mqc_report.getClass() == ArrayList && mqc_report.size() >= 1) {
|
||||||
|
if (mqc_report.size() > 1) {
|
||||||
|
log.warn "[$workflow.manifest.name] Found multiple reports from process 'MULTIQC', will use only one"
|
||||||
|
}
|
||||||
|
mqc_report = mqc_report[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (all) {
|
||||||
|
if (multiqc_report) {
|
||||||
|
log.warn "[$workflow.manifest.name] Could not attach MultiQC report to summary email"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we are only sending emails on failure
|
||||||
|
def email_address = params.email
|
||||||
|
if (!params.email && params.email_on_fail && !workflow.success) {
|
||||||
|
email_address = params.email_on_fail
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the TXT template
|
||||||
|
def engine = new groovy.text.GStringTemplateEngine()
|
||||||
|
def tf = new File("$projectDir/assets/email_template.txt")
|
||||||
|
def txt_template = engine.createTemplate(tf).make(email_fields)
|
||||||
|
def email_txt = txt_template.toString()
|
||||||
|
|
||||||
|
// Render the HTML template
|
||||||
|
def hf = new File("$projectDir/assets/email_template.html")
|
||||||
|
def html_template = engine.createTemplate(hf).make(email_fields)
|
||||||
|
def email_html = html_template.toString()
|
||||||
|
|
||||||
|
// Render the sendmail template
|
||||||
|
def max_multiqc_email_size = params.max_multiqc_email_size as nextflow.util.MemoryUnit
|
||||||
|
def smail_fields = [ email: email_address, subject: subject, email_txt: email_txt, email_html: email_html, projectDir: "$projectDir", mqcFile: mqc_report, mqcMaxSize: max_multiqc_email_size.toBytes() ]
|
||||||
|
def sf = new File("$projectDir/assets/sendmail_template.txt")
|
||||||
|
def sendmail_template = engine.createTemplate(sf).make(smail_fields)
|
||||||
|
def sendmail_html = sendmail_template.toString()
|
||||||
|
|
||||||
|
// Send the HTML e-mail
|
||||||
|
Map colors = logColours(params.monochrome_logs)
|
||||||
|
if (email_address) {
|
||||||
|
try {
|
||||||
|
if (params.plaintext_email) { throw GroovyException('Send plaintext e-mail, not HTML') }
|
||||||
|
// Try to send HTML e-mail using sendmail
|
||||||
|
[ 'sendmail', '-t' ].execute() << sendmail_html
|
||||||
|
log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Sent summary e-mail to $email_address (sendmail)-"
|
||||||
|
} catch (all) {
|
||||||
|
// Catch failures and try with plaintext
|
||||||
|
def mail_cmd = [ 'mail', '-s', subject, '--content-type=text/html', email_address ]
|
||||||
|
if ( mqc_report.size() <= max_multiqc_email_size.toBytes() ) {
|
||||||
|
mail_cmd += [ '-A', mqc_report ]
|
||||||
|
}
|
||||||
|
mail_cmd.execute() << email_html
|
||||||
|
log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Sent summary e-mail to $email_address (mail)-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write summary e-mail HTML to a file
|
||||||
|
def output_d = new File("${params.outdir}/pipeline_info/")
|
||||||
|
if (!output_d.exists()) {
|
||||||
|
output_d.mkdirs()
|
||||||
|
}
|
||||||
|
def output_hf = new File(output_d, "pipeline_report.html")
|
||||||
|
output_hf.withWriter { w -> w << email_html }
|
||||||
|
def output_tf = new File(output_d, "pipeline_report.txt")
|
||||||
|
output_tf.withWriter { w -> w << email_txt }
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Print pipeline summary on completion
|
||||||
|
//
|
||||||
|
public static void summary(workflow, params, log, fail_mapped_reads=[:], pass_mapped_reads=[:]) {
|
||||||
|
Map colors = logColours(params.monochrome_logs)
|
||||||
|
|
||||||
|
if (pass_mapped_reads.size() > 0) {
|
||||||
|
def idx = 0
|
||||||
|
def samp_aln = ''
|
||||||
|
def total_aln_count = pass_mapped_reads.size() + fail_mapped_reads.size()
|
||||||
|
for (samp in pass_mapped_reads) {
|
||||||
|
samp_aln += " ${samp.value}: ${samp.key}\n"
|
||||||
|
idx += 1
|
||||||
|
if (idx > 5) {
|
||||||
|
samp_aln += " ..see pipeline reports for full list\n"
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} ${pass_mapped_reads.size()}/$total_aln_count samples passed Bowtie2 ${params.min_mapped_reads} mapped read threshold:\n${samp_aln}${colors.reset}-"
|
||||||
|
}
|
||||||
|
if (fail_mapped_reads.size() > 0) {
|
||||||
|
def samp_aln = ''
|
||||||
|
for (samp in fail_mapped_reads) {
|
||||||
|
samp_aln += " ${samp.value}: ${samp.key}\n"
|
||||||
|
}
|
||||||
|
log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} ${fail_mapped_reads.size()} samples skipped since they failed Bowtie2 ${params.min_mapped_reads} mapped read threshold:\n${samp_aln}${colors.reset}-"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workflow.success) {
|
||||||
|
if (workflow.stats.ignoredCount == 0) {
|
||||||
|
log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Pipeline completed successfully${colors.reset}-"
|
||||||
|
} else {
|
||||||
|
log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed successfully, but with errored process(es) ${colors.reset}-"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hostName(workflow, params, log)
|
||||||
|
log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed with errors${colors.reset}-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ANSII Colours used for terminal logging
|
||||||
|
//
|
||||||
|
public static Map logColours(Boolean monochrome_logs) {
|
||||||
|
Map colorcodes = [:]
|
||||||
|
|
||||||
|
// Reset / Meta
|
||||||
|
colorcodes['reset'] = monochrome_logs ? '' : "\033[0m"
|
||||||
|
colorcodes['bold'] = monochrome_logs ? '' : "\033[1m"
|
||||||
|
colorcodes['dim'] = monochrome_logs ? '' : "\033[2m"
|
||||||
|
colorcodes['underlined'] = monochrome_logs ? '' : "\033[4m"
|
||||||
|
colorcodes['blink'] = monochrome_logs ? '' : "\033[5m"
|
||||||
|
colorcodes['reverse'] = monochrome_logs ? '' : "\033[7m"
|
||||||
|
colorcodes['hidden'] = monochrome_logs ? '' : "\033[8m"
|
||||||
|
|
||||||
|
// Regular Colors
|
||||||
|
colorcodes['black'] = monochrome_logs ? '' : "\033[0;30m"
|
||||||
|
colorcodes['red'] = monochrome_logs ? '' : "\033[0;31m"
|
||||||
|
colorcodes['green'] = monochrome_logs ? '' : "\033[0;32m"
|
||||||
|
colorcodes['yellow'] = monochrome_logs ? '' : "\033[0;33m"
|
||||||
|
colorcodes['blue'] = monochrome_logs ? '' : "\033[0;34m"
|
||||||
|
colorcodes['purple'] = monochrome_logs ? '' : "\033[0;35m"
|
||||||
|
colorcodes['cyan'] = monochrome_logs ? '' : "\033[0;36m"
|
||||||
|
colorcodes['white'] = monochrome_logs ? '' : "\033[0;37m"
|
||||||
|
|
||||||
|
// Bold
|
||||||
|
colorcodes['bblack'] = monochrome_logs ? '' : "\033[1;30m"
|
||||||
|
colorcodes['bred'] = monochrome_logs ? '' : "\033[1;31m"
|
||||||
|
colorcodes['bgreen'] = monochrome_logs ? '' : "\033[1;32m"
|
||||||
|
colorcodes['byellow'] = monochrome_logs ? '' : "\033[1;33m"
|
||||||
|
colorcodes['bblue'] = monochrome_logs ? '' : "\033[1;34m"
|
||||||
|
colorcodes['bpurple'] = monochrome_logs ? '' : "\033[1;35m"
|
||||||
|
colorcodes['bcyan'] = monochrome_logs ? '' : "\033[1;36m"
|
||||||
|
colorcodes['bwhite'] = monochrome_logs ? '' : "\033[1;37m"
|
||||||
|
|
||||||
|
// Underline
|
||||||
|
colorcodes['ublack'] = monochrome_logs ? '' : "\033[4;30m"
|
||||||
|
colorcodes['ured'] = monochrome_logs ? '' : "\033[4;31m"
|
||||||
|
colorcodes['ugreen'] = monochrome_logs ? '' : "\033[4;32m"
|
||||||
|
colorcodes['uyellow'] = monochrome_logs ? '' : "\033[4;33m"
|
||||||
|
colorcodes['ublue'] = monochrome_logs ? '' : "\033[4;34m"
|
||||||
|
colorcodes['upurple'] = monochrome_logs ? '' : "\033[4;35m"
|
||||||
|
colorcodes['ucyan'] = monochrome_logs ? '' : "\033[4;36m"
|
||||||
|
colorcodes['uwhite'] = monochrome_logs ? '' : "\033[4;37m"
|
||||||
|
|
||||||
|
// High Intensity
|
||||||
|
colorcodes['iblack'] = monochrome_logs ? '' : "\033[0;90m"
|
||||||
|
colorcodes['ired'] = monochrome_logs ? '' : "\033[0;91m"
|
||||||
|
colorcodes['igreen'] = monochrome_logs ? '' : "\033[0;92m"
|
||||||
|
colorcodes['iyellow'] = monochrome_logs ? '' : "\033[0;93m"
|
||||||
|
colorcodes['iblue'] = monochrome_logs ? '' : "\033[0;94m"
|
||||||
|
colorcodes['ipurple'] = monochrome_logs ? '' : "\033[0;95m"
|
||||||
|
colorcodes['icyan'] = monochrome_logs ? '' : "\033[0;96m"
|
||||||
|
colorcodes['iwhite'] = monochrome_logs ? '' : "\033[0;97m"
|
||||||
|
|
||||||
|
// Bold High Intensity
|
||||||
|
colorcodes['biblack'] = monochrome_logs ? '' : "\033[1;90m"
|
||||||
|
colorcodes['bired'] = monochrome_logs ? '' : "\033[1;91m"
|
||||||
|
colorcodes['bigreen'] = monochrome_logs ? '' : "\033[1;92m"
|
||||||
|
colorcodes['biyellow'] = monochrome_logs ? '' : "\033[1;93m"
|
||||||
|
colorcodes['biblue'] = monochrome_logs ? '' : "\033[1;94m"
|
||||||
|
colorcodes['bipurple'] = monochrome_logs ? '' : "\033[1;95m"
|
||||||
|
colorcodes['bicyan'] = monochrome_logs ? '' : "\033[1;96m"
|
||||||
|
colorcodes['biwhite'] = monochrome_logs ? '' : "\033[1;97m"
|
||||||
|
|
||||||
|
return colorcodes
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Does what is says on the tin
|
||||||
|
//
|
||||||
|
public static String dashedLine(monochrome_logs) {
|
||||||
|
Map colors = logColours(monochrome_logs)
|
||||||
|
return "-${colors.dim}----------------------------------------------------${colors.reset}-"
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// nf-core logo
|
||||||
|
//
|
||||||
|
public static String logo(workflow, monochrome_logs) {
|
||||||
|
Map colors = logColours(monochrome_logs)
|
||||||
|
String.format(
|
||||||
|
"""\n
|
||||||
|
${dashedLine(monochrome_logs)}
|
||||||
|
${colors.green},--.${colors.black}/${colors.green},-.${colors.reset}
|
||||||
|
${colors.blue} ___ __ __ __ ___ ${colors.green}/,-._.--~\'${colors.reset}
|
||||||
|
${colors.blue} |\\ | |__ __ / ` / \\ |__) |__ ${colors.yellow}} {${colors.reset}
|
||||||
|
${colors.blue} | \\| | \\__, \\__/ | \\ |___ ${colors.green}\\`-._,-`-,${colors.reset}
|
||||||
|
${colors.green}`._,._,\'${colors.reset}
|
||||||
|
${colors.purple} ${workflow.manifest.name} v${workflow.manifest.version}${colors.reset}
|
||||||
|
${dashedLine(monochrome_logs)}
|
||||||
|
""".stripIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
44
lib/WorkflowMain.groovy
Normal file
44
lib/WorkflowMain.groovy
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
class WorkflowMain {
|
||||||
|
|
||||||
|
// Citation string for pipeline
|
||||||
|
public static String citation(workflow) {
|
||||||
|
return "If you use wf-template for your analysis please cite:\n\n" +
|
||||||
|
"* The nf-core framework\n" +
|
||||||
|
" https://doi.org/10.1038/s41587-020-0439-x\n\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print help to screen
|
||||||
|
public static String help(workflow, params, log) {
|
||||||
|
def command = "nextflow run epi2me-labs/wf-template --fastq <input folder> -profile docker"
|
||||||
|
def help_string = ''
|
||||||
|
help_string += NfcoreSchema.paramsHelp(workflow, params, command)
|
||||||
|
help_string += '\n' + citation(workflow) + '\n'
|
||||||
|
return help_string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print parameter summary log to screen
|
||||||
|
public static String paramsSummaryLog(workflow, params, log) {
|
||||||
|
def summary_log = ''
|
||||||
|
summary_log += NfcoreSchema.paramsSummaryLog(workflow, params)
|
||||||
|
summary_log += '\n' + citation(workflow) + '\n'
|
||||||
|
return summary_log
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate parameters and print summary to screen
|
||||||
|
public static void initialise(workflow, params, log) {
|
||||||
|
// Print help to screen if required
|
||||||
|
if (params.help) {
|
||||||
|
log.info help(workflow, params, log)
|
||||||
|
System.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate workflow parameters via the JSON schema
|
||||||
|
if (params.validate_params) {
|
||||||
|
NfcoreSchema.validateParameters(workflow, params, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print parameter summary log to screen
|
||||||
|
log.info paramsSummaryLog(workflow, params, log)
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
lib/nfcore_external_java_deps.jar
Normal file
BIN
lib/nfcore_external_java_deps.jar
Normal file
Binary file not shown.
29
main.nf
29
main.nf
@ -15,22 +15,6 @@ nextflow.enable.dsl = 2
|
|||||||
|
|
||||||
include { fastq_ingress } from './lib/fastqingress'
|
include { fastq_ingress } from './lib/fastqingress'
|
||||||
|
|
||||||
def helpMessage(){
|
|
||||||
log.info """
|
|
||||||
Workflow template'
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
nextflow run epi2melabs/wf-template [options]
|
|
||||||
|
|
||||||
Script Options:
|
|
||||||
--fastq DIR Path to FASTQ directory (required)
|
|
||||||
--samples FILE CSV file with columns named `barcode` and `sample_name`
|
|
||||||
(or simply a sample name for non-multiplexed data).
|
|
||||||
--out_dir DIR Path for output (default: $params.out_dir)
|
|
||||||
--report_name STR Optional report suffix (default: $params.report_name)
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
process summariseReads {
|
process summariseReads {
|
||||||
// concatenate fastq and fastq.gz in a dir
|
// concatenate fastq and fastq.gz in a dir
|
||||||
@ -121,20 +105,9 @@ workflow pipeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// entrypoint workflow
|
// entrypoint workflow
|
||||||
|
WorkflowMain.initialise(workflow, params, log)
|
||||||
workflow {
|
workflow {
|
||||||
|
|
||||||
if (params.help) {
|
|
||||||
helpMessage()
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!params.fastq) {
|
|
||||||
helpMessage()
|
|
||||||
println("")
|
|
||||||
println("`--fastq` is required")
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
samples = fastq_ingress(
|
samples = fastq_ingress(
|
||||||
params.fastq, params.out_dir, params.samples, params.sanitize_fastq)
|
params.fastq, params.out_dir, params.samples, params.sanitize_fastq)
|
||||||
|
|
||||||
|
|||||||
@ -20,8 +20,22 @@ params {
|
|||||||
aws_image_prefix = null
|
aws_image_prefix = null
|
||||||
aws_queue = null
|
aws_queue = null
|
||||||
report_name = "report"
|
report_name = "report"
|
||||||
|
|
||||||
|
monochrome_logs = false
|
||||||
|
validate_params = true
|
||||||
|
show_hidden_params = false
|
||||||
|
schema_ignore_params = 'show_hidden_params,validate_params,monochrome_logs,aws_queue,aws_image_prefix,wfversion'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
manifest {
|
||||||
|
name = 'epi2me-labs/wf-template'
|
||||||
|
author = 'Oxford Nanopore Technologies'
|
||||||
|
homePage = 'https://github.com/epi2me-labs/wf-template'
|
||||||
|
description = 'Template workflow'
|
||||||
|
mainScript = 'main.nf'
|
||||||
|
nextflowVersion = '>=20.10.0'
|
||||||
|
//version = 'v0.0.7' // TODO: do switch to this?
|
||||||
|
}
|
||||||
|
|
||||||
executor {
|
executor {
|
||||||
$local {
|
$local {
|
||||||
|
|||||||
102
nextflow_schema.json
Normal file
102
nextflow_schema.json
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema",
|
||||||
|
"$id": "https://raw.githubusercontent.com/./master/nextflow_schema.json",
|
||||||
|
"title": ". pipeline parameters",
|
||||||
|
"description": "",
|
||||||
|
"type": "object",
|
||||||
|
"definitions": {
|
||||||
|
"basic_input_output_options": {
|
||||||
|
"title": "Basic Input/Output Options",
|
||||||
|
"type": "object",
|
||||||
|
"fa_icon": "fas fa-terminal",
|
||||||
|
"description": "Define where the pipeline should find input data and save output data.",
|
||||||
|
"properties": {
|
||||||
|
"out_dir": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "output",
|
||||||
|
"description": "Directory for output of all user-facing files."
|
||||||
|
},
|
||||||
|
"fastq": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Directory containing fastq input files. May contain fastq files directly or directories name barcodeXX relating to independent samples."
|
||||||
|
},
|
||||||
|
"samples": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "CSV file with columns named `barcode` and `sample_name`. (or simply a sample name for non-multiplexed data)"
|
||||||
|
},
|
||||||
|
"sanitize_fastq": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Use additional heuristics to identify barcodes from file paths.",
|
||||||
|
"help_text": "Enabling this option will group together files into samples by the presence of strings of the form `barcodeXXX` present in filenames, rather than simply files grouped into directories (as output by MinKNOW and the Guppy basecaller)."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"fastq"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"meta_data": {
|
||||||
|
"title": "Meta Data",
|
||||||
|
"type": "object",
|
||||||
|
"description": "",
|
||||||
|
"default": "",
|
||||||
|
"properties": {
|
||||||
|
"report_name": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "report",
|
||||||
|
"description": "Output report filename suffix."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"generic_options": {
|
||||||
|
"title": "Generic options",
|
||||||
|
"type": "object",
|
||||||
|
"fa_icon": "far fa-question-circle",
|
||||||
|
"description": "Less common options for the pipeline, typically set in a config file.",
|
||||||
|
"help_text": "These options are common to all nf-core pipelines and allow you to customise some of the core preferences for how the pipeline runs.\n\nTypically these options would be set in a Nextflow config file loaded for all pipeline runs, such as `~/.nextflow/config`.",
|
||||||
|
"properties": {
|
||||||
|
"help": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Display help text.",
|
||||||
|
"fa_icon": "fas fa-question-circle",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/basic_input_output_options"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/meta_data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/generic_options"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"aws_image_prefix": {
|
||||||
|
"type": "string",
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"aws_queue": {
|
||||||
|
"type": "string",
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"wfversion": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "v0.3.3",
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"monochrome_logs": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"validate_params": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"show_hidden_params": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user