constance

Scripts for generating (an earlier obsolete version of) my personal web site
git clone https://code.djc.id.au/git/constance/
commit f91a913bbc245bbb9a53ad30b41f3f57aa7ffd31
parent a3a7cf1cf4f9479895fb4f554c2664fa52122a34
Author: Dan Callaghan <djc@djc.id.au>
Date:   Sat,  7 Jun 2008 17:08:08 +1000

category and tag pages

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

--HG--
extra : convert_revision : c2b5d0d89fbf691d0aa04a061efc3e11511d15cd

Diffstat:
Mapp.py | 36+++++++++++++++++++++++++++++++-----
Mblog.py | 25+++++++++++++++++++++----
Mtemplates/_entry.xml | 11+++--------
Atemplates/category.xml | 19+++++++++++++++++++
Atemplates/tag.xml | 19+++++++++++++++++++
5 files changed, 93 insertions(+), 17 deletions(-)
diff --git a/app.py b/app.py
@@ -4,7 +4,7 @@ from colubrid import RegexApplication, HttpResponse, execute
 from colubrid.exceptions import PageNotFound, HttpFound
 from colubrid.server import StaticExports
 
-from blog import Entries
+import blog
 
 ENTRIES_DIR = os.path.join(os.path.dirname(__file__), u'entries')
 BASE_URL = ''
@@ -18,12 +18,14 @@ class BlogApplication(RegexApplication):
 
 	urls = [(r'^$', 'index'), 
 			(r'^feed$', 'feed'), 
-			(r'^([^/]+)/?$', 'post')]
+			(r'^\+categories/(.+)$', 'category'), 
+			(r'^\+tags/(.+)$', 'tag'), 
+			(r'^([^+/][^/]*)/?$', 'post')]
 	charset = 'utf-8'
 
 	def __init__(self, *args, **kwargs):
 		super(BlogApplication, self).__init__(*args, **kwargs)
-		self.entries = Entries(ENTRIES_DIR)
+		self.entries = blog.Entries(ENTRIES_DIR)
 
 	def index(self):
 		rendered = template_loader.load('index.xml').generate(entries=self.entries).render('xhtml')
@@ -31,10 +33,34 @@ class BlogApplication(RegexApplication):
 	
 	def post(self, id):
 		id = id.decode(self.charset) # shouldn't Colubrid do this?
-		entry = self.entries[id]
-		rendered = template_loader.load('single.xml').generate(entry=entry).render('xhtml')
+		try:
+			entry = self.entries[id]
+			rendered = template_loader.load('single.xml').generate(entry=entry).render('xhtml')
+			return HttpResponse(rendered, [('Content-Type', 'text/html')], 200)
+		except blog.EntryNotFoundError:
+			raise PageNotFound()
+
+	def category(self, category):
+		category = category.decode(self.charset)
+		categories = self.entries.by_category()
+		if category not in categories:
+			raise PageNotFound()
+		entries = categories[category]
+		rendered = template_loader.load('category.xml').generate(
+				category=category, entries=entries).render('xhtml')
+		return HttpResponse(rendered, [('Content-Type', 'text/html')], 200)
+
+	def tag(self, tag):
+		tag = tag.decode(self.charset)
+		by_tag = self.entries.by_tag()
+		if tag not in by_tag:
+			raise PageNotFound()
+		entries = by_tag[tag]
+		rendered = template_loader.load('tag.xml').generate(
+				tag=tag, entries=entries).render('xhtml')
 		return HttpResponse(rendered, [('Content-Type', 'text/html')], 200)
 
+
 app = BlogApplication
 app = StaticExports(app, {'/static': os.path.join(os.path.dirname(__file__), 'static')})
 
diff --git a/blog.py b/blog.py
@@ -38,6 +38,23 @@ class Entries(object):
 				for filename in os.listdir(self.entries_dir)
 				if not filename.startswith('.'))
 
+	def by_category(self):
+		"""
+		Returns a dict of (category -> set of entries).
+		"""
+		d = {}
+		for entry in self:
+			for category in entry.categories:
+				d.setdefault(category, set()).add(entry)
+		return d
+
+	def by_tag(self):
+		d = {}
+		for entry in self:
+			for tag in entry.tags:
+				d.setdefault(tag, set()).add(entry)
+		return d
+
 
 class Entry(object):
 
@@ -60,14 +77,14 @@ class Entry(object):
 
 		raw_cats = self.metadata.get('categories', '').strip()
 		if raw_cats:
-			self.categories = [cat.strip() for cat in raw_cats.split(',')]
+			self.categories = frozenset(cat.strip() for cat in raw_cats.split(','))
 		else:
-			self.categories = []
+			self.categories = frozenset()
 		raw_tags = self.metadata.get('tags', '').strip()
 		if raw_tags:
-			self.tags = [tag.strip() for tag in raw_tags.split(',')]
+			self.tags = frozenset(tag.strip() for tag in raw_tags.split(','))
 		else:
-			self.tags = []
+			self.tags = frozenset()
 
 		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
diff --git a/templates/_entry.xml b/templates/_entry.xml
@@ -11,20 +11,15 @@
 	<div class="entrymeta">
 		Posted ${entry.publication_date.strftime(str('%-1d %B %Y'))}
 		<py:if test="entry.categories">
-			in
-			<py:with vars="category = entry.categories[0]">
-				<a href="${BASE_URL}/+categories/${category}">${category}</a>
-			</py:with>
-			<py:for each="category in entry.categories[1:]">
-				, <a href="${BASE_URL}/+categories/${category}">${category}</a>
-			</py:for>
+			in <a py:for="category in entry.categories" href="${BASE_URL}/+categories/${category}">${category}</a>
 		</py:if>
 	</div>
   
 	<div class="entrybody">
 		${Markup(entry.body)}
 		<p py:if="entry.tags">
-			<img src="${BASE_URL}/static/images/tag_blue.png" alt="Tags:" /> ${', '.join(entry.tags)}
+			<img src="${BASE_URL}/static/images/tag_blue.png" alt="Tags:" />
+			<a py:for="tag in entry.tags" rel="tag" href="${BASE_URL}/+tags/${tag}">${tag}</a>
 		</p>
 	</div>
 
diff --git a/templates/category.xml b/templates/category.xml
@@ -0,0 +1,19 @@
+<!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/"
+	  xmlns:xi="http://www.w3.org/2001/XInclude"
+	  lang="en-AU">
+<xi:include href="_commonwrapper.xml" />
+<xi:include href="_entry.xml" />
+
+<head>
+	<title>${category} category</title>
+</head>
+<body>
+
+<py:for each="entry in sorted(entries, key=lambda e: e.publication_date, reverse=True)[:20]">
+	${show_entry(entry, show_comments=False)}
+</py:for>
+
+</body>
+</html>
diff --git a/templates/tag.xml b/templates/tag.xml
@@ -0,0 +1,19 @@
+<!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/"
+	  xmlns:xi="http://www.w3.org/2001/XInclude"
+	  lang="en-AU">
+<xi:include href="_commonwrapper.xml" />
+<xi:include href="_entry.xml" />
+
+<head>
+	<title>${tag} tag</title>
+</head>
+<body>
+
+<py:for each="entry in sorted(entries, key=lambda e: e.publication_date, reverse=True)[:20]">
+	${show_entry(entry, show_comments=False)}
+</py:for>
+
+</body>
+</html>