Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions api_tests/notifications/test_notification_digest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from django.utils import timezone
from django.contrib.contenttypes.models import ContentType

from osf.models import Notification, NotificationType, NotificationTypeEnum, EmailTask, Email
Expand Down Expand Up @@ -186,6 +187,26 @@ def test_get_users_emails(self):
assert user_info['user_id'] == user._id
assert any(msg['notification_id'] == notification1.id for msg in user_info['info'])

def test_get_users_emails_ignore_scheduled(self):
user = AuthUserFactory()
notification_type = NotificationType.objects.get(name=NotificationTypeEnum.USER_FILE_UPDATED)
notification1 = Notification.objects.create(
subscription=add_notification_subscription(user, notification_type, 'daily'),
event_context={},
sent=None
)
Notification.objects.create(
subscription=add_notification_subscription(user, notification_type, 'daily'),
event_context={},
sent=None,
scheduled=timezone.now()
)
res = list(get_users_emails('daily'))
assert len(res) == 1
user_info = res[0]
assert user_info['user_id'] == user._id
assert any(msg['notification_id'] == notification1.id for msg in user_info['info'])

def test_get_moderators_emails(self):
user = AuthUserFactory()
provider = RegistrationProviderFactory()
Expand All @@ -204,6 +225,30 @@ def test_get_moderators_emails(self):
]
assert entry, 'Expected moderator digest group'

def test_get_moderators_emails_ignore_scheduled(self):
user = AuthUserFactory()
provider = RegistrationProviderFactory()
reg = RegistrationFactory(provider=provider)
notification_type = NotificationType.objects.get(name=NotificationTypeEnum.PROVIDER_NEW_PENDING_SUBMISSIONS)
subscription = add_notification_subscription(user, notification_type, 'daily', subscribed_object=reg)
Notification.objects.create(
subscription=subscription,
event_context={},
sent=None
)
Notification.objects.create(
subscription=subscription,
event_context={},
sent=None,
scheduled=timezone.now()
)
res = list(get_moderators_emails('daily'))
assert len(res) >= 1
entry = [
x for x in res if x['user_id'] == user._id and subscription.subscribed_object.id == reg.id
]
assert entry, 'Expected moderator digest group'

def test_send_users_digest_email_end_to_end(self):
user = AuthUserFactory()
notification_type = NotificationType.objects.get(name=NotificationTypeEnum.USER_FILE_UPDATED)
Expand Down
6 changes: 6 additions & 0 deletions notifications/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,8 @@ def send_users_digest_email(dry_run=False):
user_id = group['user_id']
notification_ids = [msg['notification_id'] for msg in group['info']]
if not dry_run:
notifications_qs = Notification.objects.filter(id__in=notification_ids)
notifications_qs.update(scheduled=timezone.now())
send_user_email_task.delay(user_id, notification_ids)

@celery_app.task(name='notifications.tasks.send_moderators_digest_email')
Expand All @@ -334,6 +336,8 @@ def send_moderators_digest_email(dry_run=False):
provider_content_type_id = group['provider_content_type_id']
notification_ids = [msg['notification_id'] for msg in group['info']]
if not dry_run:
notifications_qs = Notification.objects.filter(id__in=notification_ids)
notifications_qs.update(scheduled=timezone.now())
send_moderator_email_task.delay(user_id, notification_ids, provider_content_type_id, provider_id)

def get_moderators_emails(message_freq: str):
Expand All @@ -358,6 +362,7 @@ def get_moderators_emails(message_freq: str):
INNER JOIN osf_notificationtype AS nt ON ns.notification_type_id = nt.id
LEFT JOIN osf_guid ON ns.user_id = osf_guid.object_id
WHERE n.sent IS NULL
AND n.scheduled IS NULL
AND ns.message_frequency = %s
AND nt.name IN (%s, %s)
AND nt.name NOT IN (%s, %s, %s)
Expand Down Expand Up @@ -401,6 +406,7 @@ def get_users_emails(message_freq):
INNER JOIN osf_notificationtype AS nt ON ns.notification_type_id = nt.id
LEFT JOIN osf_guid ON ns.user_id = osf_guid.object_id
WHERE n.sent IS NULL
AND n.scheduled IS NULL
AND ns.message_frequency = %s
AND nt.name NOT IN (%s, %s, %s, %s, %s)
AND osf_guid.content_type_id = (
Expand Down
18 changes: 18 additions & 0 deletions osf/migrations/0040_notification_scheduled.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.26 on 2026-06-04 13:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('osf', '0039_merge_20260427_1359'),
]

operations = [
migrations.AddField(
model_name='notification',
name='scheduled',
field=models.DateTimeField(blank=True, null=True),
),
]
1 change: 1 addition & 0 deletions osf/models/notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Notification(models.Model):
sent = models.DateTimeField(null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
fake_sent = models.BooleanField(default=False)
scheduled = models.DateTimeField(null=True, blank=True)

def send(
self,
Expand Down