# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
from django.db.models import Q
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from enumfields import EnumIntegerField
from jsonfield.fields import JSONField
from shuup.core.fields import InternalIdentifierField
from shuup.notify.enums import Priority, RecipientType
from shuup.utils.django_compat import NoReverseMatch, is_anonymous, reverse
[docs]class NotificationManager(models.Manager):
[docs] def for_user(self, user):
"""
:type user: django.contrib.auth.models.AbstractUser
"""
if not (user and not is_anonymous(user)):
return self.none()
q = Q(recipient_type=RecipientType.SPECIFIC_USER) & Q(recipient=user)
if getattr(user, "is_superuser", False):
q |= Q(recipient_type=RecipientType.ADMINS)
return self.filter(q)
[docs] def unread_for_user(self, user):
return self.for_user(user).exclude(marked_read=True)
[docs]class Notification(models.Model):
"""
A model for persistent notifications to be shown in the admin, etc.
"""
shop = models.ForeignKey(on_delete=models.CASCADE, to="shuup.Shop", verbose_name=_("shop"))
recipient_type = EnumIntegerField(RecipientType, default=RecipientType.ADMINS, verbose_name=_("recipient type"))
recipient = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True,
null=True,
related_name="+",
on_delete=models.SET_NULL,
verbose_name=_("recipient"),
)
created_on = models.DateTimeField(auto_now_add=True, editable=False, verbose_name=_("created on"))
message = models.CharField(max_length=140, editable=False, default="", verbose_name=_("message"))
identifier = InternalIdentifierField(unique=False)
priority = EnumIntegerField(Priority, default=Priority.NORMAL, db_index=True, verbose_name=_("priority"))
_data = JSONField(blank=True, null=True, editable=False, db_column="data")
marked_read = models.BooleanField(db_index=True, editable=False, default=False, verbose_name=_("marked read"))
marked_read_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True,
null=True,
editable=False,
related_name="+",
on_delete=models.SET_NULL,
verbose_name=_("marked read by"),
)
marked_read_on = models.DateTimeField(null=True, blank=True, verbose_name=_("marked read on"))
objects = NotificationManager()
def __init__(self, *args, **kwargs):
url = kwargs.pop("url", None)
super(Notification, self).__init__(*args, **kwargs)
if url:
self.url = url
[docs] def save(self, *args, **kwargs):
if self.recipient_type == RecipientType.SPECIFIC_USER and not self.recipient_id:
raise ValueError("Error! With `RecipientType.SPECIFIC_USER`, recipient is required.")
super(Notification, self).save(*args, **kwargs)
[docs] def mark_read(self, user):
if self.marked_read:
return False
self.marked_read = True
self.marked_read_by = user
self.marked_read_on = now()
self.save(update_fields=("marked_read", "marked_read_by", "marked_read_on"))
return True
@property
def is_read(self):
return self.marked_read
@property
def data(self):
if not self._data:
self._data = {}
return self._data
@property
def url(self):
url = self.data.get("_url")
if isinstance(url, dict):
return reverse(**url)
return url
@url.setter
def url(self, value):
if self.pk:
raise ValueError("Error! URL can't be set on a saved notification.")
self.data["_url"] = value
[docs] def set_reverse_url(self, **reverse_kwargs):
if self.pk:
raise ValueError("Error! URL can't be set on a saved notification.")
try:
reverse(**reverse_kwargs)
except NoReverseMatch: # pragma: no cover
raise ValueError("Error! Invalid reverse URL parameters.")
self.data["_url"] = reverse_kwargs