From 0a67f082afad4c591ec258f300779b032bdffab1 Mon Sep 17 00:00:00 2001 From: William Fondrie Date: Mon, 4 Dec 2023 15:14:49 -0800 Subject: [PATCH] Added Slack report --- assets/slackreport.json | 34 ++++++++++++++++++++++++++++ lib/TalusTemplate.groovy | 49 ++++++++++++++++++++++++++++++++++++++++ main.nf | 15 +++++++++++- nextflow.config | 1 + 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 assets/slackreport.json diff --git a/assets/slackreport.json b/assets/slackreport.json new file mode 100644 index 0000000..a5ed0d5 --- /dev/null +++ b/assets/slackreport.json @@ -0,0 +1,34 @@ +{ + "attachments": [ + { + "fallback": "Plain-text summary of the attachment.", + "color": "<% if (success) { %>good<% } else { %>danger<%} %>", + "author_name": "TalusBio/nf-encyclopedia - ${runName}", + "author_icon": "https://www.nextflow.io/docs/latest/_static/favicon.ico", + "text": "<% if (success) { %>Pipeline completed successfully!<% } else { %>Pipeline completed with errors<% } %>", + "fields": [ + { + "title": "Command used to launch the workflow", + "value": "```${commandLine}```", + "short": false + } + <% + if (!success) { %> + , + { + "title": "Full error message", + "value": "```${errorReport}```", + "short": false + }, + { + "title": "Pipeline configuration", + "value": "<% out << summary.collect{ k,v -> k == "hook_url" ? "_${k}_: (_hidden_)" : ( ( v.class.toString().contains('Path') || ( v.class.toString().contains('String') && v.contains('/') ) ) ? "_${k}_: `${v}`" : (v.class.toString().contains('DateTime') ? ("_${k}_: " + v.format(java.time.format.DateTimeFormatter.ofLocalizedDateTime(java.time.format.FormatStyle.MEDIUM))) : "_${k}_: ${v}") ) }.join(",\n") %>", + "short": false + } + <% } + %> + ], + "footer": "Completed at <% out << dateComplete.format(java.time.format.DateTimeFormatter.ofLocalizedDateTime(java.time.format.FormatStyle.MEDIUM)) %> (duration: ${duration})" + } + ] +} diff --git a/lib/TalusTemplate.groovy b/lib/TalusTemplate.groovy index aabd3c0..c42fa1f 100644 --- a/lib/TalusTemplate.groovy +++ b/lib/TalusTemplate.groovy @@ -41,4 +41,53 @@ class TalusTemplate { def email_html = html_template.toString() return [subject, email_html] } + + // + // Construct and send a notification to a web server as JSON + // e.g. Microsoft Teams and Slack + // + public static void IM_notification(workflow, params, projectDir) { + def hook_url = params.hook_url + def misc_fields = [:] + misc_fields['start'] = workflow.start + misc_fields['complete'] = workflow.complete + misc_fields['scriptfile'] = workflow.scriptFile + misc_fields['scriptid'] = workflow.scriptId + if (workflow.repository) misc_fields['repository'] = workflow.repository + if (workflow.commitId) misc_fields['commitid'] = workflow.commitId + if (workflow.revision) misc_fields['revision'] = workflow.revision + misc_fields['nxf_version'] = workflow.nextflow.version + misc_fields['nxf_build'] = workflow.nextflow.build + misc_fields['nxf_timestamp'] = workflow.nextflow.timestamp + + def summary = [:] + def msg_fields = [:] + msg_fields['runName'] = workflow.runName + msg_fields['success'] = workflow.success + msg_fields['dateComplete'] = workflow.complete + msg_fields['duration'] = workflow.duration + msg_fields['exitStatus'] = workflow.exitStatus + msg_fields['errorMessage'] = (workflow.errorMessage ?: 'None') + msg_fields['errorReport'] = (workflow.errorReport ?: 'None') + msg_fields['commandLine'] = workflow.commandLine.replaceFirst(/ +--hook_url +[^ ]+/, "") + msg_fields['projectDir'] = workflow.projectDir + msg_fields['summary'] = summary << misc_fields + + // Render the JSON template + def engine = new groovy.text.GStringTemplateEngine() + // Different JSON depending on the service provider + // Defaults to "Adaptive Cards" (https://adaptivecards.io), except Slack which has its own format + def json_path = hook_url.contains("hooks.slack.com") ? "slackreport.json" : "adaptivecard.json" + def hf = new File("$projectDir/assets/${json_path}") + def json_template = engine.createTemplate(hf).make(msg_fields) + def json_message = json_template.toString() + + // POST + def post = new URL(hook_url).openConnection(); + post.setRequestMethod("POST") + post.setDoOutput(true) + post.setRequestProperty("Content-Type", "application/json") + post.getOutputStream().write(json_message.getBytes("UTF-8")); + def postRC = post.getResponseCode(); + } } diff --git a/main.nf b/main.nf index 3b2be0a..6a01579 100644 --- a/main.nf +++ b/main.nf @@ -31,6 +31,16 @@ def email() { } +// +// Used for Slack notifications +// +def slack() { + if (params.hook_url) { + TalusTemplate.IM_notification(workflow, params, projectDir) + } +} + + // // Use the DLIB when the ELIB is unavailable. // @@ -133,4 +143,7 @@ workflow dummy { } // Email notifications: -workflow.onComplete { email() } +workflow.onComplete { + email() + slack() +} diff --git a/nextflow.config b/nextflow.config index 0acaf12..f5fc5de 100644 --- a/nextflow.config +++ b/nextflow.config @@ -54,6 +54,7 @@ params { report_dir = 'reports' /** \type{str} Where reports will be saved. */ mzml_dir = 'mzml' /** \type{str} Where mzML files will be saved. */ email = null /** \type{str} An email to alert on completion. */ + hook_url = null /** \type{str} A URL for Slack notifications */ /** \group{Grouping Parameters} */ /** \type{boolean}