annotate 0.6.1/lib/routines.nf @ 15:1972677994a6

"planemo upload"
author kkonganti
date Thu, 07 Sep 2023 10:27:12 -0400
parents 749faef1caa9
children
rev   line source
kkonganti@11 1 // Hold methods to print:
kkonganti@11 2 // 1. Colored logo.
kkonganti@11 3 // 2. Summary of parameters.
kkonganti@11 4 // 3. Single dashed line.
kkonganti@11 5 // 4. Double dashed line.
kkonganti@11 6 //
kkonganti@11 7
kkonganti@11 8 import groovy.json.JsonSlurper
kkonganti@11 9 import nextflow.config.ConfigParser
kkonganti@11 10 // import groovy.json.JsonOutput
kkonganti@11 11
kkonganti@11 12 // ASCII logo
kkonganti@11 13 def pipelineBanner() {
kkonganti@11 14
kkonganti@11 15 def padding = (params.pad) ?: 30
kkonganti@11 16 Map fgcolors = getANSIColors()
kkonganti@11 17
kkonganti@11 18 def banner = [
kkonganti@11 19 name: "${fgcolors.magenta}${workflow.manifest.name}${fgcolors.reset}",
kkonganti@11 20 author: "${fgcolors.cyan}${workflow.manifest.author}${fgcolors.reset}",
kkonganti@11 21 // workflow: "${fgcolors.magenta}${params.pipeline}${fgcolors.reset}",
kkonganti@11 22 version: "${fgcolors.green}${workflow.manifest.version}${fgcolors.reset}",
kkonganti@11 23 center: "${fgcolors.green}${params.center}${fgcolors.reset}",
kkonganti@11 24 pad: padding
kkonganti@11 25 ]
kkonganti@11 26
kkonganti@11 27 manifest = addPadding(banner)
kkonganti@11 28
kkonganti@11 29 return """${fgcolors.white}${dashedLine(type: '=')}${fgcolors.magenta}
kkonganti@11 30 (o)
kkonganti@11 31 ___ _ __ _ _ __ ___ ___
kkonganti@11 32 / __|| '_ \\ | || '_ \\ / _ \\/ __|
kkonganti@11 33 | (__ | |_) || || |_) || __/\\__ \\
kkonganti@11 34 \\___|| .__/ |_|| .__/ \\___||___/
kkonganti@11 35 | | | |
kkonganti@11 36 |_| |_|${fgcolors.reset}
kkonganti@11 37 ${dashedLine()}
kkonganti@11 38 ${fgcolors.blue}A collection of modular pipelines at CFSAN, FDA.${fgcolors.reset}
kkonganti@11 39 ${dashedLine()}
kkonganti@11 40 ${manifest}
kkonganti@11 41 ${dashedLine(type: '=')}
kkonganti@11 42 """.stripIndent()
kkonganti@11 43 }
kkonganti@11 44
kkonganti@11 45 // Add padding to keys so that
kkonganti@11 46 // they indent nicely on the
kkonganti@11 47 // terminal
kkonganti@11 48 def addPadding(values) {
kkonganti@11 49
kkonganti@11 50 def pad = (params.pad) ?: 30
kkonganti@11 51 values.pad = pad
kkonganti@11 52
kkonganti@11 53 def padding = values.pad.toInteger()
kkonganti@11 54 def nocapitalize = values.nocapitalize
kkonganti@11 55 def stopnow = values.stopNow
kkonganti@11 56 def help = values.help
kkonganti@11 57
kkonganti@11 58 values.removeAll {
kkonganti@11 59 k, v -> [
kkonganti@11 60 'nocapitalize',
kkonganti@11 61 'pad',
kkonganti@11 62 'stopNow',
kkonganti@11 63 'help'
kkonganti@11 64 ].contains(k)
kkonganti@11 65 }
kkonganti@11 66
kkonganti@11 67 values.keySet().each { k ->
kkonganti@11 68 v = values[k]
kkonganti@11 69 s = params.linewidth - (pad + 5)
kkonganti@11 70 if (v.toString().size() > s && !stopnow) {
kkonganti@11 71 def sen = ''
kkonganti@11 72 // v.toString().findAll(/.{1,${s}}\b(?:\W*|\s*)/).each {
kkonganti@11 73 // sen += ' '.multiply(padding + 2) + it + '\n'
kkonganti@11 74 // }
kkonganti@11 75 v.toString().eachMatch(/.{1,${s}}(?=.*)\b|\w+/) {
kkonganti@11 76 sen += ' '.multiply(padding + 2) + it.trim() + '\n'
kkonganti@11 77 }
kkonganti@11 78 values[k] = (
kkonganti@11 79 help ? sen.replaceAll(/^(\n|\s)*/, '') : sen.trim()
kkonganti@11 80 )
kkonganti@11 81 } else {
kkonganti@11 82 values[k] = (help ? v + "\n" : v)
kkonganti@11 83 }
kkonganti@11 84 k = k.replaceAll(/\./, '_')
kkonganti@11 85 }
kkonganti@11 86
kkonganti@11 87 return values.findResults {
kkonganti@11 88 k, v -> nocapitalize ?
kkonganti@11 89 k.padRight(padding) + ': ' + v :
kkonganti@11 90 k.capitalize().padRight(padding) + ': ' + v
kkonganti@11 91 }.join("\n")
kkonganti@11 92 }
kkonganti@11 93
kkonganti@11 94 // Method for error messages
kkonganti@11 95 def stopNow(msg) {
kkonganti@11 96
kkonganti@11 97 Map fgcolors = getANSIColors()
kkonganti@11 98 Map errors = [:]
kkonganti@11 99
kkonganti@11 100 if (msg == null) {
kkonganti@11 101 msg = "Unknown error"
kkonganti@11 102 }
kkonganti@11 103
kkonganti@11 104 errors['stopNow'] = true
kkonganti@11 105 errors["${params.cfsanpipename} - ${params.pipeline} - ERROR"] = """
kkonganti@11 106 ${fgcolors.reset}${dashedLine()}
kkonganti@11 107 ${fgcolors.red}${msg}${fgcolors.reset}
kkonganti@11 108 ${dashedLine()}
kkonganti@11 109 """.stripIndent()
kkonganti@11 110 // println dashedLine() // defaults to stdout
kkonganti@11 111 // log.info addPadding(errors) // prints to stdout
kkonganti@11 112 exit 1, "\n" + dashedLine() +
kkonganti@11 113 "${fgcolors.red}\n" + addPadding(errors)
kkonganti@11 114 }
kkonganti@11 115
kkonganti@11 116 // Method to validate 4 required parameters
kkonganti@11 117 // if input for entry point is FASTQ files
kkonganti@11 118 def validateParamsForFASTQ() {
kkonganti@11 119 switch (params) {
kkonganti@11 120 case { params.metadata == null && params.input == null }:
kkonganti@11 121 stopNow("Either metadata CSV file with 5 required columns\n" +
kkonganti@11 122 "in order: sample, fq1, fq2, strandedness, single_end or \n" +
kkonganti@11 123 "input directory of only FASTQ files (gzipped or unzipped) should be provided\n" +
kkonganti@11 124 "using --metadata or --input options.\n" +
kkonganti@11 125 "None of these two options were provided!")
kkonganti@11 126 break
kkonganti@11 127 case { params.metadata != null && params.input != null }:
kkonganti@11 128 stopNow("Either metadata or input directory of FASTQ files\n" +
kkonganti@11 129 "should be provided using --metadata or --input options.\n" +
kkonganti@11 130 "Using both these options is not allowed!")
kkonganti@11 131 break
kkonganti@11 132 case { params.output == null }:
kkonganti@11 133 stopNow("Please mention output directory to store all results " +
kkonganti@11 134 "using --output option!")
kkonganti@11 135 break
kkonganti@11 136 }
kkonganti@11 137 return 1
kkonganti@11 138 }
kkonganti@11 139
kkonganti@11 140 // Method to print summary of parameters
kkonganti@11 141 // before running
kkonganti@11 142 def summaryOfParams() {
kkonganti@11 143
kkonganti@11 144 def pipeline_specific_config = new ConfigParser().setIgnoreIncludes(true).parse(
kkonganti@11 145 file("${params.workflowsconf}${params.fs}${params.pipeline}.config").text
kkonganti@11 146 )
kkonganti@11 147 Map fgcolors = getANSIColors()
kkonganti@11 148 Map globalparams = [:]
kkonganti@11 149 Map localparams = params.subMap(
kkonganti@11 150 pipeline_specific_config.params.keySet().toList() + params.logtheseparams
kkonganti@11 151 )
kkonganti@11 152
kkonganti@11 153 if (localparams !instanceof Map) {
kkonganti@11 154 stopNow("Need a Map of paramters. We got: " + localparams.getClass())
kkonganti@11 155 }
kkonganti@11 156
kkonganti@11 157 if (localparams.size() != 0) {
kkonganti@11 158 localparams['nocapitalize'] = true
kkonganti@11 159 globalparams['nocapitalize'] = true
kkonganti@11 160 globalparams['nextflow_version'] = "${nextflow.version}"
kkonganti@11 161 globalparams['nextflow_build'] = "${nextflow.build}"
kkonganti@11 162 globalparams['nextflow_timestamp'] = "${nextflow.timestamp}"
kkonganti@11 163 globalparams['workflow_projectDir'] = "${workflow.projectDir}"
kkonganti@11 164 globalparams['workflow_launchDir'] = "${workflow.launchDir}"
kkonganti@11 165 globalparams['workflow_workDir'] = "${workflow.workDir}"
kkonganti@11 166 globalparams['workflow_container'] = "${workflow.container}"
kkonganti@11 167 globalparams['workflow_containerEngine'] = "${workflow.containerEngine}"
kkonganti@11 168 globalparams['workflow_runName'] = "${workflow.runName}"
kkonganti@11 169 globalparams['workflow_sessionId'] = "${workflow.sessionId}"
kkonganti@11 170 globalparams['workflow_profile'] = "${workflow.profile}"
kkonganti@11 171 globalparams['workflow_start'] = "${workflow.start}"
kkonganti@11 172 globalparams['workflow_commandLine'] = "${workflow.commandLine}"
kkonganti@11 173 return """${dashedLine()}
kkonganti@11 174 Summary of the current workflow (${fgcolors.magenta}${params.pipeline}${fgcolors.reset}) parameters
kkonganti@11 175 ${dashedLine()}
kkonganti@11 176 ${addPadding(localparams)}
kkonganti@11 177 ${dashedLine()}
kkonganti@11 178 ${fgcolors.cyan}N E X T F L O W${fgcolors.reset} - ${fgcolors.magenta}${params.cfsanpipename}${fgcolors.reset} - Runtime metadata
kkonganti@11 179 ${dashedLine()}
kkonganti@11 180 ${addPadding(globalparams)}
kkonganti@11 181 ${dashedLine()}""".stripIndent()
kkonganti@11 182 }
kkonganti@11 183 return 1
kkonganti@11 184 }
kkonganti@11 185
kkonganti@11 186 // Method to display
kkonganti@11 187 // Return dashed line either '-'
kkonganti@11 188 // type or '=' type
kkonganti@11 189 def dashedLine(Map defaults = [:]) {
kkonganti@11 190
kkonganti@11 191 Map fgcolors = getANSIColors()
kkonganti@11 192 def line = [color: 'white', type: '-']
kkonganti@11 193
kkonganti@11 194 if (!defaults.isEmpty()) {
kkonganti@11 195 line.putAll(defaults)
kkonganti@11 196 }
kkonganti@11 197
kkonganti@11 198 return fgcolors."${line.color}" +
kkonganti@11 199 "${line.type}".multiply(params.linewidth) +
kkonganti@11 200 fgcolors.reset
kkonganti@11 201 }
kkonganti@11 202
kkonganti@11 203 // Return slurped keys parsed from JSON
kkonganti@11 204 def slurpJson(file) {
kkonganti@11 205 def slurped = null
kkonganti@11 206 def jsonInst = new JsonSlurper()
kkonganti@11 207
kkonganti@11 208 try {
kkonganti@11 209 slurped = jsonInst.parse(new File ("${file}"))
kkonganti@11 210 }
kkonganti@11 211 catch (Exception e) {
kkonganti@11 212 log.error 'Please check your JSON schema. Invalid JSON file: ' + file
kkonganti@11 213 }
kkonganti@11 214
kkonganti@11 215 // Declare globals for the nanofactory
kkonganti@11 216 // workflow.
kkonganti@11 217 return [keys: slurped.keySet().toList(), cparams: slurped]
kkonganti@11 218 }
kkonganti@11 219
kkonganti@11 220 // Default help text in a map if the entry point
kkonganti@11 221 // to a pipeline is FASTQ files.
kkonganti@11 222 def fastqEntryPointHelp() {
kkonganti@11 223
kkonganti@11 224 Map helptext = [:]
kkonganti@11 225 Map fgcolors = getANSIColors()
kkonganti@11 226
kkonganti@11 227 helptext['Workflow'] = "${fgcolors.magenta}${params.pipeline}${fgcolors.reset}"
kkonganti@11 228 helptext['Author'] = "${fgcolors.cyan}${params.workflow_built_by}${fgcolors.reset}"
kkonganti@11 229 helptext['Version'] = "${fgcolors.green}${params.workflow_version}${fgcolors.reset}\n"
kkonganti@11 230 helptext['Usage'] = "cpipes --pipeline ${params.pipeline} [options]\n"
kkonganti@11 231 helptext['Required'] = ""
kkonganti@11 232 helptext['--input'] = "Absolute path to directory containing FASTQ files. " +
kkonganti@11 233 "The directory should contain only FASTQ files as all the " +
kkonganti@11 234 "files within the mentioned directory will be read. " +
kkonganti@11 235 "Ex: --input /path/to/fastq_pass"
kkonganti@11 236 helptext['--output'] = "Absolute path to directory where all the pipeline " +
kkonganti@11 237 "outputs should be stored. Ex: --output /path/to/output"
kkonganti@11 238 helptext['Other options'] = ""
kkonganti@11 239 helptext['--metadata'] = "Absolute path to metadata CSV file containing five " +
kkonganti@11 240 "mandatory columns: sample,fq1,fq2,strandedness,single_end. The fq1 and fq2 " +
kkonganti@11 241 "columns contain absolute paths to the FASTQ files. This option can be used in place " +
kkonganti@11 242 "of --input option. This is rare. Ex: --metadata samplesheet.csv"
kkonganti@11 243 helptext['--fq_suffix'] = "The suffix of FASTQ files (Unpaired reads or R1 reads or Long reads) if " +
kkonganti@11 244 "an input directory is mentioned via --input option. Default: ${params.fq_suffix}"
kkonganti@11 245 helptext['--fq2_suffix'] = "The suffix of FASTQ files (Paired-end reads or R2 reads) if an input directory is mentioned via " +
kkonganti@11 246 "--input option. Default: ${params.fq2_suffix}"
kkonganti@11 247 helptext['--fq_filter_by_len'] = "Remove FASTQ reads that are less than this many bases. " +
kkonganti@11 248 "Default: ${params.fq_filter_by_len}"
kkonganti@11 249 helptext['--fq_strandedness'] = "The strandedness of the sequencing run. This is mostly needed " +
kkonganti@11 250 "if your sequencing run is RNA-SEQ. For most of the other runs, it is probably safe to use " +
kkonganti@11 251 "unstranded for the option. Default: ${params.fq_strandedness}"
kkonganti@11 252 helptext['--fq_single_end'] = "SINGLE-END information will be auto-detected but this option forces " +
kkonganti@11 253 "PAIRED-END FASTQ files to be treated as SINGLE-END so only read 1 information is included in " +
kkonganti@11 254 "auto-generated samplesheet. Default: ${params.fq_single_end}"
kkonganti@11 255 helptext['--fq_filename_delim'] = "Delimiter by which the file name is split to obtain sample name. " +
kkonganti@11 256 "Default: ${params.fq_filename_delim}"
kkonganti@11 257 helptext['--fq_filename_delim_idx'] = "After splitting FASTQ file name by using the --fq_filename_delim option," +
kkonganti@11 258 " all elements before this index (1-based) will be joined to create final sample name." +
kkonganti@11 259 " Default: ${params.fq_filename_delim_idx}"
kkonganti@11 260
kkonganti@11 261 return helptext
kkonganti@11 262 }
kkonganti@11 263
kkonganti@11 264 // Show concise help text if configured within the main workflow.
kkonganti@11 265 def conciseHelp(def tool = null) {
kkonganti@11 266 Map fgcolors = getANSIColors()
kkonganti@11 267
kkonganti@11 268 tool ?= "fastp"
kkonganti@11 269 tools = tool?.tokenize(',')
kkonganti@11 270
kkonganti@11 271 return """
kkonganti@11 272 ${dashedLine()}
kkonganti@11 273 Show configurable CLI options for each tool within ${fgcolors.magenta}${params.pipeline}${fgcolors.reset}
kkonganti@11 274 ${dashedLine()}
kkonganti@11 275 Ex: cpipes --pipeline ${params.pipeline} --help
kkonganti@11 276 """ + (tools.size() > 1 ? "Ex: cpipes --pipeline ${params.pipeline} --help ${tools[0]}"
kkonganti@11 277 + """
kkonganti@11 278 Ex: cpipes --pipeline ${params.pipeline} --help ${tools[0]},${tools[1]}
kkonganti@11 279 ${dashedLine()}""".stripIndent() : """Ex: cpipes --pipeline ${params.pipeline} --help ${tool}
kkonganti@11 280 ${dashedLine()}""".stripIndent())
kkonganti@11 281
kkonganti@11 282 }
kkonganti@11 283
kkonganti@11 284 // Wrap help text with the following options
kkonganti@11 285 def wrapUpHelp() {
kkonganti@11 286
kkonganti@11 287 return [
kkonganti@11 288 'Help options' : "",
kkonganti@11 289 '--help': "Display this message.\n",
kkonganti@11 290 'help': true,
kkonganti@11 291 'nocapitalize': true
kkonganti@11 292 ]
kkonganti@11 293 }
kkonganti@11 294
kkonganti@11 295 // Method to send email on workflow complete.
kkonganti@11 296 def sendMail() {
kkonganti@11 297
kkonganti@11 298 if (params.user_email == null) {
kkonganti@11 299 return 1
kkonganti@11 300 }
kkonganti@11 301
kkonganti@11 302 def pad = (params.pad) ?: 30
kkonganti@11 303 def contact_emails = [
kkonganti@11 304 stakeholder: (params.workflow_blueprint_by ?: 'Not defined'),
kkonganti@11 305 author: (params.workflow_built_by ?: 'Not defined')
kkonganti@11 306 ]
kkonganti@11 307 def msg = """
kkonganti@11 308 ${pipelineBanner()}
kkonganti@11 309 ${summaryOfParams()}
kkonganti@11 310 ${params.cfsanpipename} - ${params.pipeline}
kkonganti@11 311 ${dashedLine()}
kkonganti@11 312 Please check the following directory for N E X T F L O W
kkonganti@11 313 reports. You can view the HTML files directly by double clicking
kkonganti@11 314 them on your workstation.
kkonganti@11 315 ${dashedLine()}
kkonganti@11 316 ${params.tracereportsdir}
kkonganti@11 317 ${dashedLine()}
kkonganti@11 318 Please send any bug reports to CFSAN Dev Team or the author or
kkonganti@11 319 the stakeholder of the current pipeline.
kkonganti@11 320 ${dashedLine()}
kkonganti@11 321 Error messages (if any)
kkonganti@11 322 ${dashedLine()}
kkonganti@11 323 ${workflow.errorMessage}
kkonganti@11 324 ${workflow.errorReport}
kkonganti@11 325 ${dashedLine()}
kkonganti@11 326 Contact emails
kkonganti@11 327 ${dashedLine()}
kkonganti@11 328 ${addPadding(contact_emails)}
kkonganti@11 329 ${dashedLine()}
kkonganti@11 330 Thank you for using ${params.cfsanpipename} - ${params.pipeline}!
kkonganti@11 331 ${dashedLine()}
kkonganti@11 332 """.stripIndent()
kkonganti@11 333
kkonganti@11 334 def mail_cmd = [
kkonganti@11 335 'sendmail',
kkonganti@11 336 '-f', 'noreply@gmail.com',
kkonganti@11 337 '-F', 'noreply',
kkonganti@11 338 '-t', "${params.user_email}"
kkonganti@11 339 ]
kkonganti@11 340
kkonganti@11 341 def email_subject = "${params.cfsanpipename} - ${params.pipeline}"
kkonganti@11 342 Map fgcolors = getANSIColors()
kkonganti@11 343
kkonganti@11 344 if (workflow.success) {
kkonganti@11 345 email_subject += ' completed successfully!'
kkonganti@11 346 }
kkonganti@11 347 else if (!workflow.success) {
kkonganti@11 348 email_subject += ' has failed!'
kkonganti@11 349 }
kkonganti@11 350
kkonganti@11 351 try {
kkonganti@11 352 ['env', 'bash'].execute() << """${mail_cmd.join(' ')}
kkonganti@11 353 Subject: ${email_subject}
kkonganti@11 354 Mime-Version: 1.0
kkonganti@11 355 Content-Type: text/html
kkonganti@11 356 <pre>
kkonganti@11 357 ${msg.replaceAll(/\x1b\[[0-9;]*m/, '')}
kkonganti@11 358 </pre>
kkonganti@11 359 """.stripIndent()
kkonganti@11 360 } catch (all) {
kkonganti@11 361 def warning_msg = "${fgcolors.yellow}${params.cfsanpipename} - ${params.pipeline} - WARNING"
kkonganti@11 362 .padRight(pad) + ':'
kkonganti@11 363 log.info """
kkonganti@11 364 ${dashedLine()}
kkonganti@11 365 ${warning_msg}
kkonganti@11 366 ${dashedLine()}
kkonganti@11 367 Could not send mail with the sendmail command!
kkonganti@11 368 ${dashedLine()}
kkonganti@11 369 """.stripIndent()
kkonganti@11 370 }
kkonganti@11 371 return 1
kkonganti@11 372 }
kkonganti@11 373
kkonganti@11 374 // Set ANSI colors for any and all
kkonganti@11 375 // STDOUT or STDERR
kkonganti@11 376 def getANSIColors() {
kkonganti@11 377
kkonganti@11 378 Map fgcolors = [:]
kkonganti@11 379
kkonganti@11 380 fgcolors['reset'] = "\033[0m"
kkonganti@11 381 fgcolors['black'] = "\033[0;30m"
kkonganti@11 382 fgcolors['red'] = "\033[0;31m"
kkonganti@11 383 fgcolors['green'] = "\033[0;32m"
kkonganti@11 384 fgcolors['yellow'] = "\033[0;33m"
kkonganti@11 385 fgcolors['blue'] = "\033[0;34m"
kkonganti@11 386 fgcolors['magenta'] = "\033[0;35m"
kkonganti@11 387 fgcolors['cyan'] = "\033[0;36m"
kkonganti@11 388 fgcolors['white'] = "\033[0;37m"
kkonganti@11 389
kkonganti@11 390 return fgcolors
kkonganti@11 391 }