From 59e7aa0c798e5ea828e42393db9690548f7ef8b5 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 13:34:49 +0200 Subject: [PATCH 01/25] Updated to Wagtail 1.11 --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1f5a1a5..2fb4de8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ # Updated: 30.5.2017 # Core -wagtail==1.10.1 -Django==1.11.1 +wagtail==1.11 +Django==1.11.3 # Database psycopg2==2.7.1 @@ -19,8 +19,8 @@ django-redis==4.8.0 # Frontend django-libsass==0.7 -libsass==0.12.3 -Pillow==4.1.1 +libsass==0.13.2 +Pillow==4.2.0 # Development tools stellar==0.4.3 From 180c6b0f0b3ad4a15f0b6e26f93c5689c09026ec Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 13:35:06 +0200 Subject: [PATCH 02/25] Added RSS feed link to blog template --- publichealth/home/templates/puput/blog_page.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/publichealth/home/templates/puput/blog_page.html b/publichealth/home/templates/puput/blog_page.html index 38d342f..7a2ccbc 100644 --- a/publichealth/home/templates/puput/blog_page.html +++ b/publichealth/home/templates/puput/blog_page.html @@ -20,6 +20,7 @@ + {% endblock social_share %} {% block content %} @@ -56,4 +57,8 @@ {% endwith %} + + + RSS Feed + {% endblock content %} From 7f35cc41f5f5db918fc8801a41bec0ecad92ef50 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 13:35:36 +0200 Subject: [PATCH 03/25] Moved out custom settings areas --- publichealth/home/models/__init__.py | 2 +- publichealth/home/models/{settings.py => admin.py} | 4 +++- publichealth/home/models/models.py | 2 +- publichealth/settings/base.py | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) rename publichealth/home/models/{settings.py => admin.py} (93%) diff --git a/publichealth/home/models/__init__.py b/publichealth/home/models/__init__.py index 472bee0..52e04bb 100644 --- a/publichealth/home/models/__init__.py +++ b/publichealth/home/models/__init__.py @@ -1,4 +1,4 @@ from .forms import * from .models import * from .snippets import * -from .settings import * +from .admin import * diff --git a/publichealth/home/models/settings.py b/publichealth/home/models/admin.py similarity index 93% rename from publichealth/home/models/settings.py rename to publichealth/home/models/admin.py index 8dfaa0a..8cf4eec 100644 --- a/publichealth/home/models/settings.py +++ b/publichealth/home/models/admin.py @@ -7,6 +7,8 @@ from django.core.mail import send_mail from wagtail.contrib.settings.models import BaseSetting, register_setting +# A simple feedback module built into the site admin + @register_setting class DataletsSettings(BaseSetting): feedback_question = models.TextField( @@ -23,7 +25,7 @@ class DataletsSettings(BaseSetting): feedback_comment = models.TextField( help_text='Any general feedback', blank=True) class Meta: - verbose_name = 'Datalets' + verbose_name = 'Get support' @receiver(pre_save, sender=DataletsSettings) def handle_save_settings(sender, instance, *args, **kwargs): diff --git a/publichealth/home/models/models.py b/publichealth/home/models/models.py index 852df91..19681db 100644 --- a/publichealth/home/models/models.py +++ b/publichealth/home/models/models.py @@ -230,6 +230,6 @@ class HomePage(Page): context['newsfeed'] = self.newsfeed return context - parent_page_types = [] + parent_page_types = ['wagtailcore.Page'] class Meta: verbose_name = "Frontpage" diff --git a/publichealth/settings/base.py b/publichealth/settings/base.py index 0aa5702..8c2aff4 100644 --- a/publichealth/settings/base.py +++ b/publichealth/settings/base.py @@ -23,11 +23,13 @@ BASE_DIR = os.path.dirname(PROJECT_DIR) INSTALLED_APPS = [ 'publichealth.home', 'publichealth.home.templatetags', + 'publichealth.feedler', 'publichealth.search', 'wagtail.contrib.wagtailsearchpromotions', 'wagtail.contrib.wagtailroutablepage', 'wagtail.contrib.wagtailsitemaps', + 'wagtail.contrib.modeladmin', 'wagtail.contrib.settings', 'wagtail.wagtailforms', 'wagtail.wagtailredirects', From 1a624950b57df5d6b0270c4d44c76c4a8764183f Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 13:35:46 +0200 Subject: [PATCH 04/25] Migration to Wagtail 1.11 merge --- .../migrations/0019_auto_20170703_1244.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 publichealth/home/migrations/0019_auto_20170703_1244.py diff --git a/publichealth/home/migrations/0019_auto_20170703_1244.py b/publichealth/home/migrations/0019_auto_20170703_1244.py new file mode 100644 index 0000000..6565d9d --- /dev/null +++ b/publichealth/home/migrations/0019_auto_20170703_1244.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.3 on 2017-07-03 10:44 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0018_contact_contact_form'), + ] + + operations = [ + migrations.AlterModelOptions( + name='dataletssettings', + options={'verbose_name': 'Get support'}, + ), + ] From dfa1606d5866d3acf7f3e27490b740b0b86af677 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 13:36:01 +0200 Subject: [PATCH 05/25] Feedler application --- publichealth/feedler/__init__.py | 0 publichealth/feedler/apps.py | 5 ++ .../feedler/migrations/0001_initial.py | 63 +++++++++++++++++ publichealth/feedler/migrations/__init__.py | 0 publichealth/feedler/models/__init__.py | 2 + publichealth/feedler/models/admin.py | 49 +++++++++++++ publichealth/feedler/models/models.py | 70 +++++++++++++++++++ publichealth/feedler/tests.py | 3 + publichealth/feedler/views.py | 3 + publichealth/feedler/wagtail_hooks.py | 18 +++++ 10 files changed, 213 insertions(+) create mode 100644 publichealth/feedler/__init__.py create mode 100644 publichealth/feedler/apps.py create mode 100644 publichealth/feedler/migrations/0001_initial.py create mode 100644 publichealth/feedler/migrations/__init__.py create mode 100644 publichealth/feedler/models/__init__.py create mode 100644 publichealth/feedler/models/admin.py create mode 100644 publichealth/feedler/models/models.py create mode 100644 publichealth/feedler/tests.py create mode 100644 publichealth/feedler/views.py create mode 100644 publichealth/feedler/wagtail_hooks.py diff --git a/publichealth/feedler/__init__.py b/publichealth/feedler/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/publichealth/feedler/apps.py b/publichealth/feedler/apps.py new file mode 100644 index 0000000..7bc8f1e --- /dev/null +++ b/publichealth/feedler/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class FeedlerConfig(AppConfig): + name = 'feedler' diff --git a/publichealth/feedler/migrations/0001_initial.py b/publichealth/feedler/migrations/0001_initial.py new file mode 100644 index 0000000..9fb3abd --- /dev/null +++ b/publichealth/feedler/migrations/0001_initial.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.3 on 2017-07-03 11:24 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('wagtailcore', '0040_merge_20170703_1238'), + ] + + operations = [ + migrations.CreateModel( + name='Entry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('raw', models.TextField(blank=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('published', models.DateTimeField(auto_now_add=True)), + ('entry_id', models.IntegerField(blank=True)), + ('title', models.CharField(max_length=255)), + ('origin_title', models.CharField(blank=True, max_length=255)), + ('link', models.URLField()), + ('visual', models.URLField(blank=True)), + ('content', models.TextField()), + ('tags', models.TextField(blank=True)), + ], + options={ + 'verbose_name_plural': 'Entries', + }, + ), + migrations.CreateModel( + name='EntryCategory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('label', models.CharField(max_length=255)), + ('feedly_id', models.CharField(max_length=200)), + ], + ), + migrations.CreateModel( + name='FeedlySettings', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('feedly_auth', models.TextField(blank=True, help_text='Your developer authorization key')), + ('feedly_pages', models.IntegerField(blank=True, choices=[(1, '2'), (2, '5'), (3, '10'), (4, '50')], help_text='How many pages to fetch?', null=True)), + ('feedly_stream', models.TextField(blank=True, help_text='Stream ID to fetch')), + ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')), + ], + options={ + 'verbose_name': 'Feedly', + }, + ), + migrations.AddField( + model_name='entry', + name='categories', + field=models.ManyToManyField(blank=True, to='feedler.EntryCategory'), + ), + ] diff --git a/publichealth/feedler/migrations/__init__.py b/publichealth/feedler/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/publichealth/feedler/models/__init__.py b/publichealth/feedler/models/__init__.py new file mode 100644 index 0000000..012e5e5 --- /dev/null +++ b/publichealth/feedler/models/__init__.py @@ -0,0 +1,2 @@ +from .models import * +from .admin import * diff --git a/publichealth/feedler/models/admin.py b/publichealth/feedler/models/admin.py new file mode 100644 index 0000000..ed64f50 --- /dev/null +++ b/publichealth/feedler/models/admin.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import requests, json, codecs + +from django.contrib import admin + +from django.db import models +from django.db.models.signals import pre_save +from django.dispatch import receiver +from django.core.mail import send_mail + +from wagtail.contrib.settings.models import BaseSetting, register_setting + +# Feedly integration module + +@register_setting +class FeedlySettings(BaseSetting): + feedly_auth = models.TextField( + help_text='Your developer authorization key', blank=True) + feedly_pages = models.IntegerField( + choices=( + (1, '2'), + (2, '5'), + (3, '10'), + (4, '50'), + ), blank=True, null=True, + help_text='How many pages to fetch?' + ) + feedly_stream = models.TextField( + help_text='Stream ID to fetch', blank=True) + class Meta: + verbose_name = 'Feedly' + +@receiver(pre_save, sender=FeedlySettings) +def handle_save_settings(sender, instance, *args, **kwargs): + if instance.feedly_stream and instance.feedly_auth: + entries = [] + url = 'https://cloud.feedly.com/v3/streams/contents?streamId=' + url = url + instance.feedly_stream + headers = { + 'Authorization': 'OAuth '+instance.feedly_auth + } + contents = requests.get(url, headers=headers).json() + if 'errorMessage' in contents: + raise PermissionError(contents['errorMessage']) + for raw_entry in contents['items']: + entry = Entry(raw_entry) + entries.append(entry) + print(json.dumps(entries)) diff --git a/publichealth/feedler/models/models.py b/publichealth/feedler/models/models.py new file mode 100644 index 0000000..223428a --- /dev/null +++ b/publichealth/feedler/models/models.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime + +from django.db import models + +class EntryCategory(models.Model): + """A structure for sorting through entry models + """ + label = models.CharField(max_length=255) + feedly_id = models.CharField(max_length=200) + +class Entry(models.Model): + """Implementation of the Entry from the feedly API as generic Django model + """ + raw = models.TextField(blank=True) + + updated = models.DateTimeField(auto_now=True) + published = models.DateTimeField(auto_now_add=True) + entry_id = models.IntegerField(blank=True) + + title = models.CharField(max_length=255) + origin_title = models.CharField(max_length=255, blank=True) + link = models.URLField() + visual = models.URLField(blank=True) + + content = models.TextField() + tags = models.TextField(blank=True) + + categories = models.ManyToManyField(EntryCategory, blank=True) + + class Meta: + verbose_name_plural = 'Entries' + + def _buildInstance(self, raw): + """ + Parse the raw JSON implementation from the Feedly API + """ + self.raw = raw + + self.published = datetime.utcfromtimestamp(raw['published']) + self.entry_id = raw['id'] + + self.title = raw['title'] + self.origin_title = raw['origin']['title'] + self.link = raw['alternate'][0]['href'] + self.visual = raw['visual']['url'] + + self._buildContent() + + # if 'categories' in raw: + # self.categories = raw['categories'] + # else: + # self.categories = [] + + def _buildContent(self): + # Collect text content + if 'content' in self.raw: + self.content = self.raw['content'] + else: + if 'summary' in self.raw: + self.content = self.raw['summary'] + else: + self.content = '' + # Collect tags + tags = [] + for tag in self.raw['tags']: + if 'label' in tag: + tags.push(tag['label'].replace(',','-')) + self.tags = ','.join('tags') diff --git a/publichealth/feedler/tests.py b/publichealth/feedler/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/publichealth/feedler/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/publichealth/feedler/views.py b/publichealth/feedler/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/publichealth/feedler/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/publichealth/feedler/wagtail_hooks.py b/publichealth/feedler/wagtail_hooks.py new file mode 100644 index 0000000..8d1797a --- /dev/null +++ b/publichealth/feedler/wagtail_hooks.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from wagtail.contrib.modeladmin.options import ( + ModelAdmin, modeladmin_register) + +from .models import Entry + +class EntryModelAdmin(ModelAdmin): + model = Entry + menu_icon = 'date' + menu_order = 200 + add_to_settings_menu = False + exclude_from_explorer = True + list_display = ('updated', 'title', 'origin_title', 'tags') + # list_filter = ('origin_title') + # search_fields = ('title', 'origin_title', 'content', 'tags') + +modeladmin_register(EntryModelAdmin) From a4e784556028692cf56ed14caa25705e40b27014 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 16:01:44 +0200 Subject: [PATCH 06/25] Complete initial Feedly engine --- .../feedler/migrations/0001_initial.py | 40 +++++++----- publichealth/feedler/models/admin.py | 47 +++++++++----- publichealth/feedler/models/models.py | 62 +++++++++++-------- publichealth/feedler/wagtail_hooks.py | 18 ++++-- 4 files changed, 106 insertions(+), 61 deletions(-) diff --git a/publichealth/feedler/migrations/0001_initial.py b/publichealth/feedler/migrations/0001_initial.py index 9fb3abd..5a43b33 100644 --- a/publichealth/feedler/migrations/0001_initial.py +++ b/publichealth/feedler/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.3 on 2017-07-03 11:24 +# Generated by Django 1.11.3 on 2017-07-03 13:46 from __future__ import unicode_literals from django.db import migrations, models @@ -19,12 +19,12 @@ class Migration(migrations.Migration): name='Entry', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('raw', models.TextField(blank=True)), + ('raw', models.TextField(blank=True, editable=False)), ('updated', models.DateTimeField(auto_now=True)), ('published', models.DateTimeField(auto_now_add=True)), - ('entry_id', models.IntegerField(blank=True)), + ('entry_id', models.CharField(blank=True, editable=False, max_length=255, unique=True)), ('title', models.CharField(max_length=255)), - ('origin_title', models.CharField(blank=True, max_length=255)), + ('author', models.CharField(blank=True, max_length=255)), ('link', models.URLField()), ('visual', models.URLField(blank=True)), ('content', models.TextField()), @@ -34,30 +34,38 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'Entries', }, ), - migrations.CreateModel( - name='EntryCategory', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('label', models.CharField(max_length=255)), - ('feedly_id', models.CharField(max_length=200)), - ], - ), migrations.CreateModel( name='FeedlySettings', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('feedly_auth', models.TextField(blank=True, help_text='Your developer authorization key')), ('feedly_pages', models.IntegerField(blank=True, choices=[(1, '2'), (2, '5'), (3, '10'), (4, '50')], help_text='How many pages to fetch?', null=True)), - ('feedly_stream', models.TextField(blank=True, help_text='Stream ID to fetch')), - ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')), ], options={ 'verbose_name': 'Feedly', }, ), + migrations.CreateModel( + name='Stream', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255)), + ('ident', models.CharField(max_length=255)), + ], + ), + migrations.AddField( + model_name='feedlysettings', + name='feedly_stream', + field=models.ManyToManyField(to='feedler.Stream'), + ), + migrations.AddField( + model_name='feedlysettings', + name='site', + field=models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site'), + ), migrations.AddField( model_name='entry', - name='categories', - field=models.ManyToManyField(blank=True, to='feedler.EntryCategory'), + name='stream', + field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='feedler.Stream', verbose_name='Original stream'), ), ] diff --git a/publichealth/feedler/models/admin.py b/publichealth/feedler/models/admin.py index ed64f50..43c0298 100644 --- a/publichealth/feedler/models/admin.py +++ b/publichealth/feedler/models/admin.py @@ -11,6 +11,11 @@ from django.core.mail import send_mail from wagtail.contrib.settings.models import BaseSetting, register_setting +from .models import Entry, Stream + +import logging +logger = logging.getLogger('feedler') + # Feedly integration module @register_setting @@ -26,24 +31,34 @@ class FeedlySettings(BaseSetting): ), blank=True, null=True, help_text='How many pages to fetch?' ) - feedly_stream = models.TextField( - help_text='Stream ID to fetch', blank=True) + feedly_stream = models.ManyToManyField(Stream) class Meta: verbose_name = 'Feedly' +API_BASEURL = 'https://cloud.feedly.com/v3/streams/contents?streamId=' + @receiver(pre_save, sender=FeedlySettings) def handle_save_settings(sender, instance, *args, **kwargs): - if instance.feedly_stream and instance.feedly_auth: - entries = [] - url = 'https://cloud.feedly.com/v3/streams/contents?streamId=' - url = url + instance.feedly_stream - headers = { - 'Authorization': 'OAuth '+instance.feedly_auth - } - contents = requests.get(url, headers=headers).json() - if 'errorMessage' in contents: - raise PermissionError(contents['errorMessage']) - for raw_entry in contents['items']: - entry = Entry(raw_entry) - entries.append(entry) - print(json.dumps(entries)) + if instance.feedly_auth: + streams = instance.feedly_stream.all() + for stream in streams: + # Start a request to download the feed + logger.info("Processing stream %s" % stream.title) + url = API_BASEURL + stream.ident + headers = { + 'Authorization': 'OAuth ' + instance.feedly_auth + } + contents = requests.get(url, headers=headers).json() + if 'errorMessage' in contents: + raise PermissionError(contents['errorMessage']) + for raw_entry in contents['items']: + eid = raw_entry['id'] + # Create or update data + try: + entry = Entry.objects.get(entry_id=eid) + logger.info("Updating entry '%s'" % eid) + except Entry.DoesNotExist: + logger.info("Adding entry '%s'" % eid) + entry = Entry() + entry.parse(raw_entry, stream) + entry.save() diff --git a/publichealth/feedler/models/models.py b/publichealth/feedler/models/models.py index 223428a..7d2da8f 100644 --- a/publichealth/feedler/models/models.py +++ b/publichealth/feedler/models/models.py @@ -4,67 +4,79 @@ from datetime import datetime from django.db import models -class EntryCategory(models.Model): - """A structure for sorting through entry models - """ - label = models.CharField(max_length=255) - feedly_id = models.CharField(max_length=200) +class Stream(models.Model): + title = models.CharField(max_length=255) + ident = models.CharField(max_length=255) + + def __str__(self): + return self.title class Entry(models.Model): """Implementation of the Entry from the feedly API as generic Django model """ - raw = models.TextField(blank=True) - - updated = models.DateTimeField(auto_now=True) - published = models.DateTimeField(auto_now_add=True) - entry_id = models.IntegerField(blank=True) + raw = models.TextField(blank=True, editable=False) + updated = models.DateTimeField(auto_now=True, editable=False) + published = models.DateTimeField(auto_now_add=True, editable=False) + entry_id = models.CharField(max_length=255, unique=True, blank=True, editable=False) title = models.CharField(max_length=255) - origin_title = models.CharField(max_length=255, blank=True) + author = models.CharField(max_length=255, blank=True) link = models.URLField() visual = models.URLField(blank=True) content = models.TextField() tags = models.TextField(blank=True) - categories = models.ManyToManyField(EntryCategory, blank=True) + stream = models.ForeignKey(Stream, + blank=True, on_delete=models.CASCADE, + verbose_name='Original stream') class Meta: verbose_name_plural = 'Entries' - def _buildInstance(self, raw): + def parse(self, raw, stream): """ Parse the raw JSON implementation from the Feedly API """ self.raw = raw + self.stream = stream - self.published = datetime.utcfromtimestamp(raw['published']) + ts = raw['published'] / 1000 + self.published = datetime.utcfromtimestamp(ts) self.entry_id = raw['id'] self.title = raw['title'] - self.origin_title = raw['origin']['title'] - self.link = raw['alternate'][0]['href'] - self.visual = raw['visual']['url'] + + if 'author' in raw['origin']: + self.author = raw['author'] + elif 'title' in raw['origin']: + self.author = raw['origin']['title'] + + if len(raw['alternate']) > 0: + self.link = raw['alternate'][0]['href'] + if 'visual' in raw and 'url' in raw['visual']: + self.visual = raw['visual']['url'] self._buildContent() - # if 'categories' in raw: - # self.categories = raw['categories'] - # else: - # self.categories = [] - def _buildContent(self): # Collect text content if 'content' in self.raw: self.content = self.raw['content'] else: if 'summary' in self.raw: - self.content = self.raw['summary'] + if 'content' in self.raw['summary']: + self.content = self.raw['summary']['content'] + else: + self.content = self.raw['summary'] else: self.content = '' # Collect tags tags = [] for tag in self.raw['tags']: if 'label' in tag: - tags.push(tag['label'].replace(',','-')) - self.tags = ','.join('tags') + label = tag['label'].replace(',','-') + label = label.strip().lower() + if len(label) > 3 and not label in tags: + tags.append(label) + self.tags = ','.join(tags) diff --git a/publichealth/feedler/wagtail_hooks.py b/publichealth/feedler/wagtail_hooks.py index 8d1797a..dea8ddc 100644 --- a/publichealth/feedler/wagtail_hooks.py +++ b/publichealth/feedler/wagtail_hooks.py @@ -3,7 +3,7 @@ from wagtail.contrib.modeladmin.options import ( ModelAdmin, modeladmin_register) -from .models import Entry +from .models import Entry, Stream class EntryModelAdmin(ModelAdmin): model = Entry @@ -11,8 +11,18 @@ class EntryModelAdmin(ModelAdmin): menu_order = 200 add_to_settings_menu = False exclude_from_explorer = True - list_display = ('updated', 'title', 'origin_title', 'tags') - # list_filter = ('origin_title') - # search_fields = ('title', 'origin_title', 'content', 'tags') + list_display = ('updated', 'title', 'author', 'tags') + list_filter = ('author', 'tags') + search_fields = ('title', 'author', 'content', 'tags') modeladmin_register(EntryModelAdmin) + +class StreamModelAdmin(ModelAdmin): + model = Stream + menu_icon = 'date' + menu_order = 1000 + add_to_settings_menu = True + exclude_from_explorer = True + list_display = ('title', 'ident') + +modeladmin_register(StreamModelAdmin) From 341ccb35c565c401ed390f48f6903042821de3b9 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 16:23:20 +0200 Subject: [PATCH 07/25] Moved Feedler to separate app --- feedler/__init__.py | 18 ++++++++++++++++++ {publichealth/feedler => feedler}/apps.py | 0 .../migrations/0001_initial.py | 0 .../migrations}/__init__.py | 0 .../feedler => feedler}/models/__init__.py | 0 .../feedler => feedler}/models/admin.py | 0 .../feedler => feedler}/models/models.py | 0 feedler/routes.py | 19 +++++++++++++++++++ {publichealth/feedler => feedler}/tests.py | 0 {publichealth/feedler => feedler}/views.py | 0 .../feedler => feedler}/wagtail_hooks.py | 2 +- publichealth/feedler/migrations/__init__.py | 0 publichealth/home/models/models.py | 2 +- publichealth/settings/base.py | 2 +- tests/testapp/settings.py | 2 ++ 15 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 feedler/__init__.py rename {publichealth/feedler => feedler}/apps.py (100%) rename {publichealth/feedler => feedler}/migrations/0001_initial.py (100%) rename {publichealth/feedler => feedler/migrations}/__init__.py (100%) rename {publichealth/feedler => feedler}/models/__init__.py (100%) rename {publichealth/feedler => feedler}/models/admin.py (100%) rename {publichealth/feedler => feedler}/models/models.py (100%) create mode 100644 feedler/routes.py rename {publichealth/feedler => feedler}/tests.py (100%) rename {publichealth/feedler => feedler}/views.py (100%) rename {publichealth/feedler => feedler}/wagtail_hooks.py (91%) delete mode 100644 publichealth/feedler/migrations/__init__.py diff --git a/feedler/__init__.py b/feedler/__init__.py new file mode 100644 index 0000000..4037eda --- /dev/null +++ b/feedler/__init__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +__author__ = 'Oleg Lavrovsky' +__email__ = 'oleg@datalets.ch' +__version__ = '0.1' + +FEEDLER_APPS = ( + # Wagtail apps + 'wagtail.wagtailcore', + 'wagtail.wagtailadmin', + 'wagtail.contrib.wagtailroutablepage', + 'wagtail.contrib.modeladmin', + + # Third-party apps + + # My apps + 'feedler', +) diff --git a/publichealth/feedler/apps.py b/feedler/apps.py similarity index 100% rename from publichealth/feedler/apps.py rename to feedler/apps.py diff --git a/publichealth/feedler/migrations/0001_initial.py b/feedler/migrations/0001_initial.py similarity index 100% rename from publichealth/feedler/migrations/0001_initial.py rename to feedler/migrations/0001_initial.py diff --git a/publichealth/feedler/__init__.py b/feedler/migrations/__init__.py similarity index 100% rename from publichealth/feedler/__init__.py rename to feedler/migrations/__init__.py diff --git a/publichealth/feedler/models/__init__.py b/feedler/models/__init__.py similarity index 100% rename from publichealth/feedler/models/__init__.py rename to feedler/models/__init__.py diff --git a/publichealth/feedler/models/admin.py b/feedler/models/admin.py similarity index 100% rename from publichealth/feedler/models/admin.py rename to feedler/models/admin.py diff --git a/publichealth/feedler/models/models.py b/feedler/models/models.py similarity index 100% rename from publichealth/feedler/models/models.py rename to feedler/models/models.py diff --git a/feedler/routes.py b/feedler/routes.py new file mode 100644 index 0000000..aa2e5bc --- /dev/null +++ b/feedler/routes.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from datetime import date + +from django.utils.dateformat import DateFormat +from django.utils.formats import date_format +from django.utils.translation import ugettext_lazy as _ +from django.conf import settings + +from wagtail.contrib.wagtailroutablepage.models import RoutablePageMixin, route +from wagtail.wagtailcore.models import Page +from wagtail.wagtailsearch.models import Query + +class FeedlerRoutes(RoutablePageMixin): + + @route(r'^$') + def entries_list(self, request, *args, **kwargs): + self.entries = self.get_entries() + return Page.serve(self, request, *args, **kwargs) diff --git a/publichealth/feedler/tests.py b/feedler/tests.py similarity index 100% rename from publichealth/feedler/tests.py rename to feedler/tests.py diff --git a/publichealth/feedler/views.py b/feedler/views.py similarity index 100% rename from publichealth/feedler/views.py rename to feedler/views.py diff --git a/publichealth/feedler/wagtail_hooks.py b/feedler/wagtail_hooks.py similarity index 91% rename from publichealth/feedler/wagtail_hooks.py rename to feedler/wagtail_hooks.py index dea8ddc..ed8abd5 100644 --- a/publichealth/feedler/wagtail_hooks.py +++ b/feedler/wagtail_hooks.py @@ -11,7 +11,7 @@ class EntryModelAdmin(ModelAdmin): menu_order = 200 add_to_settings_menu = False exclude_from_explorer = True - list_display = ('updated', 'title', 'author', 'tags') + list_display = ('published', 'title', 'author', 'tags') list_filter = ('author', 'tags') search_fields = ('title', 'author', 'content', 'tags') diff --git a/publichealth/feedler/migrations/__init__.py b/publichealth/feedler/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/publichealth/home/models/models.py b/publichealth/home/models/models.py index 19681db..5ad3f7b 100644 --- a/publichealth/home/models/models.py +++ b/publichealth/home/models/models.py @@ -15,7 +15,7 @@ from wagtail.wagtailimages.blocks import ImageChooserBlock from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from wagtail.wagtailsearch import index -from puput.models import EntryPage, BlogPage +# from puput.models import EntryPage, BlogPage from ..util import TranslatedField diff --git a/publichealth/settings/base.py b/publichealth/settings/base.py index 8c2aff4..fb02160 100644 --- a/publichealth/settings/base.py +++ b/publichealth/settings/base.py @@ -23,7 +23,6 @@ BASE_DIR = os.path.dirname(PROJECT_DIR) INSTALLED_APPS = [ 'publichealth.home', 'publichealth.home.templatetags', - 'publichealth.feedler', 'publichealth.search', 'wagtail.contrib.wagtailsearchpromotions', @@ -48,6 +47,7 @@ INSTALLED_APPS = [ 'taggit', 'puput', 'anymail', + 'feedler', 'django.contrib.admin', 'django.contrib.auth', diff --git a/tests/testapp/settings.py b/tests/testapp/settings.py index 91bc60b..603a7f6 100644 --- a/tests/testapp/settings.py +++ b/tests/testapp/settings.py @@ -1,5 +1,6 @@ import os from puput import PUPUT_APPS +from feedler import FEEDLER_APPS WAGTAIL_SITE_NAME = 'Public Health' BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -16,6 +17,7 @@ INSTALLED_APPS = ( 'django.contrib.staticfiles', ) INSTALLED_APPS += PUPUT_APPS +INSTALLED_APPS += FEEDLER_APPS MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', From aa5215d6034a9575e6a3f1cfa49ed9c7ad3f0ea1 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 16:42:27 +0200 Subject: [PATCH 08/25] Feedler APIv2 integrated --- feedler/__init__.py | 4 +++- feedler/api.py | 7 +++++++ feedler/endpoints.py | 26 ++++++++++++++++++++++++++ feedler/routes.py | 19 ------------------- feedler/urls.py | 10 ++++++++++ feedler/views.py | 3 --- publichealth/settings/base.py | 3 +++ publichealth/urls.py | 5 ++++- 8 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 feedler/api.py create mode 100644 feedler/endpoints.py delete mode 100644 feedler/routes.py create mode 100644 feedler/urls.py delete mode 100644 feedler/views.py diff --git a/feedler/__init__.py b/feedler/__init__.py index 4037eda..a522364 100644 --- a/feedler/__init__.py +++ b/feedler/__init__.py @@ -8,10 +8,12 @@ FEEDLER_APPS = ( # Wagtail apps 'wagtail.wagtailcore', 'wagtail.wagtailadmin', - 'wagtail.contrib.wagtailroutablepage', 'wagtail.contrib.modeladmin', + 'wagtail.contrib.wagtailroutablepage', + 'wagtail.api.v2', # Third-party apps + 'rest_framework', # My apps 'feedler', diff --git a/feedler/api.py b/feedler/api.py new file mode 100644 index 0000000..9dbcffc --- /dev/null +++ b/feedler/api.py @@ -0,0 +1,7 @@ +from wagtail.api.v2.router import WagtailAPIRouter +from .endpoints import EntriesAPIEndpoint + +# Create the router. "wagtailapi" is the URL namespace +api_router = WagtailAPIRouter('wagtailapi') + +api_router.register_endpoint('entries', EntriesAPIEndpoint) diff --git a/feedler/endpoints.py b/feedler/endpoints.py new file mode 100644 index 0000000..3bafe75 --- /dev/null +++ b/feedler/endpoints.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +from wagtail.contrib.wagtailapi.endpoints import BaseAPIEndpoint +from wagtail.contrib.wagtailapi.serializers import BaseSerializer +from wagtail.contrib.wagtailapi.filters import FieldsFilter, OrderingFilter, SearchFilter +from wagtail.contrib.wagtailapi.pagination import WagtailPagination + +from .models import Entry + +class EntrySerializer(BaseSerializer): + pass + +class EntriesAPIEndpoint(BaseAPIEndpoint): + base_serializer_class = EntrySerializer + filter_backends = [FieldsFilter, OrderingFilter, SearchFilter] + extra_api_fields = [ + 'title', + 'author', + 'link', + 'visual', + 'content', + 'tags', + 'published', + ] + name = 'entries' + model = Entry diff --git a/feedler/routes.py b/feedler/routes.py deleted file mode 100644 index aa2e5bc..0000000 --- a/feedler/routes.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- - -from datetime import date - -from django.utils.dateformat import DateFormat -from django.utils.formats import date_format -from django.utils.translation import ugettext_lazy as _ -from django.conf import settings - -from wagtail.contrib.wagtailroutablepage.models import RoutablePageMixin, route -from wagtail.wagtailcore.models import Page -from wagtail.wagtailsearch.models import Query - -class FeedlerRoutes(RoutablePageMixin): - - @route(r'^$') - def entries_list(self, request, *args, **kwargs): - self.entries = self.get_entries() - return Page.serve(self, request, *args, **kwargs) diff --git a/feedler/urls.py b/feedler/urls.py new file mode 100644 index 0000000..5289b17 --- /dev/null +++ b/feedler/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls import include, url +from django.conf import settings +from django.contrib import admin +from django.conf.urls.i18n import i18n_patterns + +from .api import api_router + +urlpatterns = [ + url(r'^api/v2/', api_router.urls), +] diff --git a/feedler/views.py b/feedler/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/feedler/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/publichealth/settings/base.py b/publichealth/settings/base.py index fb02160..13a16c0 100644 --- a/publichealth/settings/base.py +++ b/publichealth/settings/base.py @@ -42,6 +42,9 @@ INSTALLED_APPS = [ 'wagtail.wagtailadmin', 'wagtail.wagtailcore', + 'wagtail.api.v2', + 'rest_framework', + 'modelcluster', 'compressor', 'taggit', diff --git a/publichealth/urls.py b/publichealth/urls.py index 1b7d87a..b7d6cbb 100644 --- a/publichealth/urls.py +++ b/publichealth/urls.py @@ -6,13 +6,16 @@ from django.conf.urls.i18n import i18n_patterns from wagtail.wagtailadmin import urls as wagtailadmin_urls from wagtail.wagtaildocs import urls as wagtaildocs_urls from wagtail.wagtailcore import urls as wagtail_urls + from puput import urls as puput_urls +from feedler import urls as feedler_urls from publichealth.search import views as search_views - urlpatterns = [ url(r'', include(puput_urls)), + url(r'', include(feedler_urls)), + url(r'^django-admin/', include(admin.site.urls)), url(r'^admin/', include(wagtailadmin_urls)), From 9bd87050f2fddae2b0dbadfea6b2069210b5beec Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 16:49:12 +0200 Subject: [PATCH 09/25] Renamed newsfeed to blogentries in home page --- publichealth/home/models/models.py | 4 ++-- publichealth/home/templates/news.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/publichealth/home/models/models.py b/publichealth/home/models/models.py index 5ad3f7b..8741a98 100644 --- a/publichealth/home/models/models.py +++ b/publichealth/home/models/models.py @@ -212,7 +212,7 @@ class HomePage(Page): return articles[:4] @property - def newsfeed(self): + def blogentries(self): # Get list of latest news curlang = translation.get_language() if not curlang in ['de', 'fr']: curlang = 'de' # Default language @@ -227,7 +227,7 @@ class HomePage(Page): # Update template context context = super(HomePage, self).get_context(request) context['featured'] = self.featured - context['newsfeed'] = self.newsfeed + context['blogentries'] = self.blogentries return context parent_page_types = ['wagtailcore.Page'] diff --git a/publichealth/home/templates/news.html b/publichealth/home/templates/news.html index bdd951a..966e472 100644 --- a/publichealth/home/templates/news.html +++ b/publichealth/home/templates/news.html @@ -3,7 +3,7 @@
- {% for entry in newsfeed %} + {% for entry in blogentries %}
{% if entry.header_image %} From 11bcb50f72f25c51cc3128dfc27901ea55570274 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:04:09 +0200 Subject: [PATCH 10/25] 6 posts on homepage, not 4 --- publichealth/home/models/models.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/publichealth/home/models/models.py b/publichealth/home/models/models.py index 8741a98..758cd26 100644 --- a/publichealth/home/models/models.py +++ b/publichealth/home/models/models.py @@ -62,6 +62,10 @@ class ArticleIndexPage(Page): context['subcategories'] = subcategories return context + parent_page_types = [ + 'home.ArticleIndexPage', + 'home.HomePage' + ] subpage_types = [ 'home.ArticlePage', 'home.ArticleIndexPage', @@ -145,6 +149,10 @@ class ArticlePage(Page): MultiFieldPanel(Page.promote_panels, "Einstellungen"), ] + parent_page_types = [ + 'home.ArticleIndexPage', + 'home.HomePage' + ] subpage_types = [] class Meta: verbose_name = "Artikel" @@ -221,7 +229,7 @@ class HomePage(Page): entries = EntryPage.objects.live().descendant_of(parent[0]) # Order by most recent date first entries = entries.order_by('-date') - return entries[:4] + return entries[:6] def get_context(self, request): # Update template context From b0c1d6ba1e3fcd59bbfa308e6c0ffd192abc3753 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:21:31 +0200 Subject: [PATCH 11/25] Feed page module --- feedler/migrations/0002_feedpage.py | 30 +++++++++++++++ feedler/models/models.py | 34 +++++++++++++++++ feedler/templates/feedler/feed_page.html | 47 ++++++++++++++++++++++++ publichealth/home/templates/news.html | 2 +- 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 feedler/migrations/0002_feedpage.py create mode 100644 feedler/templates/feedler/feed_page.html diff --git a/feedler/migrations/0002_feedpage.py b/feedler/migrations/0002_feedpage.py new file mode 100644 index 0000000..10b3449 --- /dev/null +++ b/feedler/migrations/0002_feedpage.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.3 on 2017-07-03 15:21 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import wagtail.wagtailcore.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailcore', '0040_merge_20170703_1238'), + ('feedler', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='FeedPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), + ('intro', wagtail.wagtailcore.fields.RichTextField(blank=True, default='')), + ('stream', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='feedler.Stream', verbose_name='Filter to stream (optional)')), + ], + options={ + 'verbose_name': 'Feeds', + }, + bases=('wagtailcore.page',), + ), + ] diff --git a/feedler/models/models.py b/feedler/models/models.py index 7d2da8f..47b3e4e 100644 --- a/feedler/models/models.py +++ b/feedler/models/models.py @@ -4,6 +4,10 @@ from datetime import datetime from django.db import models +from wagtail.wagtailcore.models import Page, Orderable +from wagtail.wagtailadmin.edit_handlers import FieldPanel +from wagtail.wagtailcore.fields import RichTextField + class Stream(models.Model): title = models.CharField(max_length=255) ident = models.CharField(max_length=255) @@ -80,3 +84,33 @@ class Entry(models.Model): if len(label) > 3 and not label in tags: tags.append(label) self.tags = ','.join(tags) + +class FeedPage(Page): + intro = RichTextField(default='', blank=True) + stream = models.ForeignKey(Stream, on_delete=models.PROTECT, + null=True, blank=True, verbose_name='Filter to stream (optional)') + + content_panels = [ + FieldPanel('title'), + FieldPanel('intro'), + FieldPanel('stream'), + ] + + @property + def feedentries(self): + if self.stream: + entries = Entry.objects.filter(stream=self.stream) + else: + entries = Entry.objects.all() + # Order by most recent date first + entries = entries.order_by('-published') + return entries[:10] + + def get_context(self, request): + # Update template context + context = super(FeedPage, self).get_context(request) + context['feedentries'] = self.feedentries + return context + + class Meta: + verbose_name = "Feeds" diff --git a/feedler/templates/feedler/feed_page.html b/feedler/templates/feedler/feed_page.html new file mode 100644 index 0000000..04a9421 --- /dev/null +++ b/feedler/templates/feedler/feed_page.html @@ -0,0 +1,47 @@ +{% extends "base.html" %} +{% load static wagtailcore_tags %} + +{% block body_class %}template-{{ self.get_verbose_name|slugify }}{% endblock %} + +{% block extra_css %} +{% endblock %} + +{% block title %}Feeds{% endblock %} + +{% block content %} + +
+
+

{{ page.title }}

+

{{ page.intro|richtext }}

+
+
+ + +
+
+
+ {% for entry in feedentries %} +
+
+ {% if entry.visual %} + + {% endif %} +
+

{{ entry.title }}

+

+ {{ entry.content|striptags|truncatewords_html:40 }} +
{{ entry.author }} +

+
+ +
+
+ {% empty %} + + {% endfor %} +
+
+
+ +{% endblock %} diff --git a/publichealth/home/templates/news.html b/publichealth/home/templates/news.html index 966e472..91bbb18 100644 --- a/publichealth/home/templates/news.html +++ b/publichealth/home/templates/news.html @@ -15,7 +15,7 @@ {% if entry.excerpt %} {{ entry.excerpt|striptags }} {% else %} - {{ entry.body|striptags|truncatewords_html:70 }} + {{ entry.body|striptags|truncatewords_html:40 }} {% endif %}

Mehr erfahren From 0eaa04b86f2de6c0d6bb675c26cf81da90bbaca9 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:27:35 +0200 Subject: [PATCH 12/25] Remove initial dependency --- feedler/migrations/0001_initial.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/feedler/migrations/0001_initial.py b/feedler/migrations/0001_initial.py index 5a43b33..bad00d8 100644 --- a/feedler/migrations/0001_initial.py +++ b/feedler/migrations/0001_initial.py @@ -10,10 +10,6 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ('wagtailcore', '0040_merge_20170703_1238'), - ] - operations = [ migrations.CreateModel( name='Entry', From defa3cdd3b044c1368f51ba52cf1772a2bf9f58c Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:28:02 +0200 Subject: [PATCH 13/25] Remove initial dependency --- feedler/migrations/0002_feedpage.py | 1 - 1 file changed, 1 deletion(-) diff --git a/feedler/migrations/0002_feedpage.py b/feedler/migrations/0002_feedpage.py index 10b3449..63263d4 100644 --- a/feedler/migrations/0002_feedpage.py +++ b/feedler/migrations/0002_feedpage.py @@ -10,7 +10,6 @@ import wagtail.wagtailcore.fields class Migration(migrations.Migration): dependencies = [ - ('wagtailcore', '0040_merge_20170703_1238'), ('feedler', '0001_initial'), ] From fefbc90175545febca9b144ced424b8895441273 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:36:00 +0200 Subject: [PATCH 14/25] Remove initial dependency --- feedler/migrations/0001_initial.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/feedler/migrations/0001_initial.py b/feedler/migrations/0001_initial.py index bad00d8..bf8fadc 100644 --- a/feedler/migrations/0001_initial.py +++ b/feedler/migrations/0001_initial.py @@ -10,6 +10,10 @@ class Migration(migrations.Migration): initial = True + dependencies = [ + ('wagtailcore', '__latest__'), + ] + operations = [ migrations.CreateModel( name='Entry', From 4869a79b43f1dfef3d03a22cff58c8e5899ed1e4 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:38:13 +0200 Subject: [PATCH 15/25] Restore puput models --- publichealth/home/models/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/publichealth/home/models/models.py b/publichealth/home/models/models.py index 758cd26..af3aae1 100644 --- a/publichealth/home/models/models.py +++ b/publichealth/home/models/models.py @@ -15,7 +15,7 @@ from wagtail.wagtailimages.blocks import ImageChooserBlock from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from wagtail.wagtailsearch import index -# from puput.models import EntryPage, BlogPage +from puput.models import EntryPage, BlogPage from ..util import TranslatedField From dd52f23d543213e03b36b1fda3fad133eebdbbd7 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:47:56 +0200 Subject: [PATCH 16/25] trans_title optional --- publichealth/home/templatetags/navigation.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/publichealth/home/templatetags/navigation.py b/publichealth/home/templatetags/navigation.py index 7ea6d0f..a3c9c22 100644 --- a/publichealth/home/templatetags/navigation.py +++ b/publichealth/home/templatetags/navigation.py @@ -39,7 +39,8 @@ def top_menu(context, parent, calling_page=None): menuitem.show_dropdown = has_menu_children(menuitem) menuitem.active = (calling_page.url.startswith(menuitem.url) if calling_page else False) - menuitem.title = menuitem.trans_title + if 'trans_title' in menuitem: + menuitem.title = menuitem.trans_title return { 'calling_page': calling_page, 'menuitems': menuitems, @@ -49,7 +50,8 @@ def top_menu(context, parent, calling_page=None): def menuitems_children(parent): menuitems_children = parent.get_children().live().in_menu().specific() for menuitem in menuitems_children: - menuitem.title = menuitem.trans_title + if 'trans_title' in menuitem: + menuitem.title = menuitem.trans_title return menuitems_children # Retrieves the children of the top menu items for the drop downs From 48d40eefa633281f9c6f545f4b74902b721c4904 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:49:40 +0200 Subject: [PATCH 17/25] trans_title optional --- publichealth/home/templatetags/navigation.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/publichealth/home/templatetags/navigation.py b/publichealth/home/templatetags/navigation.py index a3c9c22..78e1ebd 100644 --- a/publichealth/home/templatetags/navigation.py +++ b/publichealth/home/templatetags/navigation.py @@ -39,8 +39,10 @@ def top_menu(context, parent, calling_page=None): menuitem.show_dropdown = has_menu_children(menuitem) menuitem.active = (calling_page.url.startswith(menuitem.url) if calling_page else False) - if 'trans_title' in menuitem: + try: menuitem.title = menuitem.trans_title + except AttributeError: + pass return { 'calling_page': calling_page, 'menuitems': menuitems, @@ -50,8 +52,10 @@ def top_menu(context, parent, calling_page=None): def menuitems_children(parent): menuitems_children = parent.get_children().live().in_menu().specific() for menuitem in menuitems_children: - if 'trans_title' in menuitem: + try: menuitem.title = menuitem.trans_title + except AttributeError: + pass return menuitems_children # Retrieves the children of the top menu items for the drop downs From 616a33bc9bb93cb61d8078634a994266dd21b9ae Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:53:03 +0200 Subject: [PATCH 18/25] Diagnose with raw --- feedler/templates/feedler/feed_page.html | 1 + 1 file changed, 1 insertion(+) diff --git a/feedler/templates/feedler/feed_page.html b/feedler/templates/feedler/feed_page.html index 04a9421..392be5c 100644 --- a/feedler/templates/feedler/feed_page.html +++ b/feedler/templates/feedler/feed_page.html @@ -37,6 +37,7 @@
+ {% empty %} {% endfor %} From e805e16fca65bd0b56bbe7231a8d26b4cbfa947d Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:56:40 +0200 Subject: [PATCH 19/25] Add image support --- feedler/models/models.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/feedler/models/models.py b/feedler/models/models.py index 47b3e4e..330369f 100644 --- a/feedler/models/models.py +++ b/feedler/models/models.py @@ -58,7 +58,11 @@ class Entry(models.Model): if len(raw['alternate']) > 0: self.link = raw['alternate'][0]['href'] - if 'visual' in raw and 'url' in raw['visual']: + + if 'enclosure' in raw and len(raw['enclosure']) > 0: + if 'href' in raw['enclosure'][0]: + self.visual = raw['enclosure'][0]['href'] + elif 'visual' in raw and 'url' in raw['visual']: self.visual = raw['visual']['url'] self._buildContent() From fd14271ca864eeec484a3a33aeb3d03958519261 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Mon, 3 Jul 2017 17:59:42 +0200 Subject: [PATCH 20/25] Even more image parsing --- feedler/models/models.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/feedler/models/models.py b/feedler/models/models.py index 330369f..9dea969 100644 --- a/feedler/models/models.py +++ b/feedler/models/models.py @@ -59,7 +59,10 @@ class Entry(models.Model): if len(raw['alternate']) > 0: self.link = raw['alternate'][0]['href'] - if 'enclosure' in raw and len(raw['enclosure']) > 0: + if 'thumbnail' in raw and len(raw['thumbnail']) > 0: + if 'url' in raw['thumbnail'][0]: + self.visual = raw['thumbnail'][0]['url'] + elif 'enclosure' in raw and len(raw['enclosure']) > 0: if 'href' in raw['enclosure'][0]: self.visual = raw['enclosure'][0]['href'] elif 'visual' in raw and 'url' in raw['visual']: From d04bbbd7eee4dfb31aad537576dee89aa1ada1a1 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Tue, 4 Jul 2017 10:26:53 +0200 Subject: [PATCH 21/25] Open external links in a new window --- publichealth/static/js/main.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/publichealth/static/js/main.js b/publichealth/static/js/main.js index 1eafc03..c802387 100644 --- a/publichealth/static/js/main.js +++ b/publichealth/static/js/main.js @@ -1,5 +1,11 @@ $(document).ready(function() { + // All external links in a new window + $('a[href^="http"]').filter(function() { + return this.hostname && this.hostname !== location.hostname; + }).attr('target', '_blank'); + + // Initialise front page carousel component $('.carousel-inner.slick').slick({ autoplay: true, autoplaySpeed: '10000', From 3796ae158189064953638d90831e83f0b7e6ded9 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Tue, 4 Jul 2017 10:28:38 +0200 Subject: [PATCH 22/25] Allow Page subpage in ArticleIndex --- publichealth/home/models/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/publichealth/home/models/models.py b/publichealth/home/models/models.py index af3aae1..7283987 100644 --- a/publichealth/home/models/models.py +++ b/publichealth/home/models/models.py @@ -69,7 +69,8 @@ class ArticleIndexPage(Page): subpage_types = [ 'home.ArticlePage', 'home.ArticleIndexPage', - 'home.ContactForm' + 'home.ContactForm', + 'wagtailcore.Page' ] class Meta: verbose_name = "Rubrik" From 9d3b278e4fc75b59a77601db5dd438bad6a00c14 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Tue, 4 Jul 2017 10:44:22 +0200 Subject: [PATCH 23/25] Refactored parser module --- feedler/feedparser.py | 58 ++++++++++++++++++++++++++++++++++++++++ feedler/models/models.py | 58 +++------------------------------------- 2 files changed, 62 insertions(+), 54 deletions(-) create mode 100644 feedler/feedparser.py diff --git a/feedler/feedparser.py b/feedler/feedparser.py new file mode 100644 index 0000000..ed83a1f --- /dev/null +++ b/feedler/feedparser.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime + +def parse(obj, raw, stream): + """ + Parse raw JSON implementation from the Feedly API + """ + obj.raw = raw + obj.stream = stream + obj.entry_id = raw['id'] + + # Date stamp handling + ts = raw['published'] / 1000 + obj.published = datetime.utcfromtimestamp(ts) + + # Authorship and title + obj.title = raw['title'] + if 'author' in raw['origin']: + obj.author = raw['author'] + elif 'title' in raw['origin']: + obj.author = raw['origin']['title'] + + # Parse links and references + if len(raw['alternate']) > 0: + obj.link = raw['alternate'][0]['href'] + if 'thumbnail' in raw and len(raw['thumbnail']) > 0: + if 'url' in raw['thumbnail'][0]: + obj.visual = raw['thumbnail'][0]['url'] + elif 'enclosure' in raw and len(raw['enclosure']) > 0: + if 'href' in raw['enclosure'][0]: + obj.visual = raw['enclosure'][0]['href'] + elif 'visual' in raw and 'url' in raw['visual']: + obj.visual = raw['visual']['url'] + + # Collect text in nested JSON content + if 'content' in obj.raw: + obj.content = obj.raw['content'] + else: + if 'summary' in obj.raw: + if 'content' in obj.raw['summary']: + obj.content = obj.raw['summary']['content'] + else: + obj.content = obj.raw['summary'] + else: + obj.content = '' + + # Collect tags + tags = [] + for tag in obj.raw['tags']: + if 'label' in tag: + label = tag['label'].replace(',','-') + label = label.strip().lower() + if len(label) > 3 and not label in tags: + tags.append(label) + obj.tags = ','.join(tags) + + return obj diff --git a/feedler/models/models.py b/feedler/models/models.py index 9dea969..bb256f9 100644 --- a/feedler/models/models.py +++ b/feedler/models/models.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -from datetime import datetime - from django.db import models from wagtail.wagtailcore.models import Page, Orderable from wagtail.wagtailadmin.edit_handlers import FieldPanel from wagtail.wagtailcore.fields import RichTextField +import feedler.feedparser as feedparser + class Stream(models.Model): title = models.CharField(max_length=255) ident = models.CharField(max_length=255) @@ -39,58 +39,8 @@ class Entry(models.Model): verbose_name_plural = 'Entries' def parse(self, raw, stream): - """ - Parse the raw JSON implementation from the Feedly API - """ - self.raw = raw - self.stream = stream - - ts = raw['published'] / 1000 - self.published = datetime.utcfromtimestamp(ts) - self.entry_id = raw['id'] - - self.title = raw['title'] - - if 'author' in raw['origin']: - self.author = raw['author'] - elif 'title' in raw['origin']: - self.author = raw['origin']['title'] - - if len(raw['alternate']) > 0: - self.link = raw['alternate'][0]['href'] - - if 'thumbnail' in raw and len(raw['thumbnail']) > 0: - if 'url' in raw['thumbnail'][0]: - self.visual = raw['thumbnail'][0]['url'] - elif 'enclosure' in raw and len(raw['enclosure']) > 0: - if 'href' in raw['enclosure'][0]: - self.visual = raw['enclosure'][0]['href'] - elif 'visual' in raw and 'url' in raw['visual']: - self.visual = raw['visual']['url'] - - self._buildContent() - - def _buildContent(self): - # Collect text content - if 'content' in self.raw: - self.content = self.raw['content'] - else: - if 'summary' in self.raw: - if 'content' in self.raw['summary']: - self.content = self.raw['summary']['content'] - else: - self.content = self.raw['summary'] - else: - self.content = '' - # Collect tags - tags = [] - for tag in self.raw['tags']: - if 'label' in tag: - label = tag['label'].replace(',','-') - label = label.strip().lower() - if len(label) > 3 and not label in tags: - tags.append(label) - self.tags = ','.join(tags) + # TODO: Exception handling + feedparser.parse(self, raw, stream) class FeedPage(Page): intro = RichTextField(default='', blank=True) From 48501957a10e5972649dfd467adb1df80d42d52f Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Tue, 4 Jul 2017 11:05:58 +0200 Subject: [PATCH 24/25] Feed parsing and formatting --- feedler/feedparser.py | 4 ++- feedler/models/admin.py | 3 +- feedler/models/models.py | 6 ---- feedler/templates/feedler/feed_page.html | 18 +++++++----- publichealth/static/css/modules/_news.scss | 33 ++++++++++++---------- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/feedler/feedparser.py b/feedler/feedparser.py index ed83a1f..bc39f98 100644 --- a/feedler/feedparser.py +++ b/feedler/feedparser.py @@ -12,7 +12,7 @@ def parse(obj, raw, stream): # Date stamp handling ts = raw['published'] / 1000 - obj.published = datetime.utcfromtimestamp(ts) + obj.published = datetime.fromtimestamp(ts) # Authorship and title obj.title = raw['title'] @@ -32,6 +32,8 @@ def parse(obj, raw, stream): obj.visual = raw['enclosure'][0]['href'] elif 'visual' in raw and 'url' in raw['visual']: obj.visual = raw['visual']['url'] + if obj.visual.lower().strip() == 'none': + obj.visual = '' # Collect text in nested JSON content if 'content' in obj.raw: diff --git a/feedler/models/admin.py b/feedler/models/admin.py index 43c0298..604b92f 100644 --- a/feedler/models/admin.py +++ b/feedler/models/admin.py @@ -12,6 +12,7 @@ from django.core.mail import send_mail from wagtail.contrib.settings.models import BaseSetting, register_setting from .models import Entry, Stream +import feedler.feedparser as feedparser import logging logger = logging.getLogger('feedler') @@ -60,5 +61,5 @@ def handle_save_settings(sender, instance, *args, **kwargs): except Entry.DoesNotExist: logger.info("Adding entry '%s'" % eid) entry = Entry() - entry.parse(raw_entry, stream) + entry = feedparser.parse(entry, raw_entry, stream) entry.save() diff --git a/feedler/models/models.py b/feedler/models/models.py index bb256f9..8777e58 100644 --- a/feedler/models/models.py +++ b/feedler/models/models.py @@ -6,8 +6,6 @@ from wagtail.wagtailcore.models import Page, Orderable from wagtail.wagtailadmin.edit_handlers import FieldPanel from wagtail.wagtailcore.fields import RichTextField -import feedler.feedparser as feedparser - class Stream(models.Model): title = models.CharField(max_length=255) ident = models.CharField(max_length=255) @@ -38,10 +36,6 @@ class Entry(models.Model): class Meta: verbose_name_plural = 'Entries' - def parse(self, raw, stream): - # TODO: Exception handling - feedparser.parse(self, raw, stream) - class FeedPage(Page): intro = RichTextField(default='', blank=True) stream = models.ForeignKey(Stream, on_delete=models.PROTECT, diff --git a/feedler/templates/feedler/feed_page.html b/feedler/templates/feedler/feed_page.html index 392be5c..df5c33e 100644 --- a/feedler/templates/feedler/feed_page.html +++ b/feedler/templates/feedler/feed_page.html @@ -13,7 +13,9 @@

{{ page.title }}

-

{{ page.intro|richtext }}

+ {% if page.intro %} +

{{ page.intro|richtext }}

+ {% endif %}
@@ -23,15 +25,17 @@
{% for entry in feedentries %}
+ {% if entry.visual %}
- {% if entry.visual %} - - {% endif %} + + {% else %} +
+ {% endif %}
-

{{ entry.title }}

+

{{ entry.title|striptags|truncatewords_html:10 }}

- {{ entry.content|striptags|truncatewords_html:40 }} -
{{ entry.author }} + {{ entry.author }}

+ {{ entry.content|striptags|truncatewords_html:25 }}

diff --git a/publichealth/static/css/modules/_news.scss b/publichealth/static/css/modules/_news.scss index ea71ef4..b3edf6a 100644 --- a/publichealth/static/css/modules/_news.scss +++ b/publichealth/static/css/modules/_news.scss @@ -2,21 +2,13 @@ // News overview #news { - .panel-default { + .panel-default, .panel-fulltext { font-size: 90%; padding-top: 75%; /* 1:1 Aspect Ratio */ position: relative; /* If you want text inside of it */ overflow: hidden; background: lighten($brand-primary, 10%); - img { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - } - .panel-body { position: absolute; top: 0; @@ -24,10 +16,7 @@ width: 100%; height: 100%; padding: 0; - transform: translateY(60%); background-color: rgba($brand-primary, .8); - transition: transform .65s; - h3, p { color: white; } @@ -58,9 +47,6 @@ text-align: center; } } - &:hover .panel-body { - transform: rotateY(0); - } // expand link over the thumbnail a.fill { @@ -72,6 +58,23 @@ font-size: 0; } } + + .panel-default { + img { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: auto; + } + .panel-body { + transform: translateY(60%); + transition: transform .65s; + } + &:hover .panel-body { + transform: rotateY(0); + } + } } // News detail article From 5564f49b1fd3aea2e29aa092a4aa3b3c53478495 Mon Sep 17 00:00:00 2001 From: Oleg Lavrovsky Date: Tue, 4 Jul 2017 11:07:53 +0200 Subject: [PATCH 25/25] Pastel colors --- publichealth/static/js/main.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/publichealth/static/js/main.js b/publichealth/static/js/main.js index c802387..3c51033 100644 --- a/publichealth/static/js/main.js +++ b/publichealth/static/js/main.js @@ -18,4 +18,11 @@ $(document).ready(function() { nextArrow: '', }); + // Formatting of live news + $('.feedpage-body .panel').each(function() { + var hue = Math.floor(Math.random() * 360); + var pastel = 'hsl(' + hue + ', 100%, 87.5%)'; + $(this).css('border-top', '3px solid ' + pastel); + }); + });