Projet

Général

Profil

« Précédent | Suivant » 

Révision 8759c981

Ajouté par Serghei Mihai il y a presque 8 ans

api: subscriptions management endpoint (#10794)

Voir les différences:

corbo/api_urls.py
16 16

  
17 17
from django.conf.urls import patterns, include, url
18 18

  
19
from .api_views import NewslettersView
19
from .api_views import NewslettersView, SubscriptionsView
20 20

  
21 21
urlpatterns = patterns('',
22 22
            url(r'^newsletters/', NewslettersView.as_view(), name='newsletters'),
23
            url(r'^subscriptions/', SubscriptionsView.as_view(), name='subscriptions'),
23 24
)
corbo/api_views.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import json
18
from collections import defaultdict
19

  
20
from django.core.exceptions import PermissionDenied
21
from django.db.models import Q
22
from django.db import transaction
23

  
17 24
from rest_framework.views import APIView
18 25
from rest_framework.response import Response
19 26

  
......
30 37
                          'transports': transports}
31 38
            newsletters.append(newsletter)
32 39
        return Response({'data': newsletters})
40

  
41

  
42
class SubscriptionsView(APIView):
43

  
44
    def get_subscriptions(self, email, uuid=None):
45
        subscriptions = defaultdict(dict)
46
        identifier = 'mailto:'+email
47
        for s in Subscription.objects.filter(Q(identifier=identifier)|Q(uuid=uuid)):
48
            cat_id = s.category.pk
49
            subscriptions[cat_id]['id'] = str(cat_id)
50
            subscriptions[cat_id]['text'] = s.category.name
51
            transport_id, transport_name = identifier.split(':')
52
            transport = {'id': transport_id,
53
                         'text': transport_id}
54
            if 'transports' in subscriptions[cat_id]:
55
                subscriptions[cat_id]['transports'].append(transport)
56
            else:
57
                subscriptions[cat_id]['transports'] = [transport]
58

  
59
        return subscriptions.values()
60

  
61
    @transaction.atomic
62
    def update_subscriptions(self, category_id, transports, email, uuid=None):
63
        uuid = uuid or u''
64
        identifier = u'mailto:'
65
        if email:
66
            identifier += email
67
        try:
68
            cat = Category.objects.get(pk=category_id)
69
        except Category.DoesNotExist:
70
            return
71

  
72
        subcriptions = cat.subscription_set
73

  
74
        if email:
75
            if 'mailto' in transports:
76
                subcriptions.get_or_create(identifier=identifier, uuid=uuid)
77
                if uuid:
78
                    subcriptions.exclude(uuid=uuid).filter(identifier=identifier).delete()
79
            else:
80
                subcriptions.filter(identifier=identifier).delete()
81

  
82
        if uuid:
83
            if 'homepage' in transports:
84
                subcriptions.get_or_create(uuid=uuid)
85
            else:
86
                subcriptions.filter(identifier='', uuid=uuid).delete()
87

  
88
    def get(self, request):
89
        email = request.GET.get('email')
90
        uuid = request.GET.get('uuid')
91
        if not email:
92
            raise PermissionDenied('Email parameter required')
93
        return Response({'data': self.get_subscriptions(email, uuid)})
94

  
95
    def post(self, request):
96
        email = request.GET.get('email')
97
        uuid = request.GET.get('uuid')
98
        if not email:
99
            raise PermissionDenied('Email parameter required')
100
        data = json.loads(request.body)
101
        for subscription in data:
102
            self.update_subscriptions(subscription['id'], subscription['transports'],
103
                                      email, uuid)
104
        return Response({'data': True})
105

  
106
    def delete(self, request):
107
        email = request.GET.get('email')
108
        uuid = request.GET.get('uuid')
109
        if not email:
110
            raise PermissionDenied('Email parameter required')
111

  
112
        for subscription in self.get_subscriptions(email):
113
            self.update_subscriptions(subscription['id'], [], email, uuid)
114
        return Response({'data': True})
corbo/forms.py
5 5

  
6 6

  
7 7
class AnnounceForm(forms.ModelForm):
8
    transport_channel = forms.MultipleChoiceField(required=False,
9
                                                  choices=channel_choices,
10
                                                  widget=forms.CheckboxSelectMultiple())
11 8

  
12 9
    class Meta:
13 10
        model = Announce
......
20 17
        }
21 18
        fields = '__all__'
22 19

  
23
    def __init__(self, *args, **kwargs):
24
        super(AnnounceForm, self).__init__(*args, **kwargs)
25
        self.fields['transport_channel'].initial = [b.channel for \
26
                                                    b in self.instance.broadcast_set.all()]
27

  
28
    def clean_transport_channel(self):
29
        channels = self.cleaned_data['transport_channel']
30
        limit = 160
31
        if 'sms' in channels and \
32
           len(self.cleaned_data['text']) > limit:
33
            raise forms.ValidationError(_('Announce content exceeds %s chars'
34
                                          ' and cannot be sent by SMS' % limit))
35
        return channels
36

  
37 20
    def save(self, *args, **kwargs):
38 21
        instance = super(AnnounceForm, self).save(*args, **kwargs)
39 22
        if instance:
40
            print "CD: %s" % self.cleaned_data
41
            channels = self.cleaned_data['transport_channel']
42
            for channel in channels:
43
                Broadcast.objects.get_or_create(announce=instance,
44
                                                channel=channel)
45
            self.instance.broadcast_set.exclude(announce=instance,
46
                                                channel__in=channels).delete()
23
            Broadcast.objects.get_or_create(announce=instance)
47 24
        return instance
48 25

  
26

  
49 27
class CategoryForm(forms.ModelForm):
50 28
    class Meta:
51 29
        fields = ('name', )
corbo/migrations/0004_auto_20160504_1744.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models, migrations
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('corbo', '0003_auto_20160427_1342'),
11
    ]
12

  
13
    operations = [
14
        migrations.RemoveField(
15
            model_name='subscriptiontype',
16
            name='subscription',
17
        ),
18
        migrations.DeleteModel(
19
            name='SubscriptionType',
20
        ),
21
        migrations.AddField(
22
            model_name='subscription',
23
            name='identifier',
24
            field=models.CharField(help_text='ex.: mailto, homepage, ...', max_length=128, verbose_name='identifier', blank=True),
25
            preserve_default=True,
26
        ),
27
        migrations.AddField(
28
            model_name='subscription',
29
            name='uuid',
30
            field=models.CharField(max_length=128, verbose_name='User identifier', blank=True),
31
            preserve_default=True,
32
        ),
33
        migrations.AlterField(
34
            model_name='subscription',
35
            name='category',
36
            field=models.ForeignKey(verbose_name='Category', to='corbo.Category'),
37
            preserve_default=True,
38
        ),
39
        migrations.AlterUniqueTogether(
40
            name='broadcast',
41
            unique_together=set([]),
42
        ),
43
        migrations.RemoveField(
44
            model_name='broadcast',
45
            name='channel',
46
        ),
47
        migrations.AlterUniqueTogether(
48
            name='subscription',
49
            unique_together=set([('category', 'identifier', 'uuid')]),
50
        ),
51
        migrations.RemoveField(
52
            model_name='subscription',
53
            name='user',
54
        ),
55
    ]
corbo/models.py
53 53

  
54 54
class Broadcast(models.Model):
55 55
    announce = models.ForeignKey(Announce, verbose_name=_('announce'))
56
    channel = models.CharField(_('channel'), max_length=32,
57
            choices=channel_choices, blank=False)
58 56
    time = models.DateTimeField(_('sent time'), auto_now_add=True)
59 57
    result = models.TextField(_('result'), blank=True)
60 58

  
......
65 63
    class Meta:
66 64
        verbose_name = _('sent')
67 65
        ordering = ('-time',)
68
        unique_together = ('announce', 'channel')
69

  
70

  
71
class SubscriptionType(models.Model):
72
    subscription = models.ForeignKey('Subscription')
73
    identifier = models.CharField(_('identifier'), max_length=128, blank=True,
74
            help_text=_('ex.: email, mobile phone number, jabber id'))
75 66

  
76 67

  
77 68
class Subscription(models.Model):
78
    user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'),
79
                             blank=True, null=True)
80
    category = models.ForeignKey('Category', verbose_name=_('category'))
69
    category = models.ForeignKey('Category', verbose_name=_('Category'))
70
    uuid = models.CharField(_('User identifier'), max_length=128, blank=True)
71
    identifier = models.CharField(_('identifier'), max_length=128, blank=True,
72
            help_text=_('ex.: mailto, homepage, ...'))
81 73
    class Meta:
82
        unique_together = ('user', 'category')
74
        unique_together = ('category', 'identifier', 'uuid')
tests/test_api.py
1 1
import pytest
2 2
import json
3
from uuid import uuid4
3 4

  
4 5

  
5 6
from django.core.urlresolvers import reverse
7
from django.utils.http import urlencode
6 8

  
7
from corbo.models import Category, Announce, Broadcast
9
from corbo.models import Category, Announce, Broadcast, Subscription
10
from corbo.models import channel_choices
8 11

  
9 12
pytestmark = pytest.mark.django_db
10 13

  
......
24 27
    announces = []
25 28
    for category in Category.objects.all():
26 29
        a = Announce.objects.create(category=category, title='By email')
27
        Broadcast.objects.create(announce=a, channel='mailto')
30
        Broadcast.objects.create(announce=a)
28 31
        announces.append(a)
29 32
        a = Announce.objects.create(category=category, title='On homepage')
30
        Broadcast.objects.create(announce=a, channel='homepage')
33
        Broadcast.objects.create(announce=a)
31 34
        announces.append(a)
32 35
    return announces
33 36

  
......
44 47
        assert category['transports'] == [{'id': 'mailto', 'text': 'Email'},
45 48
                                          {'id': 'homepage', 'text': 'Homepage'}
46 49
                                          ]
50

  
51

  
52
def test_get_subscriptions_by_email(app, categories, announces):
53
    resp = app.get(reverse('subscriptions'), status=403)
54
    foo = 'foo@example.com'
55
    for identifier, name in channel_choices[:1]:
56
        for category in categories:
57
            uri = '%s:%s' % (identifier, foo)
58
            subscription = Subscription.objects.create(identifier=uri,
59
                                category=category)
60
            resp = app.get(reverse('subscriptions'), {'email': foo}, status=200)
61
            assert 'data' in resp.json
62
            data = resp.json['data']
63
            for d in data:
64
                assert d['id'] in [str(category.id) for category in categories]
65
                assert d['text'] in [category.name for category in categories]
66
                for t in d['transports']:
67
                    assert t['id'] == identifier
68

  
69

  
70
def test_update_subscriptions(app, categories, announces):
71
    params = urlencode({'email': 'foo@example.com',
72
                        'uuid': str(uuid4())})
73
    subscriptions_url = reverse('subscriptions') + '?' + params
74
    for category in categories:
75
        transports = []
76
        for identifier, name in channel_choices[:1]:
77
            transports.append(identifier)
78
            category_id = str(category.id)
79
            subscriptions = [{'id': category_id,
80
                              'text': category.name,
81
                              'transports': transports}]
82
            resp = app.post_json(subscriptions_url , subscriptions)
83
            if resp.json['data']:
84
                resp = app.get(subscriptions_url, status=200)
85
                print resp.json['data']
86
                for cat in resp.json['data']:
87
                    if cat['id'] == category_id:
88
                        sub_transports = [c['id'] for c in cat['transports']]
89
                        assert sub_transports == transports
90

  
91

  
92
def test_delete_subscriptions(app, categories, announces):
93
    params = urlencode({'email': 'foo@example.com', 'uuid': str(uuid4())})
94
    subscriptions_url = reverse('subscriptions') + '?' + params
95
    resp = app.delete(subscriptions_url)
96
    if resp.json['data']:
97
        resp = app.get(subscriptions_url, status=200)
98
        assert resp.json['data'] == []

Formats disponibles : Unified diff