#!/usr/bin/env python """ azimuth.cgi - Azimuth Publishing Software Author: Sean B. Palmer, inamidst.com Acknowldegements: Logo Author: Cody Woodard, http://purl.org/net/d8uv Exposes certain aspects of a textual dataset. """ import cgitb; cgitb.enable() import sys, os, re, datetime env = os.environ.get config = {} # Configuration Station # This is where you set all the configuration options config['title'] = "What Planet is This?" config['tagline'] = "Serendipitous sense sans sloganity." config['author'] = "Sean B. Palmer" config['datadir'] = '../notes' config['entries'] = '10' # @@ config['validate'] = "True" # Examples: # config['entries'] = "10" # config['datadir'] = "/home/yourname" def mappings(): """Return the Dispatchment Mappings. For the slightly more adventurous, it's possible to change the mapping of your site's URIs. For example, if you want your contents page at /inside instead of /contents, you can change the relevant entry in the dictionary below. Only do this if you're sure you know what's going on. """ return { '/(?P\d+)-(?P\d+)': archives('', ''), # '/(?P\w+).comments)': comment(''), '/contents': archives(), '/index.atom': atomfeed(), '/': homepage() } def uri(): """Return the base URI for the weblog.""" host = env('SERVER_NAME') parts = [env('REQUEST_URI'), env('SCRIPT_NAME')] uri = 'http://' + host + os.path.commonprefix(parts) if not uri.endswith('/'): uri += '/' return uri # Defaults config.setdefault('title', 'My Weblog') config.setdefault('tagline', 'We all live in a yellow submarine.') config.setdefault('author', 'A.N. Other') config.setdefault('datadir', '.') config.setdefault('entries', '25') config.setdefault('dateformat', '$day $monthname $year') config.setdefault('timezone', 'GMT') config.setdefault('tag', 'tag:' + env('SERVER_NAME') + ',2005:') config.setdefault('uri', uri()) config.setdefault('comments', 'inline') # At the third pip, the time will be... timezone = config.get('timezone') if timezone: os.environ['TZ'] = timezone def filenames(): def yielder(): for filename in os.listdir(config['datadir']): if filename.endswith('.txt'): fn = os.path.join(config['datadir'], filename) date = datetime.datetime.fromtimestamp(os.path.getmtime(fn)) yield (date, filename.lstrip('./')) return reversed(sorted(yielder())) def months(): result = set() for filename in os.listdir(config['datadir']): if filename.endswith('.txt'): fn = os.path.join(config['datadir'], filename) date = datetime.date.fromtimestamp(os.path.getmtime(fn)) date = date.replace(day=1) result.add(date) return reversed(sorted(result)) def printfile(filename, mapdict): from string import Template f = open(filename) for line in f: sys.stdout.write(Template(line).substitute(mapdict)) f.close() def header(status, mime=None): if mime is None: mime = "text/html; charset=utf-8" if status != 200: print "Status: %s" % status print "Content-Type: %s" % mime print def notfound(filename): header(404) print '

Page Not Found: %s %s.

' % (os.getcwd(), filename) def info(filename): fn = os.path.join(config['datadir'], filename + '.txt') f = open(fn) notetitle = f.next() notebody = ''.join(f) f.close() mtime = os.path.getmtime(fn) notetime = datetime.datetime.fromtimestamp(mtime) notetitle = notetitle.rstrip('\r\n') notebody = notebody.lstrip('\r\n') return notetime, notetitle, notebody def head(flavour, headaug=None): if headaug is None: title = config['title'] else: title = config['title'] + ' - ' + headaug dictmap = {'heading': config['title'], 'now': datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ')} dictmap.update(config) dictmap['title'] = title import itertools entries = itertools.islice(filenames(), 0, int(config['entries'])) notetime, filename = entries.next() notetime = notetime.replace(second=0) dictmap['updated'] = notetime.strftime('%Y-%m-%dT%H:%M:%SZ') printfile(os.path.join(config['datadir'], 'head.' + flavour), dictmap) def date(dateformat, notetime, prefix=None): dictmap = {'year': notetime.year, 'month': '%02i' % notetime.month, 'monthname': notetime.strftime('%b'), 'day': '%02i' % notetime.day} dictmap.update(config) from string import Template if prefix is not None: dateformat = prefix + dateformat print Template('

%s

' % dateformat).substitute(dictmap) print def entry(flavour, filename, notetime, notetitle, notebody): dictmap = {'notetitle': notetitle, 'notebody': notebody, 'year': notetime.year, 'month': '%02i' % notetime.month, 'day': '%02i' % notetime.day, 'hour': '%02i' % notetime.hour, 'minute': '%02i' % notetime.minute, 'mtime': notetime.strftime('%Y-%m-%dT%H:%M:%SZ'), 'filename': filename} dictmap.update(config) printfile(os.path.join(config['datadir'], 'entry.' + flavour), dictmap) def foot(flavour): archives = [] for month in months(): fmt = '
  • %B %Y
  • \n' archives.append(month.strftime(fmt)) dictmap = {'archives': ''.join(archives)} dictmap.update(config) if flavour == 'ads': sys.path.append('../odds/ads') try: import ads, brokerage except ImportError: pass else: dictmap['ads'] = ads.get() + brokerage.text() printfile(os.path.join(config['datadir'], 'foot.' + flavour), dictmap) def dispatcher(func): def outer(*args): args = (arg.strip('<>') for arg in args) def inner(m): kargs = {} groupdict = m.groupdict() for arg in args: if groupdict.has_key(arg): kargs[arg] = groupdict[arg] else: raise ValueError("No such group: %s" % arg) return func(**kargs) return inner return outer @dispatcher def archives(**kargs): if kargs.has_key('year') and kargs.has_key('month'): year = int(kargs['year']) month = int(kargs['month'].lstrip('0')) header(200) head('html') print '

    Contents

    ' # @@ h2! print '
      ' for (notetime, filename) in filenames(): if kargs.has_key('year') and kargs.has_key('month'): if (year != notetime.year) or (month != notetime.month): continue filename = filename[:-len('.txt')] notetime, notetitle, notebody = info(filename) sys.stdout.write('
    • ' % filename) sys.stdout.write(notetitle) sys.stdout.write(' (') sys.stdout.write(str(notetime.date())) print ')
    • ' print '
    ' foot('html') @dispatcher def comment(**kargs): header(200) head('html') commentfn = os.path.join(config['datadir'], 'comment.html') if os.path.isfile(commentfn): print open(commentfn).read() else: print '

    (You need to write <comment.html>)

    ' filename = os.path.join(config['datadir'], kargs['filename']) if os.path.isfile(filename + '.html'): print open(filename + '.html').read() foot('html') @dispatcher def atomfeed(**kargs): header(200, 'application/atom+xml') head('atom') import itertools entries = itertools.islice(filenames(), 0, int(config['entries'])) for (notetime, filename) in entries: filename = filename[:-len('.txt')] notetime, notetitle, notebody = info(filename) notetime = notetime.replace(second=0) notebody = notebody.replace(']]>', ']]>]]>(You need to write <comment.html>)

    ' fn = os.path.join(config['datadir'], filename) if os.path.isfile(fn + '-annotations.html'): print open(fn + '-annotations.html').read() foot('html') def main(): path = env('PATH_INFO') or '/' # weird = '/redirect:' + env('SCRIPT_NAME') # if path.startswith(weird): # path = path[len(weird):] maps = mappings() for regexp in maps.iterkeys(): m = re.compile('^%s$' % regexp).match(path) if m: maps[regexp](m) return filename = path.lstrip('/') if os.path.exists(filename + '.txt'): entrypage(filename) else: notfound(filename) if __name__=="__main__": main()