From c6b5a0961012efa089b851fbd2c49d51073686a9 Mon Sep 17 00:00:00 2001 From: Alvaro Mariano Date: Fri, 7 Jul 2023 00:54:47 +0000 Subject: [PATCH] Restructured files --- notifications/admin.py | 21 +- notifications/apps.py | 9 + notifications/base/__init__.py | 0 notifications/base/admin.py | 16 - notifications/base/models.py | 365 ------------------ .../migrations/0012_auto_20230601_1905.py | 2 +- notifications/models.py | 22 -- notifications/models/__init__.py | 7 + notifications/models/base.py | 196 ++++++++++ notifications/models/notification.py | 9 + notifications/querysets.py | 118 ++++++ notifications/settings.py | 2 +- notifications/signals.py | 74 ++++ .../test_migrations/test_level_migration.py | 4 +- notifications/tests/tests.py | 3 +- notifications/types.py | 5 + pyproject.toml | 2 +- 17 files changed, 433 insertions(+), 422 deletions(-) delete mode 100644 notifications/base/__init__.py delete mode 100644 notifications/base/admin.py delete mode 100644 notifications/base/models.py delete mode 100644 notifications/models.py create mode 100644 notifications/models/__init__.py create mode 100644 notifications/models/base.py create mode 100644 notifications/models/notification.py create mode 100644 notifications/querysets.py create mode 100644 notifications/types.py diff --git a/notifications/admin.py b/notifications/admin.py index 591e55f..27f3eaf 100644 --- a/notifications/admin.py +++ b/notifications/admin.py @@ -4,19 +4,11 @@ from django.contrib import admin from django.utils.translation import gettext_lazy from swapper import load_model -from notifications.base.admin import AbstractNotificationAdmin - Notification = load_model("notifications", "Notification") -def mark_unread(queryset, *args, **kwargs): - queryset.update(unread=True) - - -mark_unread.short_description = gettext_lazy("Mark selected notifications as unread") - - -class NotificationAdmin(AbstractNotificationAdmin): +@admin.register(Notification) +class NotificationAdmin(admin.ModelAdmin): raw_id_fields = ("recipient",) readonly_fields = ("action_object_url", "actor_object_url", "target_object_url") list_display = ("recipient", "actor", "level", "target", "unread", "public") @@ -26,11 +18,16 @@ class NotificationAdmin(AbstractNotificationAdmin): "public", "timestamp", ) - actions = [mark_unread] + actions = ("mark_unread", "mark_read") def get_queryset(self, request): qs = super().get_queryset(request) return qs.prefetch_related("actor") + @admin.action(description=gettext_lazy("Mark selected notifications as unread")) + def mark_unread(self, request, queryset): + queryset.update(unread=True) -admin.site.register(Notification, NotificationAdmin) + @admin.action(description=gettext_lazy("Mark selected notifications as read")) + def mark_read(self, request, queryset): + queryset.update(unread=False) diff --git a/notifications/apps.py b/notifications/apps.py index 463011d..f00baa1 100644 --- a/notifications/apps.py +++ b/notifications/apps.py @@ -8,3 +8,12 @@ class Config(AppConfig): name = "notifications" verbose_name = _("Notifications") default_auto_field = "django.db.models.AutoField" + + def ready(self) -> None: + from notifications.signals import ( # pylint: disable=import-outside-toplevel + notify, + notify_handler, + ) + + notify.connect(notify_handler, dispatch_uid="notifications.models.notification") + return super().ready() diff --git a/notifications/base/__init__.py b/notifications/base/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/notifications/base/admin.py b/notifications/base/admin.py deleted file mode 100644 index 5ff42f9..0000000 --- a/notifications/base/admin.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.contrib import admin - - -class AbstractNotificationAdmin(admin.ModelAdmin): - raw_id_fields = ("recipient",) - list_display = ("recipient", "actor", "level", "target", "unread", "public") - list_filter = ( - "level", - "unread", - "public", - "timestamp", - ) - - def get_queryset(self, request): - qs = super().get_queryset(request) - return qs.prefetch_related("actor") diff --git a/notifications/base/models.py b/notifications/base/models.py deleted file mode 100644 index 70a9aed..0000000 --- a/notifications/base/models.py +++ /dev/null @@ -1,365 +0,0 @@ -# -*- coding: utf-8 -*- -# pylint: disable=too-many-lines -from django.conf import settings -from django.contrib.auth.models import Group -from django.contrib.contenttypes.fields import GenericForeignKey -from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ImproperlyConfigured -from django.db import models -from django.db.models.query import QuerySet -from django.urls import NoReverseMatch, reverse -from django.utils import timesince, timezone -from django.utils.html import format_html -from django.utils.translation import gettext_lazy as _ -from swapper import load_model - -from notifications import settings as notifications_settings -from notifications.signals import notify - -EXTRA_DATA = notifications_settings.get_config()["USE_JSONFIELD"] - - -def is_soft_delete(): - return notifications_settings.get_config()["SOFT_DELETE"] - - -def assert_soft_delete(): - if not is_soft_delete(): - # msg = """To use 'deleted' field, please set 'SOFT_DELETE'=True in settings. - # Otherwise NotificationQuerySet.unread and NotificationQuerySet.read do NOT filter by 'deleted' field. - # """ - msg = "REVERTME" - raise ImproperlyConfigured(msg) - - -class NotificationQuerySet(models.query.QuerySet): - """Notification QuerySet""" - - def unsent(self): - return self.filter(emailed=False) - - def sent(self): - return self.filter(emailed=True) - - def unread(self, include_deleted=False): - """Return only unread items in the current queryset""" - if is_soft_delete() and not include_deleted: - return self.filter(unread=True, deleted=False) - - # When SOFT_DELETE=False, developers are supposed NOT to touch 'deleted' field. - # In this case, to improve query performance, don't filter by 'deleted' field - return self.filter(unread=True) - - def read(self, include_deleted=False): - """Return only read items in the current queryset""" - if is_soft_delete() and not include_deleted: - return self.filter(unread=False, deleted=False) - - # When SOFT_DELETE=False, developers are supposed NOT to touch 'deleted' field. - # In this case, to improve query performance, don't filter by 'deleted' field - return self.filter(unread=False) - - def mark_all_as_read(self, recipient=None): - """Mark as read any unread messages in the current queryset. - - Optionally, filter these by recipient first. - """ - # We want to filter out read ones, as later we will store - # the time they were marked as read. - qset = self.unread(True) - if recipient: - qset = qset.filter(recipient=recipient) - - return qset.update(unread=False) - - def mark_all_as_unread(self, recipient=None): - """Mark as unread any read messages in the current queryset. - - Optionally, filter these by recipient first. - """ - qset = self.read(True) - - if recipient: - qset = qset.filter(recipient=recipient) - - return qset.update(unread=True) - - def deleted(self): - """Return only deleted items in the current queryset""" - assert_soft_delete() - return self.filter(deleted=True) - - def active(self): - """Return only active(un-deleted) items in the current queryset""" - assert_soft_delete() - return self.filter(deleted=False) - - def mark_all_as_deleted(self, recipient=None): - """Mark current queryset as deleted. - Optionally, filter by recipient first. - """ - assert_soft_delete() - qset = self.active() - if recipient: - qset = qset.filter(recipient=recipient) - - return qset.update(deleted=True) - - def mark_all_as_active(self, recipient=None): - """Mark current queryset as active(un-deleted). - Optionally, filter by recipient first. - """ - assert_soft_delete() - qset = self.deleted() - if recipient: - qset = qset.filter(recipient=recipient) - - return qset.update(deleted=False) - - def mark_as_unsent(self, recipient=None): - qset = self.sent() - if recipient: - qset = qset.filter(recipient=recipient) - return qset.update(emailed=False) - - def mark_as_sent(self, recipient=None): - qset = self.unsent() - if recipient: - qset = qset.filter(recipient=recipient) - return qset.update(emailed=True) - - -class NotificationLevel(models.IntegerChoices): - SUCCESS = 1 - INFO = 2 - WARNING = 3 - ERROR = 4 - - -class AbstractNotification(models.Model): - """ - Action model describing the actor acting out a verb (on an optional - target). - Nomenclature based on http://activitystrea.ms/specs/atom/1.0/ - - Generalized Format:: - -