From 2600820544962ca606f48fa95bfcd762b0fd3936 Mon Sep 17 00:00:00 2001 From: Serghei Mihai Date: Mon, 2 May 2016 15:24:29 +0200 Subject: [PATCH 1/3] api: newsletters retrieval endpoint (#10794) --- corbo/api_urls.py | 23 +++++++++++++++++++++++ corbo/api_views.py | 32 ++++++++++++++++++++++++++++++++ corbo/channels.py | 36 ------------------------------------ corbo/forms.py | 6 +++--- corbo/models.py | 7 +++++-- corbo/settings.py | 1 + corbo/urls.py | 4 +++- jenkins.sh | 13 +++++++++++++ requirements.txt | 1 + setup.py | 3 ++- tests/conftest.py | 9 +++++++++ tests/test_api.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ tox.ini | 23 +++++++++++++++++++++++ 13 files changed, 161 insertions(+), 43 deletions(-) create mode 100644 corbo/api_urls.py create mode 100644 corbo/api_views.py delete mode 100644 corbo/channels.py create mode 100755 jenkins.sh create mode 100644 tests/conftest.py create mode 100644 tests/test_api.py create mode 100644 tox.ini diff --git a/corbo/api_urls.py b/corbo/api_urls.py new file mode 100644 index 0000000..cb275e5 --- /dev/null +++ b/corbo/api_urls.py @@ -0,0 +1,23 @@ +# corbo - Announces Manager +# Copyright (C) 2016 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.conf.urls import patterns, include, url + +from .api_views import NewslettersView + +urlpatterns = patterns('', + url(r'^newsletters/', NewslettersView.as_view(), name='newsletters'), +) diff --git a/corbo/api_views.py b/corbo/api_views.py new file mode 100644 index 0000000..a1f15be --- /dev/null +++ b/corbo/api_views.py @@ -0,0 +1,32 @@ +# corbo - Announces Manager +# Copyright (C) 2016 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from rest_framework.views import APIView +from rest_framework.response import Response + +from .models import Category, Subscription, channel_choices + + +class NewslettersView(APIView): + + def get(self, request): + newsletters = [] + transports = [{'id': identifier, 'text': name} for identifier, name in channel_choices] + for c in Category.objects.all(): + newsletter = {'id': str(c.pk), 'text': c.name, + 'transports': transports} + newsletters.append(newsletter) + return Response({'data': newsletters}) diff --git a/corbo/channels.py b/corbo/channels.py deleted file mode 100644 index a8ab660..0000000 --- a/corbo/channels.py +++ /dev/null @@ -1,36 +0,0 @@ -from django.utils.translation import ugettext_lazy as _ - -def get_channel_choices(include=[], exclude=[]): - for channel in HomepageChannel, SMSChannel, EmailChannel: - if include and channel.identifier not in include: - continue - if exclude and channel.identifier in exclude: - continue - for identifier, display_name in channel.get_choices(): - yield (identifier, display_name) - -class HomepageChannel(object): - identifier = 'homepage' - - @classmethod - def get_choices(self): - return (('homepage', _('Homepage')),) - -class SMSChannel(object): - - @classmethod - def get_choices(self): - return (('sms', _('SMS')),) - - def send(self, announce): - pass - -class EmailChannel(object): - identifier = 'email' - - @classmethod - def get_choices(self): - return (('email', _('Email')),) - - def send(self, announce): - pass diff --git a/corbo/forms.py b/corbo/forms.py index 6b62d91..e7a3ac8 100644 --- a/corbo/forms.py +++ b/corbo/forms.py @@ -1,12 +1,12 @@ from django import forms from django.utils.translation import ugettext_lazy as _ -from .models import Announce, Category, Broadcast -from .channels import get_channel_choices +from .models import Announce, Category, Broadcast, channel_choices + class AnnounceForm(forms.ModelForm): transport_channel = forms.MultipleChoiceField(required=False, - choices=get_channel_choices(), + choices=channel_choices, widget=forms.CheckboxSelectMultiple()) class Meta: diff --git a/corbo/models.py b/corbo/models.py index d4b942c..1f48e60 100644 --- a/corbo/models.py +++ b/corbo/models.py @@ -5,7 +5,10 @@ from django.utils.translation import ugettext_lazy as _ from ckeditor.fields import RichTextField -import channels +channel_choices = ( + ('mailto', _('Email')), + ('homepage', _('Homepage')) +) class Category(models.Model): name = models.CharField(max_length=64, blank=False, null=False) @@ -51,7 +54,7 @@ class Announce(models.Model): class Broadcast(models.Model): announce = models.ForeignKey(Announce, verbose_name=_('announce')) channel = models.CharField(_('channel'), max_length=32, - choices=channels.get_channel_choices(), blank=False) + choices=channel_choices, blank=False) time = models.DateTimeField(_('sent time'), auto_now_add=True) result = models.TextField(_('result'), blank=True) diff --git a/corbo/settings.py b/corbo/settings.py index f90e4e6..654c98c 100644 --- a/corbo/settings.py +++ b/corbo/settings.py @@ -41,6 +41,7 @@ INSTALLED_APPS = ( 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', ) MIDDLEWARE_CLASSES = ( diff --git a/corbo/urls.py b/corbo/urls.py index f592202..f8b074d 100644 --- a/corbo/urls.py +++ b/corbo/urls.py @@ -8,6 +8,7 @@ from .urls_utils import decorated_includes, manager_required from .views import homepage, atom from manage_urls import urlpatterns as manage_urls +from api_urls import urlpatterns as api_urls urlpatterns = patterns('', url(r'^$', homepage, name='home'), @@ -15,7 +16,8 @@ urlpatterns = patterns('', url(r'^manage/', decorated_includes(manager_required, include(manage_urls))), url(r'^ckeditor/', include('ckeditor.urls')), - url(r'^admin/', include(admin.site.urls)) + url(r'^admin/', include(admin.site.urls)), + url(r'^api/', include(api_urls)) ) if 'mellon' in settings.INSTALLED_APPS: diff --git a/jenkins.sh b/jenkins.sh new file mode 100755 index 0000000..cbde55c --- /dev/null +++ b/jenkins.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e + +rm -f coverage.xml +rm -f test_results.xml + +pip install --upgrade tox +pip install --upgrade pylint pylint-django +tox -r +test -f pylint.out && cp pylint.out pylint.out.prev +(pylint -f parseable --rcfile /var/lib/jenkins/pylint.django.rc corbo/ | tee pylint.out) || /bin/true +test -f pylint.out.prev && (diff pylint.out.prev pylint.out | grep '^[><]' | grep .py) || /bin/true diff --git a/requirements.txt b/requirements.txt index b2383ad..2f6bdfc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Django>=1.7, <1.8 django-ckeditor<4.5.3 +djangorestframework -e git+http://repos.entrouvert.org/gadjo.git/#egg=gadjo diff --git a/setup.py b/setup.py index 4209333..e59a798 100644 --- a/setup.py +++ b/setup.py @@ -94,7 +94,8 @@ setup( 'Programming Language :: Python :: 2', ], install_requires=['django>=1.7, <1.8', - 'django-ckeditor<4.5.3' + 'django-ckeditor<4.5.3', + 'djangorestframework', 'gadjo' ], zip_safe=False, diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..674a805 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,9 @@ +import pytest +import django_webtest + +@pytest.fixture +def app(request): + wtm = django_webtest.WebTestMixin() + wtm._patch_settings() + request.addfinalizer(wtm._unpatch_settings) + return django_webtest.DjangoTestApp() diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..d72c51f --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,46 @@ +import pytest +import json + + +from django.core.urlresolvers import reverse + +from corbo.models import Category, Announce, Broadcast + +pytestmark = pytest.mark.django_db + +CATEGORIES = ('Alerts', 'News') + + +@pytest.fixture +def categories(): + categories = [] + for category in CATEGORIES: + c, created = Category.objects.get_or_create(name=category) + categories.append(c) + return categories + +@pytest.fixture +def announces(): + announces = [] + for category in Category.objects.all(): + a = Announce.objects.create(category=category, title='By email') + Broadcast.objects.create(announce=a, channel='mailto') + announces.append(a) + a = Announce.objects.create(category=category, title='On homepage') + Broadcast.objects.create(announce=a, channel='homepage') + announces.append(a) + return announces + + +def test_get_newsletters(app, categories, announces): + resp = app.get(reverse('newsletters'), status=200) + data = resp.json + assert data['data'] + for category in data['data']: + assert 'id' in category + assert 'text' in category + assert category['text'] in CATEGORIES + assert 'transports' in category + assert category['transports'] == [{'id': 'mailto', 'text': 'Email'}, + {'id': 'homepage', 'text': 'Homepage'} + ] diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..54658f5 --- /dev/null +++ b/tox.ini @@ -0,0 +1,23 @@ +[tox] +envlist = coverage-{django17,django18} + +[testenv] +usedevelop = + coverage: True +setenv = + DJANGO_SETTINGS_MODULE=corbo.settings + coverage: COVERAGE=--junitxml=test_results.xml --cov-report xml --cov=corbo/ --cov-config .coveragerc +deps = + django17: django>1.7,<1.8 + django18: django>=1.8,<1.9 + pytest-cov + pytest-django + pytest + pytest-capturelog + django-webtest + django-ckeditor<4.5.3 + djangorestframework + pylint==1.4.0 + astroid==1.3.2 +commands = + py.test {env:COVERAGE:} {posargs:tests/} -- 2.8.1