Projet

Général

Profil

0001-create-announces-from-external-RSS-feed-12919.patch

Serghei Mihai, 16 septembre 2016 16:06

Télécharger (9,41 ko)

Voir les différences:

Subject: [PATCH] create announces from external RSS feed (#12919)

 corbo/forms.py                                   |  2 +-
 corbo/management/commands/sync_external_feeds.py | 25 +++++++++++++
 corbo/migrations/0007_category_rss_stream_url.py | 20 +++++++++++
 corbo/models.py                                  | 20 +++++++++++
 corbo/templates/corbo/category_detail.html       |  2 +-
 corbo/templates/corbo/category_form.html         |  2 +-
 debian/control                                   |  4 ++-
 debian/corbo.cron.d                              |  1 +
 requirements.txt                                 |  2 ++
 setup.py                                         |  2 ++
 tests/test_announces.py                          | 45 ++++++++++++++++++++++++
 tox.ini                                          |  1 +
 12 files changed, 122 insertions(+), 4 deletions(-)
 create mode 100644 corbo/management/commands/sync_external_feeds.py
 create mode 100644 corbo/migrations/0007_category_rss_stream_url.py
 create mode 100644 tests/test_announces.py
corbo/forms.py
25 25

  
26 26
class CategoryForm(forms.ModelForm):
27 27
    class Meta:
28
        fields = ('name', )
28
        fields = ('name', 'rss_stream_url')
29 29
        model = Category
30 30

  
31 31

  
corbo/management/commands/sync_external_feeds.py
1
# corbo - Announces Manager
2
# Copyright (C) 2016 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.core.management.base import BaseCommand, CommandError
18

  
19
from corbo.models import Category
20

  
21
class Command(BaseCommand):
22

  
23
    def handle(self, *args, **kwargs):
24
        for c in Category.objects.filter(rss_stream_url__isnull=False):
25
            c.save()
corbo/migrations/0007_category_rss_stream_url.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', '0006_sms'),
11
    ]
12

  
13
    operations = [
14
        migrations.AddField(
15
            model_name='category',
16
            name='rss_stream_url',
17
            field=models.URLField(help_text='if defined, announces will be automatically created from rss items', null=True, blank=True),
18
            preserve_default=True,
19
        ),
20
    ]
corbo/models.py
5 5
from html2text import HTML2Text
6 6
from emails.django import Message
7 7
from lxml.etree import HTML as HTMLTree
8
import requests
9
import feedparser
8 10

  
9 11
from django.utils import timezone
10 12
from django.conf import settings
......
27 29

  
28 30
class Category(models.Model):
29 31
    name = models.CharField(max_length=64, blank=False, null=False)
32
    rss_stream_url = models.URLField(blank=True, null=True,
33
                help_text=_('if defined, announces will be automatically created from rss items'))
30 34
    ctime = models.DateTimeField(auto_now_add=True)
31 35

  
32 36
    def __unicode__(self):
......
41 45
    def get_subscriptions_count(self):
42 46
        return self.subscription_set.all().count()
43 47

  
48
    def save(self, *args, **kwargs):
49
        super(Category, self).save(*args, **kwargs)
50
        if not self.rss_stream_url:
51
            return
52
        feed_response = requests.get(self.rss_stream_url)
53
        if feed_response.ok:
54
            content = feedparser.parse(feed_response.content)
55
            for entry in content.get('entries', []):
56
                announce, created = Announce.objects.get_or_create(title=entry['title'],
57
                                                            category=self)
58
                if created:
59
                    announce.publication_time = entry['updated']
60
                    announce.text = entry['summary']
61
                    announce.save()
62

  
63

  
44 64

  
45 65
class Announce(models.Model):
46 66
    category  = models.ForeignKey('Category', verbose_name=_('category'))
corbo/templates/corbo/category_detail.html
11 11
{% block appbar %}
12 12
<h2>{{ object.name }}</h2>
13 13
<a href="{% url 'delete_category' object.id %}" rel="popup">{% trans 'Delete' %}</a>
14
<a href="{% url 'edit_category' object.id %}" rel="popup">{% trans 'Rename' %}</a>
14
<a href="{% url 'edit_category' object.id %}" rel="popup">{% trans 'Edit' %}</a>
15 15
<a href="{% url 'add_sms_for_category' pk=object.pk %}">{% trans 'New SMS' %}</a>
16 16
<a href="{% url 'add_announce_for_category' pk=object.pk %}">{% trans 'New announce' %}</a>
17 17
{% endblock %}
corbo/templates/corbo/category_form.html
3 3

  
4 4
{% block appbar %}
5 5
{% if object %}
6
  <h2>{% trans "Modify Category" %}</h2>
6
  <h2>{% trans "Edit Category" %}</h2>
7 7
{% else %}
8 8
  <h2>{% trans "New Category" %}</h2>
9 9
{% endif %}
debian/control
11 11
    python-django (>= 1.7),
12 12
    python-django-ckeditor,
13 13
    python-gadjo,
14
    python-emails
14
    python-emails,
15
    python-requests,
16
    python-feedparser
15 17
Description: Announces Manager
16 18

  
17 19
Package: corbo
debian/corbo.cron.d
1 1
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
2 2

  
3 3
*/5 * * * * corbo corbo-manage tenant_command send_announces --all-tenants
4
0 * * * * corbo corbo-manage tenant_command sync_external_feeds --all-tenants
requirements.txt
3 3
djangorestframework>=3.3,<3.4
4 4
html2text
5 5
emails
6
feedparser
7
requests
6 8
-e git+http://repos.entrouvert.org/gadjo.git/#egg=gadjo
setup.py
100 100
        'gadjo',
101 101
        'emails',
102 102
        'lxml',
103
        'feedparser',
104
        'requests'
103 105
        ],
104 106
    zip_safe=False,
105 107
    cmdclass={
tests/test_announces.py
1
import pytest
2
import mock
3
import feedparser
4

  
5
from django.core.urlresolvers import reverse
6
from django.core.urlresolvers import reverse
7

  
8
from corbo.models import Category
9

  
10
pytestmark = pytest.mark.django_db
11

  
12
CATEGORIES = ('Alerts',)
13

  
14
ATOM_FEED = """
15
<?xml version="1.0" encoding="UTF-8"?>
16
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
17
  <id>tag:linuxfr.org,2005:/news</id>
18
  <title>Sample RSS Feeds</title>
19
  <updated>2016-09-16T10:29:46+02:00</updated>
20
  <entry>
21
    <id>tag:linuxfr.org,2005:News/37537</id>
22
    <published>2016-09-16T10:29:46+02:00</published>
23
    <updated>2016-09-16T11:27:00+02:00</updated>
24
    <title>Feed entry sample</title>
25
    <content type="html">
26
      Feed entry content
27
    </content>
28
    <author>
29
      <name>Foo Bar</name>
30
    </author>
31
  </entry>
32
</feed>
33
"""
34

  
35

  
36

  
37
@mock.patch('corbo.models.requests.get')
38
def test_announces_from_feed(mocked_get):
39
    feed_content = feedparser.parse(ATOM_FEED)
40
    mocked_get.return_value = mock.Mock(ok=True, content=ATOM_FEED)
41
    for category in CATEGORIES:
42
        c = Category.objects.create(name=category, rss_stream_url='http://example.com/atom')
43
        assert c.announce_set.count() == len(feed_content['entries'])
44
        for announce in c.announce_set.all():
45
            assert announce.title in [feed['title'] for feed in feed_content['entries']]
tox.ini
19 19
  djangorestframework>=3.3,<3.4
20 20
  pylint==1.4.0
21 21
  astroid==1.3.2
22
  mock
22 23
commands =
23 24
  py.test {env:COVERAGE:} {posargs:tests/}
24
-