#!/usr/bin/python -u

"""bzr-doap.py

A DOAP generator for a Bazaar repository.

See also: http://www.wasab.dk/morten/blog/archives/2008/01/15/doap-from-the-bazaar

Based in part on bzr-feed.
"""

__author__ = "Morten Frederiksen (morten@mfd-consult.dk)"
__copyright__ = "copyright 2008, MFD Consult"
__contributors__ = ["Sam Ruby"]
__license__ = "Python"

from bzrlib.branch import BzrBranch
from bzrlib.bzrdir import BzrDir
from xml.sax import saxutils
from ConfigParser import SafeConfigParser
import time, os, cgi, sys, re, hashlib

class DoapConfig(SafeConfigParser):
    """DoapConfig
    """
    def pl(self, section, option, default=None, level=1):
        elem = option
        if -1==elem.find(":"):
            elem = "doap:" + elem
        else:
            option = option.replace(":", "_")
        if self.has_option(section, option):
            print "  " * level + "<" + elem + ">" + _e(self.get(section, option)) + "</" + elem + ">"
        elif default:
            print "  " * level + "<" + elem + ">" + _e(default) + "</" + elem + ">"

    def pr(self, section, option, default=None, level=1):
        elem = option
        if -1==elem.find(":"):
            elem = "doap:" + elem
        else:
            option = option.replace(":", "_")
        if self.has_option(section, option):
            print "  " * level + "<" + elem + " rdf:resource='" + _e(self.get(section, option)) + "'/>"
        elif default:
            print "  " * level + "<" + elem + " rdf:resource='" + _e(default) + "'/>"

def status(code, msg):
    if code!=304: print "Content-Type: text/plain"
    print "Status: " + str(code) + " " + msg
    print
    if code!=304: print msg
    sys.exit()

def _e(msg):
    return saxutils.escape(msg.encode('utf-8'))

# Sanity check
if not os.environ.get('REQUEST_METHOD','GET') in ['GET', 'HEAD']:
    status(405, "Method Not Allowed")

# Basic repository info
try:
    if cgi.FieldStorage().has_key("dir"):
        dir = cgi.FieldStorage().getvalue("dir")
    else:
        dir = "."
    repo = BzrDir.open(dir).open_repository()
    branch_dir = dir
    if cgi.FieldStorage().has_key("branch"):
        branch_dir += '/' + cgi.FieldStorage().getvalue("branch")
except:
    status(404, "Repository Not Found")
if not len(repo.all_revision_ids()):
    status(404, "No Revisions Found")
if not os.path.exists(dir + "/.doaprc"):
    status(404, "DOAP Configuration Not Found")

# Basic branch info
branch = BzrDir.open(branch_dir).open_branch()
first_rev = repo.get_revision(branch.get_rev_id(1))
last_rev = repo.get_revision(branch.get_rev_id(branch.revno()))

# Support 304
if_none_match = os.environ.get('HTTP_IF_NONE_MATCH', '')
if_modified_since = os.environ.get('HTTP_IF_MODIFIED_SINCE', '')
if if_none_match and ('"' + str(hashlib.md5(branch.nick + str(last_rev.timestamp)).hexdigest()) + '"') == if_none_match:
    status(304, "Not Modified")

# Location
try:
    baseuri = "http://" + os.environ['HTTP_HOST']
    if os.environ['SERVER_PORT']!="80": baseuri += ":" + os.environ['SERVER_PORT']
    if os.path.basename(os.path.dirname(os.environ['REQUEST_URI'])) == dir:
        baseuri += os.path.dirname(os.environ['REQUEST_URI'])
    else:
        baseuri += re.sub(r'\.\w+$', '', os.environ['REQUEST_URI'])
except:
    baseuri = "."

# Web?
if os.environ.get('SCRIPT_NAME',None):
    print "Last-Modified: " + time.ctime(last_rev.timestamp + time.timezone) + " GMT"
    print 'ETag: "%s"' % str(hashlib.md5(branch.nick + str(last_rev.timestamp)).hexdigest())
    print "Content-Type: application/rdf+xml\r\n\r\n",

# HEAD?
if os.environ.get('REQUEST_METHOD','GET') in ['HEAD']:
    sys.exit()

# Prepare for output
doap = DoapConfig()
doap.read(branch_dir + "/../.doaprc")
doap.read(branch_dir + "/.doaprc")

# Header
print """<?xml version='1.0' encoding='utf-8'?>
<rdf:RDF
  xmlns:doap='http://usefulinc.com/ns/doap#'
  xmlns:foaf='http://xmlns.com/foaf/0.1/'
  xmlns:dc='http://purl.org/dc/elements/1.1/'
  xmlns:dcterms='http://purl.org/dc/terms/'
  xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<doap:Project>"""

# Project
doap.pl("Project", "name", branch._get_nick())
doap.pl("Project", "shortname", branch._get_nick())
doap.pl("Project", "shortdesc")
doap.pl("Project", "description")
doap.pr("Project", "homepage", baseuri + "/")
doap.pr("Project", "download-page", baseuri + "/")
doap.pl("Project", "programming-language")
doap.pr("Project", "license")
print "  <doap:created>" + time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime(first_rev.timestamp)) + "</doap:created>"
print "  <dcterms:modified>" + time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime(last_rev.timestamp)) + "</dcterms:modified>"

# Maintainer
print "  <doap:maintainer>"
print "    <foaf:Person>"
doap.pl("Maintainer", "foaf:name", first_rev.committer.split("<")[0].rstrip(), 3)
if doap.has_option("Maintainer", "email"):
    mbox = hashlib.sha1("mailto:" + doap.get("Maintainer", "email"))
else:
    mbox = hashlib.sha1("mailto:" + first_rev.committer.split("<")[1].split(">")[0])
print "      <foaf:mbox_sha1sum>" + mbox.hexdigest() + "</foaf:mbox_sha1sum>"
doap.pr("Maintainer", "foaf:homepage", None, 3)
print "    </foaf:Person>"
print "  </doap:maintainer>"

# Other project members
roles = ["Developer", "Documenter", "Translator", "Tester", "Helper"]
for role in roles:
    member = 1
    while doap.has_section(role + " " + str(member)):
        rolemember = role + " " + str(member)
        print "  <doap:" + role.lower() + ">"
        print "    <foaf:Person>"
        doap.pl(rolemember, "foaf:name", None, 3)
        if doap.has_option(rolemember, "email"):
            mbox = hashlib.sha1("mailto:" + doap.get(rolemember, "email"))
            print "      <foaf:mbox_sha1sum>" + mbox.hexdigest() + "</foaf:mbox_sha1sum>"
        doap.pr(rolemember, "foaf:homepage", None, 3)
        print "    </foaf:Person>"
        print "  </doap:" + role.lower() + ">"
        member += 1

# Repository
print "  <doap:repository>"
print "    <doap:Repository>"
doap.pr("Repository", "browse", baseuri + "/", 3)
doap.pr("Repository", "location", baseuri + "/", 3)
print "    </doap:Repository>"
print "  </doap:repository>"

# Version
print "  <doap:release>"
print "    <doap:Version>"
doap.pl("Version", "name")
print "      <doap:revision>" + str(branch.revno()) +"</doap:revision>"
print "      <doap:created>" + time.strftime("%Y-%m-%dT%H:%M:%SZ",time.gmtime(last_rev.timestamp)) + "</doap:created>"
print "    </doap:Version>"
print "  </doap:release>"

# Footer
print """  <foaf:isPrimaryTopicOf rdf:resource=''/>
</doap:Project>
</rdf:RDF>"""

