template updates
This commit is contained in:
parent
b44ec7d98d
commit
fa1818a81d
76
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
76
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
title: "[Bug]: "
|
||||
labels: ["bug", "triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Tell us what you see!
|
||||
value: "A bug happened!"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: What operating system are you running?
|
||||
options:
|
||||
- Windows 10
|
||||
- Windows 11
|
||||
- macOS
|
||||
- ubuntu 18.04
|
||||
- ubuntu 20.04
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: execution
|
||||
attributes:
|
||||
label: Workflow Execution
|
||||
description: Where are you running the workflow?
|
||||
options:
|
||||
- EPI2ME Labs desktop application
|
||||
- Command line
|
||||
- EPI2ME
|
||||
- Other (please describe)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: labs-version
|
||||
attributes:
|
||||
label: Workflow Execution - EPI2ME Labs Versions
|
||||
description: If you're running using EPI2ME Labs please provide the version and the environment version (Click the blue help icon bottom left and select ":bout")?
|
||||
validations:
|
||||
required: false
|
||||
- type: dropdown
|
||||
id: profile
|
||||
attributes:
|
||||
label: Workflow Execution - Execution Profile
|
||||
description: If you're using the CLI to run the workflow, what profile are you using?
|
||||
options:
|
||||
- Docker
|
||||
- Singularity
|
||||
- Conda
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Workflow Version
|
||||
description: What version of the workflow are you running?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Nanopore customer support
|
||||
url: https://nanoporetech.com/contact
|
||||
about: For general support, including bioinformatics questions.
|
||||
@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [v0.1.4]
|
||||
### Changed
|
||||
- Args parser for fastqingress
|
||||
- Set out_dir option type to ensure output is written to correct directory on Windows
|
||||
|
||||
## [v0.1.3]
|
||||
### Changed
|
||||
- Better help text on cli
|
||||
|
||||
61
lib/ArgumentParser.groovy
Normal file
61
lib/ArgumentParser.groovy
Normal file
@ -0,0 +1,61 @@
|
||||
/* Check arguments of a Nextflow function
|
||||
*
|
||||
* Nextflow script does not support the Groovy idiom:
|
||||
*
|
||||
* def function(Map args[:], arg1, arg2, ...)
|
||||
*
|
||||
* to support unordered kwargs. The methods here are designed
|
||||
* to reduce boileplate while allowing Nextflow script to implement
|
||||
*
|
||||
* def function(Map args[:])
|
||||
*
|
||||
* with required and default values. This is similar to some Python
|
||||
* libraries' (notably matplotlib) extensive use of things like:
|
||||
*
|
||||
* def function(*args, **kwargs)
|
||||
*
|
||||
* to implement generic APIs. Why do we want to do all this? Because
|
||||
* we want to write library code with a clean set of required parameters
|
||||
* but also extensible with non-required parameters with default values.
|
||||
* This allows us to later add parameters without breaking existing code,
|
||||
* and is very common practice elsewhere.
|
||||
*/
|
||||
|
||||
import java.util.Set
|
||||
|
||||
class ArgumentParser {
|
||||
Set args
|
||||
Map kwargs
|
||||
String name
|
||||
|
||||
/* Parse arguments, raising an error on unknown keys */
|
||||
public Map parse_args(LinkedHashMap given_args) {
|
||||
Set opt_keys = kwargs.keySet()
|
||||
Set given_keys = given_args.keySet()
|
||||
check_required(given_keys)
|
||||
check_unknown(given_keys, opt_keys)
|
||||
return kwargs + given_args
|
||||
}
|
||||
|
||||
/* Parse arguments, without raising an error for extra keys */
|
||||
public Map parse_known_args(LinkedHashMap given_args) {
|
||||
Set opt_keys = kwargs.keySet()
|
||||
Set given_keys = given_args.keySet()
|
||||
check_required(given_keys)
|
||||
return kwargs + given_args
|
||||
}
|
||||
|
||||
private void check_required(Set given) {
|
||||
Set missing_keys = args - given
|
||||
if (!missing_keys.isEmpty()) {
|
||||
throw new Exception("Missing arguments for function ${name}: ${missing_keys}")
|
||||
}
|
||||
}
|
||||
|
||||
private void check_unknown(Set given, Set kwargs_keys) {
|
||||
Set extra_keys = given - (args + kwargs_keys)
|
||||
if (!extra_keys.isEmpty()) {
|
||||
throw new Exception("Unknown arguments provided to function ${name}: ${extra_keys}.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
import ArgumentParser
|
||||
|
||||
process handleSingleFile {
|
||||
label "isoforms"
|
||||
label params.process_label
|
||||
cpus 1
|
||||
input:
|
||||
file reads
|
||||
@ -16,7 +18,7 @@ process handleSingleFile {
|
||||
|
||||
|
||||
process checkSampleSheet {
|
||||
label "isoforms"
|
||||
label params.process_label
|
||||
cpus 1
|
||||
input:
|
||||
file "sample_sheet.txt"
|
||||
@ -73,7 +75,7 @@ def find_fastq(pattern, maxdepth)
|
||||
* @param input_folder Top-level input directory.
|
||||
* @param staging Top-level output_directory.
|
||||
* @return A File object representating the staging directory created
|
||||
* under output_folder
|
||||
* under output
|
||||
*/
|
||||
def sanitize_fastq(input_folder, staging)
|
||||
{
|
||||
@ -111,8 +113,8 @@ def sanitize_fastq(input_folder, staging)
|
||||
*/
|
||||
def get_subdirectories(input_directory)
|
||||
{
|
||||
barcode_dirs = file("$input_directory/barcode*", type: 'dir', maxdepth: 1)
|
||||
all_dirs = file("$input_directory/*", type: 'dir', maxdepth: 1)
|
||||
barcode_dirs = file(input_directory.resolve("barcode*"), type: 'dir', maxdepth: 1)
|
||||
all_dirs = file(input_directory.resolve("*"), type: 'dir', maxdepth: 1)
|
||||
non_barcoded = ( all_dirs + barcode_dirs ) - all_dirs.intersect(barcode_dirs)
|
||||
return [barcode_dirs, non_barcoded]
|
||||
}
|
||||
@ -223,9 +225,11 @@ def handle_flat_dir(input_directory, sample_name)
|
||||
* @param barcoded_dirs List of barcoded directories (barcodeXX,...)
|
||||
* @param sample_sheet List of tuples mapping barcode to sample name
|
||||
* or a simple string for non-multiplexed data.
|
||||
* @param min_barcode Minimum barcode to accept.
|
||||
* @param max_barcode Maximum (inclusive) barcode to accept.
|
||||
* @return Channel of tuples (path, sample_id, type)
|
||||
*/
|
||||
def handle_barcoded_dirs(barcoded_dirs, sample_sheet)
|
||||
def handle_barcoded_dirs(barcoded_dirs, sample_sheet, min_barcode, max_barcode)
|
||||
{
|
||||
valid_dirs = get_valid_directories(barcoded_dirs)
|
||||
// link sample names to barcode through sample sheet
|
||||
@ -233,17 +237,36 @@ def handle_barcoded_dirs(barcoded_dirs, sample_sheet)
|
||||
sample_sheet = Channel
|
||||
.fromPath(valid_dirs)
|
||||
.filter(~/.*barcode[0-9]{1,3}$/) // up to 192
|
||||
.filter { barcode_in_range(it, min_barcode, max_barcode) }
|
||||
.map { path -> tuple(path.baseName, path.baseName, 'test_sample') }
|
||||
}
|
||||
return Channel
|
||||
.fromPath(valid_dirs)
|
||||
.filter(~/.*barcode[0-9]{1,3}$/) // up to 192
|
||||
.filter { barcode_in_range(it, min_barcode, max_barcode) }
|
||||
.map { path -> tuple(path.baseName, path) }
|
||||
.join(sample_sheet)
|
||||
.map { barcode, path, sample, type -> tuple(path, sample, type) }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if a barcode path is within a required numeric range
|
||||
*
|
||||
* @param path barcoded directory (barcodeXX).
|
||||
* @param min_barcode Minimum barcode to accept.
|
||||
* @param max_barcode Maximum (inclusive) barcode to accept.
|
||||
*/
|
||||
def barcode_in_range(path, min_barcode, max_barcode)
|
||||
{
|
||||
pattern = ~/barcode(\d+)/
|
||||
matcher = "${path}" =~ pattern
|
||||
value = matcher[0][1].toInteger()
|
||||
valid = ((value >= min_barcode) && (value <= max_barcode))
|
||||
return valid
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Take a list of non-barcode directories to return a channel
|
||||
* of named samples. Samples are named by directory baseName.
|
||||
@ -264,33 +287,49 @@ def handle_non_barcoded_dirs(non_barcoded_dirs)
|
||||
* Take an input (file or directory) and return a channel of
|
||||
* named samples.
|
||||
*
|
||||
*
|
||||
* @param input Top level input file or folder to locate fastq data.
|
||||
* @param sample string to name single sample data.
|
||||
* @param sample_sheet Path to sample sheet CSV file.
|
||||
* @param sanitize regularize inputs from EPI2ME platform.
|
||||
* @param output output location, required if sanitize==true
|
||||
* @param min_barcode Minimum barcode to accept.
|
||||
* @param max_barcode Maximum (inclusive) barcode to accept.
|
||||
*
|
||||
* @return Channel of tuples (path, sample_id, type)
|
||||
*/
|
||||
def fastq_ingress(input, output_folder, sample, sample_sheet, sanitize)
|
||||
def fastq_ingress(Map arguments)
|
||||
{
|
||||
def parser = new ArgumentParser(
|
||||
args:["input"],
|
||||
kwargs:[
|
||||
"sample":null, "sample_sheet":null, "sanitize":false, "output":null,
|
||||
"min_barcode":0, "max_barcode":Integer.MAX_VALUE],
|
||||
name:"fastq_ingress")
|
||||
Map margs = parser.parse_args(arguments)
|
||||
|
||||
if (margs.sanitize && margs.output == null) {
|
||||
throw new Exception("Argument 'output' required if 'sanitize' is true.")
|
||||
}
|
||||
|
||||
println("Checking fastq input.")
|
||||
input = file(input);
|
||||
input = file(margs.input)
|
||||
|
||||
// Handle file input
|
||||
if (input.isFile()) {
|
||||
// Assume sample is a string at this point
|
||||
println('Single file input detected.')
|
||||
if (sample_sheet) {
|
||||
if (margs.sample_sheet) {
|
||||
println('Warning: `--sample_sheet` given but single file input found. Ignoring.')
|
||||
}
|
||||
return handle_single_file(input, sample)
|
||||
return handle_single_file(input, margs.sample)
|
||||
}
|
||||
|
||||
// Handle directory input
|
||||
if (input.isDirectory()) {
|
||||
// EPI2ME harness
|
||||
if (sanitize) {
|
||||
staging = file(output_folder).resolve("staging")
|
||||
input = sanitize_fastq(file(input), staging)
|
||||
if (margs.sanitize) {
|
||||
staging = file(margs.output).resolve("staging")
|
||||
input = sanitize_fastq(input, staging)
|
||||
}
|
||||
|
||||
// Get barcoded and non barcoded subdirectories
|
||||
@ -299,13 +338,13 @@ def fastq_ingress(input, output_folder, sample, sample_sheet, sanitize)
|
||||
// Case 03: If no subdirectories, handle the single dir
|
||||
if (!barcoded && !non_barcoded) {
|
||||
println("Single directory input detected.")
|
||||
if (sample_sheet) {
|
||||
if (margs.sample_sheet) {
|
||||
println('Warning: `--sample_sheet` given but single non-barcode directory found. Ignoring.')
|
||||
}
|
||||
return handle_flat_dir(input, sample)
|
||||
return handle_flat_dir(input, margs.sample)
|
||||
}
|
||||
|
||||
if (sample) {
|
||||
if (margs.sample) {
|
||||
println('Warning: `--sample` given but multiple directories found, ignoring.')
|
||||
}
|
||||
|
||||
@ -314,16 +353,17 @@ def fastq_ingress(input, output_folder, sample, sample_sheet, sanitize)
|
||||
barcoded_samples = Channel.empty()
|
||||
if (barcoded) {
|
||||
println("Barcoded directories detected.")
|
||||
if (sample_sheet) {
|
||||
sample_sheet = get_sample_sheet(sample_sheet)
|
||||
sample_sheet = null
|
||||
if (margs.sample_sheet) {
|
||||
sample_sheet = get_sample_sheet(margs.sample_sheet)
|
||||
}
|
||||
barcoded_samples = handle_barcoded_dirs(barcoded, sample_sheet)
|
||||
barcoded_samples = handle_barcoded_dirs(barcoded, sample_sheet, margs.min_barcode, margs.max_barcode)
|
||||
}
|
||||
|
||||
non_barcoded_samples = Channel.empty()
|
||||
if (non_barcoded) {
|
||||
println("Non barcoded directories detected.")
|
||||
if (!barcoded && sample_sheet) {
|
||||
if (!barcoded && margs.sample_sheet) {
|
||||
println('Warning: `--sample_sheet` given but no barcode directories found.')
|
||||
}
|
||||
non_barcoded_samples = handle_non_barcoded_dirs(non_barcoded)
|
||||
|
||||
9
main.nf
9
main.nf
@ -502,9 +502,12 @@ workflow {
|
||||
ref_annotation = file("$projectDir/data/OPTIONAL_FILE")
|
||||
}
|
||||
|
||||
reads = fastq_ingress(
|
||||
params.fastq, params.out_dir, params.sample, params.sample_sheet, params.sanitize_fastq
|
||||
)
|
||||
reads = fastq_ingress([
|
||||
"input":params.fastq,
|
||||
"sample":params.sample,
|
||||
"sample_sheet":params.sample_sheet,
|
||||
"sanitize": params.sanitize_fastq,
|
||||
"output":params.out_dir])
|
||||
|
||||
pipeline(reads, ref_genome, ref_annotation)
|
||||
|
||||
|
||||
@ -23,15 +23,16 @@ params {
|
||||
sample = null
|
||||
sample_sheet = null
|
||||
sanitize_fastq = false
|
||||
wfversion = "v0.1.3"
|
||||
wfversion = "v0.1.4"
|
||||
aws_image_prefix = null
|
||||
aws_queue = null
|
||||
report_name = "report"
|
||||
process_label = "isoforms"
|
||||
|
||||
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,wf'
|
||||
schema_ignore_params = 'show_hidden_params,validate_params,monochrome_logs,aws_queue,aws_image_prefix,wfversion,wf,process_label'
|
||||
|
||||
// Process cDNA reads using pychopper, turn off for direct RNA:
|
||||
direct_rna = false
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
"out_dir": {
|
||||
"type": "string",
|
||||
"default": "output",
|
||||
"format": "path",
|
||||
"description": "Directory for output of all user-facing files."
|
||||
},
|
||||
"fastq": {
|
||||
@ -280,7 +281,7 @@
|
||||
},
|
||||
"wfversion": {
|
||||
"type": "string",
|
||||
"default": "v0.1.3",
|
||||
"default": "v0.1.4",
|
||||
"hidden": true
|
||||
},
|
||||
"monochrome_logs": {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user