From 46ec29ede49d934bd470ceba42c063e5f9973a4a Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Thu, 26 Dec 2019 18:35:21 +0000 Subject: [PATCH 01/11] Update starting message format --- knockknock/slack_sender.py | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/knockknock/slack_sender.py b/knockknock/slack_sender.py index 7ca63f3..bd2ca63 100644 --- a/knockknock/slack_sender.py +++ b/knockknock/slack_sender.py @@ -9,6 +9,7 @@ DATE_FORMAT = "%Y-%m-%d %H:%M:%S" + def slack_sender(webhook_url: str, channel: str, user_mentions: List[str] = []): """ Slack sender wrapper: execute func, send a Slack notification with the end status @@ -21,7 +22,7 @@ def slack_sender(webhook_url: str, channel: str, user_mentions: List[str] = []): `channel`: str The slack room to log. `user_mentions`: List[str] (default=[]) - Optional users ids to notify. + Optional usernames to notify. Visit https://api.slack.com/methods/users.identity for more details. """ @@ -30,6 +31,10 @@ def slack_sender(webhook_url: str, channel: str, user_mentions: List[str] = []): "channel": channel, "icon_emoji": ":clapper:", } + + if user_mentions: + user_mentions = ["@{}".format(user) for user in user_mentions if not user.startswith("@")] + def decorator_sender(func): @functools.wraps(func) def wrapper_sender(*args, **kwargs): @@ -50,13 +55,28 @@ def wrapper_sender(*args, **kwargs): master_process = True if master_process: - contents = ['Your training has started 🎬', - 'Machine name: %s' % host_name, - 'Main call: %s' % func_name, - 'Starting date: %s' % start_time.strftime(DATE_FORMAT)] - contents.append(' '.join(user_mentions)) - dump['text'] = '\n'.join(contents) + notification = "Your training has started! 🎬" + if user_mentions: + notification = _add_mentions(notification) + + dump['blocks'] =[{"type": "section", + "text": {"type": "mrkdwn", "text": notification}}, + {"type": "divider"}, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": + '*Machine name:* {}\n' + '*Main call:* {}\n' + '*Starting date:* {}\n'.format(host_name, func_name, start_time.strftime(DATE_FORMAT)) + }, + ], + }] + dump['text'] = notification dump['icon_emoji'] = ':clapper:' + requests.post(webhook_url, json.dumps(dump)) try: @@ -104,6 +124,10 @@ def wrapper_sender(*args, **kwargs): requests.post(webhook_url, json.dumps(dump)) raise ex + def _add_mentions(notification): + notification = " ".join(user_mentions) + " " + notification + return notification + return wrapper_sender return decorator_sender From 0f5f717a9c5f39e410c1f25ff02dcd3619bc04bb Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Thu, 26 Dec 2019 18:44:33 +0000 Subject: [PATCH 02/11] Update success message formatting --- knockknock/slack_sender.py | 50 +++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/knockknock/slack_sender.py b/knockknock/slack_sender.py index bd2ca63..c9e466e 100644 --- a/knockknock/slack_sender.py +++ b/knockknock/slack_sender.py @@ -83,23 +83,49 @@ def wrapper_sender(*args, **kwargs): value = func(*args, **kwargs) if master_process: + notification = "Your training is complete 🎉" + if user_mentions: + notification = _add_mentions(notification) + end_time = datetime.datetime.now() elapsed_time = end_time - start_time - contents = ["Your training is complete 🎉", - 'Machine name: %s' % host_name, - 'Main call: %s' % func_name, - 'Starting date: %s' % start_time.strftime(DATE_FORMAT), + hours, remainder = divmod(elapsed_time.seconds, 3600) + minutes, seconds = divmod(remainder, 60) + training_time = "{:2d}:{:02d}:{:02d}".format(hours, minutes, seconds) + + contents = [, 'End date: %s' % end_time.strftime(DATE_FORMAT), 'Training duration: %s' % str(elapsed_time)] - try: - str_value = str(value) - contents.append('Main call returned value: %s'% str_value) - except: - contents.append('Main call returned value: %s'% "ERROR - Couldn't str the returned value.") - - contents.append(' '.join(user_mentions)) - dump['text'] = '\n'.join(contents) + dump['blocks'] = [{"type": "section", + "text": {"type": "mrkdwn", "text": notification}}, + {"type": "divider"}, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": + '*Machine name:* {}\n' + '*Main call:* {}\n' + '*Starting date:* {}\n' + '*End date:* {}\n' + '*Training Duration:* {}'.format(host_name, func_name, + start_time.strftime(DATE_FORMAT), end_time.strftime(DATE_FORMAT), training_time) + }, + ], + }] + + if value is not None: + try: + str_value = str(value) + dump["blocks"].append({"type": "section", + "text": {"type": "mrkdwn", + "text": 'Main call returned value: {}'.format(str_value)}}) + except Exception as e: + dump["blocks"].append("Couldn't str the returned value due to the following error: \n`{}`".format(e)) + + dump['text'] = notification dump['icon_emoji'] = ':tada:' requests.post(webhook_url, json.dumps(dump)) From 92233531d727f4413b89766ca99668138a74be64 Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Thu, 26 Dec 2019 18:46:38 +0000 Subject: [PATCH 03/11] Fix formatting --- knockknock/slack_sender.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/knockknock/slack_sender.py b/knockknock/slack_sender.py index c9e466e..100aa09 100644 --- a/knockknock/slack_sender.py +++ b/knockknock/slack_sender.py @@ -93,10 +93,6 @@ def wrapper_sender(*args, **kwargs): minutes, seconds = divmod(remainder, 60) training_time = "{:2d}:{:02d}:{:02d}".format(hours, minutes, seconds) - contents = [, - 'End date: %s' % end_time.strftime(DATE_FORMAT), - 'Training duration: %s' % str(elapsed_time)] - dump['blocks'] = [{"type": "section", "text": {"type": "mrkdwn", "text": notification}}, {"type": "divider"}, @@ -117,11 +113,12 @@ def wrapper_sender(*args, **kwargs): }] if value is not None: + dump["blocks"].append({"type": "divider"}) try: str_value = str(value) dump["blocks"].append({"type": "section", "text": {"type": "mrkdwn", - "text": 'Main call returned value: {}'.format(str_value)}}) + "text": '*Main call returned value:* {}'.format(str_value)}}) except Exception as e: dump["blocks"].append("Couldn't str the returned value due to the following error: \n`{}`".format(e)) From 6fcf11aceff1ea2ed4063cb60db2501445e78285 Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Thu, 26 Dec 2019 18:54:26 +0000 Subject: [PATCH 04/11] Update error formatting --- knockknock/slack_sender.py | 58 ++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/knockknock/slack_sender.py b/knockknock/slack_sender.py index 100aa09..15f597e 100644 --- a/knockknock/slack_sender.py +++ b/knockknock/slack_sender.py @@ -131,18 +131,52 @@ def wrapper_sender(*args, **kwargs): except Exception as ex: end_time = datetime.datetime.now() elapsed_time = end_time - start_time - contents = ["Your training has crashed ☠️", - 'Machine name: %s' % host_name, - 'Main call: %s' % func_name, - 'Starting date: %s' % start_time.strftime(DATE_FORMAT), - 'Crash date: %s' % end_time.strftime(DATE_FORMAT), - 'Crashed training duration: %s\n\n' % str(elapsed_time), - "Here's the error:", - '%s\n\n' % ex, - "Traceback:", - '%s' % traceback.format_exc()] - contents.append(' '.join(user_mentions)) - dump['text'] = '\n'.join(contents) + hours, remainder = divmod(elapsed_time.seconds, 3600) + minutes, seconds = divmod(remainder, 60) + training_time = "{:2d}:{:02d}:{:02d}".format(hours, minutes, seconds) + + notification = "Your training has crashed ☠️" + if user_mentions: + notification = _add_mentions(notification) + + dump['blocks'] = [{"type": "section", + "text": {"type": "mrkdwn", "text": notification}}, + {"type": "divider"}, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": + '*Machine name:* {}\n' + '*Main call:* {}\n' + '*Starting date:* {}\n' + '*Crash date:* {}\n' + '*Time elapsed before crash:* {}'.format(host_name, func_name, + start_time.strftime(DATE_FORMAT), + end_time.strftime(DATE_FORMAT), + training_time) + }, + ], + }, {"type": "divider"}, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Error:* `{}`".format(ex), + }, + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Traceback:*\n```{}```".format( + traceback.format_exc() + ), + }, + },] + + dump['text'] = notification dump['icon_emoji'] = ':skull_and_crossbones:' requests.post(webhook_url, json.dumps(dump)) raise ex From 58ac7e3ca78ff674c474a89f13b323dfbd282107 Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Thu, 26 Dec 2019 18:55:39 +0000 Subject: [PATCH 05/11] reformat code --- knockknock/slack_sender.py | 199 +++++++++++++++++++++---------------- 1 file changed, 116 insertions(+), 83 deletions(-) diff --git a/knockknock/slack_sender.py b/knockknock/slack_sender.py index 15f597e..f41e495 100644 --- a/knockknock/slack_sender.py +++ b/knockknock/slack_sender.py @@ -59,23 +59,30 @@ def wrapper_sender(*args, **kwargs): if user_mentions: notification = _add_mentions(notification) - dump['blocks'] =[{"type": "section", - "text": {"type": "mrkdwn", "text": notification}}, - {"type": "divider"}, - { - "type": "context", - "elements": [ - { - "type": "mrkdwn", - "text": - '*Machine name:* {}\n' - '*Main call:* {}\n' - '*Starting date:* {}\n'.format(host_name, func_name, start_time.strftime(DATE_FORMAT)) - }, - ], - }] - dump['text'] = notification - dump['icon_emoji'] = ':clapper:' + dump["blocks"] = [ + { + "type": "section", + "text": {"type": "mrkdwn", "text": notification}, + }, + {"type": "divider"}, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": "*Machine name:* {}\n" + "*Main call:* {}\n" + "*Starting date:* {}\n".format( + host_name, + func_name, + start_time.strftime(DATE_FORMAT), + ), + } + ], + }, + ] + dump["text"] = notification + dump["icon_emoji"] = ":clapper:" requests.post(webhook_url, json.dumps(dump)) @@ -91,39 +98,61 @@ def wrapper_sender(*args, **kwargs): elapsed_time = end_time - start_time hours, remainder = divmod(elapsed_time.seconds, 3600) minutes, seconds = divmod(remainder, 60) - training_time = "{:2d}:{:02d}:{:02d}".format(hours, minutes, seconds) - - dump['blocks'] = [{"type": "section", - "text": {"type": "mrkdwn", "text": notification}}, - {"type": "divider"}, - { - "type": "context", - "elements": [ - { - "type": "mrkdwn", - "text": - '*Machine name:* {}\n' - '*Main call:* {}\n' - '*Starting date:* {}\n' - '*End date:* {}\n' - '*Training Duration:* {}'.format(host_name, func_name, - start_time.strftime(DATE_FORMAT), end_time.strftime(DATE_FORMAT), training_time) - }, - ], - }] + training_time = "{:2d}:{:02d}:{:02d}".format( + hours, minutes, seconds + ) + + dump["blocks"] = [ + { + "type": "section", + "text": {"type": "mrkdwn", "text": notification}, + }, + {"type": "divider"}, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": "*Machine name:* {}\n" + "*Main call:* {}\n" + "*Starting date:* {}\n" + "*End date:* {}\n" + "*Training Duration:* {}".format( + host_name, + func_name, + start_time.strftime(DATE_FORMAT), + end_time.strftime(DATE_FORMAT), + training_time, + ), + } + ], + }, + ] if value is not None: dump["blocks"].append({"type": "divider"}) try: str_value = str(value) - dump["blocks"].append({"type": "section", - "text": {"type": "mrkdwn", - "text": '*Main call returned value:* {}'.format(str_value)}}) + dump["blocks"].append( + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Main call returned value:* {}".format( + str_value + ), + }, + } + ) except Exception as e: - dump["blocks"].append("Couldn't str the returned value due to the following error: \n`{}`".format(e)) - - dump['text'] = notification - dump['icon_emoji'] = ':tada:' + dump["blocks"].append( + "Couldn't str the returned value due to the following error: \n`{}`".format( + e + ) + ) + + dump["text"] = notification + dump["icon_emoji"] = ":tada:" requests.post(webhook_url, json.dumps(dump)) return value @@ -139,45 +168,49 @@ def wrapper_sender(*args, **kwargs): if user_mentions: notification = _add_mentions(notification) - dump['blocks'] = [{"type": "section", - "text": {"type": "mrkdwn", "text": notification}}, - {"type": "divider"}, - { - "type": "context", - "elements": [ - { - "type": "mrkdwn", - "text": - '*Machine name:* {}\n' - '*Main call:* {}\n' - '*Starting date:* {}\n' - '*Crash date:* {}\n' - '*Time elapsed before crash:* {}'.format(host_name, func_name, - start_time.strftime(DATE_FORMAT), - end_time.strftime(DATE_FORMAT), - training_time) - }, - ], - }, {"type": "divider"}, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*Error:* `{}`".format(ex), - }, - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*Traceback:*\n```{}```".format( - traceback.format_exc() - ), - }, - },] - - dump['text'] = notification - dump['icon_emoji'] = ':skull_and_crossbones:' + dump["blocks"] = [ + { + "type": "section", + "text": {"type": "mrkdwn", "text": notification}, + }, + {"type": "divider"}, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": "*Machine name:* {}\n" + "*Main call:* {}\n" + "*Starting date:* {}\n" + "*Crash date:* {}\n" + "*Time elapsed before crash:* {}".format( + host_name, + func_name, + start_time.strftime(DATE_FORMAT), + end_time.strftime(DATE_FORMAT), + training_time, + ), + } + ], + }, + {"type": "divider"}, + { + "type": "section", + "text": {"type": "mrkdwn", "text": "*Error:* `{}`".format(ex)}, + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Traceback:*\n```{}```".format( + traceback.format_exc() + ), + }, + }, + ] + + dump["text"] = notification + dump["icon_emoji"] = ":skull_and_crossbones:" requests.post(webhook_url, json.dumps(dump)) raise ex From b3de6c0d5ba7d48927248afea93511b0df6eda4e Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Thu, 26 Dec 2019 19:04:53 +0000 Subject: [PATCH 06/11] Extract messages and training time formatting methods --- knockknock/slack_sender.py | 248 ++++++++++++++++++------------------- 1 file changed, 124 insertions(+), 124 deletions(-) diff --git a/knockknock/slack_sender.py b/knockknock/slack_sender.py index f41e495..0d68a7c 100644 --- a/knockknock/slack_sender.py +++ b/knockknock/slack_sender.py @@ -59,28 +59,9 @@ def wrapper_sender(*args, **kwargs): if user_mentions: notification = _add_mentions(notification) - dump["blocks"] = [ - { - "type": "section", - "text": {"type": "mrkdwn", "text": notification}, - }, - {"type": "divider"}, - { - "type": "context", - "elements": [ - { - "type": "mrkdwn", - "text": "*Machine name:* {}\n" - "*Main call:* {}\n" - "*Starting date:* {}\n".format( - host_name, - func_name, - start_time.strftime(DATE_FORMAT), - ), - } - ], - }, - ] + dump["blocks"] = _starting_message( + func_name, host_name, notification, start_time + ) dump["text"] = notification dump["icon_emoji"] = ":clapper:" @@ -94,63 +75,9 @@ def wrapper_sender(*args, **kwargs): if user_mentions: notification = _add_mentions(notification) - end_time = datetime.datetime.now() - elapsed_time = end_time - start_time - hours, remainder = divmod(elapsed_time.seconds, 3600) - minutes, seconds = divmod(remainder, 60) - training_time = "{:2d}:{:02d}:{:02d}".format( - hours, minutes, seconds + dump["blocks"] = _successful_message( + func_name, host_name, notification, start_time, value ) - - dump["blocks"] = [ - { - "type": "section", - "text": {"type": "mrkdwn", "text": notification}, - }, - {"type": "divider"}, - { - "type": "context", - "elements": [ - { - "type": "mrkdwn", - "text": "*Machine name:* {}\n" - "*Main call:* {}\n" - "*Starting date:* {}\n" - "*End date:* {}\n" - "*Training Duration:* {}".format( - host_name, - func_name, - start_time.strftime(DATE_FORMAT), - end_time.strftime(DATE_FORMAT), - training_time, - ), - } - ], - }, - ] - - if value is not None: - dump["blocks"].append({"type": "divider"}) - try: - str_value = str(value) - dump["blocks"].append( - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*Main call returned value:* {}".format( - str_value - ), - }, - } - ) - except Exception as e: - dump["blocks"].append( - "Couldn't str the returned value due to the following error: \n`{}`".format( - e - ) - ) - dump["text"] = notification dump["icon_emoji"] = ":tada:" requests.post(webhook_url, json.dumps(dump)) @@ -158,62 +85,135 @@ def wrapper_sender(*args, **kwargs): return value except Exception as ex: - end_time = datetime.datetime.now() - elapsed_time = end_time - start_time - hours, remainder = divmod(elapsed_time.seconds, 3600) - minutes, seconds = divmod(remainder, 60) - training_time = "{:2d}:{:02d}:{:02d}".format(hours, minutes, seconds) - notification = "Your training has crashed ☠️" if user_mentions: notification = _add_mentions(notification) - dump["blocks"] = [ - { - "type": "section", - "text": {"type": "mrkdwn", "text": notification}, - }, - {"type": "divider"}, - { - "type": "context", - "elements": [ - { - "type": "mrkdwn", - "text": "*Machine name:* {}\n" - "*Main call:* {}\n" - "*Starting date:* {}\n" - "*Crash date:* {}\n" - "*Time elapsed before crash:* {}".format( - host_name, - func_name, - start_time.strftime(DATE_FORMAT), - end_time.strftime(DATE_FORMAT), - training_time, - ), - } - ], - }, - {"type": "divider"}, - { - "type": "section", - "text": {"type": "mrkdwn", "text": "*Error:* `{}`".format(ex)}, - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*Traceback:*\n```{}```".format( - traceback.format_exc() - ), - }, - }, - ] + dump["blocks"] = _error_message( + ex, func_name, host_name, notification, start_time + ) dump["text"] = notification dump["icon_emoji"] = ":skull_and_crossbones:" requests.post(webhook_url, json.dumps(dump)) raise ex + def _error_message(ex, func_name, host_name, notification, start_time): + end_time = datetime.datetime.now() + training_time = _format_train_time(end_time, start_time) + return [ + {"type": "section", "text": {"type": "mrkdwn", "text": notification}}, + {"type": "divider"}, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": "*Machine name:* {}\n" + "*Main call:* {}\n" + "*Starting date:* {}\n" + "*Crash date:* {}\n" + "*Time elapsed before crash:* {}".format( + host_name, + func_name, + start_time.strftime(DATE_FORMAT), + end_time.strftime(DATE_FORMAT), + training_time, + ), + } + ], + }, + {"type": "divider"}, + { + "type": "section", + "text": {"type": "mrkdwn", "text": "*Error:* `{}`".format(ex)}, + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Traceback:*\n```{}```".format(traceback.format_exc()), + }, + }, + ] + + def _starting_message(func_name, host_name, notification, start_time): + return [ + {"type": "section", "text": {"type": "mrkdwn", "text": notification}}, + {"type": "divider"}, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": "*Machine name:* {}\n" + "*Main call:* {}\n" + "*Starting date:* {}\n".format( + host_name, func_name, start_time.strftime(DATE_FORMAT) + ), + } + ], + }, + ] + + def _successful_message(func_name, host_name, notification, start_time, value): + end_time = datetime.datetime.now() + training_time = _format_train_time(end_time, start_time) + blocks = [ + {"type": "section", "text": {"type": "mrkdwn", "text": notification}}, + {"type": "divider"}, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": "*Machine name:* {}\n" + "*Main call:* {}\n" + "*Starting date:* {}\n" + "*End date:* {}\n" + "*Training Duration:* {}".format( + host_name, + func_name, + start_time.strftime(DATE_FORMAT), + end_time.strftime(DATE_FORMAT), + training_time, + ), + } + ], + }, + ] + + if value is not None: + blocks.append({"type": "divider"}) + try: + str_value = str(value) + dump["blocks"].append( + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Main call returned value:* {}".format( + str_value + ), + }, + } + ) + except Exception as e: + blocks.append( + "Couldn't str the returned value due to the following error: \n`{}`".format( + e + ) + ) + + return blocks + + def _format_train_time(end_time, start_time): + elapsed_time = end_time - start_time + hours, remainder = divmod(elapsed_time.seconds, 3600) + minutes, seconds = divmod(remainder, 60) + training_time = "{:2d}:{:02d}:{:02d}".format(hours, minutes, seconds) + return training_time + def _add_mentions(notification): notification = " ".join(user_mentions) + " " + notification return notification From 30c088aeed4cbdebf31d04d059428168b92db8b1 Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Thu, 26 Dec 2019 19:15:52 +0000 Subject: [PATCH 07/11] docstrings --- knockknock/slack_sender.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/knockknock/slack_sender.py b/knockknock/slack_sender.py index 0d68a7c..9e3233b 100644 --- a/knockknock/slack_sender.py +++ b/knockknock/slack_sender.py @@ -99,6 +99,7 @@ def wrapper_sender(*args, **kwargs): raise ex def _error_message(ex, func_name, host_name, notification, start_time): + """Uses Slack blocks to create a formatted report of exception 'ex'.""" end_time = datetime.datetime.now() training_time = _format_train_time(end_time, start_time) return [ @@ -138,6 +139,7 @@ def _error_message(ex, func_name, host_name, notification, start_time): ] def _starting_message(func_name, host_name, notification, start_time): + """Uses Slack blocks to create an initial report of training.""" return [ {"type": "section", "text": {"type": "mrkdwn", "text": notification}}, {"type": "divider"}, @@ -157,6 +159,7 @@ def _starting_message(func_name, host_name, notification, start_time): ] def _successful_message(func_name, host_name, notification, start_time, value): + """Uses Slack blocks to report a successful training run with statistics.""" end_time = datetime.datetime.now() training_time = _format_train_time(end_time, start_time) blocks = [ @@ -208,6 +211,7 @@ def _successful_message(func_name, host_name, notification, start_time, value): return blocks def _format_train_time(end_time, start_time): + """Returns a time delta in format HH:MM:SS""" elapsed_time = end_time - start_time hours, remainder = divmod(elapsed_time.seconds, 3600) minutes, seconds = divmod(remainder, 60) @@ -221,3 +225,4 @@ def _add_mentions(notification): return wrapper_sender return decorator_sender + From 33e5c251b97db844f7dc78b6f00cb36023519c0f Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Thu, 26 Dec 2019 19:20:40 +0000 Subject: [PATCH 08/11] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2273e28..60c4d78 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Note that launching this will asks you for the sender's email password. It will ### Slack -Similarly, you can also use Slack to get notifications. You'll have to get your Slack room [webhook URL](https://api.slack.com/incoming-webhooks#create_a_webhook) and optionally your [user id](https://api.slack.com/methods/users.identity) (if you want to tag yourself or someone else). +Similarly, you can also use Slack to get notifications. You'll have to get your Slack room [webhook URL](https://api.slack.com/incoming-webhooks#create_a_webhook) and optionally your username (if you want to tag yourself or someone else). #### Python @@ -77,7 +77,7 @@ def train_your_nicest_model(your_nicest_parameters): return {'loss': 0.9} # Optional return value ``` -You can also specify an optional argument to tag specific people: `user_mentions=[, ]`. +You can also specify an optional argument to tag specific people: `user_mentions=[, ]`. #### Command-line From 7e34a8a6651e9e3b3576038a000494aadaec6a14 Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Tue, 10 Mar 2020 16:58:29 +0100 Subject: [PATCH 09/11] Add days, swap _add_mentions order, use slack code formatting where applicable --- knockknock/slack_sender.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/knockknock/slack_sender.py b/knockknock/slack_sender.py index 9e3233b..58721dd 100644 --- a/knockknock/slack_sender.py +++ b/knockknock/slack_sender.py @@ -111,7 +111,7 @@ def _error_message(ex, func_name, host_name, notification, start_time): { "type": "mrkdwn", "text": "*Machine name:* {}\n" - "*Main call:* {}\n" + "*Main call:* `{}`\n" "*Starting date:* {}\n" "*Crash date:* {}\n" "*Time elapsed before crash:* {}".format( @@ -171,7 +171,7 @@ def _successful_message(func_name, host_name, notification, start_time, value): { "type": "mrkdwn", "text": "*Machine name:* {}\n" - "*Main call:* {}\n" + "*Main call:* `{}`\n" "*Starting date:* {}\n" "*End date:* {}\n" "*Training Duration:* {}".format( @@ -195,7 +195,7 @@ def _successful_message(func_name, host_name, notification, start_time, value): "type": "section", "text": { "type": "mrkdwn", - "text": "*Main call returned value:* {}".format( + "text": "*Main call returned value:* `{}`".format( str_value ), }, @@ -211,15 +211,19 @@ def _successful_message(func_name, host_name, notification, start_time, value): return blocks def _format_train_time(end_time, start_time): - """Returns a time delta in format HH:MM:SS""" + """Returns a time delta as a string in the format '%d %H:%M:%S'""" elapsed_time = end_time - start_time - hours, remainder = divmod(elapsed_time.seconds, 3600) + days, remainder = divmod(elapsed_time.seconds, 86400) + hours, remainder = divmod(remainder, 24) minutes, seconds = divmod(remainder, 60) training_time = "{:2d}:{:02d}:{:02d}".format(hours, minutes, seconds) + + if days: + training_time = "{}d ".format(days) + training_time return training_time def _add_mentions(notification): - notification = " ".join(user_mentions) + " " + notification + notification = notification + " " + " ".join(user_mentions) return notification return wrapper_sender From e4b6af58e5a7a97e3571a44932463e18fe526123 Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Tue, 10 Mar 2020 17:36:21 +0100 Subject: [PATCH 10/11] Fix mentions and return val --- README.md | 4 ++-- knockknock/slack_sender.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 60c4d78..2273e28 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Note that launching this will asks you for the sender's email password. It will ### Slack -Similarly, you can also use Slack to get notifications. You'll have to get your Slack room [webhook URL](https://api.slack.com/incoming-webhooks#create_a_webhook) and optionally your username (if you want to tag yourself or someone else). +Similarly, you can also use Slack to get notifications. You'll have to get your Slack room [webhook URL](https://api.slack.com/incoming-webhooks#create_a_webhook) and optionally your [user id](https://api.slack.com/methods/users.identity) (if you want to tag yourself or someone else). #### Python @@ -77,7 +77,7 @@ def train_your_nicest_model(your_nicest_parameters): return {'loss': 0.9} # Optional return value ``` -You can also specify an optional argument to tag specific people: `user_mentions=[, ]`. +You can also specify an optional argument to tag specific people: `user_mentions=[, ]`. #### Command-line diff --git a/knockknock/slack_sender.py b/knockknock/slack_sender.py index 58721dd..4adf8a3 100644 --- a/knockknock/slack_sender.py +++ b/knockknock/slack_sender.py @@ -22,7 +22,7 @@ def slack_sender(webhook_url: str, channel: str, user_mentions: List[str] = []): `channel`: str The slack room to log. `user_mentions`: List[str] (default=[]) - Optional usernames to notify. + Optional user ids to notify. Visit https://api.slack.com/methods/users.identity for more details. """ @@ -33,7 +33,7 @@ def slack_sender(webhook_url: str, channel: str, user_mentions: List[str] = []): } if user_mentions: - user_mentions = ["@{}".format(user) for user in user_mentions if not user.startswith("@")] + user_mentions = ["<@{}>".format(user) for user in user_mentions] def decorator_sender(func): @functools.wraps(func) @@ -149,7 +149,7 @@ def _starting_message(func_name, host_name, notification, start_time): { "type": "mrkdwn", "text": "*Machine name:* {}\n" - "*Main call:* {}\n" + "*Main call:* `{}`\n" "*Starting date:* {}\n".format( host_name, func_name, start_time.strftime(DATE_FORMAT) ), @@ -190,7 +190,7 @@ def _successful_message(func_name, host_name, notification, start_time, value): blocks.append({"type": "divider"}) try: str_value = str(value) - dump["blocks"].append( + blocks.append( { "type": "section", "text": { From 6cbb3c03a8a2c8dee05df67040b792d1d06fb2b5 Mon Sep 17 00:00:00 2001 From: Thomas Metcalfe Date: Tue, 10 Mar 2020 18:11:19 +0100 Subject: [PATCH 11/11] Remove channel and dump --- README.md | 2 +- knockknock/slack_sender.py | 51 ++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 2273e28..599a4fe 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Similarly, you can also use Slack to get notifications. You'll have to get your from knockknock import slack_sender webhook_url = "" -@slack_sender(webhook_url=webhook_url, channel="") +@slack_sender(webhook_url=webhook_url) def train_your_nicest_model(your_nicest_parameters): import time time.sleep(10000) diff --git a/knockknock/slack_sender.py b/knockknock/slack_sender.py index 4adf8a3..763c5d3 100644 --- a/knockknock/slack_sender.py +++ b/knockknock/slack_sender.py @@ -10,7 +10,7 @@ DATE_FORMAT = "%Y-%m-%d %H:%M:%S" -def slack_sender(webhook_url: str, channel: str, user_mentions: List[str] = []): +def slack_sender(webhook_url: str, user_mentions: List[str] = []): """ Slack sender wrapper: execute func, send a Slack notification with the end status (sucessfully finished or crashed) at the end. Also send a Slack notification before @@ -19,19 +19,11 @@ def slack_sender(webhook_url: str, channel: str, user_mentions: List[str] = []): `webhook_url`: str The webhook URL to access your slack room. Visit https://api.slack.com/incoming-webhooks#create_a_webhook for more details. - `channel`: str - The slack room to log. `user_mentions`: List[str] (default=[]) Optional user ids to notify. Visit https://api.slack.com/methods/users.identity for more details. """ - dump = { - "username": "Knock Knock", - "channel": channel, - "icon_emoji": ":clapper:", - } - if user_mentions: user_mentions = ["<@{}>".format(user) for user in user_mentions] @@ -59,13 +51,14 @@ def wrapper_sender(*args, **kwargs): if user_mentions: notification = _add_mentions(notification) - dump["blocks"] = _starting_message( - func_name, host_name, notification, start_time - ) - dump["text"] = notification - dump["icon_emoji"] = ":clapper:" + payload = { + "blocks": _starting_message( + func_name, host_name, notification, start_time + ), + "text": notification, + } - requests.post(webhook_url, json.dumps(dump)) + requests.post(webhook_url, json.dumps(payload)) try: value = func(*args, **kwargs) @@ -75,12 +68,13 @@ def wrapper_sender(*args, **kwargs): if user_mentions: notification = _add_mentions(notification) - dump["blocks"] = _successful_message( - func_name, host_name, notification, start_time, value - ) - dump["text"] = notification - dump["icon_emoji"] = ":tada:" - requests.post(webhook_url, json.dumps(dump)) + payload = { + "blocks": _successful_message( + func_name, host_name, notification, start_time, value + ), + "text": notification, + } + requests.post(webhook_url, json.dumps(payload)) return value @@ -89,13 +83,13 @@ def wrapper_sender(*args, **kwargs): if user_mentions: notification = _add_mentions(notification) - dump["blocks"] = _error_message( - ex, func_name, host_name, notification, start_time - ) - - dump["text"] = notification - dump["icon_emoji"] = ":skull_and_crossbones:" - requests.post(webhook_url, json.dumps(dump)) + payload = { + "blocks": _error_message( + ex, func_name, host_name, notification, start_time + ), + "text": notification, + } + requests.post(webhook_url, json.dumps(payload)) raise ex def _error_message(ex, func_name, host_name, notification, start_time): @@ -229,4 +223,3 @@ def _add_mentions(notification): return wrapper_sender return decorator_sender -