comparison 0.4.0/lib/routines.nf @ 101:ce6d9548fe89

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