feat: Completely rework notification framework
This change is needed as a) there are many different types of notifications. A notification about a new comment does not only relate the comment but also adoption notice and a user that made the comment. Therefore, I introduce a new general notification that has optional links to other objects. b) this change allows to define a different representation of e-mails vs. display on the website. The new notification type is in charge of representation from now on. Title and text will be deprecated and eventually removed
This commit is contained in:
@@ -11,7 +11,7 @@ from .models import User, Language, Text, ReportComment, ReportAdoptionNotice, L
|
||||
SpeciesSpecificURL, ImportantLocation, SpeciesSpecialization
|
||||
|
||||
from .models import Animal, Species, RescueOrganization, AdoptionNotice, Location, Rule, Image, ModerationAction, \
|
||||
Comment, Report, Announcement, AdoptionNoticeStatus, User, Subscriptions, BaseNotification
|
||||
Comment, Report, Announcement, AdoptionNoticeStatus, User, Subscriptions, Notification
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
@@ -127,9 +127,9 @@ class CommentAdmin(admin.ModelAdmin):
|
||||
list_filter = ("user",)
|
||||
|
||||
|
||||
@admin.register(BaseNotification)
|
||||
@admin.register(Notification)
|
||||
class BaseNotificationAdmin(admin.ModelAdmin):
|
||||
list_filter = ("user", "read")
|
||||
list_filter = ("user_to_notify", "read")
|
||||
|
||||
|
||||
@admin.register(SearchSubscription)
|
||||
|
@@ -66,22 +66,22 @@ class AdoptionNoticeApiView(APIView):
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
adoption_notice = serializer.save(owner=request.user)
|
||||
adoption_notice = serializer.save(owner=request.user_to_notify)
|
||||
|
||||
# Add the location
|
||||
post_adoption_notice_save.delay_on_commit(adoption_notice.pk)
|
||||
|
||||
# Only set active when user has trust level moderator or higher
|
||||
if request.user.trust_level >= TrustLevel.MODERATOR:
|
||||
if request.user_to_notify.trust_level >= TrustLevel.MODERATOR:
|
||||
adoption_notice.set_active()
|
||||
else:
|
||||
adoption_notice.set_unchecked()
|
||||
|
||||
# Log the action
|
||||
Log.objects.create(
|
||||
user=request.user,
|
||||
user=request.user_to_notify,
|
||||
action="add_adoption_notice",
|
||||
text=f"{request.user} added adoption notice {adoption_notice.pk} via API",
|
||||
text=f"{request.user_to_notify} added adoption notice {adoption_notice.pk} via API",
|
||||
)
|
||||
|
||||
# Return success response with new adoption notice details
|
||||
@@ -130,7 +130,7 @@ class AnimalApiView(APIView):
|
||||
"""
|
||||
serializer = AnimalCreateSerializer(data=request.data, context={"request": request})
|
||||
if serializer.is_valid():
|
||||
animal = serializer.save(owner=request.user)
|
||||
animal = serializer.save(owner=request.user_to_notify)
|
||||
return Response(
|
||||
{"message": "Animal created successfully!", "id": animal.id},
|
||||
status=status.HTTP_201_CREATED,
|
||||
@@ -289,7 +289,7 @@ class AddImageApiView(APIView):
|
||||
raise ValueError("Unknown attach_to_type given, should not happen. Check serializer")
|
||||
serializer.validated_data.pop('attach_to_type', None)
|
||||
serializer.validated_data.pop('attach_to', None)
|
||||
image = serializer.save(owner=request.user)
|
||||
image = serializer.save(owner=request.user_to_notify)
|
||||
object_to_attach_to.photos.add(image)
|
||||
return Response(
|
||||
{"message": "Image added successfully!", "id": image.id},
|
||||
@@ -360,9 +360,9 @@ class LocationApiView(APIView):
|
||||
|
||||
# Log the action
|
||||
Log.objects.create(
|
||||
user=request.user,
|
||||
user=request.user_to_notify,
|
||||
action="add_location",
|
||||
text=f"{request.user} added adoption notice {location.pk} via API",
|
||||
text=f"{request.user_to_notify} added adoption notice {location.pk} via API",
|
||||
)
|
||||
|
||||
# Return success response with new adoption notice details
|
||||
|
@@ -6,7 +6,7 @@ from django.utils.html import strip_tags
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django.core import mail
|
||||
from fellchensammlung.models import User, CommentNotification, BaseNotification, TrustLevel
|
||||
from fellchensammlung.models import User, Notification, TrustLevel, NotificationTypeChoices
|
||||
from notfellchen.settings import host
|
||||
|
||||
NEWLINE = "\r\n"
|
||||
@@ -19,10 +19,10 @@ def mail_admins_new_report(report):
|
||||
for moderator in User.objects.filter(trust_level__gt=TrustLevel.MODERATOR):
|
||||
report_url = "https://" + host + report.get_absolute_url()
|
||||
context = {"report_url": report_url,
|
||||
"user_comment": report.user_comment,}
|
||||
"user_comment": report.user_comment, }
|
||||
|
||||
subject = _("Neue Meldung")
|
||||
html_message = render_to_string('fellchensammlung/mail/report.html', context)
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/report.html', context)
|
||||
plain_message = strip_tags(html_message)
|
||||
|
||||
mail.send_mail(subject,
|
||||
@@ -33,11 +33,28 @@ def mail_admins_new_report(report):
|
||||
|
||||
|
||||
def send_notification_email(notification_pk):
|
||||
try:
|
||||
notification = CommentNotification.objects.get(pk=notification_pk)
|
||||
except CommentNotification.DoesNotExist:
|
||||
notification = BaseNotification.objects.get(pk=notification_pk)
|
||||
notification = Notification.objects.get(pk=notification_pk)
|
||||
|
||||
subject = f"🔔 {notification.title}"
|
||||
body_text = notification.text
|
||||
message = mail.EmailMessage(subject, body_text, settings.DEFAULT_FROM_EMAIL, [notification.user.email])
|
||||
message.send()
|
||||
context = {"notification": notification, }
|
||||
if notification.notification_type == NotificationTypeChoices.NEW_REPORT_COMMENT or notification.notification_type == NotificationTypeChoices.NEW_REPORT_AN:
|
||||
context["user_comment"] = notification.report.user_comment
|
||||
context["report_url"] = notification.report.get_absolute_url()
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/report.html', context)
|
||||
elif notification.notification_type == NotificationTypeChoices.NEW_USER:
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/new-user.html', context)
|
||||
elif notification.notification_type == NotificationTypeChoices.AN_IS_TO_BE_CHECKED:
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/an-to-be-checked.html', context)
|
||||
elif notification.notification_type == NotificationTypeChoices.AN_WAS_DEACTIVATED:
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/an-deactivated.html', context)
|
||||
elif notification.notification_type == NotificationTypeChoices.AN_FOR_SEARCH_FOUND:
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/report.html', context)
|
||||
elif notification.notification_type == NotificationTypeChoices.NEW_COMMENT:
|
||||
html_message = render_to_string('fellchensammlung/mail/notifications/new-comment.html', context)
|
||||
else:
|
||||
raise NotImplementedError("Unknown notification type")
|
||||
|
||||
plain_message = strip_tags(html_message)
|
||||
mail.send_mail(subject, plain_message, settings.DEFAULT_FROM_EMAIL,
|
||||
[notification.user_to_notify.email],
|
||||
html_message=html_message)
|
||||
|
@@ -0,0 +1,54 @@
|
||||
# Generated by Django 5.2.1 on 2025-07-11 09:01
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0051_rescueorganization_parent_org_speciesspecialization'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='basenotification',
|
||||
name='user',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='commentnotification',
|
||||
name='basenotification_ptr',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='commentnotification',
|
||||
name='comment',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Notification',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('read_at', models.DateTimeField(blank=True, null=True, verbose_name='Gelesen am')),
|
||||
('notification_type', models.CharField(choices=[('new_user', 'Useraccount wurde erstellt'), ('new_report_an', 'Vermittlung wurde gemeldet'), ('new_report_comment', 'Kommentar wurde gemeldet'), ('an_is_to_be_checked', 'Vermittlung muss überprüft werden'), ('an_was_deactivated', 'Vermittlung wurde deaktiviert'), ('an_for_search_found', 'Vermittlung für Suche gefunden')], max_length=200, verbose_name='Benachrichtigungsgrund')),
|
||||
('title', models.CharField(max_length=100, verbose_name='Titel')),
|
||||
('text', models.TextField(verbose_name='Inhalt')),
|
||||
('read', models.BooleanField(default=False)),
|
||||
('adoption_notice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.adoptionnotice', verbose_name='Vermittlung')),
|
||||
('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.comment', verbose_name='Antwort')),
|
||||
('report', models.ForeignKey(help_text='Report auf den sich die Benachrichtigung bezieht.', on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.report', verbose_name='Report')),
|
||||
('user_related', models.ForeignKey(help_text='Useraccount auf den sich die Benachrichtigung bezieht.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Verwandter Useraccount')),
|
||||
('user_to_notify', models.ForeignKey(help_text='Useraccount der Benachrichtigt wird', on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL, verbose_name='Nutzer*in')),
|
||||
],
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='AdoptionNoticeNotification',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='BaseNotification',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='CommentNotification',
|
||||
),
|
||||
]
|
@@ -0,0 +1,40 @@
|
||||
# Generated by Django 5.2.1 on 2025-07-11 09:10
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0052_remove_basenotification_user_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='adoption_notice',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.adoptionnotice', verbose_name='Vermittlung'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='notification_type',
|
||||
field=models.CharField(choices=[('new_user', 'Useraccount wurde erstellt'), ('new_report_an', 'Vermittlung wurde gemeldet'), ('new_report_comment', 'Kommentar wurde gemeldet'), ('an_is_to_be_checked', 'Vermittlung muss überprüft werden'), ('an_was_deactivated', 'Vermittlung wurde deaktiviert'), ('an_for_search_found', 'Vermittlung für Suche gefunden'), ('new_comment', 'Neuer Kommentar')], max_length=200, verbose_name='Benachrichtigungsgrund'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='report',
|
||||
field=models.ForeignKey(blank=True, help_text='Report auf den sich die Benachrichtigung bezieht.', null=True, on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.report', verbose_name='Report'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='user_related',
|
||||
field=models.ForeignKey(blank=True, help_text='Useraccount auf den sich die Benachrichtigung bezieht.', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Verwandter Useraccount'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='user_to_notify',
|
||||
field=models.ForeignKey(blank=True, help_text='Useraccount der Benachrichtigt wird', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL, verbose_name='Nutzer*in'),
|
||||
),
|
||||
]
|
@@ -256,10 +256,10 @@ class User(AbstractUser):
|
||||
return self.get_absolute_url()
|
||||
|
||||
def get_unread_notifications(self):
|
||||
return BaseNotification.objects.filter(user=self, read=False)
|
||||
return Notification.objects.filter(user=self, read=False)
|
||||
|
||||
def get_num_unread_notifications(self):
|
||||
return BaseNotification.objects.filter(user=self, read=False).count()
|
||||
return Notification.objects.filter(user=self, read=False).count()
|
||||
|
||||
@property
|
||||
def adoption_notices(self):
|
||||
@@ -479,7 +479,11 @@ class AdoptionNotice(models.Model):
|
||||
for subscription in self.get_subscriptions():
|
||||
notification_title = _("Vermittlung deaktiviert:") + f" {self.name}"
|
||||
text = _("Die folgende Vermittlung wurde deaktiviert: ") + f"[{self.name}]({self.get_absolute_url()})"
|
||||
BaseNotification.objects.create(user=subscription.owner, text=text, title=notification_title)
|
||||
Notification.objects.create(user_to_notify=subscription.owner,
|
||||
notification_type=NotificationTypeChoices.AN_WAS_DEACTIVATED,
|
||||
adoption_notice=self,
|
||||
text=text,
|
||||
title=notification_title)
|
||||
|
||||
|
||||
class AdoptionNoticeStatus(models.Model):
|
||||
@@ -902,20 +906,49 @@ class Comment(models.Model):
|
||||
return self.adoption_notice.get_absolute_url()
|
||||
|
||||
|
||||
class BaseNotification(models.Model):
|
||||
class NotificationTypeChoices(models.TextChoices):
|
||||
NEW_USER = "new_user", _("Useraccount wurde erstellt")
|
||||
NEW_REPORT_AN = "new_report_an", _("Vermittlung wurde gemeldet")
|
||||
NEW_REPORT_COMMENT = "new_report_comment", _("Kommentar wurde gemeldet")
|
||||
AN_IS_TO_BE_CHECKED = "an_is_to_be_checked", _("Vermittlung muss überprüft werden")
|
||||
AN_WAS_DEACTIVATED = "an_was_deactivated", _("Vermittlung wurde deaktiviert")
|
||||
AN_FOR_SEARCH_FOUND = "an_for_search_found", _("Vermittlung für Suche gefunden")
|
||||
NEW_COMMENT = "new_comment", _("Neuer Kommentar")
|
||||
|
||||
|
||||
class Notification(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
read_at = models.DateTimeField(blank=True, null=True, verbose_name=_("Gelesen am"))
|
||||
notification_type = models.CharField(max_length=200,
|
||||
choices=NotificationTypeChoices.choices,
|
||||
verbose_name=_('Benachrichtigungsgrund'))
|
||||
title = models.CharField(max_length=100, verbose_name=_("Titel"))
|
||||
text = models.TextField(verbose_name="Inhalt")
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('Nutzer*in'))
|
||||
user_to_notify = models.ForeignKey(User,
|
||||
on_delete=models.CASCADE,
|
||||
blank=True, null=True,
|
||||
verbose_name=_('Nutzer*in'),
|
||||
help_text=_("Useraccount der Benachrichtigt wird"),
|
||||
related_name='user')
|
||||
read = models.BooleanField(default=False)
|
||||
comment = models.ForeignKey(Comment, blank=True, null=True, on_delete=models.CASCADE, verbose_name=_('Antwort'))
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, blank=True, null=True, on_delete=models.CASCADE, verbose_name=_('Vermittlung'))
|
||||
user_related = models.ForeignKey(User,
|
||||
blank=True, null=True,
|
||||
on_delete=models.CASCADE, verbose_name=_('Verwandter Useraccount'),
|
||||
help_text=_("Useraccount auf den sich die Benachrichtigung bezieht."))
|
||||
report = models.ForeignKey(Report,
|
||||
blank=True, null=True,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('Report'),
|
||||
help_text=_("Report auf den sich die Benachrichtigung bezieht."))
|
||||
|
||||
def __str__(self):
|
||||
return f"[{self.user}] {self.title} ({self.created_at})"
|
||||
return f"[{self.user_to_notify}] {self.title} ({self.created_at})"
|
||||
|
||||
def get_absolute_url(self):
|
||||
self.user.get_notifications_url()
|
||||
self.user_to_notify.get_notifications_url()
|
||||
|
||||
def mark_read(self):
|
||||
self.read = True
|
||||
@@ -923,22 +956,6 @@ class BaseNotification(models.Model):
|
||||
self.save()
|
||||
|
||||
|
||||
class CommentNotification(BaseNotification):
|
||||
comment = models.ForeignKey(Comment, on_delete=models.CASCADE, verbose_name=_('Antwort'))
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.comment.get_absolute_url
|
||||
|
||||
|
||||
class AdoptionNoticeNotification(BaseNotification):
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE, verbose_name=_('Vermittlung'))
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.adoption_notice.get_absolute_url
|
||||
|
||||
|
||||
class Subscriptions(models.Model):
|
||||
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('Nutzer*in'))
|
||||
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE, verbose_name=_('AdoptionNotice'))
|
||||
|
@@ -1,23 +1,22 @@
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from fellchensammlung.models import BaseNotification, CommentNotification, User, TrustLevel, RescueOrganization
|
||||
from fellchensammlung.models import Notification, User, TrustLevel, RescueOrganization, \
|
||||
NotificationTypeChoices
|
||||
from .tasks import task_send_notification_email
|
||||
from notfellchen.settings import host
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
@receiver(post_save, sender=CommentNotification)
|
||||
def comment_notification_receiver(sender, instance: BaseNotification, created: bool, **kwargs):
|
||||
base_notification_receiver(sender, instance, created, **kwargs)
|
||||
|
||||
|
||||
@receiver(post_save, sender=BaseNotification)
|
||||
def base_notification_receiver(sender, instance: BaseNotification, created: bool, **kwargs):
|
||||
if not created or not instance.user.email_notifications:
|
||||
@receiver(post_save, sender=Notification)
|
||||
def base_notification_receiver(sender, instance: Notification, created: bool, **kwargs):
|
||||
print("Dada")
|
||||
if not created or not instance.user_to_notify.email_notifications:
|
||||
return
|
||||
else:
|
||||
print("Dodo")
|
||||
task_send_notification_email.delay(instance.pk)
|
||||
|
||||
|
||||
@receiver(post_save, sender=RescueOrganization)
|
||||
def rescue_org_receiver(sender, instance: RescueOrganization, created: bool, **kwargs):
|
||||
if instance.location:
|
||||
@@ -40,5 +39,9 @@ def notification_new_user(sender, instance: User, created: bool, **kwargs):
|
||||
link_text = f"Um alle Details zu sehen, geh bitte auf: {user_url}"
|
||||
body_text = new_user_text + user_detail_text + link_text
|
||||
for moderator in User.objects.filter(trust_level__gt=TrustLevel.MODERATOR):
|
||||
notification = BaseNotification.objects.create(title=subject, text=body_text, user=moderator)
|
||||
notification = Notification.objects.create(title=subject,
|
||||
text=body_text,
|
||||
notification_type=NotificationTypeChoices.NEW_USER,
|
||||
user_to_notify=moderator,
|
||||
user_related=instance)
|
||||
notification.save()
|
||||
|
@@ -0,0 +1,16 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% translate 'Vermittlung wurde deaktiviert' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
die Vermittlung {{ notification.adoption_notice }} wurde deaktiviert.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ notification.adoption_notice.get_absolute_url }}" class="cta-button">{% translate 'Vermittlung anzeigen' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,16 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% translate 'Neue Vermittlung gefunden' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
es wurde eine neue Vermittlung gefunden die deinen Kriterien entspricht: {{ notification.adoption_notice }}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ notification.adoption_notice.get_absolute_url }}" class="cta-button">{% translate 'Vermittlung anzeigen' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,16 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% translate 'Vermittlung muss überprüft werden' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
die Vermittlung {{ notification.adoption_notice }} muss überprüft werden.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ notification.adoption_notice.get_absolute_url }}" class="cta-button">{% translate 'Vermittlung anzeigen' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,19 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load custom_tags %}
|
||||
{% block title %}
|
||||
{% translate 'Vermittlung muss überprüft werden' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
folgender Kommentar wurde zur Vermittlung {{ notification.adoption_notice }} hinzugefügt:
|
||||
</p>
|
||||
<p><i>
|
||||
{{ notification.comment.text | render_markdown }}
|
||||
</i></p>
|
||||
<p>
|
||||
<a href="{{ notification.adoption_notice.get_absolute_url }}" class="cta-button">{% translate 'Vermittlung anzeigen' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -0,0 +1,19 @@
|
||||
{% extends "fellchensammlung/mail/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% translate 'Neuer User' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
es wurde ein neuer Useraccount erstellt.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Details findest du hier
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ notification.user_related.get_absolute_url }}" class="cta-button">{% translate 'User anzeigen' %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
@@ -7,7 +7,7 @@
|
||||
{% block content %}
|
||||
<p>Moin,</p>
|
||||
<p>
|
||||
es gibt eine neue Meldung. Folgende Nachricht wurde zur Meldung hinzugefügt.
|
||||
es gibt eine neue Meldung. Folgende Nachricht wurde zur Meldung hinzugefügt:
|
||||
|
||||
</p>
|
||||
<p>
|
||||
@@ -17,7 +17,7 @@
|
||||
</p>
|
||||
<p>
|
||||
|
||||
Bitte bearbeite die Meldung möglichst bald
|
||||
Bitte bearbeite die Meldung möglichst bald.
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ report_url }}" class="cta-button">{% translate 'Report bearbeiten' %}</a>
|
@@ -10,7 +10,7 @@ from django.core import mail
|
||||
from django.utils.html import strip_tags
|
||||
|
||||
from fellchensammlung.models import AdoptionNotice, Location, RescueOrganization, AdoptionNoticeStatus, Log, \
|
||||
AdoptionNoticeNotification
|
||||
Notification, NotificationTypeChoices
|
||||
from fellchensammlung.tools.misc import is_404
|
||||
|
||||
|
||||
@@ -92,10 +92,11 @@ def deactivate_404_adoption_notices():
|
||||
|
||||
deactivation_message = f'Die Vermittlung [{adoption_notice.name}]({adoption_notice.get_absolute_url()}) wurde automatisch deaktiviert, da die Website unter "Mehr Informationen" nicht mehr online ist.'
|
||||
for subscription in adoption_notice.get_subscriptions():
|
||||
AdoptionNoticeNotification.objects.create(user=subscription.owner,
|
||||
title="Vermittlung deaktiviert",
|
||||
adoption_notice=adoption_notice,
|
||||
text=deactivation_message)
|
||||
Notification.objects.create(user_to_notify=subscription.owner,
|
||||
notification_type=NotificationTypeChoices.AN_WAS_DEACTIVATED,
|
||||
title="Vermittlung deaktiviert",
|
||||
adoption_notice=adoption_notice,
|
||||
text=deactivation_message)
|
||||
|
||||
|
||||
def dedup_location(location: Location, destructive=False):
|
||||
|
@@ -1,4 +1,4 @@
|
||||
from fellchensammlung.models import User, AdoptionNoticeNotification, TrustLevel
|
||||
from fellchensammlung.models import User, Notification, TrustLevel, NotificationTypeChoices
|
||||
|
||||
|
||||
def notify_of_AN_to_be_checked(adoption_notice):
|
||||
@@ -6,8 +6,9 @@ def notify_of_AN_to_be_checked(adoption_notice):
|
||||
users_to_notify = set(User.objects.filter(trust_level__gt=TrustLevel.MODERATOR))
|
||||
users_to_notify.add(adoption_notice.owner)
|
||||
for user in users_to_notify:
|
||||
AdoptionNoticeNotification.objects.create(adoption_notice=adoption_notice,
|
||||
user=user,
|
||||
title=f" Prüfe Vermittlung {adoption_notice}",
|
||||
text=f"{adoption_notice} muss geprüft werden bevor sie veröffentlicht wird.",
|
||||
)
|
||||
Notification.objects.create(adoption_notice=adoption_notice,
|
||||
user_to_notify=user,
|
||||
notification_type=NotificationTypeChoices.AN_IS_TO_BE_CHECKED,
|
||||
title=f" Prüfe Vermittlung {adoption_notice}",
|
||||
text=f"{adoption_notice} muss geprüft werden bevor sie veröffentlicht wird.",
|
||||
)
|
||||
|
@@ -3,7 +3,8 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .geo import LocationProxy, Position
|
||||
from ..forms import AdoptionNoticeSearchForm
|
||||
from ..models import SearchSubscription, AdoptionNotice, AdoptionNoticeNotification, SexChoicesWithAll, Location
|
||||
from ..models import SearchSubscription, AdoptionNotice, SexChoicesWithAll, Location, \
|
||||
Notification, NotificationTypeChoices
|
||||
|
||||
|
||||
def notify_search_subscribers(adoption_notice: AdoptionNotice, only_if_active: bool = True):
|
||||
@@ -20,10 +21,11 @@ def notify_search_subscribers(adoption_notice: AdoptionNotice, only_if_active: b
|
||||
search = Search(search_subscription=search_subscription)
|
||||
if search.adoption_notice_fits_search(adoption_notice):
|
||||
notification_text = f"{_('Zu deiner Suche')} {search_subscription} wurde eine neue Vermittlung gefunden"
|
||||
AdoptionNoticeNotification.objects.create(user=search_subscription.owner,
|
||||
title=f"{_('Neue Vermittlung')}: {adoption_notice}",
|
||||
adoption_notice=adoption_notice,
|
||||
text=notification_text)
|
||||
Notification.objects.create(user_to_notify=search_subscription.owner,
|
||||
notification_type=NotificationTypeChoices.AN_FOR_SEARCH_FOUND,
|
||||
title=f"{_('Neue Vermittlung')}: {adoption_notice}",
|
||||
adoption_notice=adoption_notice,
|
||||
text=notification_text)
|
||||
logging.debug(f"Notification for search subscription {search_subscription} was sent.")
|
||||
else:
|
||||
logging.debug(f"Adoption notice {adoption_notice} was not fitting the search subscription.")
|
||||
|
@@ -20,9 +20,9 @@ from notfellchen import settings
|
||||
|
||||
from fellchensammlung import logger
|
||||
from .models import AdoptionNotice, Text, Animal, Rule, Image, Report, ModerationAction, \
|
||||
User, Location, AdoptionNoticeStatus, Subscriptions, CommentNotification, BaseNotification, RescueOrganization, \
|
||||
Species, Log, Timestamp, TrustLevel, SexChoicesWithAll, SearchSubscription, AdoptionNoticeNotification, \
|
||||
ImportantLocation, SpeciesSpecificURL
|
||||
User, Location, AdoptionNoticeStatus, Subscriptions, Notification, RescueOrganization, \
|
||||
Species, Log, Timestamp, TrustLevel, SexChoicesWithAll, SearchSubscription, \
|
||||
ImportantLocation, SpeciesSpecificURL, NotificationTypeChoices
|
||||
from .forms import AdoptionNoticeForm, ImageForm, ReportAdoptionNoticeForm, \
|
||||
CommentForm, ReportCommentForm, AnimalForm, AdoptionNoticeFormAutoAnimal, SpeciesURLForm, RescueOrgInternalComment
|
||||
from .models import Language, Announcement
|
||||
@@ -121,10 +121,12 @@ def adoption_notice_detail(request, adoption_notice_id):
|
||||
for subscription in adoption_notice.get_subscriptions():
|
||||
# Create a notification but only if the user is not the one that posted the comment
|
||||
if subscription.owner != request.user:
|
||||
notification = CommentNotification(user=subscription.owner,
|
||||
title=f"{adoption_notice.name} - Neuer Kommentar",
|
||||
text=f"{request.user}: {comment_instance.text}",
|
||||
comment=comment_instance)
|
||||
notification = Notification(user_to_notify=subscription.owner,
|
||||
adoption_notice=adoption_notice,
|
||||
notification_type=NotificationTypeChoices.NEW_COMMENT,
|
||||
title=f"{adoption_notice.name} - Neuer Kommentar",
|
||||
text=f"{request.user}: {comment_instance.text}",
|
||||
comment=comment_instance)
|
||||
notification.save()
|
||||
else:
|
||||
comment_form = CommentForm(instance=adoption_notice)
|
||||
@@ -177,7 +179,8 @@ def search_important_locations(request, important_location_slug):
|
||||
search.search_from_predefined_i_location(i_location)
|
||||
|
||||
site_title = _("Ratten in %(location_name)s") % {"location_name": i_location.name}
|
||||
site_description = _("Ratten in Tierheimen und Rattenhilfen in der Nähe von %(location_name)s suchen.") % {"location_name": i_location.name}
|
||||
site_description = _("Ratten in Tierheimen und Rattenhilfen in der Nähe von %(location_name)s suchen.") % {
|
||||
"location_name": i_location.name}
|
||||
canonical_url = reverse("search-by-location", args=[i_location.slug])
|
||||
|
||||
context = {"adoption_notices": search.get_adoption_notices(),
|
||||
@@ -528,7 +531,7 @@ def report_detail_success(request, report_id):
|
||||
def user_detail(request, user, token=None):
|
||||
context = {"user": user,
|
||||
"adoption_notices": AdoptionNotice.objects.filter(owner=user),
|
||||
"notifications": BaseNotification.objects.filter(user=user, read=False),
|
||||
"notifications": Notification.objects.filter(user_to_notify=user, read=False),
|
||||
"search_subscriptions": SearchSubscription.objects.filter(owner=user), }
|
||||
if token is not None:
|
||||
context["token"] = token
|
||||
@@ -561,13 +564,11 @@ def my_profile(request):
|
||||
action = request.POST.get("action")
|
||||
if action == "notification_mark_read":
|
||||
notification_id = request.POST.get("notification_id")
|
||||
try:
|
||||
notification = CommentNotification.objects.get(pk=notification_id)
|
||||
except CommentNotification.DoesNotExist:
|
||||
notification = BaseNotification.objects.get(pk=notification_id)
|
||||
|
||||
notification = Notification.objects.get(pk=notification_id)
|
||||
notification.mark_read()
|
||||
elif action == "notification_mark_all_read":
|
||||
notifications = CommentNotification.objects.filter(user=request.user, mark_read=False)
|
||||
notifications = Notification.objects.filter(user=request.user, mark_read=False)
|
||||
for notification in notifications:
|
||||
notification.mark_read()
|
||||
elif action == "search_subscription_delete":
|
||||
@@ -770,11 +771,13 @@ def rescue_organization_check(request, context=None):
|
||||
}
|
||||
rescue_orgs_last_checked = RescueOrganization.objects.filter().order_by("-last_checked")[:10]
|
||||
timeframe = timezone.now().date() - timedelta(days=14)
|
||||
num_rescue_orgs_to_check = RescueOrganization.objects.filter(exclude_from_check=False).filter(last_checked__lt=timeframe).count()
|
||||
num_rescue_orgs_checked = RescueOrganization.objects.filter(exclude_from_check=False).filter(last_checked__gte=timeframe).count()
|
||||
num_rescue_orgs_to_check = RescueOrganization.objects.filter(exclude_from_check=False).filter(
|
||||
last_checked__lt=timeframe).count()
|
||||
num_rescue_orgs_checked = RescueOrganization.objects.filter(exclude_from_check=False).filter(
|
||||
last_checked__gte=timeframe).count()
|
||||
|
||||
try:
|
||||
percentage_checked = 100*num_rescue_orgs_checked/(num_rescue_orgs_to_check+num_rescue_orgs_checked)
|
||||
percentage_checked = 100 * num_rescue_orgs_checked / (num_rescue_orgs_to_check + num_rescue_orgs_checked)
|
||||
except ZeroDivisionError:
|
||||
percentage_checked = 100
|
||||
|
||||
|
@@ -4,7 +4,7 @@ from django.utils import timezone
|
||||
from django.test import TestCase
|
||||
from model_bakery import baker
|
||||
|
||||
from fellchensammlung.models import Announcement, Language, User, TrustLevel, BaseNotification
|
||||
from fellchensammlung.models import Announcement, Language, User, TrustLevel, Notification
|
||||
|
||||
|
||||
class UserTest(TestCase):
|
||||
@@ -85,9 +85,9 @@ class TestNotifications(TestCase):
|
||||
cls.test_user_1 = User.objects.create(username="Testuser1", password="SUPERSECRET", email="test@example.org")
|
||||
|
||||
def test_mark_read(self):
|
||||
not1 = BaseNotification.objects.create(user=self.test_user_1, text="New rats to adopt", title="🔔 New Rat alert")
|
||||
not2 = BaseNotification.objects.create(user=self.test_user_1,
|
||||
text="New wombat to adopt", title="🔔 New Wombat alert")
|
||||
not1 = Notification.objects.create(user=self.test_user_1, text="New rats to adopt", title="🔔 New Rat alert")
|
||||
not2 = Notification.objects.create(user=self.test_user_1,
|
||||
text="New wombat to adopt", title="🔔 New Wombat alert")
|
||||
not1.mark_read()
|
||||
|
||||
self.assertTrue(not1.read)
|
||||
|
Reference in New Issue
Block a user