Skip to content

Commit 54f1f93

Browse files
committed
New Notification system proof of concept
1 parent c43cc32 commit 54f1f93

37 files changed

+773
-370
lines changed

addons/base/views.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,10 @@ def create_waterbutler_log(payload, **kwargs):
611611
update_storage_usage_with_size(payload)
612612

613613
with transaction.atomic():
614-
file_signals.file_updated.send(target=node, user=user, event_type=action, payload=payload)
614+
print('!', action, payload)
615+
from osf.models.notification import NotificationType
616+
NotificationType.objects.get(name=action).emit()
617+
# file_signals.file_updated.send(target=node, user=user, event_type=action, payload=payload)
615618

616619
return {'status': 'success'}
617620

admin/notifications/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from osf.models.notifications import NotificationSubscription
1+
from osf.models.notification import NotificationSubscription
22
from django.db.models import Count
33

44
def delete_selected_notifications(selected_ids):

api/base/views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,14 @@ def get_serializer_context(self):
143143
multiple levels of nesting.
144144
"""
145145
context = super().get_serializer_context()
146-
if self.kwargs.get('is_embedded'):
146+
if self.kwargs.get('is_embedded') or not self.request:
147147
embeds = []
148148
else:
149149
embeds = self.request.query_params.getlist('embed') or self.request.query_params.getlist('embed[]')
150150

151151
fields_check = self.get_serializer_class()._declared_fields.copy()
152152
serializer_class_type = get_meta_type(self.serializer_class, self.request)
153-
if f'fields[{serializer_class_type}]' in self.request.query_params:
153+
if self.request and f'fields[{serializer_class_type}]' in self.request.query_params:
154154
# Check only requested and mandatory fields
155155
sparse_fields = self.request.query_params[f'fields[{serializer_class_type}]']
156156
for field in list(fields_check.copy().keys()):

api/subscriptions/fields.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from rest_framework import serializers as ser
2+
from osf.models import NotificationSubscription
3+
4+
class FrequencyField(ser.ChoiceField):
5+
def __init__(self, **kwargs):
6+
super().__init__(choices=['none', 'instantly', 'daily', 'weekly', 'monthly'], **kwargs)
7+
8+
def to_representation(self, obj: NotificationSubscription):
9+
return obj.message_frequency
10+
11+
def to_internal_value(self, freq):
12+
return super().to_internal_value(freq)

api/subscriptions/permissions.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
from rest_framework import permissions
2-
3-
from osf.models.notifications import NotificationSubscription
4-
2+
from osf.models import NotificationSubscription
53

64
class IsSubscriptionOwner(permissions.BasePermission):
7-
85
def has_object_permission(self, request, view, obj):
9-
assert isinstance(obj, NotificationSubscription), f'obj must be a NotificationSubscription; got {obj}'
10-
user_id = request.user.id
11-
return obj.none.filter(id=user_id).exists() \
12-
or obj.email_transactional.filter(id=user_id).exists() \
13-
or obj.email_digest.filter(id=user_id).exists()
6+
assert isinstance(obj, NotificationSubscription), f'obj must be NotificationSubscription; got {obj}'
7+
return obj.user == request.user

api/subscriptions/serializers.py

Lines changed: 24 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,69 @@
11
from rest_framework import serializers as ser
2-
from rest_framework.exceptions import ValidationError
2+
from api.base.serializers import JSONAPISerializer, LinksField
3+
from website.util import api_v2_url
4+
from .fields import FrequencyField
35
from api.nodes.serializers import RegistrationProviderRelationshipField
46
from api.collections_providers.fields import CollectionProviderRelationshipField
57
from api.preprints.serializers import PreprintProviderRelationshipField
6-
from website.util import api_v2_url
7-
8-
9-
from api.base.serializers import JSONAPISerializer, LinksField
10-
11-
NOTIFICATION_TYPES = {
12-
'none': 'none',
13-
'instant': 'email_transactional',
14-
'daily': 'email_digest',
15-
}
168

179

18-
class FrequencyField(ser.Field):
19-
def to_representation(self, obj):
20-
user_id = self.context['request'].user.id
21-
if obj.email_transactional.filter(id=user_id).exists():
22-
return 'instant'
23-
if obj.email_digest.filter(id=user_id).exists():
24-
return 'daily'
25-
return 'none'
26-
27-
def to_internal_value(self, frequency):
28-
notification_type = NOTIFICATION_TYPES.get(frequency)
29-
if notification_type:
30-
return {'notification_type': notification_type}
31-
raise ValidationError(f'Invalid frequency "{frequency}"')
32-
3310
class SubscriptionSerializer(JSONAPISerializer):
34-
filterable_fields = frozenset([
35-
'id',
36-
'event_name',
37-
])
38-
39-
id = ser.CharField(source='_id', read_only=True)
40-
event_name = ser.CharField(read_only=True)
41-
frequency = FrequencyField(source='*', required=True)
42-
links = LinksField({
43-
'self': 'get_absolute_url',
44-
})
11+
filterable_fields = frozenset(['id', 'type'])
12+
13+
id = ser.CharField(read_only=True)
14+
type = ser.SerializerMethodField()
15+
email_freq = FrequencyField(required=True)
16+
links = LinksField({'self': 'get_absolute_url'})
4517

4618
class Meta:
4719
type_ = 'subscription'
4820

21+
def get_type(self, obj):
22+
return self.Meta.type_
23+
4924
def get_absolute_url(self, obj):
50-
return obj.absolute_api_v2_url
25+
return api_v2_url(f'subscriptions/{obj.pk}')
5126

5227
def update(self, instance, validated_data):
53-
user = self.context['request'].user
54-
notification_type = validated_data.get('notification_type')
55-
instance.add_user_to_subscription(user, notification_type, save=True)
28+
instance.message_frequency = validated_data.get('message_frequency', instance.message_frequency)
29+
instance.save(update_fields=['message_frequency'])
5630
return instance
5731

58-
5932
class RegistrationSubscriptionSerializer(SubscriptionSerializer):
6033
provider = RegistrationProviderRelationshipField(
6134
related_view='providers:registration-providers:registration-provider-detail',
62-
related_view_kwargs={'provider_id': '<provider._id>'},
63-
read_only=False,
64-
required=False,
35+
related_view_kwargs={'provider_id': '<subscribed_object._id>'},
36+
read_only=True, # We're not modifying the relationship from here
6537
)
6638

6739
def get_absolute_url(self, obj):
68-
return api_v2_url(f'registration_subscriptions/{obj._id}')
40+
return api_v2_url(f'registration_subscriptions/{obj.pk}')
6941

7042
class Meta:
7143
type_ = 'registration-subscription'
7244

73-
7445
class CollectionSubscriptionSerializer(SubscriptionSerializer):
7546
provider = CollectionProviderRelationshipField(
7647
related_view='providers:collection-providers:collection-provider-detail',
77-
related_view_kwargs={'provider_id': '<provider._id>'},
78-
read_only=False,
79-
required=False,
48+
related_view_kwargs={'provider_id': '<subscribed_object._id>'},
49+
read_only=True,
8050
)
8151

8252
def get_absolute_url(self, obj):
83-
return api_v2_url(f'collection_subscriptions/{obj._id}')
53+
return api_v2_url(f'collection_subscriptions/{obj.pk}')
8454

8555
class Meta:
8656
type_ = 'collection-subscription'
8757

88-
8958
class PreprintSubscriptionSerializer(SubscriptionSerializer):
9059
provider = PreprintProviderRelationshipField(
9160
related_view='providers:preprint-providers:preprint-provider-detail',
92-
related_view_kwargs={'provider_id': '<provider._id>'},
93-
read_only=False,
61+
related_view_kwargs={'provider_id': '<subscribed_object._id>'},
62+
read_only=True,
9463
)
9564

9665
def get_absolute_url(self, obj):
97-
return api_v2_url(f'preprints_subscriptions/{obj._id}')
66+
return api_v2_url(f'preprints_subscriptions/{obj.pk}')
9867

9968
class Meta:
10069
type_ = 'preprint-subscription'

0 commit comments

Comments
 (0)