Source code for feedreader.utils
from __future__ import absolute_import
from datetime import datetime
from time import mktime
import feedparser
import pytz
from django.conf import settings
from django.utils import html
from django.utils import timezone
from .models import Entry, Options, Group, Feed
import logging
logger = logging.getLogger('feedreader')
[docs]def build_context(request, context={}):
"""
Find flag and id values in the request and use them
to build a common context dictionary. Including the
list of entries to display.
"""
options = Options.get_options.get_options()
poll_flag = request.GET.get('poll_flag', None)
mark_read_flag = request.GET.get('mark_read_flag', None)
show_read_flag = request.GET.get('show_read_flag', None)
last_entry = None
last_entry_id = request.GET.get('entry_id', None) # Last entry on page
if last_entry_id:
try:
last_entry = Entry.objects.get(pk=last_entry_id)
except Entry.DoesNotExist:
pass
context['show_read_flag'] = show_read_flag
feed = None
feed_id = request.GET.get('feed_id', None)
if feed_id:
try:
feed = Feed.objects.get(pk=feed_id)
except Feed.DoesNotExist:
pass
group = None
group_id = request.GET.get('group_id', None)
if group_id:
try:
group = Group.objects.get(pk=group_id)
except Group.DoesNotExist:
pass
if feed:
if mark_read_flag:
entries = Entry.objects.filter(feed=feed, read_flag=False)
entries.update(read_flag=True)
if poll_flag:
poll_feed(feed)
if show_read_flag:
entries = Entry.objects.filter(feed=feed)
else:
entries = Entry.objects.filter(feed=feed, read_flag=False)
context['entries_header'] = feed.title
elif group:
feeds = Feed.objects.filter(group=group)
if mark_read_flag:
entries = Entry.objects.filter(feed__group=group, read_flag=False)
entries.update(read_flag=True)
if poll_flag:
for feed in feeds:
poll_feed(feed)
if show_read_flag:
entries = Entry.objects.filter(feed__group=group)
else:
entries = Entry.objects.filter(feed__group=group, read_flag=False)
context['entries_header'] = group.name
else:
if mark_read_flag:
entries = Entry.objects.filter(read_flag=False)
entries.update(read_flag=True)
if show_read_flag:
entries = Entry.objects.all()
else:
entries = Entry.objects.filter(read_flag=False)
context['entries_header'] = 'All items'
if last_entry:
entry_list = list(entries)
if last_entry in entry_list:
last_entry_pos = entry_list.index(last_entry)
for i in range(last_entry_pos + 1):
if entry_list[i].read_flag == False:
entry_list[i].read_flag = True
entry_list[i].save()
del entry_list[:last_entry_pos + 1]
context['entry_list'] = entry_list[:options.number_additionally_displayed]
else:
context['entry_list'] = []
context['entries_header'] = None
else:
context['entry_list'] = entries[:options.number_initially_displayed]
return context
[docs]def poll_feed(db_feed, verbose=False):
"""
Read through a feed looking for new entries.
"""
options = Options.objects.all()
if options:
options = options[0]
else: # Create options row with default values
options = Options.objects.create()
parsed = feedparser.parse(db_feed.xml_url)
if hasattr(parsed.feed, 'bozo_exception'):
# Malformed feed
msg = 'Feedreader poll_feeds found Malformed feed, %s: %s' % (db_feed.xml_url, parsed.feed.bozo_exception)
logger.warning(msg)
if verbose:
print(msg)
return
if hasattr(parsed.feed, 'published_parsed'):
published_time = datetime.fromtimestamp(mktime(parsed.feed.published_parsed))
published_time = pytz.timezone(settings.TIME_ZONE).localize(published_time, is_dst=None)
if db_feed.published_time and db_feed.published_time >= published_time:
return
db_feed.published_time = published_time
for attr in ['title', 'title_detail', 'link', 'description', 'description_detail']:
if not hasattr(parsed.feed, attr):
msg = 'Feedreader poll_feeds. Feed "%s" has no %s' % (db_feed.xml_url, attr)
logger.error(msg)
if verbose:
print(msg)
return
if parsed.feed.title_detail.type == 'text/plain':
db_feed.title = html.escape(parsed.feed.title)
else:
db_feed.title = parsed.feed.title
db_feed.link = parsed.feed.link
if parsed.feed.description_detail.type == 'text/plain':
db_feed.description = html.escape(parsed.feed.description)
else:
db_feed.description = parsed.feed.description
db_feed.last_polled_time = timezone.now()
db_feed.save()
if verbose:
print('%d entries to process in %s' % (len(parsed.entries), db_feed.title))
for i, entry in enumerate(parsed.entries):
if i > options.max_entries_saved:
break
missing_attr = False
for attr in ['title', 'title_detail', 'link', 'description']:
if not hasattr(entry, attr):
msg = 'Feedreader poll_feeds. Entry "%s" has no %s' % (entry.link, attr)
logger.error(msg)
if verbose:
print(msg)
missing_attr = True
if missing_attr:
continue
if entry.title == "":
msg = 'Feedreader poll_feeds. Entry "%s" has a blank title' % (entry.link)
logger.warning(msg)
if verbose:
print(msg)
continue
db_entry, created = Entry.objects.get_or_create(feed=db_feed, link=entry.link)
if created:
if hasattr(entry, 'published_parsed'):
published_time = datetime.fromtimestamp(mktime(entry.published_parsed))
published_time = pytz.timezone(settings.TIME_ZONE).localize(published_time, is_dst=None)
now = timezone.now()
if published_time > now:
published_time = now
db_entry.published_time = published_time
if entry.title_detail.type == 'text/plain':
db_entry.title = html.escape(entry.title)
else:
db_entry.title = entry.title
# Lots of entries are missing descrtion_detail attributes. Escape their content by default
if hasattr(entry, 'description_detail') and entry.description_detail.type != 'text/plain':
db_entry.description = entry.description
else:
db_entry.description = html.escape(entry.description)
db_entry.save()