commit 4bc38febbb04c38d1e236ae28457328d709d1257
parent e2844930d23a708dd1e30d3d25c0f576a0c64647
Author: Dan Callaghan <djc@djc.id.au>
Date: Tue, 2 Sep 2008 20:12:49 +1000
expanded tabs
Diffstat:
M | app.py | | | 144 | ++++++++++++++++++++++++++++++++++++++++---------------------------------------- |
M | blog.py | | | 252 | ++++++++++++++++++++++++++++++++++++++++---------------------------------------- |
M | viewutils.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)