constance

Scripts for generating (an earlier obsolete version of) my personal web site
git clone https://code.djc.id.au/git/constance/
commit d78b313afff4b31c52f9b500fc441cea400ef81c
parent e85797d6bb556cbf827118fc5ca46339dcb4d06f
Author: Dan Callaghan <djc@djc.id.au>
Date:   Thu,  4 Dec 2008 18:24:59 +1000

atom

--HG--
rename : templates/multiple_atom.xml => templates/atom/BlogEntry.xml
rename : templates/multiple_atom.xml => templates/atom/ReadingLogEntry.xml
rename : templates/multiple_atom.xml => templates/atom/multiple.xml

Diffstat:
Mapp.py | 67++++++++++++++++++++++++++++++++++++++++++-------------------------
Mitemtypes.py | 6++++++
Atemplates/atom/BlogEntry.xml | 27+++++++++++++++++++++++++++
Atemplates/atom/ReadingLogEntry.xml | 26++++++++++++++++++++++++++
Atemplates/atom/multiple.xml | 21+++++++++++++++++++++
Dtemplates/multiple_atom.xml | 52----------------------------------------------------
Mviewutils.py | 2++
7 files changed, 124 insertions(+), 77 deletions(-)
diff --git a/app.py b/app.py
@@ -36,7 +36,7 @@ class Constance(object):
 
     def __iter__(self):
         try:
-            resp = self.dispatch(self.req.path_info)
+            resp = self.dispatch()
         except exc.HTTPException, e:
             resp = e
         return iter(resp(self.environ, self.start))
@@ -51,10 +51,13 @@ class Constance(object):
             (r'/blog/([^/]+)/?$', 'blog_entry'), 
             (r'/blog/([^/]+)/comments/\+new$', 'add_post_comment')]
     urls = [(re.compile(patt), method) for patt, method in urls]
-    def dispatch(self, path_info):
-        if path_info == '/':
-            return self.index()
-        path_info = urllib.unquote(path_info).decode(self.encoding)
+    def dispatch(self):
+        format = self.req.accept.best_match(['text/html', 'application/atom+xml']) # XXX don't hardcode
+
+        if self.req.path_info == '/':
+            return self.index(format)
+
+        path_info = urllib.unquote(self.req.path_info).decode(self.encoding)
         for item_set in self.item_sets:
             try:
                 result = item_set.get(path_info)
@@ -62,37 +65,51 @@ class Constance(object):
                 pass
             else:
                 if hasattr(result, '__iter__'):
-                    return self.render_multiple(result)
+                    return self.render_multiple(result, format)
                 else:
-                    return self.render_single(result)
+                    return self.render_single(result, format)
         # no matching URI found, so give a 404
         raise exc.HTTPNotFound().exception
 
-    def render_single(self, item):
-        template = template_loader.load('html/single.xml')
-        rendered = template.generate(
-                config=self.config, 
-                item=item
-                ).render('xhtml')
-        return Response(rendered, content_type='text/html')
+    def render_single(self, item, format):
+        if format == 'text/html':
+            template = template_loader.load('html/single.xml')
+            rendered = template.generate(
+                    config=self.config, 
+                    item=item
+                    ).render('xhtml')
+        else:
+            raise exc.HTTPBadRequest('Unacceptable format for render_single %r' % format).exception
+        return Response(rendered, content_type=format)
 
-    def render_multiple(self, items):
+    def render_multiple(self, items, format):
         try:
             offset = int(self.req.GET.get('offset', 0))
         except ValueError:
             raise exc.HTTPBadRequest('Invalid offset %r' % self.GET['offset']).exception
-        template = template_loader.load('html/multiple.xml')
-        rendered = template.generate(
-                config=self.config, 
-                items=items, 
-                title=None, 
-                offset=offset
-                ).render('xhtml')
-        return Response(rendered, content_type='text/html')
+        if format == 'text/html':
+            template = template_loader.load('html/multiple.xml')
+            rendered = template.generate(
+                    config=self.config, 
+                    items=items, 
+                    title=None, 
+                    offset=offset
+                    ).render('xhtml')
+        elif format == 'application/atom+xml':
+            template = template_loader.load('atom/multiple.xml')
+            rendered = template.generate(
+                    config=self.config, 
+                    items=items, 
+                    title=None, 
+                    self_url=self.req.path_url
+                    ).render('xml')
+        else:
+            raise exc.HTTPBadRequest('Unacceptable format for render_multiple %r' % format).exception
+        return Response(rendered, content_type=format)
 
-    def index(self):
+    def index(self, format):
         items = chain(*self.item_sets)
-        return self.render_multiple(items)
+        return self.render_multiple(items, format)
     
     def tag_cloud(self):
         tag_freqs = {}
diff --git a/itemtypes.py b/itemtypes.py
@@ -91,6 +91,9 @@ class BlogEntry(object):
         if format == 'text/html':
             template = template_loader.load('html/' + self.__class__.__name__ + '.xml')
             return template.generate(item=self)
+        elif format == 'application/atom+xml':
+            template = template_loader.load('atom/' + self.__class__.__name__ + '.xml')
+            return template.generate(item=self)
         else:
             raise UnsupportedFormatError(format)
 
@@ -159,6 +162,9 @@ class ReadingLogEntry(object):
         if format == 'text/html':
             template = template_loader.load('html/' + self.__class__.__name__ + '.xml')
             return template.generate(item=self)
+        elif format == 'application/atom+xml':
+            template = template_loader.load('atom/' + self.__class__.__name__ + '.xml')
+            return template.generate(item=self)
         else:
             raise UnsupportedFormatError(format)
 
diff --git a/templates/atom/BlogEntry.xml b/templates/atom/BlogEntry.xml
@@ -0,0 +1,27 @@
+<entry xmlns="http://www.w3.org/2005/Atom"
+       xmlns:py="http://genshi.edgewall.org/"
+       xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<?python
+import blog
+from viewutils import markdown, tag_list, ATOM_TIME_FORMAT
+?>
+
+<id>${item.guid}</id>
+<published>${item.publication_date.strftime(ATOM_TIME_FORMAT)}</published>
+<updated>${item.modified_date.strftime(ATOM_TIME_FORMAT)}</updated>
+<!--author py:with="email = config.getunicode('global', 'email')">
+    <name>${config.getunicode('global', 'author')}</name>
+    <email py:if="email">${email}</email>
+</author-->
+<category py:for="tag in item.tags" scheme="/tags/" term="${tag}" /><!-- XXX app_uri -->
+<link rel="alternate" href="${item.uri_path}" /><!-- XXX app_uri -->
+<title type="text">${item.title}</title>
+<content type="xhtml" xml:base="${item.uri_path}"><!-- XXX app_uri -->
+    <div xmlns="http://www.w3.org/1999/xhtml">
+        <p py:if="item.tags">Tagged: ${tag_list('', item.tags)}</p><!-- XXX script_name -->
+        ${markdown(item.body)}
+    </div>
+</content>
+
+</entry>
diff --git a/templates/atom/ReadingLogEntry.xml b/templates/atom/ReadingLogEntry.xml
@@ -0,0 +1,26 @@
+<entry xmlns="http://www.w3.org/2005/Atom"
+       xmlns:py="http://genshi.edgewall.org/"
+	   xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<?python
+from viewutils import ATOM_TIME_FORMAT
+?>
+
+<id>${item.guid}</id>
+<published>${item.publication_date.strftime(ATOM_TIME_FORMAT)}</published>
+<updated>${item.modified_date.strftime(ATOM_TIME_FORMAT)}</updated>
+<!--author py:with="email = config.getunicode('global', 'email')">
+    <name>${config.getunicode('global', 'author')}</name>
+    <email py:if="email">${email}</email>
+</author-->
+<category py:for="tag in item.tags" scheme="/tags/" term="${tag}" /><!-- XXX app_uri -->
+<title type="text">${item.title} by ${item.author}</title>
+<summary py:if="item.rating" type="text">${item.rating} stars</summary>
+<content type="xhtml">
+    <div xmlns="http://www.w3.org/1999/xhtml">
+        <p><a href="${item.url}">${item.title}</a> by ${item.author}</p>
+        <p py:if="item.rating">${item.rating} stars</p>
+    </div>
+</content>
+
+</entry>
diff --git a/templates/atom/multiple.xml b/templates/atom/multiple.xml
@@ -0,0 +1,21 @@
+<feed xmlns="http://www.w3.org/2005/Atom"
+      xmlns:py="http://genshi.edgewall.org/"
+	  xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<?python
+from viewutils import ATOM_TIME_FORMAT
+sorted_items = sorted(items, key=lambda item: item.publication_date, reverse=True)[:config.getint('global', 'items_in_feed')]
+?>
+
+<id>${self_url}</id>
+<title type="text">${config.getunicode('global', 'name')}<py:if test="title"> (${title})</py:if></title>
+<link rel="self" type="application/atom+xml" href="${self_url}" />
+<link rel="alternate" href="${self_url}" />
+<generator>constance</generator>
+<updated py:if="sorted_items">${max(item.modified_date for item in sorted_items).strftime(ATOM_TIME_FORMAT)}</updated>
+
+<py:for each="item in sorted_items">
+    ${item.render('application/atom+xml')}
+</py:for>
+
+</feed>
diff --git a/templates/multiple_atom.xml b/templates/multiple_atom.xml
@@ -1,52 +0,0 @@
-<feed xmlns="http://www.w3.org/2005/Atom"
-      xmlns:py="http://genshi.edgewall.org/"
-	  xmlns:xi="http://www.w3.org/2001/XInclude">
-
-<xi:include href="_fragments.xml" />
-
-<?python
-import blog
-from viewutils import markdown, tag_list
-ATOM_TIME_FORMAT = str('%Y-%m-%dT%H:%M:%S+10:00')
-?>
-
-<id>${self_url}?format=atom</id>
-<title type="text">${config.getunicode('global', 'name')}<py:if test="title"> (${title})</py:if></title>
-<link rel="self" type="application/atom+xml" href="${self_url}?format=atom" />
-<link rel="alternate" href="${self_url}" />
-<generator>constance</generator>
-<updated>${feed_updated.strftime(ATOM_TIME_FORMAT)}</updated>
-
-<entry py:for="entry in sorted_entries">
-	<id>${entry.guid}</id>
-	<published>${entry.publication_date.strftime(ATOM_TIME_FORMAT)}</published>
-	<updated>${entry.modified_date.strftime(ATOM_TIME_FORMAT)}</updated>
-	<author py:with="email = config.getunicode('global', 'email')">
-		<name>${config.getunicode('global', 'author')}</name>
-		<email py:if="email">${email}</email>
-	</author>
-	<category py:for="tag in entry.tags" scheme="${abs_uri('tags', '')}" term="${tag}" />
-	<py:if test="isinstance(entry, blog.BlogEntry)">
-		<link rel="alternate" href="${abs_uri('blog', entry.id)}" />
-		<title type="text">${entry.title}</title>
-		<content type="xhtml" xml:base="${abs_uri('blog', entry.id)}">
-            <div xmlns="http://www.w3.org/1999/xhtml">
-                <p py:if="entry.tags">Tagged: ${tag_list(environ.get('SCRIPT_NAME', ''), entry.tags)}</p>
-                ${markdown(entry.body)}
-            </div>
-		</content>
-	</py:if>
-	<py:if test="isinstance(entry, blog.ReadingLogEntry)">
-		<link rel="alternate" href="${abs_uri('reading', '')}#entry-${entry.id}" />
-		<title type="text">${entry.title} by ${entry.author}</title>
-		<summary py:if="entry.rating" type="text">${entry.rating} stars</summary>
-		<content type="xhtml">
-            <div xmlns="http://www.w3.org/1999/xhtml">
-                <p><a href="${entry.url}">${entry.title}</a> by ${entry.author}</p>
-                <p py:if="entry.rating">${entry.rating} stars</p>
-            </div>
-        </content>
-	</py:if>
-</entry>
-
-</feed>
diff --git a/viewutils.py b/viewutils.py
@@ -21,6 +21,8 @@ def idify(s):
     s = IDIFY_WHITESPACE_PATT.sub(u'-', s)
     return u''.join(c for c in s if IDIFY_ACCEPT_PATT.match(c))
 
+ATOM_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S+10:00'
+
 def tag_list(script_name, tags):
     # XXX urllib.quote
     return genshi.Markup(u', ').join(