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