from collections.abc import Iterable
from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import cached_property
from .helpers import register
from .models import AdminNotificationTemplate, Notification, NotificationTemplate
[docs]class BaseHandler(metaclass=HandlerMeta):
"""
Base class for handling creation of notification(s). Its purpose is to process signal kwargs sent over a defined
signal. There should be typically one handler (inherited from this class) for each signal. The handler must define
inner class ``Meta`` with following supported attributes:
* ``signal``: Signal to which handler will be registered
* ``allowed_senders``: Handler will be called only if signal was sent by allowed sender
* ``abstract``: If set to True, the handler will not be registered
Attributes:
dispatcher_classes: An iterable of dispatcher classes that will be used to dispatch each notification.
template_slug: Slug of an existing admin template to be used. If not defined, you must define
``get_template_data()`` method.
"""
dispatcher_classes = ()
template_slug = None
@cached_property
def _admin_template(self):
template_slug = self.get_template_slug()
if template_slug:
return AdminNotificationTemplate.objects.get(slug=template_slug)
else:
return None
@cached_property
def _template(self):
if self._admin_template:
template, _ = NotificationTemplate.objects.get_or_create(
title=self._admin_template.title,
text=self._admin_template.text,
trigger_action=self._admin_template.trigger_action,
extra_fields=self._admin_template.extra_fields,
admin_template=self._admin_template,
)
else:
template, _ = NotificationTemplate.objects.get_or_create(**self.get_template_data())
return template
def _init_dispatchers(self):
self.dispatchers = []
for dispatcher_class in set(self.get_dispatcher_classes()):
self.dispatchers.append(self._init_dispatcher(dispatcher_class))
def _init_dispatcher(self, dispatcher_class):
return dispatcher_class()
def _can_create_notification(self, recipient):
"""
Returns ``True`` if notification can be created for ``recipient``.
"""
return True
def _create_notification(self, recipient):
"""
Creates notification for ``recipient``.
"""
if self._can_create_notification(recipient):
return Notification.objects.create(
recipient=recipient,
template=self._template,
related_objects=self.get_related_objects(),
extra_data=self.get_extra_data(),
)
def _can_dispatch_notification(self, notification, dispatcher):
"""
Returns ``True`` if ``notification`` can be dispatched using ``dispatcher``.
"""
return True
def _dispatch_notification(self, notification, dispatcher):
"""
Dispatches ``notification`` using ``dispatcher``.
"""
if self._can_dispatch_notification(notification, dispatcher):
dispatcher.dispatch(notification)
def _can_handle(self):
"""
Returns ``True`` if handler should handle creating of notification(s).
"""
return not self._admin_template or self._admin_template.is_active
[docs] def handle(self, signal_kwargs):
"""
Handles creation of notifications from ``signal_kwargs``.
"""
self.signal_kwargs = signal_kwargs
notifications = []
if self._can_handle():
self._init_dispatchers()
for recipient in self.get_recipients():
notification = self._create_notification(recipient)
if notification:
for dispatcher in self.dispatchers:
self._dispatch_notification(notification, dispatcher)
notifications.append(notification)
return notifications
[docs] def get_recipients(self):
"""
Returns an iterable of recipients for which notification will be created.
"""
raise NotImplementedError() # pragma: no cover
[docs] def get_template_data(self):
"""
Returns kwargs used to create a template. Not called if template slug is used.
"""
raise NotImplementedError() # pragma: no cover
[docs] def get_extra_data(self):
"""
Returns a dictionary with extra data, the values must be JSON serializable.
"""
return None
[docs] def get_template_slug(self):
"""
Returns slug of an admin template to be used.
"""
return self.template_slug
[docs] def get_dispatcher_classes(self):
"""
Returns iterable of dispatcher classes used to dispatch notification(s).
"""
return self.dispatcher_classes