constance

Scripts for generating (an earlier obsolete version of) my personal web site
git clone https://code.djc.id.au/git/constance/
commit efbd6cce6fda52db11fb43f9352192493684f627
parent b310f46eee5ea79e09fe3e00d6d28b41f379bb17
Author: Dan Callaghan <djc@djc.id.au>
Date:   Mon,  7 Apr 2008 11:57:16 +1000

WIP

committer: Dan Callaghan <djc@djc.id.au>

--HG--
extra : convert_revision : 6725f9c7ea2ce129b9ac9acd922b6a661cce1450

Diffstat:
A.gitignore | 2++
Ablog.py | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahandler.py | 9+++++++++
Atemplates/post.xml | 48++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 148 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+.*.sw?
diff --git a/blog.py b/blog.py
@@ -0,0 +1,89 @@
+import os
+from datetime import datetime
+import markdown
+
+
+BASE_DIR = '.'
+BASE_URL = '/blog'
+
+
+def cleanup_metadata(meta):
+	cleaned = {}
+	for k, v in meta.iteritems():
+		v = '\n'.join(v)
+		if k.endswith('date'):
+			v = datetime.strptime(v, '%Y-%m-%d %H:%M:%S')
+		cleaned[k] = v
+	return cleaned
+
+
+class EntryNotFoundError(ValueError): pass
+
+class EntryForbiddenError(ValueError): pass
+
+class CommentNotFoundError(ValueError): pass
+
+class CommentForbiddenError(ValueError): pass
+
+
+class Entry(object):
+
+	def __init__(self, id):
+		self.id = id
+		self.dir = os.path.join(BASE_DIR, id)
+		self.comments_dir = os.path.join(self.dir, 'comments')
+
+		if not os.path.exists(self.dir):
+			raise EntryNotFoundError()
+		if not os.access(self.dir, os.R_OK):
+			raise EntryForbiddenError()
+
+		self.raw = open(os.path.join(self.dir, 'content.txt'), 'r').read()
+		md = markdown.Markdown(extensions=['meta'])
+		self.body = md.convert(self.raw)
+		self.metadata = cleanup_metadata(md.Meta)
+		self.title = self.metadata['title']
+
+		self.categories = [cat.strip() for cat in self.metadata.get('categories', '').split(',')]
+		self.tags = [tag.strip() for tag in self.metadata.get('tags', '').split(',')]
+
+		self.modified_date = datetime.fromtimestamp(os.path.getmtime(os.path.join(self.dir, 'content.txt')))
+		self.publication_date = self.metadata.get('publication-date', None) or self.modified_date
+
+	def permalink(self):
+		return '%s/%s/' % (BASE_URL, self.id)
+
+	def has_comments(self):
+		"""
+		Returns True if this Entry could *possibly* have comments, although it 
+		may still have no comments (yet).
+		"""
+		return os.path.isdir(self.comments_dir) and \
+				os.access(self.comments_dir, os.R_OK)
+
+	def comments(self):
+		for filename in sorted(os.listdir(self.comments_dir), key=int):
+			yield Comment(os.path.join(self.comments_dir, filename))
+
+	def comment(self, id):
+		return Comment(os.path.join(self.comments_dir, str(id)))
+
+
+class Comment(object):
+
+	def __init__(self, path):
+		if not os.path.exists(path):
+			raise CommentNotFoundError()
+		if not os.access(path, os.R_OK):
+			raise CommentForbiddenError()
+
+		self.raw = open(path, 'r').read()
+		md = markdown.Markdown(extensions=['meta'])
+		self.body = md.convert(self.raw)
+		self.metadata = md.Meta
+		
+		self.author = self.metadata.get('from', None)
+		self.date = self.metadata['date']
+
+	def author_link(self):
+		return self.author or u'Anonymous'
diff --git a/handler.py b/handler.py
@@ -0,0 +1,9 @@
+import os
+from genshi.template import TemplateLoader
+
+from blog import BASE_DIR, Entry
+
+template_loader = TemplateLoader(os.path.join(BASE_DIR, 'templates'), auto_reload=True)
+
+def post(id):
+	print template_loader.load('post.xml').generate(entry=Entry(id)).render('xhtml')
diff --git a/templates/post.xml b/templates/post.xml
@@ -0,0 +1,48 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/">
+<head>
+  <title>${entry.title}</title>
+</head>
+<body>
+
+<div id="content">
+  <div class="entry">
+
+    <h3 class="entrytitle" id="post-${entry.id}"><a href="${entry.permalink()}" rel="bookmark">${entry.title}</a></h3>
+
+    <div class="entrymeta">
+      Posted ${entry.publication_date.strftime(str('%-1d %B %Y'))}
+	  <py:if test="entry.categories">
+	    in ${', '.join(entry.categories)}
+	  </py:if>
+    </div>
+	  
+    <div class="entrybody">
+      ${Markup(entry.body)}
+	  <p py:if="entry.tags">
+	    <img src="${BASE_URL}/templates/images/tag_blue.png" alt="Tags:" /> ${', '.join(entry.tags)}
+	  </p>
+    </div>
+
+  </div>
+
+  <div id="commentblock" py:if="entry.has_comments()">
+    <ol class="commentlist" id="commentlist">
+      <li py:for="comment in entry.comments()"
+          class="(comment.id % 2) and 'alt' or 'standard'"
+          id="comment-${comment.id}">
+        <div class="commentmeta">
+          ${comment.author_link()}
+          on ${comment.date.strftime(str('F j, Y'))}
+        </div>
+        <div class="commentbody">
+          ${comment.body}
+        </div>
+      </li>
+    </ol>
+  </div>
+</div>
+
+</body>
+</html>