Skip to content

Commit

Permalink
Add an RSS feed
Browse files Browse the repository at this point in the history
Adds an RSS feed of the /stream page to h.feeds.

Also does some refactorings, including:

Don't use Pyramid renderers for Atom and RSS

Our Atom and RSS renderers are too specific for implementing them as renderers
to really be useful. All they do is produce Atom and RSS feeds from lists of
annotations, as opposed to more generic things like Pyramid's JSON or Jinja2
renderers. They can be implemented as simple functions just as usefully, and
then the code is easier to understand for not using the framework-specific
renderer concept.

Change util functions into Annotation properties

Change shared util functions for dealing with annotations as dicts into
properties on the Annotation object instead. This is just nicer.
  • Loading branch information
seanh committed Sep 9, 2015
1 parent 9f438aa commit 57544d4
Show file tree
Hide file tree
Showing 20 changed files with 816 additions and 839 deletions.
76 changes: 76 additions & 0 deletions h/api/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import cgi
from pyramid import security
from dateutil import parser

from annotator import annotation
from annotator import document
Expand Down Expand Up @@ -185,6 +187,80 @@ def __acl__(self):
def get_analysis(cls):
return cls.__analysis__

@property
def title(self):
"""A title for this annotation."""
document_ = self.get("document")
if document_:
return document_.get("title", "")
else:
return ""

@property
def description(self):
"""An HTML-formatted description of this annotation.
The description contains the target text that the user selected to
annotate, as a <blockquote>, and the body text of the annotation
itself.
"""
def get_selection():
targets = self.get("target")
if not isinstance(targets, list):
return
for target in targets:
if not isinstance(target, dict):
continue
selectors = target.get("selector")
if not isinstance(selectors, list):
continue
for selector in selectors:
if not isinstance(selector, dict):
continue
if "exact" in selector:
return selector["exact"]

description = ""

selection = get_selection()
if selection:
selection = cgi.escape(selection)
description += u"&lt;blockquote&gt;{selection}&lt;/blockquote&gt;".format(
selection=selection)

text = self.get("text")
if text:
text = cgi.escape(text)
description += u"{text}".format(text=text)

return description

@property
def created_day_string(self):
"""A simple created day string for this annotation.
Returns a day string like '2015-03-11' from the annotation's 'created'
date.
"""
return parser.parse(self["created"]).strftime("%Y-%m-%d")

@property
def target_links(self):
"""A list of the URLs to this annotation's targets."""
links = []
targets = self.get("target")
if isinstance(targets, list):
for target in targets:
if not isinstance(target, dict):
continue
source = target.get("source")
if source is None:
continue
links.append(source)
return links


class Document(document.Document):
__analysis__ = {}
Expand Down
34 changes: 34 additions & 0 deletions h/api/test/models_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,37 @@ def captures(patterns, text):

def groups(pattern, text):
return re.search(pattern, text).groups() or []


def test_title_with_a_document_that_has_a_title():
annotation = models.Annotation(document={'title': 'document title'})
assert annotation.title == 'document title'


def test_title_with_a_document_that_has_no_title():
annotation = models.Annotation(document={})
assert annotation.title == ''


def test_title_annotation_that_has_no_document():
assert models.Annotation().title == ''


def test_description():
annotation = models.Annotation(
target=[{'selector': [{'exact': 'selected text'}]}],
text='entered text'
)

assert annotation.description == (
"&lt;blockquote&gt;selected text&lt;/blockquote&gt;entered text")


def test_created_day_string_from_annotation():
annotation = models.Annotation(created='2015-09-04T17:37:49.517852+00:00')
assert annotation.created_day_string == '2015-09-04'


def test_target_links_from_annotation():
annotation = models.Annotation(target=[{'source': 'target link'}])
assert annotation.target_links == ['target link']
2 changes: 1 addition & 1 deletion h/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def includeme(config):
config.include('h.hashids')
config.include('h.models')
config.include('h.views')
config.include('h.renderers')
config.include('h.feeds')

config.include('pyramid_jinja2')
config.add_jinja2_extension('h.jinja_extensions.IncludeRawExtension')
Expand Down
213 changes: 0 additions & 213 deletions h/atom_feed.py

This file was deleted.

7 changes: 7 additions & 0 deletions h/feeds/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Code for generating feeds (e.g. Atom and RSS feeds)."""
from h.feeds.render import render_atom
from h.feeds.render import render_rss


def includeme(config):
config.include('.views')

0 comments on commit 57544d4

Please sign in to comment.