/trac-attack.py
#!/usr/bin/python
import ConfigParser
from optparse import OptionParser
import os, os.path, sys
import xmlrpclib
import textwrap

config = ConfigParser.RawConfigParser()
config.read(os.path.join(os.environ['HOME'], '.trac-attack.rc'))
default_trac = config.get('main', 'default')

options = OptionParser()
options.add_option('-t', '--trac', dest = "trac",
                   help = "Which trac to connect to")

def open_rpc(trac):
    url = config.get(trac, 'url')
    return xmlrpclib.ServerProxy(url)

def list_tickets(tickets):
    listing_wrapper = textwrap.TextWrapper(
            width = 63, replace_whitespace = True, drop_whitespace = True,
            subsequent_indent = ' ' * 17)

    print "%-7s %-8s %s" % ('ID', 'priority', 'summary')
    print '-' * 70
    for t in tickets:
        print "#%-6d %-8s" % (t[0], t[3]['priority']), listing_wrapper.fill(t[3]['summary'])

def show_ticket(ticket):
    description_wrap = textwrap.TextWrapper(width = 80)

    (id, time_created, time_changed, attributes) = ticket

    print "Ticket #%d (%s %s)" % (id, attributes['status'], attributes['type'])
    print attributes['summary']
    print

    fields = [
            [('Reported by', 'reporter'), ('Owned by', 'owner')],
            [('Priority', 'priority'), ('Milestone', 'milestone')],
            [('Component', 'component'), ('Version', 'version')],
            [('Keywords', 'keywords'), ('Cc', 'cc')],
    ]
    for row in fields:
        print "%-39s %-39s" % tuple(map(lambda x: "%s: %s" % (x[0], attributes[x[1]]), row))

    print
    for line in attributes['description'].split("\n"):
        print description_wrap.fill(line)
    print

def editor():
    raise NotImplementedError

def comment_on_ticket(id, comment):
    raise NotImplementedError

def main():
    (flags, args) = options.parse_args()
    trac = flags.trac or default_trac
    server = open_rpc(trac)
    user = config.get(trac, 'user')
    
    if args[0] == 'ls' or args[0] == 'list':
        if len(args) > 1:
            try:
                query = config.get('queries', args[1])
            except ConfigParser.NoOptionError:
                print "I couldn't find the query %s" % args[1]
                sys.exit(1)
        else:
            try:
                query = config.get('queries', 'default')
            except ConfigParser.NoOptionError:
                query = "status!=closed&owner=$USER"
        tickets = server.ticket.query(query)
        multicall = xmlrpclib.MultiCall(server)
        for t in tickets:
            multicall.ticket.get(t)
        list_tickets(multicall())
    elif args[0] == 'info' or args[0] == 'show':
        id = int(args[1])
        ticket = server.ticket.get(id)
        show_ticket(ticket)
    elif args[0] == 'comment':
        id = int(args[1])
        comment = editor()
        comment_on_ticket(id, comment)
    else:
        print "Command %s not understood" % args[0]

if __name__ == "__main__":
    main()