/stuff/changes/index.cgi

#!/usr/bin/env python
"""
index.cgi - Serve Changes
Author: Sean B. Palmer, inamidst.com
"""

import cgitb; cgitb.enable()
import sys, os, re, tarfile

import changes

r_month = re.compile(r'^\d{4}-\d{2}$')

def header(status=200, mime='text/html', other={}): 
   if status != 200: 
      print 'Status:', status
   for (key, value) in other.iteritems(): 
      print key + ':', value
   print 'Content-Type:', mime
   print 

def template(mappings): 
   f = open('template.txt')
   bytes = f.read()
   f.close()

   for (key, value) in mappings.iteritems(): 
      bytes = bytes.replace('$' + key, value)
   return bytes

def listMonths(): 
   months = []
   for month in changes.monthLogsBackwards(): 
      if changes.monthExists(month): 
         months.append(month)
   months = sorted(months)

   def generator(): 
      yield '<p>Access all of the archives:</p>'
      yield ''
      yield '<ul>'

      oldyear = None
      for month in months: 
         year = month[:4]
         if year != oldyear: 
            if oldyear is not None: 
               yield '</li>'
            yield '<li>' + year + ': '

            if year == '2005': 
               yield '01 &#xB7; 02 &#xB7; 03 &#xB7; 04 &#xB7; 05 &#xB7;'
            oldyear = year
         else: yield ' &#xB7; ' 

         yield ' <a href="%s">%s</a>' % (month, month[-2:])
      yield '</ul>'

   return '\n'.join(generator()) + '\n'

def homepage(method, path, match): 
   header()
   print template({
      'title': 'Changes and Updates', 
      'changes': changes.latestAsHTML(15), 
      'months': listMonths()
   })

def generateSummary(n=250): 
   oldmonth = None
   for (day, time, message) in changes.genchanges(n): 
      month = day[:7]
      if month != oldmonth: 
         if oldmonth is not None: 
            yield '</ul>'
         yield '<h2>%s</h2>' % month
         yield '<ul>'
         oldmonth = month
      link = '<a href="/changes/%s#T%s">%s</a>' % (day[:7], day, message)
      yield '<li>%s</li>' % link
   yield '</ul>'

def summary(method, path, match): 
   header()
   msg = '<p>These are the 250 most recent change summaries:</p>\n\n'
   print template({
      'title': 'Change Summaries',
      'changes': msg + '\n'.join(generateSummary()), 
      'months': listMonths()
   })

def month(method, path, match, template=template): 
   header()
   month = path[1:]
   assert r_month.match(month)
   
   print template({
      'title': 'Changes and Updates for %s' % month, 
      'changes': changes.changesByMonth(month), 
      'months': ''
   })

def diff(method, path, match): 
   filename = match.group(1)

   lines = changes.getdiff(filename)
   if lines is not False: 
      header(mime='text/plain')
      for line in lines: 
         sys.stdout.write(line)
   else: 
      header(status=404, mime='text/plain')
      print "Not found:", filename

def diffget(method, path, match): 
   header()

   hmm = match.group(1)
   if len(hmm) > 1: 
      hmm = hmm.lstrip('/')
      date, abspath = hmm.split('/', 1)
      abspath = '/' + abspath
      date = date.replace('T', ' ')
   else: date, abspath = '', ''

   print """
      <form method="post">
      <p><input type="text" name="date" value="%s" />
         <input type="text" name="path" value="%s" />
         <input type="submit" value="Get Diff" /></p>
      </form>
   """ % (date, abspath)

def diffpost(method, path, match): 
   import cgi
   form = cgi.FieldStorage()
   form.__call__ = lambda s: form[s].value
   if form.has_key('date') and form.has_key('path'): 
      date = form('date').replace(' ', '-')
      path = form('path').lstrip('/')
      path = path.replace('_', '__')
      path = path.replace('/', '_') + '.diff'

      uri = '/changes/' + date + '-' + path
      header(status=303, other={'Location': uri})
   else: barf()

def head(method, path, match): 
   header()

def request(): 
   method = os.environ['REQUEST_METHOD']

   # Assume that the script is being mod_rewritten to
   script = os.environ['SCRIPT_NAME']
   script = '/'.join(script.split('/')[:-1])

   requri = os.environ['REQUEST_URI']
   assert requri.startswith(script)
   path = requri[len(script):]

   return method, path

def main(): 
   # Get the method and path
   method, path = request()

   mappings = {
      'GET /': homepage, 
      'GET /summary': summary, 
      'GET /(\d+-\d+)': month, 
      'GET /(\d+-\d+-\d+-.+\.diff)': diff, 
      'GET /diff(/.*)?': diffget, 
      'POST /diff': diffpost, 
      # 'HEAD /.*': head
   }

   for (pattern, dispatch) in mappings.iteritems(): 
      patmethod, regexp = pattern.split(' ', 1)
      r_path = re.compile(r'^%s$' % regexp)

      if method == patmethod: 
         match = r_path.match(path)
         if match: 
            dispatch(method, path, match)
            break
   else: barf()

if __name__=="__main__": 
   main()