artemis/list.py
author Sean E. Russell <sean.russell@internationalsos.com>
Wed, 15 Mar 2017 08:30:12 -0400
changeset 89 c844431600df
parent 79 3f7593f1b0ae
permissions -rw-r--r--
Updates for Mercurial 3.8(?) API compatibility, which broadly broke extensions by forcing @commands decoration.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from mercurial import hg, util, commands
import sys, os, time, random, mailbox, glob, socket, ConfigParser
from properties import ArtemisProperties
from artemis import Artemis

__author__ = 'frostbane'
__date__ = '2016/03/04'


class ArtemisList:
    commands = [
        ('a', 'all', False, 'list all issues (by default only those '
                            'with state new)'),
        ('p', 'property', [], 'list issues with specific field '
                              'values (e.g., -p state=fixed); '
                              'lists all possible values of a '
                              'property if no = sign'),
        ("", "all-properties", None, "list all available properties"),
        ('o', 'order', 'new', 'order of the issues; '
                              'choices: "new" (date submitted), '
                              '"latest" (date of the last message)'),
        ('d', 'date', '', 'restrict to issues matching the date '
                          '(e.g., -d ">12/28/2007)"'),
        ('f', 'filter', '', 'restrict to pre-defined filter '
                            '(in %s/%s*)'
                            '' % (Artemis.default_issues_dir,
                                  Artemis.filter_prefix))
    ]
    usage = 'hg ilist [OPTIONS]'

    def __init__(self):
        pass

    def __get_properties(self, ui, repo):
        """get the list of all available properties
        :param ui: mercurial ui object
        :param repo: mercurial repo object
        :return: returns a list of all available properties
        """

        properties = []

        mboxes = Artemis.get_all_mboxes(ui, repo)
        for mbox in mboxes:
            root = Artemis.find_root_key(mbox)
            properties = list(set(properties + mbox[root].keys()))

        return properties

    def __read_formats(self, ui):
        formats = []

        for key, value in ui.configitems('artemis'):
            if not key.startswith('format'):
                continue
            if key == 'format':
                Artemis.default_format = value
                continue
            formats.append((key.split(':')[1], value))

        return formats

    def __format_match(self, props, formats):
        for key, value in formats:
            eq = key.split('&')
            eq = [e.split('*') for e in eq]

            for e in eq:
                # todo check if else
                if props[e[0]] != e[1]:
                    break
            else:
                return value

        return Artemis.default_format

    def __summary_line(self, mbox, root, issue, formats):
        props = ArtemisProperties(mbox[root])
        props['id'] = issue
        # number of replies (-1 for self)
        props['len'] = len(mbox) - 1

        return self.__format_match(props, formats) % props

    def __find_mbox_date(self, mbox, root, order):
        if order == 'latest':
            keys = Artemis.order_keys_date(mbox)
            msg = mbox[keys[-1]]

        else:  # new
            msg = mbox[root]

        return util.parsedate(msg['date'])

    def __find_issues(self, ui, repo):
        issues_path = Artemis.get_issues_path(ui, repo)
        if not issues_path:
            return

        issues = Artemis.get_all_issues(ui, repo)

        return issues

    def __proc_filters(self, properties, ui, repo, opts):
        if opts['filter']:
            filters = glob.glob(
                os.path.join(Artemis.get_issues_path(ui, repo),
                             Artemis.filter_prefix + '*'))
            config = ConfigParser.SafeConfigParser()
            config.read(filters)
            if not config.has_section(opts['filter']):
                ui.write('No filter %s defined\n' % opts['filter'])
            else:
                properties += config.items(opts['filter'])

        return properties

    def __list_summaries(self, issues, properties, ui, repo, opts):
        # Process options
        show_all = opts['all']
        match_date, date_match = False, lambda x: True
        if opts['date']:
            match_date, date_match = True, util.matchdate(
                opts['date'])
        order = 'new'
        if opts['order']:
            order = opts['order']

        # Formats
        formats = self.__read_formats(ui)

        cmd_properties = Artemis.get_properties(opts['property'])
        list_properties = [p[0] for p in cmd_properties if
                           len(p) == 1]
        list_properties_dict = {}
        properties += filter(lambda p: len(p) > 1, cmd_properties)

        summaries = []
        for issue in issues:
            mbox = mailbox.Maildir(issue, factory=mailbox.MaildirMessage)
            root = Artemis.find_root_key(mbox)
            if not root:
                continue

            property_match = True
            for property, value in properties:
                if value:
                    property_match = property_match and (
                        mbox[root][property] == value)
                else:
                    property_match = property_match and (
                        property not in mbox[root])

            has_property = properties or \
                           mbox[root]['State'].upper() in \
                           [f.upper() for f in Artemis.state['resolved']]

            if not show_all and (not properties or
                        not property_match) and has_property:
                continue

            if match_date and not date_match(
                    util.parsedate(mbox[root]['date'])[0]):
                continue

            if not list_properties:
                mbox_date = self.__find_mbox_date(mbox, root, order)
                sum_line = self.__summary_line(
                    mbox,
                    root,
                    Artemis.get_issue_id(ui, repo, issue),
                    formats)

                summaries.append((sum_line, mbox_date))

            else:
                for lp in list_properties:
                    if lp in mbox[root]:
                        list_properties_dict\
                            .setdefault(lp, set())\
                            .add(mbox[root][lp])

        if not list_properties:
            summaries.sort(lambda (s1, d1), (s2, d2): cmp(d2, d1))
            for s, d in summaries:
                ui.write(s + '\n')

        else:
            for lp in list_properties_dict.keys():
                ui.write("%s:\n" % lp)
                for value in sorted(list_properties_dict[lp]):
                    ui.write("  %s\n" % value)

    def list(self, ui, repo, **opts):
        """List issues associated with the project"""

        # Find issues
        issues = self.__find_issues(ui, repo)
        if not issues:
            return

        properties = []
        if opts["all_properties"]:
            properties = self.__get_properties(ui, repo)
            for property in properties:
                ui.write("%s\n" % property)

            return

        # Process filter
        properties = self.__proc_filters(properties, ui, repo, opts)

        self.__list_summaries(issues, properties, ui, repo, opts)