constance

Scripts for generating (an earlier obsolete version of) my personal web site
git clone https://code.djc.id.au/git/constance/
commit 1eb8f5eaf1503ab2e02e523a6dc5d82ff6ebcdc7
parent 77d9488ba0972f856d5839e4c44e34dd8d36c78b
Author: Dan Callaghan <djc@djc.id.au>
Date:   Fri,  5 Sep 2008 22:58:40 +1000

restructured blog.py a bit, now supports having no readinglog

Diffstat:
MTODO | 2--
Mapp.py | 21+++++++++++++--------
Mblog.py | 91++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mconfig.defaults | 2+-
Mtemplates/_entry.xml | 2+-
Mtemplates/multiple_atom.xml | 2+-
6 files changed, 60 insertions(+), 60 deletions(-)
diff --git a/TODO b/TODO
@@ -1,5 +1,3 @@
-- handle absent readinglog
-- restructure blog.py class hierarchy: EntrySet, DirectoryEntrySet, YamlStreamSet, ... or something
 - ignore pages in export_wp.py
 - feeds everywhere else
 - monthly archives
diff --git a/app.py b/app.py
@@ -2,6 +2,7 @@
 # vim:encoding=utf-8
 
 import os
+from itertools import chain
 import wsgiref.util
 from genshi.template import TemplateLoader
 from colubrid import RegexApplication, HttpResponse
@@ -28,12 +29,17 @@ class Constance(RegexApplication):
         super(Constance, self).__init__(*args, **kwargs)
         self.request.environ['APP_URI'] = wsgiref.util.application_uri(self.request.environ) # Colubrid ought to do this for us
         self.config = config.ConstanceConfigParser(self.request.environ['constance.config_filename'])
-        self.entries = blog.Entries(self.config.getunicode('blog', 'dir'), 
-                self.config.getunicode('readinglog', 'filename'))
+        self.blog_entries = blog.BlogEntrySet(self.config.getunicode('blog', 'dir'))
+        readinglog_filename = self.config.getunicode('readinglog', 'filename')
+        if readinglog_filename:
+            self.readinglog_entries = blog.ReadingLogEntrySet(readinglog_filename)
+        else:
+            self.readinglog_entries = frozenset()
 
     def index(self):
         offset = int(self.request.args.get('offset', 0))
-        sorted_entries = sorted(self.entries, key=lambda e: e.publication_date, reverse=True)
+        sorted_entries = sorted(chain(self.blog_entries, self.readinglog_entries), 
+                key=lambda e: e.publication_date, reverse=True)
         format = self.request.args.get('format', 'html')
         if format == 'html':
             rendered = template_loader.load('multiple.xml').generate(
@@ -60,7 +66,7 @@ class Constance(RegexApplication):
     def post(self, id):
         id = id.decode(self.charset) # shouldn't Colubrid do this?
         try:
-            entry = self.entries[id]
+            entry = self.blog_entries[id]
             rendered = template_loader.load('single.xml').generate(
                     config=self.config, 
                     environ=self.request.environ, 
@@ -72,12 +78,11 @@ class Constance(RegexApplication):
 
     def tag(self, tag):
         tag = tag.decode(self.charset)
-        by_tag = self.entries.by_tag()
-        if tag not in by_tag:
+        with_tag = [e for e in self.blog_entries if tag in e.tags]
+        if not with_tag:
             raise PageNotFound()
         offset = int(self.request.args.get('offset', 0))
-        entries = by_tag[tag]
-        sorted_entries = sorted(entries, key=lambda e: e.publication_date, reverse=True)
+        sorted_entries = sorted(with_tag, key=lambda e: e.publication_date, reverse=True)
         format = self.request.args.get('format', 'html')
         if format == 'html':
             rendered = template_loader.load('multiple.xml').generate(
diff --git a/blog.py b/blog.py
@@ -1,6 +1,5 @@
 import os, re
 from datetime import datetime
-from itertools import chain
 import markdown
 import genshi
 import yaml
@@ -39,37 +38,42 @@ class CommentNotFoundError(ValueError): pass
 class CommentForbiddenError(ValueError): pass
 
 
-class Entries(object):
+class DirectoryEntrySet(object):
+
+    def __init__(self, base_dir):
+        self.base_dir = base_dir
+        assert os.path.isdir(self.base_dir), self.base_dir
+
+    def __contains__(self, key):
+        return os.path.exists(os.path.join(self.base_dir, key))
+
+    def __getitem__(self, key):
+        if key not in self: raise KeyError(key)
+        return self.entry_class(self.base_dir, key)
+
+    def __len__(self):
+        return count(filename 
+                for filename in os.listdir(self.base_dir) 
+                if not filename.startswith('.'))
 
-    def __init__(self, entries_dir, readinglog_file):
-        self.entries_dir = entries_dir
-        self.readinglog_file = readinglog_file
-    
-    def __contains__(self, id):
-        return os.path.exists(os.path.join(self.entries_dir, id))
-    
-    def __getitem__(self, id):
-        # XXX reading log entries don't have a key
-        return Entry(self.entries_dir, id)
-    
     def __iter__(self):
-        return chain(
-                (Entry(self.entries_dir, filename) 
-                 for filename in os.listdir(self.entries_dir)
-                 if not filename.startswith('.')), 
-                (ReadingLogEntry(d)
-                 for d in yaml.load_all(open(self.readinglog_file, 'r')))
-                )
+        return (self.entry_class(self.base_dir, filename)
+                for filename in os.listdir(self.base_dir)
+                if not filename.startswith('.'))
+
 
-    def by_tag(self):
-        d = {}
-        for entry in self:
-            for tag in entry.tags:
-                d.setdefault(tag, set()).add(entry)
-        return d
+class YamlEntrySet(object):
+
+    def __init__(self, filename):
+        self.filename = filename
+        assert os.path.isfile(self.filename), self.filename
+
+    def __iter__(self):
+        return (self.entry_class(d)
+                for d in yaml.load_all(open(self.filename, 'r')))
 
 
-class Entry(object):
+class BlogEntry(object):
 
     def __init__(self, entries_dir, id):
         assert isinstance(id, unicode), id
@@ -99,7 +103,7 @@ class Entry(object):
         self.guid = self.metadata['guid']
 
     def comments(self):
-        return Comments(self.comments_dir)
+        return CommentSet(self.comments_dir)
 
     def has_comments(self):
         """
@@ -110,6 +114,11 @@ class Entry(object):
                 os.access(self.comments_dir, os.R_OK)
 
 
+class BlogEntrySet(DirectoryEntrySet):
+
+    entry_class = BlogEntry
+
+
 class ReadingLogEntry(object):
 
     def __init__(self, yaml_dict):
@@ -127,26 +136,9 @@ class ReadingLogEntry(object):
         return False
 
 
-class Comments(object):
+class ReadingLogEntrySet(YamlEntrySet):
 
-    def __init__(self, path):
-        self.path = path
-    
-    def __contains__(self, id):
-        return os.path.exists(os.path.join(self.path, id))
-
-    def __len__(self):
-        return count(filename 
-                for filename in os.listdir(self.path) 
-                if not filename.startswith('.'))
-    
-    def __getitem__(self, id):
-        return Comment(self.path, id)
-    
-    def __iter__(self):
-        return (Comment(self.path, filename) 
-                for filename in os.listdir(self.path)
-                if not filename.startswith('.'))
+    entry_class = ReadingLogEntry
 
 
 class Comment(object):
@@ -171,3 +163,8 @@ class Comment(object):
 
     def author_name(self):
         return self.author or u'Anonymous'
+
+
+class CommentSet(DirectoryEntrySet):
+
+    entry_class = Comment
diff --git a/config.defaults b/config.defaults
@@ -21,7 +21,7 @@ dir = ./entries
 [readinglog]
 
 # The name of the file containing a YAML stream of readinglog entries.
-filename = ./reading_log
+filename = 
 
 # Should LibraryThing covers be shown for readinglog entries?
 # See also librarything_devkey below.
diff --git a/templates/_entry.xml b/templates/_entry.xml
@@ -8,7 +8,7 @@ import blog
 from viewutils import mini_markdown, tag_list
 ?>
 
-<div class="entry" py:if="isinstance(entry, blog.Entry)">
+<div class="entry" py:if="isinstance(entry, blog.BlogEntry)">
 
 	<h3 class="entrytitle" id="post-${entry.id}">${mini_markdown(entry.title)}</h3>
 
diff --git a/templates/multiple_atom.xml b/templates/multiple_atom.xml
@@ -25,7 +25,7 @@ ATOM_TIME_FORMAT = str('%Y-%m-%dT%H:%M:%S+10:00')
 		<email py:if="email">${email}</email>
 	</author>
 	<category py:for="tag in entry.tags" scheme="${environ['APP_URI']}/+tags/" term="${tag}" />
-	<py:if test="isinstance(entry, blog.Entry)">
+	<py:if test="isinstance(entry, blog.BlogEntry)">
 		<link rel="alternate" href="${environ['APP_URI']}/${entry.id}" />
 		<title type="text">${entry.title}</title>
 		<content type="xhtml" xml:base="${environ['APP_URI']}/${entry.id}"><xhtml:div>