Project

General

Profile

« Previous | Next » 

Revision 8759c981

Added by Serghei Mihai over 9 years ago

api: subscriptions management endpoint (#10794)

View differences:

corbo/api_urls.py
from django.conf.urls import patterns, include, url
from .api_views import NewslettersView
from .api_views import NewslettersView, SubscriptionsView
urlpatterns = patterns('',
url(r'^newsletters/', NewslettersView.as_view(), name='newsletters'),
url(r'^subscriptions/', SubscriptionsView.as_view(), name='subscriptions'),
)
corbo/api_views.py
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
from collections import defaultdict
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.db import transaction
from rest_framework.views import APIView
from rest_framework.response import Response
......
'transports': transports}
newsletters.append(newsletter)
return Response({'data': newsletters})
class SubscriptionsView(APIView):
def get_subscriptions(self, email, uuid=None):
subscriptions = defaultdict(dict)
identifier = 'mailto:'+email
for s in Subscription.objects.filter(Q(identifier=identifier)|Q(uuid=uuid)):
cat_id = s.category.pk
subscriptions[cat_id]['id'] = str(cat_id)
subscriptions[cat_id]['text'] = s.category.name
transport_id, transport_name = identifier.split(':')
transport = {'id': transport_id,
'text': transport_id}
if 'transports' in subscriptions[cat_id]:
subscriptions[cat_id]['transports'].append(transport)
else:
subscriptions[cat_id]['transports'] = [transport]
return subscriptions.values()
@transaction.atomic
def update_subscriptions(self, category_id, transports, email, uuid=None):
uuid = uuid or u''
identifier = u'mailto:'
if email:
identifier += email
try:
cat = Category.objects.get(pk=category_id)
except Category.DoesNotExist:
return
subcriptions = cat.subscription_set
if email:
if 'mailto' in transports:
subcriptions.get_or_create(identifier=identifier, uuid=uuid)
if uuid:
subcriptions.exclude(uuid=uuid).filter(identifier=identifier).delete()
else:
subcriptions.filter(identifier=identifier).delete()
if uuid:
if 'homepage' in transports:
subcriptions.get_or_create(uuid=uuid)
else:
subcriptions.filter(identifier='', uuid=uuid).delete()
def get(self, request):
email = request.GET.get('email')
uuid = request.GET.get('uuid')
if not email:
raise PermissionDenied('Email parameter required')
return Response({'data': self.get_subscriptions(email, uuid)})
def post(self, request):
email = request.GET.get('email')
uuid = request.GET.get('uuid')
if not email:
raise PermissionDenied('Email parameter required')
data = json.loads(request.body)
for subscription in data:
self.update_subscriptions(subscription['id'], subscription['transports'],
email, uuid)
return Response({'data': True})
def delete(self, request):
email = request.GET.get('email')
uuid = request.GET.get('uuid')
if not email:
raise PermissionDenied('Email parameter required')
for subscription in self.get_subscriptions(email):
self.update_subscriptions(subscription['id'], [], email, uuid)
return Response({'data': True})
corbo/forms.py
class AnnounceForm(forms.ModelForm):
transport_channel = forms.MultipleChoiceField(required=False,
choices=channel_choices,
widget=forms.CheckboxSelectMultiple())
class Meta:
model = Announce
......
}
fields = '__all__'
def __init__(self, *args, **kwargs):
super(AnnounceForm, self).__init__(*args, **kwargs)
self.fields['transport_channel'].initial = [b.channel for \
b in self.instance.broadcast_set.all()]
def clean_transport_channel(self):
channels = self.cleaned_data['transport_channel']
limit = 160
if 'sms' in channels and \
len(self.cleaned_data['text']) > limit:
raise forms.ValidationError(_('Announce content exceeds %s chars'
' and cannot be sent by SMS' % limit))
return channels
def save(self, *args, **kwargs):
instance = super(AnnounceForm, self).save(*args, **kwargs)
if instance:
print "CD: %s" % self.cleaned_data
channels = self.cleaned_data['transport_channel']
for channel in channels:
Broadcast.objects.get_or_create(announce=instance,
channel=channel)
self.instance.broadcast_set.exclude(announce=instance,
channel__in=channels).delete()
Broadcast.objects.get_or_create(announce=instance)
return instance
class CategoryForm(forms.ModelForm):
class Meta:
fields = ('name', )
corbo/migrations/0004_auto_20160504_1744.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('corbo', '0003_auto_20160427_1342'),
]
operations = [
migrations.RemoveField(
model_name='subscriptiontype',
name='subscription',
),
migrations.DeleteModel(
name='SubscriptionType',
),
migrations.AddField(
model_name='subscription',
name='identifier',
field=models.CharField(help_text='ex.: mailto, homepage, ...', max_length=128, verbose_name='identifier', blank=True),
preserve_default=True,
),
migrations.AddField(
model_name='subscription',
name='uuid',
field=models.CharField(max_length=128, verbose_name='User identifier', blank=True),
preserve_default=True,
),
migrations.AlterField(
model_name='subscription',
name='category',
field=models.ForeignKey(verbose_name='Category', to='corbo.Category'),
preserve_default=True,
),
migrations.AlterUniqueTogether(
name='broadcast',
unique_together=set([]),
),
migrations.RemoveField(
model_name='broadcast',
name='channel',
),
migrations.AlterUniqueTogether(
name='subscription',
unique_together=set([('category', 'identifier', 'uuid')]),
),
migrations.RemoveField(
model_name='subscription',
name='user',
),
]
corbo/models.py
class Broadcast(models.Model):
announce = models.ForeignKey(Announce, verbose_name=_('announce'))
channel = models.CharField(_('channel'), max_length=32,
choices=channel_choices, blank=False)
time = models.DateTimeField(_('sent time'), auto_now_add=True)
result = models.TextField(_('result'), blank=True)
......
class Meta:
verbose_name = _('sent')
ordering = ('-time',)
unique_together = ('announce', 'channel')
class SubscriptionType(models.Model):
subscription = models.ForeignKey('Subscription')
identifier = models.CharField(_('identifier'), max_length=128, blank=True,
help_text=_('ex.: email, mobile phone number, jabber id'))
class Subscription(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'),
blank=True, null=True)
category = models.ForeignKey('Category', verbose_name=_('category'))
category = models.ForeignKey('Category', verbose_name=_('Category'))
uuid = models.CharField(_('User identifier'), max_length=128, blank=True)
identifier = models.CharField(_('identifier'), max_length=128, blank=True,
help_text=_('ex.: mailto, homepage, ...'))
class Meta:
unique_together = ('user', 'category')
unique_together = ('category', 'identifier', 'uuid')
tests/test_api.py
import pytest
import json
from uuid import uuid4
from django.core.urlresolvers import reverse
from django.utils.http import urlencode
from corbo.models import Category, Announce, Broadcast
from corbo.models import Category, Announce, Broadcast, Subscription
from corbo.models import channel_choices
pytestmark = pytest.mark.django_db
......
announces = []
for category in Category.objects.all():
a = Announce.objects.create(category=category, title='By email')
Broadcast.objects.create(announce=a, channel='mailto')
Broadcast.objects.create(announce=a)
announces.append(a)
a = Announce.objects.create(category=category, title='On homepage')
Broadcast.objects.create(announce=a, channel='homepage')
Broadcast.objects.create(announce=a)
announces.append(a)
return announces
......
assert category['transports'] == [{'id': 'mailto', 'text': 'Email'},
{'id': 'homepage', 'text': 'Homepage'}
]
def test_get_subscriptions_by_email(app, categories, announces):
resp = app.get(reverse('subscriptions'), status=403)
foo = 'foo@example.com'
for identifier, name in channel_choices[:1]:
for category in categories:
uri = '%s:%s' % (identifier, foo)
subscription = Subscription.objects.create(identifier=uri,
category=category)
resp = app.get(reverse('subscriptions'), {'email': foo}, status=200)
assert 'data' in resp.json
data = resp.json['data']
for d in data:
assert d['id'] in [str(category.id) for category in categories]
assert d['text'] in [category.name for category in categories]
for t in d['transports']:
assert t['id'] == identifier
def test_update_subscriptions(app, categories, announces):
params = urlencode({'email': 'foo@example.com',
'uuid': str(uuid4())})
subscriptions_url = reverse('subscriptions') + '?' + params
for category in categories:
transports = []
for identifier, name in channel_choices[:1]:
transports.append(identifier)
category_id = str(category.id)
subscriptions = [{'id': category_id,
'text': category.name,
'transports': transports}]
resp = app.post_json(subscriptions_url , subscriptions)
if resp.json['data']:
resp = app.get(subscriptions_url, status=200)
print resp.json['data']
for cat in resp.json['data']:
if cat['id'] == category_id:
sub_transports = [c['id'] for c in cat['transports']]
assert sub_transports == transports
def test_delete_subscriptions(app, categories, announces):
params = urlencode({'email': 'foo@example.com', 'uuid': str(uuid4())})
subscriptions_url = reverse('subscriptions') + '?' + params
resp = app.delete(subscriptions_url)
if resp.json['data']:
resp = app.get(subscriptions_url, status=200)
assert resp.json['data'] == []

Also available in: Unified diff