artemis/find.py
author frostbane <frostbane@programmer.net>
Tue, 24 May 2016 11:16:14 +0900
branchfeature/find
changeset 83 9cfc87967a44
parent 82 b143f9dcfb8a
child 85 fba51d84afda
permissions -rw-r--r--
add option to search the payload

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

"""
                                   ..  ┌  -:  .
                                  ([]▄▄├]▄▄▄¿┐`,
    .¡                      ,. D{}▓███████████░█∩^}                       =
    ,▓¼                      .▐▓▓█████████████▓██▓▓,                     (▓
     █▌╕.                   ─@╣████████████████████▄─                   ./▀'
    :▓█▌Ç...             └ '└▓██████████████████████Ö'`            ..  '└`'┌
     \ g█████▄Ü┌        C g██████████████████████████╗─  ²   ²   t▄▓██ù- `╞
      ¡ Å╠█▌╠█▓▓▓▓▌N@▌|   ╓██████████████████████████▌  .╣▓▓▓▓▓▓▓]▐╙ÅÅ∩!
      ` ` ╫½┌▀▀▀▀▀▀░╫▌┘    ▀▀▀▀▀██████████████████▀█▀C  :╝▀▀▀▀▀▀▀.   `:
          L ─ⁿ'``' ─       ''' ─█████████████████▌─█=       ╞º'`'º──
                                    ..Å╫█╛.             ~
                                   ╔▄▄▓██▓▄¿        .
                            -   ]:├╣██▀▀▀▀▀▒Q¿.¿{  )=
                            <─Ω╚█░▓███'──h⌂▓██╡██▌Ü─4
                           `┌ ▐▓█▄▓██╟≤¡yQ▄███▓█▌▐▓M
                           ^. └▀▀▄▓██▄▒▄▄╬▄████▀ └▀╙`
                            «-'''█▓▓█▓██▓██▓▓██└``'⌐
                                 #╛ µ:▓▄;xφ(⌠ █
                                 ╗µ  ~╙'`   `┌█╣
                                 ╝Ñ. ''  '   L▀▀
                                    ~         ,
                                    ^
                                    ÷        ─.
                                    N.   ∩ts  )
                                 ∩    ²⌠  `   ─
                                   (@   ~▒▒  .
                                 ` └▀/ .>▀▀  :
                                     ⌐   ,⌐



 Artemis
 Version 0.5.1
 Copyright (c) 2016 Frostbane Ac
 Apache-2.0, APL-1.0, 0BSD Licensed. ?
 www.??.com
 ?? inspired script.

"""

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

__author__ = 'frostbane'
__date__ = '2016/05/24'


class ArtemisFind:
    commands = [
        # dashed options will be resolved to underscores
        # case-sensitive => case_sensitive
        ('p', 'property', "subject", 'Issue property to match. '
                                     '[state, from, subject, date, '
                                     'priority, resolution, etc..].'
                                     '(e.g. hg isearch -p from me)'),
        ('n', 'no-property', None, 'Do not match the property. Use '
                                   'with --message to search only '
                                   'the message. The --property will '
                                   'be ignored.'),
        ('m', 'message', None, 'Search the message. If no match is '
                               'found it will then search the '
                               'property for a match. Use '
                               '--no-property or use a blank '
                               'property to ignore the property '
                               'search.'
                               '(e.g. hg isearch -mp "" "the bug")'),
        ('c', 'case-sensitive', None, 'Case sensitive search.'),
        ('r', 'regex', None, 'Use regular expressions. '
                             'Exact option will be ignored.'
                             '(e.g. hg isearch -rmn "todo *(:)?"'),
        ('e', 'exact', None, 'Use exact comparison. '
                             'Like comparison is used if exact is'
                             'uspecified.'),
    ]

    usage = 'hg ifind [OPTIONS] QUERY'
    ui = None
    repo = None
    opts = []

    def __init__(self):
        pass

    def __is_hit(self, query, search_string):
        opts = self.opts

        exact_comp = opts["exact"] and not opts["regex"]
        regexp_comp = opts["regex"]

        if regexp_comp:
            re_pattern = re.compile(query)
            return re_pattern.search(search_string)
        elif exact_comp:
            return query == search_string
        else:
            return query in search_string

    def __get_payload(self, mbox, key):
        # todo move to Artemis class since this is static, and can
        #      be possible reused in other classes
        payload = ""

        for part in mbox[key].walk():
            ctype = part.get_content_type()
            maintype, subtype = ctype.split('/', 1)

            if maintype == 'multipart':
                continue

            if ctype == 'text/plain':
                payload = part.get_payload().strip()

        return payload

    def __search_payload(self, mbox, query, case_sens):
        keys = mbox.keys()
        for key in keys:
            payload = self.__get_payload(mbox, key)

            if not payload:
                continue

            if not case_sens:
                payload = payload.lower()

            if self.__is_hit(query, payload):
                return True

        return False

    def __search_property(self, mbox, query, case_sens):
        query_filter = self.opts["property"]

        root = Artemis.find_root_key(mbox)
        search_string = mbox[root][query_filter]

        # non existing property
        if not search_string:
            return False

        if not case_sens:
            search_string = search_string.lower()

        if self.__is_hit(query, search_string):
            return True

        return False

    def __search_issues(self, query):
        case_sens = self.opts["case_sensitive"]
        search_payload = self.opts["message"]
        no_property = self.opts["no_property"]

        if not case_sens:
            query = query.lower()

        hits = []

        mboxes = Artemis.get_all_mboxes(self.ui, self.repo)
        for mbox in mboxes:
            has_hit = False

            if search_payload:
                has_hit = self.__search_payload(mbox,
                                                query,
                                                case_sens)

            if has_hit:
                hits.append(mbox.issue)
                # if the message has a hit there is no need to
                # continue searching for a hit with the property
                continue

            if no_property:
                # ignore the property search
                continue

            if self.__search_property(mbox, query, case_sens):
                hits.append(mbox.issue)

        return hits

    def __show_results(self, issues):
        ui = self.ui
        repo = self.repo

        for issue in issues:
            mbox = mailbox.Maildir(issue,
                                   factory=mailbox.MaildirMessage)
            root = Artemis.find_root_key(mbox)
            # print mbox.items()
            # print mbox.keys()
            # print mbox.values()

            num_replies = str(len(mbox.keys()) - 1)
            # print mbox[root]["message-id"]
            ui.write("%s (%s) [%s]: %s\n" %
                     (
                         Artemis.get_issue_id(ui, repo, issue),
                         num_replies.rjust(3, " "),
                         mbox[root]["state"],
                         mbox[root]["subject"])
                     )

    def find(self, ui, repo, query, **opts):
        self.opts = opts
        self.ui = ui
        self.repo = repo

        issues = self.__search_issues(query)
        self.__show_results(issues)