constance

Scripts for generating (an earlier obsolete version of) my personal web site
git clone https://code.djc.id.au/git/constance/
commit 4bc38febbb04c38d1e236ae28457328d709d1257
parent e2844930d23a708dd1e30d3d25c0f576a0c64647
Author: Dan Callaghan <djc@djc.id.au>
Date:   Tue,  2 Sep 2008 20:12:49 +1000

expanded tabs

Diffstat:
Mapp.py | 144++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mblog.py | 252++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mviewutils.py | 20++++++++++----------
3 files changed, 208 insertions(+), 208 deletions(-)
diff --git a/app.py b/app.py
@@ -11,84 +11,84 @@ import config
 import blog
 
 template_loader = TemplateLoader(
-		os.path.join(os.path.dirname(__file__), 'templates'), 
-		variable_lookup='strict', 
-		auto_reload=True)
+        os.path.join(os.path.dirname(__file__), 'templates'), 
+        variable_lookup='strict', 
+        auto_reload=True)
 
 class Constance(RegexApplication):
 
-	urls = [(r'^$', 'index'), 
-			(r'^feed$', 'feed'), 
-			(r'^\+tags/(.+)$', 'tag'), 
-			(r'^([^+/][^/]*)/?$', 'post')]
-	charset = 'utf-8'
+    urls = [(r'^$', 'index'), 
+            (r'^feed$', 'feed'), 
+            (r'^\+tags/(.+)$', 'tag'), 
+            (r'^([^+/][^/]*)/?$', 'post')]
+    charset = 'utf-8'
 
-	def __init__(self, *args, **kwargs):
-		super(Constance, self).__init__(*args, **kwargs)
-		self.entries = blog.Entries(config.ENTRIES_DIR, config.READINGLOG_FILE)
+    def __init__(self, *args, **kwargs):
+        super(Constance, self).__init__(*args, **kwargs)
+        self.entries = blog.Entries(config.ENTRIES_DIR, config.READINGLOG_FILE)
 
-	def index(self):
-		offset = int(self.request.args.get('offset', 0))
-		sorted_entries = sorted(self.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(
-					title=None, 
-					sorted_entries=sorted_entries, 
-					offset=offset,
-					).render('xhtml')
-			return HttpResponse(rendered, [('Content-Type', 'text/html')], 200)
-		elif format == 'atom':
-			rendered = template_loader.load('multiple_atom.xml').generate(
-					title=None, 
-					url=config.ABS_BASE + '/', 
-					sorted_entries=sorted_entries[:config.ENTRIES_PER_PAGE], 
-					feed_updated=sorted_entries[0].modified_date
-					).render('xml')
-			return HttpResponse(rendered, [('Content-Type', 'application/atom+xml')], 200)
-		else:
-			raise PageNotFound('Unknown format %r' % format)
-	
-	def post(self, id):
-		id = id.decode(self.charset) # shouldn't Colubrid do this?
-		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 index(self):
+        offset = int(self.request.args.get('offset', 0))
+        sorted_entries = sorted(self.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(
+                    title=None, 
+                    sorted_entries=sorted_entries, 
+                    offset=offset,
+                    ).render('xhtml')
+            return HttpResponse(rendered, [('Content-Type', 'text/html')], 200)
+        elif format == 'atom':
+            rendered = template_loader.load('multiple_atom.xml').generate(
+                    title=None, 
+                    url=config.ABS_BASE + '/', 
+                    sorted_entries=sorted_entries[:config.ENTRIES_PER_PAGE], 
+                    feed_updated=sorted_entries[0].modified_date
+                    ).render('xml')
+            return HttpResponse(rendered, [('Content-Type', 'application/atom+xml')], 200)
+        else:
+            raise PageNotFound('Unknown format %r' % format)
+    
+    def post(self, id):
+        id = id.decode(self.charset) # shouldn't Colubrid do this?
+        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 tag(self, tag):
-		tag = tag.decode(self.charset)
-		by_tag = self.entries.by_tag()
-		if tag not in by_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)
-		format = self.request.args.get('format', 'html')
-		if format == 'html':
-			rendered = template_loader.load('multiple.xml').generate(
-					title=u'“%s” tag' % tag, 
-					sorted_entries=sorted_entries, 
-					offset=offset
-					).render('xhtml')
-			return HttpResponse(rendered, [('Content-Type', 'text/html')], 200)
-		elif format == 'atom':
-			rendered = template_loader.load('multiple_atom.xml').generate(
-					title=u'“%s” tag' % tag, 
-					url='%s/+tags/%s' % (config.ABS_BASE, tag.encode(self.charset)), 
-					sorted_entries=sorted_entries[:config.ENTRIES_PER_PAGE], 
-					feed_updated=sorted_entries[0].modified_date
-					).render('xml')
-			return HttpResponse(rendered, [('Content-Type', 'application/atom+xml')], 200)
-		else:
-			raise PageNotFound('Unknown format %r' % format)
+    def tag(self, tag):
+        tag = tag.decode(self.charset)
+        by_tag = self.entries.by_tag()
+        if tag not in by_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)
+        format = self.request.args.get('format', 'html')
+        if format == 'html':
+            rendered = template_loader.load('multiple.xml').generate(
+                    title=u'“%s” tag' % tag, 
+                    sorted_entries=sorted_entries, 
+                    offset=offset
+                    ).render('xhtml')
+            return HttpResponse(rendered, [('Content-Type', 'text/html')], 200)
+        elif format == 'atom':
+            rendered = template_loader.load('multiple_atom.xml').generate(
+                    title=u'“%s” tag' % tag, 
+                    url='%s/+tags/%s' % (config.ABS_BASE, tag.encode(self.charset)), 
+                    sorted_entries=sorted_entries[:config.ENTRIES_PER_PAGE], 
+                    feed_updated=sorted_entries[0].modified_date
+                    ).render('xml')
+            return HttpResponse(rendered, [('Content-Type', 'application/atom+xml')], 200)
+        else:
+            raise PageNotFound('Unknown format %r' % format)
 
 
 if __name__ == '__main__':
-	app = Constance
-	app = StaticExports(app, {'/static': os.path.join(os.path.dirname(__file__), 'static')})
-	execute(app)
+    app = Constance
+    app = StaticExports(app, {'/static': os.path.join(os.path.dirname(__file__), 'static')})
+    execute(app)
diff --git a/blog.py b/blog.py
@@ -9,27 +9,27 @@ import config
 
 
 def count(iterable):
-	count = 0
-	for _ in iterable:
-		count += 1
-	return count
+    count = 0
+    for _ in iterable:
+        count += 1
+    return count
 
 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
+    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
 
 IDIFY_WHITESPACE_PATT = re.compile(r'(?u)\s+')
 IDIFY_ACCEPT_PATT = re.compile(r'(?u)\w|[-_]')
 def idify(s):
-	# http://www.w3.org/TR/REC-xml/#NT-Name
-	s = s.lower()
-	s = IDIFY_WHITESPACE_PATT.sub(u'-', s)
-	return u''.join(c for c in s if IDIFY_ACCEPT_PATT.match(c))
+    # http://www.w3.org/TR/REC-xml/#NT-Name
+    s = s.lower()
+    s = IDIFY_WHITESPACE_PATT.sub(u'-', s)
+    return u''.join(c for c in s if IDIFY_ACCEPT_PATT.match(c))
 
 
 class EntryNotFoundError(ValueError): pass
@@ -43,139 +43,139 @@ class CommentForbiddenError(ValueError): pass
 
 class Entries(object):
 
-	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')))
-				)
-
-	def by_tag(self):
-		d = {}
-		for entry in self:
-			for tag in entry.tags:
-				d.setdefault(tag, set()).add(entry)
-		return d
+    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')))
+                )
+
+    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):
 
-	def __init__(self, entries_dir, id):
-		assert isinstance(id, unicode), id
-		self.id = id
-		self.dir = os.path.join(entries_dir, id)
-		self.comments_dir = os.path.join(self.dir, 'comments')
+    def __init__(self, entries_dir, id):
+        assert isinstance(id, unicode), id
+        self.id = id
+        self.dir = os.path.join(entries_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()
+        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().decode('utf-8')
-		md = markdown.Markdown(extensions=['meta', 'typography'])
-		self.body = genshi.Markup(md.convert(self.raw))
-		self.metadata = cleanup_metadata(md.Meta)
-		self.title = self.metadata['title']
+        self.raw = open(os.path.join(self.dir, 'content.txt'), 'r').read().decode('utf-8')
+        md = markdown.Markdown(extensions=['meta', 'typography'])
+        self.body = genshi.Markup(md.convert(self.raw))
+        self.metadata = cleanup_metadata(md.Meta)
+        self.title = self.metadata['title']
 
-		raw_tags = self.metadata.get('tags', '').strip()
-		if raw_tags:
-			self.tags = frozenset(tag.strip() for tag in raw_tags.split(','))
-		else:
-			self.tags = frozenset()
+        raw_tags = self.metadata.get('tags', '').strip()
+        if raw_tags:
+            self.tags = frozenset(tag.strip() for tag in raw_tags.split(','))
+        else:
+            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
-		self._guid = self.metadata.get('guid', None)
+        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
+        self._guid = self.metadata.get('guid', None)
 
-	def comments(self):
-		return Comments(self.comments_dir)
+    def comments(self):
+        return Comments(self.comments_dir)
 
-	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 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 guid(self):
-		return self._guid or u'%s/%s' % (config.ABS_BASE, self.id)
+    def guid(self):
+        return self._guid or u'%s/%s' % (config.ABS_BASE, self.id)
 
 
 class ReadingLogEntry(object):
 
-	def __init__(self, yaml_dict):
-		self.title = yaml_dict['Title']
-		self.id = idify(self.title)
-		self.author = yaml_dict['Author']
-		self.publication_date = self.modified_date = self.date = yaml_dict['Date']
-		self.url = yaml_dict.get('URL', None)
-		self.isbn = yaml_dict.get('ISBN', None)
-		self.rating = yaml_dict.get('Rating', None)
-		self.tags = frozenset()
-		self._guid = yaml_dict.get('GUID', None)
+    def __init__(self, yaml_dict):
+        self.title = yaml_dict['Title']
+        self.id = idify(self.title)
+        self.author = yaml_dict['Author']
+        self.publication_date = self.modified_date = self.date = yaml_dict['Date']
+        self.url = yaml_dict.get('URL', None)
+        self.isbn = yaml_dict.get('ISBN', None)
+        self.rating = yaml_dict.get('Rating', None)
+        self.tags = frozenset()
+        self._guid = yaml_dict.get('GUID', None)
 
-	def has_comments(self):
-		return False
+    def has_comments(self):
+        return False
 
-	def guid(self):
-		return self._guid or u'%s/#post-%s' % (config.ABS_BASE, self.id)
+    def guid(self):
+        return self._guid or u'%s/#post-%s' % (config.ABS_BASE, self.id)
 
 
 class Comments(object):
 
-	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('.'))
+    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('.'))
 
 
 class Comment(object):
 
-	def __init__(self, comments_dir, id):
-		path = os.path.join(comments_dir, id)
-		if not os.path.exists(path):
-			raise CommentNotFoundError()
-		if not os.access(path, os.R_OK):
-			raise CommentForbiddenError()
-
-		self.id = id
-		self.raw = open(path, 'r').read().decode('utf-8')
-		md = markdown.Markdown(extensions=['meta', 'typography'], safe_mode='escape')
-		self.body = genshi.Markup(md.convert(self.raw))
-		if not hasattr(md, 'Meta'): raise Exception(self.raw)
-		self.metadata = md.Meta
-		
-		self.author = self.metadata.get('from', None)
-		self.author_url = self.metadata.get('author-url', None)
-		self.date = datetime.fromtimestamp(os.path.getmtime(path))
-
-	def author_name(self):
-		return self.author or u'Anonymous'
+    def __init__(self, comments_dir, id):
+        path = os.path.join(comments_dir, id)
+        if not os.path.exists(path):
+            raise CommentNotFoundError()
+        if not os.access(path, os.R_OK):
+            raise CommentForbiddenError()
+
+        self.id = id
+        self.raw = open(path, 'r').read().decode('utf-8')
+        md = markdown.Markdown(extensions=['meta', 'typography'], safe_mode='escape')
+        self.body = genshi.Markup(md.convert(self.raw))
+        if not hasattr(md, 'Meta'): raise Exception(self.raw)
+        self.metadata = md.Meta
+        
+        self.author = self.metadata.get('from', None)
+        self.author_url = self.metadata.get('author-url', None)
+        self.date = datetime.fromtimestamp(os.path.getmtime(path))
+
+    def author_name(self):
+        return self.author or u'Anonymous'
diff --git a/viewutils.py b/viewutils.py
@@ -5,17 +5,17 @@ import genshi
 import config
 
 def mini_markdown(s):
-	# XXX find a more efficient way to do this?
-	m = markdown.Markdown(extensions=['typography']).convert(s)
-	the_p, = re.match(u'<p>(.*)\n</p>', m).groups()	
-	return genshi.Markup(the_p)
+    # XXX find a more efficient way to do this?
+    m = markdown.Markdown(extensions=['typography']).convert(s)
+    the_p, = re.match(u'<p>(.*)\n</p>', m).groups()    
+    return genshi.Markup(the_p)
 
 def category_list(categories):
-	return genshi.Markup(u', ').join(
-			genshi.Markup(u'<a href="%s/+categories/%s">%s</a>' % (config.REL_BASE, category, category)) 
-			for category in categories)
+    return genshi.Markup(u', ').join(
+            genshi.Markup(u'<a href="%s/+categories/%s">%s</a>' % (config.REL_BASE, category, category)) 
+            for category in categories)
 
 def tag_list(tags):
-	return genshi.Markup(u', ').join(
-			genshi.Markup(u'<a rel="tag" href="%s/+tags/%s">%s</a>' % (config.REL_BASE, tag, tag)) 
-			for tag in tags)
+    return genshi.Markup(u', ').join(
+            genshi.Markup(u'<a rel="tag" href="%s/+tags/%s">%s</a>' % (config.REL_BASE, tag, tag)) 
+            for tag in tags)