Commits

Liam Cooke  committed 252c3f5

RSS feed generation

  • Participants
  • Parent commits 903939e

Comments (0)

Files changed (3)

     dateutil    python-dateutil     http://labix.org/python-dateutil
     mako        Mako                http://www.makotemplates.org/
     markdown    Markdown            http://www.freewisdom.org/projects/python-markdown
+    PyRSS2Gen   PyRSS2Gen           http://dalkescientific.com/Python/PyRSS2Gen.html
     yaml        PyYAML              http://pyyaml.org/wiki/PyYAML
 
 At the moment there isn't any documentation, or even a list of features.
 LINKS
 =====
 
-    Template documentation          http://www.makotemplates.org/docs/
+Documentation:
+    Mako templates          http://www.makotemplates.org/docs/
+    Markdown formatting     http://daringfireball.net/projects/markdown/basics

File example-site/deploy/feed.rss

+<?xml version="1.0" encoding="utf-8"?>
+<rss version="2.0"><channel><title>Pilcrow</title><link>http:/inky.github.com/pilcrow/.html</link><description></description><language>en</language><lastBuildDate>Mon, 07 Dec 2009 21:21:00 GMT</lastBuildDate><generator>Pilcrow</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Lorem ipsum</title><link>http:/inky.github.com/pilcrow/2009/lorem-ipsum.html</link><description>&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ultrices
+tempor tincidunt. Mauris sit amet ante augue, eu pulvinar lorem. Nam eleifend
+sodales consectetur. Sed eget mi ac nunc ultrices egestas a vitae nibh.
+Maecenas mattis pharetra lorem. Fusce faucibus lacus nec tellus interdum nec
+cursus ante consequat.&lt;/p&gt;
+&lt;p&gt;Aliquam sed nisi mauris.&lt;/p&gt;
+&lt;p&gt;Nulla sit amet lacus at massa pharetra mattis at eu tortor. Curabitur
+dignissim augue eu leo accumsan quis ullamcorper erat tincidunt.&lt;/p&gt;
+&lt;p&gt;Vill koum wéi ke, dir ze welle fergiess zwëschen. Op brét Feierwon mir, hin mä
+welle Gaart erwaacht. Da hie stét Kënnt Freiesch, rifft kommen grousse am gét,
+huet bléit d'Margréitchen blo ze. Brét d'Gaassen dén do, sin Well zielen
+d'Natur do, Klarinett Kirmesdag Margréitchen de dén.&lt;/p&gt;
+&lt;p&gt;Cras enim diam, cursus id condimentum ac, gravida et quam. Suspendisse
+ultricies libero quis quam facilisis blandit. Nunc adipiscing dolor et magna
+tincidunt venenatis. Ut vel magna et neque fringilla porttitor. Nam suscipit
+consectetur justo eget rutrum. Morbi eu eros nec nunc molestie blandit.
+Suspendisse lectus mi, sollicitudin at cursus non, congue at tortor.&lt;/p&gt;
+&lt;p&gt;Integer lacinia, dolor ac consequat mollis, neque ante sodales odio, eget
+semper quam est eu quam. Quisque et nisl sit amet urna condimentum gravida.
+Nam euismod ante at orci blandit pharetra.&lt;/p&gt;</description><category>loremipsum</category><category>typography</category><category>filler</category><guid isPermaLink="true">http:/inky.github.com/pilcrow/2009/lorem-ipsum.html</guid><pubDate>Mon, 07 Dec 2009 21:21:00 GMT</pubDate></item><item><title>What's new in Python 2.6</title><link>http:/inky.github.com/pilcrow/2008/whats-new.html</link><description>&lt;p&gt;This article explains the new features in Python 2.6, released on October 1
+2008. The release schedule is described in &lt;a href="http://www.python.org/dev/peps/pep-0361"&gt;PEP 361&lt;/a&gt;.&lt;/p&gt;
+&lt;p&gt;The major theme of Python 2.6 is preparing the migration path to Python 3.0, a
+major redesign of the language. Whenever possible, Python 2.6 incorporates new
+features and syntax from 3.0 while remaining compatible with existing code by
+not removing older features or syntax. When it’s not possible to do that,
+Python 2.6 tries to do what it can, adding compatibility functions in a
+&lt;code&gt;future_builtins&lt;/code&gt; module and a &lt;code&gt;-3&lt;/code&gt; switch to warn about usages that will
+become unsupported in 3.0.&lt;/p&gt;
+&lt;p&gt;Some significant new packages have been added to the standard library, such as
+the &lt;code&gt;multiprocessing&lt;/code&gt; and &lt;code&gt;json&lt;/code&gt; modules, but there aren’t many new features
+that aren’t related to Python 3.0 in some way.&lt;/p&gt;
+&lt;p&gt;Python 2.6 also sees a number of improvements and bugfixes throughout the
+source. A search through the change logs finds there were 259 patches applied
+and 612 bugs fixed between Python 2.5 and 2.6. Both figures are likely to be
+underestimates.&lt;/p&gt;
+&lt;p&gt;This article doesn’t attempt to provide a complete specification of the new
+features, but instead provides a convenient overview. For full details, you
+should refer to the documentation for Python 2.6. If you want to understand
+the rationale for the design and implementation, refer to the PEP for a
+particular new feature. Whenever possible, “What’s New in Python” links to the
+bug/patch item for each change.&lt;/p&gt;</description><category>python</category><guid isPermaLink="true">http:/inky.github.com/pilcrow/2008/whats-new.html</guid><pubDate>Sun, 06 Dec 2009 22:44:00 GMT</pubDate></item><item><title>Turritopsis nutricula</title><link>http:/inky.github.com/pilcrow/2009/turritopsis-nutricula.html</link><description>&lt;p&gt;From &lt;a href="http://en.wikipedia.org/wiki/Turritopsis_nutricula"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;
+&lt;blockquote&gt;
+&lt;p&gt;Turritopsis nutricula is a hydrozoan with a life cycle in which it reverts to
+the polyp stage after becoming sexually mature. It is the only known case of
+a metazoan capable of reverting completely to a sexually immature, colonial
+stage after having reached sexual maturity as a solitary stage. It does this
+through the cell development process of transdifferentiation. Theoretically,
+this cycle can repeat indefinitely, rendering it biologically immortal until
+its nerve center is removed from the rest of the body.&lt;/p&gt;
+&lt;/blockquote&gt;</description><category>wikipedia</category><category>immortality</category><category>wtf</category><guid isPermaLink="true">http:/inky.github.com/pilcrow/2009/turritopsis-nutricula.html</guid><pubDate>Tue, 24 Nov 2009 00:00:00 GMT</pubDate></item></channel></rss>
 from os import path
 
 import dateutil.parser
+import PyRSS2Gen as rss2
 import yaml
 from mako.exceptions import MakoException
 from mako.lookup import TemplateLookup
     '.less': lambda s, d: run_or_die('lessc %s %s' % (s, d)),
 }
 site = yaml.load(r"""
+    domain: http://localhost/
+    root: /
     clean_urls: no
     content_extensions: [text, markdown, mkdn, md]
     dirs:
         content: content
-        deploy: deploy
         files: files
         templates: templates
+        deploy: deploy
+    feed: feed.rss
     files_exclude: "(^\\.|~$)"
     files_include: "^\\.htaccess$"
     files_rename:
         .less: .css
+    lang: en
 """)
 
 alphanum = lambda s: re.sub('[^A-Za-z0-9]', '', s)
         id = self.id
         return join_url(site['root'], id != 'index' and id)
 
+    @property
+    def full_url(self):
+        return join_url(site['domain'], self.url, ext=False)
+
 class ContentPage(Page):
     NORM = {
         'date': norm_time, 'posted': norm_time,
             return summary
         self['content'] = markdown(self.SUMMARY.sub(_summary, body).strip())
 
+    def feed_item(self):
+        url = self.full_url
+        return rss2.RSSItem(title=self.title, link=url, guid=rss2.Guid(url),
+            description=self.content, pubDate=self.posted or self.date,
+            categories=self.get('tags', None),
+            enclosure=self.get('enclosure', None))
+
 class ArchivePage(Page):
 
     def __init__(self, entries, year, month=0):
         tdir = site['dirs']['templates']
         self.lookup = TemplateLookup(directories=[tdir], input_encoding='utf-8')
 
+    def __getitem__(self, id):
+        return self.pages[id]
+
+    def __iter__(self):
+        return iter(self.pages.values())
+
     def add(self, page):
         if page.id in self.pages:
             die('duplicate page id: %s' % page.id)
         self.pages[page.id] = page
 
-    def __getitem__(self, id):
-        return self.pages[id]
-
-    def all(self, sortby_origin=False):
+    def select(self, limit=None, dated=True, chrono=False, sortby_origin=None):
+        if sortby_origin is None:
+            sortby_origin = bool(chrono)
         sortkey = sortby_origin and Page.sortkey_origin or Page.sortkey_posted
-        return sorted(self.pages.values(), key=sortkey)
-
-    def __iter__(self):
-        return iter(self.pages.values())
+        results = sorted(self.pages.values(), key=sortkey, reverse=not chrono)
+        if dated:
+            results = [page for page in results if page.date]
+        return tuple(results)[:limit]
 
     def render(self):
         for page in self:
     for d in sorted(set(dirs)):
         mkdir(os.path.join(deploy_path, d))
 
-    def select(limit=None, dated=True, chrono=False, sortby_origin=None):
-        if sortby_origin is None: sortby_origin = bool(chrono)
-        results = pages.all(sortby_origin)
-        if not chrono: results.reverse()
-        if dated: results = [page for page in results if page.date]
-        return tuple(results)[:limit]
-
     site.update({
         'get': lambda id: pages[str(id)],
-        'pages': select,
+        'pages': pages.select,
         'domain': site['domain'].rstrip('/'),
         'root': '/' + site.get('root', '').lstrip('/'),
         'head_title': site.get('site_title', ''),
     try: pages.render()
     except MakoException as e: die('template error:', e)
 
+    if site['feed']:
+        feed_posts = pages.select(10)
+        feed = rss2.RSS2(items=[p.feed_item() for p in feed_posts],
+            title=site['site_title'], description=site.get('description', ''),
+            link=join_url(site['domain'], site['root']), generator='Pilcrow',
+            language=site['lang'], lastBuildDate=feed_posts[0].date)
+        with open(path.join(deploy_path, site['feed']), 'w') as f:
+            feed.write_xml(f, 'utf-8')
+
 if __name__ == '__main__':
     parser = optparse.OptionParser()
     parser.add_option('-x', '--clean', action='store_true', default=False)