feat: Add a timestamp for when a notification is read
This commit is contained in:
		@@ -0,0 +1,18 @@
 | 
			
		||||
# Generated by Django 5.1.4 on 2025-01-14 06:28
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('fellchensammlung', '0035_alter_image_alt_text_and_more'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='basenotification',
 | 
			
		||||
            name='read_at',
 | 
			
		||||
            field=models.DateTimeField(blank=True, null=True, verbose_name='Gelesen am'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
# Generated by Django 5.1.4 on 2025-01-14 06:28
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('fellchensammlung', '0036_basenotification_read_at'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='basenotification',
 | 
			
		||||
            name='title',
 | 
			
		||||
            field=models.CharField(max_length=100, verbose_name='Titel'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -818,7 +818,8 @@ class Comment(models.Model):
 | 
			
		||||
class BaseNotification(models.Model):
 | 
			
		||||
    created_at = models.DateTimeField(auto_now_add=True)
 | 
			
		||||
    updated_at = models.DateTimeField(auto_now=True)
 | 
			
		||||
    title = models.CharField(max_length=100)
 | 
			
		||||
    read_at = models.DateTimeField(blank=True, null=True, verbose_name=_("Gelesen am"))
 | 
			
		||||
    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'))
 | 
			
		||||
    read = models.BooleanField(default=False)
 | 
			
		||||
@@ -829,6 +830,11 @@ class BaseNotification(models.Model):
 | 
			
		||||
    def get_absolute_url(self):
 | 
			
		||||
        self.user.get_notifications_url()
 | 
			
		||||
 | 
			
		||||
    def mark_read(self):
 | 
			
		||||
        self.read = True
 | 
			
		||||
        self.read_at = timezone.now()
 | 
			
		||||
        self.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommentNotification(BaseNotification):
 | 
			
		||||
    comment = models.ForeignKey(Comment, on_delete=models.CASCADE, verbose_name=_('Antwort'))
 | 
			
		||||
 
 | 
			
		||||
@@ -481,13 +481,11 @@ def my_profile(request):
 | 
			
		||||
                notification = CommentNotification.objects.get(pk=notification_id)
 | 
			
		||||
            except CommentNotification.DoesNotExist:
 | 
			
		||||
                notification = BaseNotification.objects.get(pk=notification_id)
 | 
			
		||||
            notification.read = True
 | 
			
		||||
            notification.save()
 | 
			
		||||
            notification.mark_read()
 | 
			
		||||
        elif action == "notification_mark_all_read":
 | 
			
		||||
            notifications = CommentNotification.objects.filter(user=request.user, mark_read=False)
 | 
			
		||||
            for notification in notifications:
 | 
			
		||||
                notification.read = True
 | 
			
		||||
                notification.save()
 | 
			
		||||
                notification.mark_read()
 | 
			
		||||
        elif action == "search_subscription_delete":
 | 
			
		||||
            search_subscription_id = request.POST.get("search_subscription_id")
 | 
			
		||||
            SearchSubscription.objects.get(pk=search_subscription_id).delete()
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
from fellchensammlung.models import Announcement, Language, User, TrustLevel, BaseNotification
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserTest(TestCase):
 | 
			
		||||
@@ -77,3 +77,21 @@ class AnnouncementTest(TestCase):
 | 
			
		||||
        self.assertTrue(self.announcement2 not in active_announcements)
 | 
			
		||||
        self.assertTrue(self.announcement4 not in active_announcements)
 | 
			
		||||
        self.assertTrue(self.announcement5 in active_announcements)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestNotifications(TestCase):
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def setUpTestData(cls):
 | 
			
		||||
        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.mark_read()
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(not1.read)
 | 
			
		||||
        self.assertFalse(not2.read)
 | 
			
		||||
        self.assertTrue((timezone.now() - timedelta(hours=1)) < not1.read_at < timezone.now())
 | 
			
		||||
        self.assertIsNone(not2.read_at)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user