#!/usr/bin/env python2.5
#
# This script will parse a bugs mailing list file for messages for a
# triager and return counts of their actions e.g.
# From brian@ubuntu.com: 273
# brian@ubuntu.com assigned to package: 59
# brian@ubuntu.com added tags: 45
# triager can be either an e-mail address or the name that appears in the
# "From" portion of the e-mail
#
# Copyright 2008 Canonical, Ltd
# Author: Brian Murray <brian@ubuntu.com>
# Licensed under the GNU General Public License, version 3.



from mailbox import PortableUnixMailbox
from email import message_from_file
from email.errors import MessageParseError
from email.utils import parseaddr
from email.utils import getaddresses
from sys import argv, stderr, exit
from operator import itemgetter
from re import search


MESSAGE_NOT_PARSEABLE = object()

# Information regarding how a bug is modified appears in the e-mail body
# The following changes are the ones I (bdmurray) thought interesting
# They may change depending on what Launchpad e-mails look like
# Last updated: 20080721
bodies = { 'Summary changed':'titles modified',
           'Description changed':'descriptions modified',
           'Sourcepackagename: None':'assigned to package',
           'Tags added':'added tags',
           'Bug watch added':'added bug watches',
           'This bug is a duplicate of':'marked as duplicate',
           'This bug is no longer a duplicate of':'unmarked as duplicate',
           '** bug changed to question':'made questions',
           '=> Incomplete':'incompleted',
           '=> Confirmed':'confirmed',
           '=> Triaged':'triaged',
           '=> In Progress':'in progressed',
           '=> Fix Committed':'fix committed',
           '=> Fix Released':'fix released',
           '''=> Won't Fix''':'''won't fixed''',
           '=> Invalid':'invalidated'
         }

def message_factory(fp):
    try:
        return message_from_file(fp)
    except MessageParseError:
        # Don't return None since that will stop the mailbox iterator.
        return MESSAGE_NOT_PARSEABLE


def show_progress(iterator, interval=100):
    """Show signs of progress."""
    for count, item in enumerate(iterator):
        if count % interval == 0:
            stderr.write('.')
        yield item


def scan_bugs(messages):
    for count, message in enumerate(messages):
        # Skip broken messages.
        if message is MESSAGE_NOT_PARSEABLE:
            continue

        # Check it's from a Launchpad bug.
        reply_to = message['reply-to']
        if reply_to is None:
            continue
        reply_name, reply_address = parseaddr(reply_to)
        reply_local_part, reply_domain = reply_address.split('@')
        if not (reply_domain == 'bugs.launchpad.net' and
                reply_local_part.isdigit()):
            continue
        bug_id = int(reply_local_part)

        # Only process responses to initial bug reports
        subject = message.get('subject', '')
        if message.get('message-id', '') != message.get('references', ''):
            sender = message.get('from', '')
            if triager in sender:
                yield 'From %s' % (triager)
                payload = message.get_payload()
                for string in bodies.keys():
                    if string in payload:
                        yield '%s %s' % (triager, bodies[string])

if __name__ == '__main__':
    # Check if the amount of arguments is correct
    if len(argv) < 3 or argv[1] in ('help', '-h', '--help'):
        print 'Usage: %s <email_address> <bug_mailinglist_file> [<bug_mailinglist_file> ...]' % argv[0]
        exit(1)

    triager = argv[1]


    for mailbox_file in argv[2:]:
        mailbox = PortableUnixMailbox(
            open(mailbox_file, 'rb'), message_factory)

        counts = {}
        for test in scan_bugs(show_progress(mailbox)):
            counts[test] = counts.setdefault(test, 0) + 1
        print

        for k,v in sorted(counts.items(), key=itemgetter(1), reverse=True):
            print '%20s: %d' % (k, v)
