Newsfeed templates¶
WebHelpers2 doesn't include WebHelpers' feedgenerator module, so here's a simple alternative using Mako templates. It's based on the output of feedgenerator converted to Mako templates. This example uses GeoRSS so each item has a latitude/longitude. The templates may not support all of feedgenerator's features but it's sufficient for basic sites.
To run the example:
- Create a directory and chdir to it.
- Download "atom.mako", "rss.mako", and "newsfeeds.py" below. The templates must be in the current directory.
- Install Mako.
- Run "python newsfeeds.py".
It will put the output files in the current directory: "news.atom" and "news.mako".
If you use the "--debug" option it will also write the compiled templates to files: "news.atom.py" and "news.rss.py".
Atom template¶
Download: atom.mako
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss">
<title>${title}</title>
<link href="${site_url}" rel="alternate"></link>
<link href="${newsfeed_url}" rel="self"></link>
<id>${newsfeed_url}</id>
<updated>${update_date}</updated>
<rights>${copyright}</rights>
% for it in items:
<entry>
<title>${it['title']}</title>
<link href="${it['url']}" rel="alternate"></link>
<updated>${it['update_date']}</updated>
<published>${it['date']}</published>
<author><name>${it['author']}</name></author>
<id>${it['guid']}</id>
<summary type="html">${it['description']}</summary>
% if lat is not None and lon is not None:
<georss:point>${it['lat']} ${it['lon']}</georss:point>
% endif lat lon
</entry>
% endfor incident
</feed>
RSS template¶
Download: rss.mako
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:georss="http://www.georss.org/georss">
<channel>
<title>${title}</title>
<link>${site_url}</link>
<description>${description}</description>
<copyright>${copyright}</copyright>
<lastBuildDate>${update_date}</lastBuildDate>
% for it in items:
<item>
<title>${it['title']}</title>
<link>${it['url']}</link>
<description>${it['description']}</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">${it['author']}</dc:creator>
<pubDate>${it['date']}</pubDate>
<guid>${it['guid']}</guid>
% if it['lat'] is not None and it['lon'] is not None:
<georss:point>${it['lat']} ${it['lon']}</georss:point>
% endif lat lon
</item>
% endfor it
</channel>
</rss>
The script¶
Download newsfeeds.py
from __future__ import absolute_import, print_function, unicode_literals
import copy
import datetime
import os
import sys
from mako.template import Template
from six.moves.urllib import parse
COLLECTION = {
"title": "My Collection",
"site_url": "http://example.com/",
"description": "A bunch of articles.",
"copyright": "Public domain.",
"update_date": datetime.date(2014, 8, 12),
"records": [
{
"path": "articles/2", # Relative to SITE_URL.
"title": "Article 2",
"author": "Me",
"date": datetime.date(2014, 8, 12),
"update_date": datetime.date(2014, 8, 10),
"lat": 40.56,
"lon": -90.23,
"description": "My article.",
},
{
"path": "articles/1",
"title": "Article 1",
"author": "Me",
"date": datetime.date(2014, 7, 26),
"update_date": datetime.date(2014, 7, 26),
"lat": 41.17,
"lon": -71.51,
"description": "My earlier article.",
},
]
}
def make_guid(site, date, path):
guid_fmt = "tag:{},{}-{}-{}:{}"
return guid_fmt.format(site, date.year, date.month, date.day, path)
class AtomFeedGenerator(object):
content_type = b"application/atom+xml"
date_fmt = "%Y-%m-%dT00:00:00Z" # ISO format except "Z" instead of "UTC".
output_encoding = "utf-8"
template = "atom.mako"
def __init__(self, site, site_url, newsfeed_url, debug=False):
self.site = site
self.site_url = site_url
self.newsfeed_url = newsfeed_url
self.debug = debug
def render(self, content, output_file, debug=False):
render = self.get_renderer()
template_vars = self.get_template_vars(content)
xml = render(**template_vars)
f = open(output_file, "w")
f.write(xml)
f.close()
def get_renderer(self):
kw = {
"filename": self.template,
"output_encoding": self.output_encoding,
}
if self.debug:
kw["module_directory"] = os.curdir
tpl = Template(**kw)
return tpl.render
def get_template_vars(self, content):
update_date = self.get_update_date(content)
items = self.make_news_items(content)
ret = {
"title": content["title"],
"site_url": self.site_url,
"newsfeed_url": self.newsfeed_url,
"update_date": update_date,
"copyright": content["copyright"],
"description": content["description"],
"items": items,
}
return ret
def make_news_items(self, content):
items = []
for r in content["records"]:
r = r.copy()
r["url"] = parse.urljoin(self.site_url, r["path"])
r["guid"] = make_guid(self.site, r["date"], r["path"])
r["date"] = r["date"].strftime(self.date_fmt)
r["update_date"] = r["update_date"].strftime(self.date_fmt)
items.append(r)
return items
def get_update_date(self, content):
if not content["records"]:
return None
return content["records"][0]["date"].strftime(self.date_fmt)
class RSSFeedGenerator(AtomFeedGenerator):
content_type = b"application/rss+xml"
date_fmt = "%a, %d %b %Y 00:00:00 -0000"
template = "rss.mako"
def main():
debug = "--debug" in sys.argv[1:]
site = "example.com"
site_url = "http://example.com/"
newsfeed_url = site_url + "atom"
feed = AtomFeedGenerator(site, site_url, newsfeed_url, debug)
feed.render(COLLECTION, "news.atom")
newsfeed_url = site_url + "rss"
feed = RSSFeedGenerator(site, site_url, newsfeed_url, debug)
feed.render(COLLECTION, "news.rss")
if __name__ == "__main__": main()