Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Params linting #310

Closed
wants to merge 12 commits into from
20 changes: 18 additions & 2 deletions nf_core/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Tests Nextflow-based pipelines to check that they adhere to
the nf-core community guidelines.
"""

import json
import logging
import io
import os
Expand Down Expand Up @@ -171,7 +171,8 @@ def lint_pipeline(self, release_mode=False):
'check_readme',
'check_conda_env_yaml',
'check_conda_dockerfile',
'check_pipeline_todos'
'check_pipeline_todos',
'check_params_json'
]
if release_mode:
self.release_mode = True
Expand Down Expand Up @@ -221,6 +222,7 @@ def check_files_exist(self):
['LICENSE', 'LICENSE.md', 'LICENCE', 'LICENCE.md'], # NB: British / American spelling
'README.md',
'CHANGELOG.md',
'parameters.settings.json',
'docs/README.md',
'docs/output.md',
'docs/usage.md',
Expand Down Expand Up @@ -263,6 +265,7 @@ def pf(file_path):
with open(os.path.join(self.path, 'environment.yml'), 'r') as fh:
self.conda_config = yaml.load(fh)


def check_docker(self):
"""Checks that Dockerfile contains the string ``FROM``."""
fn = os.path.join(self.path, "Dockerfile")
Expand Down Expand Up @@ -502,6 +505,7 @@ def check_ci_config(self):
elif minNextflowVersion != self.minNextflowVersion:
self.failed.append((5, "Minimum NF version differed from CI and what was set in the pipelines manifest: {}".format(fn)))


def check_readme(self):
"""Checks the repository README file for errors.

Expand Down Expand Up @@ -785,6 +789,18 @@ def check_pipeline_todos(self):
l = '{}..'.format(l[:50-len(fname)])
self.warned.append((10, "TODO string found in '{}': {}".format(fname,l)))


def check_params_json(self):
fn = os.path.join(self.path, 'parameters.settings.json')
with open(fn, 'r') as paramsfile:
try:
json.loads(paramsfile.read())
except(ValueError):
self.failed.append((11, "{} file not a valid JSON file.".format(fn)))


return

def print_results(self):
# Print results
rl = "\n Using --release mode linting tests" if self.release_mode else ''
Expand Down
81 changes: 57 additions & 24 deletions nf_core/pipeline-template/{{cookiecutter.name_noslash}}/main.nf
Original file line number Diff line number Diff line change
Expand Up @@ -10,48 +10,81 @@
*/


def helpMessage() {
// TODO nf-core: Add to this help message with new command line parameters
def readParamsFromJsonSettings() {
List paramsWithUsage
try {
paramsWithUsage = tryReadParamsFromJsonSettings()
} catch (Exception e) {
println "Could not read parameters settings from Json. $e"
paramsWithUsage = Collections.emptyMap()
}
return paramsWithUsage
}

def tryReadParamsFromJsonSettings() throws Exception {
def paramsContent = new File(config.params_description.path).text
def paramsWithUsage = new groovy.json.JsonSlurper().parseText(paramsContent)
return paramsWithUsage.get('parameters')
}

def formatParameterHelpData(param) {
result = [name: param.name, value: '', usage: param.usage]
// value descibes the expected input for the param
result.value = (param.type == boolean.toString()) ? '' : param.choices ?: param.type ?: ''
return result
}

String prettyFormatParamGroupWithPaddingAndIndent(List paramGroup, String groupName, Integer padding = 2, Integer indent = 4) {
def maxParamNameLength = paramGroup.collect { it.name.size() }.max()
def paramChoices = paramGroup.findAll { it.choices }.collect { it.choices }
def maxChoiceStringLength = paramChoices.collect { it.toString().size() }.max()
def maxTypeLength = paramGroup.collect { (it.type as String).size() }.max()

def paramsFormattedList = paramGroup.sort { it.name }.collect {
Map param ->
paramHelpData = formatParameterHelpData(param)
sprintf("%${indent}s%-${maxParamNameLength + padding}s%-${maxChoiceStringLength + padding}s %s\n", "", "--${paramHelpData.name}", "${paramHelpData.value}", "${paramHelpData.usage}")
}
return String.format("%s:\n%s", groupName.toUpperCase(), paramsFormattedList.join()).stripIndent()
}

String prettyFormatParamsWithPaddingAndIndent(List paramsWithUsage, Integer padding = 2, Integer indent = 4) {

def groupedParamsWithUsage = paramsWithUsage.groupBy { it.group }
def formattedParamsGroups = groupedParamsWithUsage.collect {
prettyFormatParamGroupWithPaddingAndIndent(it.value, it.key, padding, indent)
}
return formattedParamsGroups.join('\n')
}


def helpMessage(paramsWithUsage) {
// TODO nf-core: Add typical command for running your pipeline
log.info nfcoreHeader()
log.info"""
def usageHelp = String.format("""\

Usage:

The typical command for running the pipeline is as follows:

nextflow run {{ cookiecutter.name }} --reads '*_R{1,2}.fastq.gz' -profile docker

Mandatory arguments:
--reads Path to input data (must be surrounded with quotes)
-profile Configuration profile to use. Can use multiple (comma separated)
Available: conda, docker, singularity, awsbatch, test and more.

Options:
--genome Name of iGenomes reference
--singleEnd Specifies that the input is single end reads

References If not specified in the configuration file or you wish to overwrite any of the references.
--fasta Path to Fasta reference

Other options:
--outdir The output directory where the results will be saved
--email Set this parameter to your e-mail address to get a summary e-mail with details of the run sent to you when the workflow exits
--maxMultiqcEmailFileSize Theshold size for MultiQC report to be attached in notification email. If file generated by pipeline exceeds the threshold, it will not be attached (Default: 25MB)
-name Name for the pipeline run. If not specified, Nextflow will automatically generate a random mnemonic.

AWSBatch options:
--awsqueue The AWSBatch JobQueue that needs to be set when running on AWSBatch
--awsregion The AWS Region for your AWS Batch job to run on
""".stripIndent()
%s
""".stripIndent(), prettyFormatParamsWithPaddingAndIndent(paramsWithUsage, 2, 4))
log.info usageHelp
}

/*
* SET UP CONFIGURATION VARIABLES
*/

def paramsWithUsage = readParamsFromJsonSettings()

// Show help emssage
if (params.help){
helpMessage()
helpMessage(paramsWithUsage)
exit 0
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ params {
// Developmental code should specify :dev
process.container = '{{ cookiecutter.name_docker }}:dev'

// Specify params.settings.json settings
params_description {
path = "$baseDir/parameters.settings.json"
}

// Load base.config by default for all pipelines
includeConfig 'conf/base.config'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
{
"parameters": [
{
"name": "reads",
"label": "Input files",
"usage": "Specify the location of your input FastQ files.",
"group": "Main options",
"default_value": "'data/*{1,2}.fastq.gz'",
"render": "textfield",
"pattern": ".*\\*.*",
"type": "string"
},
{
"name": "genome",
"label": "Alignment reference iGenomes key",
"usage": "Ref. genome key for iGenomes",
"group": "Alignment",
"render": "drop-down",
"type": "string",
"choices": [
"",
"GRCh37",
"GRCm38",
"TAIR10",
"EB2",
"UMD3.1",
"WBcel235",
"CanFam3.1",
"GRCz10",
"BDGP6",
"EquCab2",
"EB1",
"Galgal4",
"Gm01",
"Mmul_1",
"IRGSP-1.0",
"CHIMP2.1.4",
"Rnor_6.0",
"R64-1-1",
"EF2",
"Sbi1",
"Sscrofa10.2",
"AGPv3"
],
"default_value": ""
},
{
"name": "singleEnd",
"label": "Single-end sequencing input",
"usage": "Use single-end sequencing inputs instead of paired-end.",
"group": "Main options",
"render": "check-box",
"default_value": false,
"type": "boolean"
},
{
"name": "fasta",
"label": "FASTA",
"usage": "Path to Fasta reference",
"group": "Alignment",
"render": "file",
"type": "string",
"pattern": ".*",
"default_value": ""
},
{
"name": "outdir",
"label": "Output directory",
"usage": "Set where to save the results from the pipeline",
"group": "Main options",
"default_value": "./results",
"render": "textfield",
"pattern": ".*",
"type": "string"
},
{
"name": "email",
"label": "Your email address",
"usage": "Your email address, required to receive completion notification.",
"group": "Pipeline defaults",
"render": "textfield",
"pattern": "^$|(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$)",
"type": "string",
"default_value": ""
},
{
"name": "maxMultiqcEmailFileSize",
"label": "Maximum MultiQC email file size",
"usage": "Theshold size for MultiQC report to be attached in notification email. If file generated by pipeline exceeds the threshold, it will not be attached.",
"group": "Pipeline defaults",
"default_value": "25.MB",
"render": "textfield",
"pattern": "\\d+\\.[KMGT]?B",
"type": "string"
},
{
"name": "awsregion",
"label": "AWS Region",
"usage": "The AWS region to run your job in.",
"group": "AWS cloud usage",
"default_value": "eu-west-1",
"render": "textfield",
"pattern": ".*",
"type": "string"
},
{
"name": "awsqueue",
"label": "AWS job queue",
"usage": "The JobQueue that you intend to use on AWS Batch.",
"group": "AWS cloud usage",
"render": "textfield",
"pattern": ".*",
"type": "string",
"default_value": ""
}
]
}
Loading