annotate 0.4.2/lib/routines.nf @ 143:620bffa66bbb tip

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