From 1cfc03d69849deeb54359137cc325dd6b325f32a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 4 Dec 2021 03:40:53 +0100 Subject: [PATCH] add a general context checker --- InvenTree/common/notifications.py | 76 ++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/InvenTree/common/notifications.py b/InvenTree/common/notifications.py index 7f99ca353d..546c30431a 100644 --- a/InvenTree/common/notifications.py +++ b/InvenTree/common/notifications.py @@ -18,8 +18,10 @@ logger = logging.getLogger('inventree') # region base classes class NotificationMethod: METHOD_NAME = '' + CONTEXT_BUILTIN = ['name', 'message', ] + CONTEXT_EXTRA = [] - def __init__(self, obj, entry_name, receivers) -> None: + def __init__(self, obj, entry_name, receivers, context) -> None: # Check if a sending fnc is defined if (not hasattr(self, 'send')) and (not hasattr(self, 'send_bulk')): raise NotImplementedError('A NotificationMethod must either define a `send` or a `send_bulk` method') @@ -32,10 +34,46 @@ class NotificationMethod: self.obj = obj self.entry_name = entry_name self.receivers = receivers + self.context = self.check_context(context) # Gather recipients self.recipients = self.get_recipients() + def check_context(self, context): + def check(ref, obj): + # the obj is not accesible so we are on the end + if not isinstance(obj, (list, dict, tuple, )): + return ref + + # check if the ref exsists + if isinstance(ref, str): + if not obj.get(ref): + return ref + return False + + # nested + elif isinstance(ref, (tuple, list)): + if len(ref) == 1: + return check(ref[0], obj) + ret = check(ref[0], obj) + if ret: + return ret + return check(ref[1:], obj[ref[0]]) + + # other cases -> raise + raise NotImplementedError('This type can not be used as a context reference') + + missing = [] + for item in (*self.CONTEXT_BUILTIN, *self.CONTEXT_EXTRA): + ret = check(item, context) + if ret: + missing.append(ret) + + if missing: + raise NotImplementedError(f'The `context` is missing the following items:\n{missing}') + + return context + def get_recipients(self): raise NotImplementedError('The `get_recipients` method must be implemented!') @@ -50,12 +88,12 @@ class NotificationMethod: class SingleNotificationMethod(NotificationMethod): - def send(self, receiver, context): + def send(self, receiver): raise NotImplementedError('The `send` method must be overriden!') class BulkNotificationMethod(NotificationMethod): - def send_bulk(self, context): + def send_bulk(self): raise NotImplementedError('The `send` method must be overriden!') # endregion @@ -63,26 +101,22 @@ class BulkNotificationMethod(NotificationMethod): # region implementations class EmailNotification(BulkNotificationMethod): METHOD_NAME = 'mail' + CONTEXT_EXTRA = [ + ('template', ), + ('template', 'html', ), + ('template', 'subject', ), + ] def get_recipients(self): return EmailAddress.objects.filter( user__in=self.receivers, ) - def send_bulk(self, context): - # TODO: In the future, include the part image in the email template - - if 'template' not in context: - raise NotImplementedError('Templates must be provided in the `context`') - if 'html' not in context['template']: - raise NotImplementedError("template['html'] must be provided in the `context`") - if 'subject' not in context['template']: - raise NotImplementedError("template['subject'] must be provided in the `context`") - - html_message = render_to_string(context['template']['html'], context) + def send_bulk(self): + html_message = render_to_string(self.context['template']['html'], self.context) recipients = self.recipients.values_list('email', flat=True) - InvenTree.tasks.send_email(context['template']['subject'], '', recipients, html_message=html_message) + InvenTree.tasks.send_email(self.context['template']['subject'], '', recipients, html_message=html_message) return True @@ -92,14 +126,14 @@ class UIMessageNotification(SingleNotificationMethod): def get_recipients(self): return self.receivers - def send(self, receiver, context): + def send(self, receiver): NotificationMessage.objects.create( target_object = self.obj, source_object = receiver, user = receiver, category = self.entry_name, - name = context['name'], - message = context['message'], + name = self.context['name'], + message = self.context['message'], ) # endregion # endregion @@ -165,7 +199,7 @@ def trigger_notifaction(obj, entry_name=None, obj_ref='pk', receivers=None, rece def deliver_notification(cls: NotificationMethod, obj, entry_name: str, receivers, notification_context: dict): # Init delivery method - method = cls(obj, entry_name, receivers) + method = cls(obj, entry_name, receivers, notification_context) if method.recipients and len(method.recipients) > 0: # Log start @@ -179,12 +213,12 @@ def deliver_notification(cls: NotificationMethod, obj, entry_name: str, receiver # Select delivery method and execute it if hasattr(method, 'send_bulk'): - success = method.send_bulk(notification_context) + success = method.send_bulk() success_count = len(method.recipients) elif hasattr(method, 'send'): for rec in method.recipients: - if method.send(rec, notification_context): + if method.send(rec): success_count += 1 else: success = False