Notification emails #80

Merged
Oleg-Komarov merged 31 commits from notifications into main 2024-04-18 16:11:20 +02:00
6 changed files with 82 additions and 16 deletions
Showing only changes of commit ec61798c03 - Show all commits

View File

@ -3,14 +3,17 @@ class Verb:
changing the values will result in a mismatch with historical values stored in db.
"""
SUBMITTED_FOR_REVIEW = 'submitted for review'
REPORTED_EXTENSION = 'reported extension'
REPORTED_REVIEW = 'reported review'
APPROVED = 'approved'
COMMENTED = 'commented'
RATED_EXTENSION = 'rated extension'
REPORTED_EXTENSION = 'reported extension'
REPORTED_REVIEW = 'reported review'
REQUESTED_CHANGES = 'requested changes'
REQUESTED_REVIEW = 'requested review'
SUBMITTED_FOR_REVIEW = 'submitted for review'
RATED_EXTENSION = 'rated extension'
class Flag:
AUTHOR = 'author'
MODERATOR = 'moderator'
REVIEWER = 'reviewer'

View File

@ -5,6 +5,7 @@ from django.contrib.auth.models import Group
from django.db.models.signals import pre_save, post_save, post_delete
from django.dispatch import receiver
from constants.activity import Flag
import extensions.models
import extensions.tasks
import files.models
@ -91,6 +92,6 @@ def _setup_followers(
return
for user in instance.authors.all():
follow(user, instance, send_action=False, flag='author')
follow(user, instance, send_action=False, flag=Flag.AUTHOR)
for user in Group.objects.get(name='moderators').user_set.all():
follow(user, instance, send_action=False, flag='moderator')
follow(user, instance, send_action=False, flag=Flag.MODERATOR)

View File

@ -0,0 +1,38 @@
# Generated by Django 4.2.11 on 2024-04-15 10:18
from actstream.actions import follow
from django.contrib.auth.models import Group
from django.db import migrations
from constants.activity import Flag
def setup_followers(apps, schema_editor):
# !!! not using apps.get_model('extensions', 'Extension')
# because it doesn't work with actstream.registry:
#
# File ".../site-packages/actstream/actions.py", line 34, in follow
# check(obj)
# File ".../site-packages/actstream/registry.py", line 105, in check
# raise ImproperlyConfigured(
# django.core.exceptions.ImproperlyConfigured: The model Extension is not registered.
# Please use actstream.registry to register it.
#
# if this ever causes issues in the future, delete this code or find a workaround
from extensions.models import Extension
for extension in Extension.objects.all():
for user in extension.authors.all():
follow(user, extension, send_action=False, flag=Flag.AUTHOR)
for user in Group.objects.get(name='moderators').user_set.all():
follow(user, extension, send_action=False, flag=Flag.MODERATOR)
class Migration(migrations.Migration):
dependencies = [
('notifications', '0001_initial'),
]
operations = [
migrations.RunPython(setup_followers, reverse_code=migrations.RunPython.noop),
]

View File

@ -1,13 +1,26 @@
import logging
from actstream.models import Action, followers
from actstream.models import Action, Follow
from django.contrib.auth import get_user_model
from django.db.models.signals import post_save
from django.dispatch import receiver
from constants.activity import Flag, Verb
from notifications.models import Notification
logger = logging.getLogger(__name__)
VERB2FLAGS = {
Verb.APPROVED: [Flag.AUTHOR, Flag.MODERATOR, Flag.REVIEWER],
Verb.COMMENTED: [Flag.AUTHOR, Flag.MODERATOR, Flag.REVIEWER],
Verb.RATED_EXTENSION: [Flag.AUTHOR],
Verb.REPORTED_EXTENSION: [Flag.MODERATOR],
Verb.REPORTED_REVIEW: [Flag.MODERATOR],
Verb.REQUESTED_CHANGES: [Flag.AUTHOR, Flag.MODERATOR, Flag.REVIEWER],
Verb.REQUESTED_REVIEW: [Flag.MODERATOR, Flag.REVIEWER],
Verb.SUBMITTED_FOR_REVIEW: [Flag.MODERATOR],
}
@receiver(post_save, sender=Action)
def _create_notifications(
@ -23,11 +36,21 @@ def _create_notifications(
return
if not instance.target:
logger.warning('ignoring an unexpected Action without a target, verb={instance.verb}')
logger.warning(f'ignoring an unexpected Action without a target, verb={instance.verb}')
return
notifications = []
for recipient in followers(instance.target):
flags = VERB2FLAGS.get(instance.verb, None)
if not flags:
logger.warning(f'no follower flags for verb={instance.verb}, nobody will be notified')
return
followers = Follow.objects.for_object(instance.target).filter(flag__in=flags)
user_ids = followers.values_list('user', flat=True)
followers = get_user_model().objects.filter(id__in=user_ids)
for recipient in followers:
if recipient == instance.actor:
continue
notifications.append(Notification(recipient=recipient, action=instance))

View File

@ -3,7 +3,7 @@ from actstream.actions import follow
from django.db.models.signals import post_save
from django.dispatch import receiver
from constants.activity import Verb
from constants.activity import Flag, Verb
from reviewers.models import ApprovalActivity
@ -23,7 +23,7 @@ def _create_action_from_review_and_follow(
# automatically follow after an interaction
# if a user had unfollowed this extension before,
# we are making them a follower again
follow(instance.user, instance.extension, send_action=False)
follow(instance.user, instance.extension, send_action=False, flag=Flag.REVIEWER)
activity_type2verb = {
ApprovalActivity.ActivityType.APPROVED: Verb.APPROVED,

View File

@ -9,6 +9,7 @@ from django.dispatch import receiver
from blender_id_oauth_client import signals as bid_signals
from constants.activity import Flag
from extensions.models import Extension
from users.blender_id import BIDSession
@ -44,7 +45,7 @@ def update_user(
def update_moderator_follows(instance, action, model, reverse, pk_set, **kwargs):
"""Users becoming moderators should follow all extensions,
and users that stop being moderators should no longer follow all extensions.
The flag='moderator' is used to avoid deleting follow relations that were created in contexts
The flag=Flag.MODERATOR is used to avoid deleting follow relations that were created in contexts
other than moderator's duties.
"""
moderators = Group.objects.get(name='moderators')
@ -62,7 +63,7 @@ def update_moderator_follows(instance, action, model, reverse, pk_set, **kwargs)
for user in users:
if action == 'post_remove':
for extension in extensions:
unfollow(user, extension, send_action=False, flag='moderator')
unfollow(user, extension, send_action=False, flag=Flag.MODERATOR)
elif action == 'post_add':
for extension in extensions:
follow(user, extension, send_action=False, flag='moderator')
follow(user, extension, send_action=False, flag=Flag.MODERATOR)