#!/usr/bin/env python2.5
#
# This script will parse a bugs mailing list file and report on the
# quantity of messages, excluding initial bug descriptions, from each
# person
#
# A minimum value can also be configured and the default is 10
#
# 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 email.header import decode_header
from operator import itemgetter
from sys import argv, stderr, exit
import codecs

minimum = 0

MESSAGE_NOT_PARSEABLE = object()

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',
           '=> Fix Released':'fix released',
           '=> 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 new bugs.
        subject = message.get('subject', '')
        if message.get('message-id', '') != message.get('references', ''):
            sender = message.get_all('from', '')
            triagers = getaddresses(sender)
            for triager in triagers:
                person = triager[0]
                for x in decode_header(person):
                    if x[1] == 'utf-8':
                        person = x[0]
                        break
                yield 'From %s' % (person)

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

    for mailbox_file in argv[1:]:
        mailbox = PortableUnixMailbox(
            codecs.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=False):
            if v >= minimum:
                print '%20s: %d' % (k, v)
