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()