artemis/artemis.py
changeset 66 4129876c8b86
parent 63 c384fa42f8a2
child 67 30bd40ef9165
equal deleted inserted replaced
65:0bfd48368c17 66:4129876c8b86
    11 from email.mime.audio import MIMEAudio
    11 from email.mime.audio import MIMEAudio
    12 from email.mime.base import MIMEBase
    12 from email.mime.base import MIMEBase
    13 from email.mime.image import MIMEImage
    13 from email.mime.image import MIMEImage
    14 from email.mime.multipart import MIMEMultipart
    14 from email.mime.multipart import MIMEMultipart
    15 from email.mime.text import MIMEText
    15 from email.mime.text import MIMEText
    16 
    16 from itertools import izip
    17 from    termcolor       import colored
       
    18 
    17 
    19 
    18 
    20 state = { 'new':      ['new'],
    19 state = { 'new':      ['new'],
    21           'resolved': ['fixed', 'resolved'] }
    20           'resolved': ['fixed', 'resolved'] }
    22 annotation = { 'resolved': 'resolution' }
       
    23 default_state = 'new'
    21 default_state = 'new'
    24 default_issues_dir = ".issues"
    22 default_issues_dir = ".issues"
    25 filter_prefix = ".filter"
    23 filter_prefix = ".filter"
    26 date_format = '%a, %d %b %Y %H:%M:%S %1%2'
    24 date_format = '%a, %d %b %Y %H:%M:%S %1%2'
    27 maildir_dirs = ['new','cur','tmp']
    25 maildir_dirs = ['new','cur','tmp']
    28 
    26 default_format = '%(id)s (%(len)3d) [%(state)s]: %(Subject)s'
    29 
    27 
    30 def ilist(ui, repo, **opts):
    28 def ilist(ui, repo, **opts):
    31     """List issues associated with the project"""
    29     """List issues associated with the project"""
    32 
    30 
    33     # Process options
    31     # Process options
    38         match_date, date_match = True, util.matchdate(opts['date'])
    36         match_date, date_match = True, util.matchdate(opts['date'])
    39     order = 'new'
    37     order = 'new'
    40     if opts['order']:
    38     if opts['order']:
    41         order = opts['order']
    39         order = opts['order']
    42 
    40 
    43     # Colors
    41     # Formats
    44     colors = _read_colors(ui)
    42     formats = _read_formats(ui)
    45 
    43 
    46     # Find issues
    44     # Find issues
    47     issues_dir = ui.config('artemis', 'issues', default = default_issues_dir)
    45     issues_dir = ui.config('artemis', 'issues', default = default_issues_dir)
    48     issues_path = os.path.join(repo.root, issues_dir)
    46     issues_path = os.path.join(repo.root, issues_dir)
    49     if not os.path.exists(issues_path): return
    47     if not os.path.exists(issues_path): return
    81 
    79 
    82         if not show_all and (not properties or not property_match) and (properties or mbox[root]['State'].upper() in [f.upper() for f in state['resolved']]): continue
    80         if not show_all and (not properties or not property_match) and (properties or mbox[root]['State'].upper() in [f.upper() for f in state['resolved']]): continue
    83         if match_date and not date_match(util.parsedate(mbox[root]['date'])[0]): continue
    81         if match_date and not date_match(util.parsedate(mbox[root]['date'])[0]): continue
    84 
    82 
    85         if not list_properties:
    83         if not list_properties:
    86             summaries.append((_summary_line(mbox, root, issue[len(issues_path)+1:], colors),     # +1 for trailing /
    84             summaries.append((_summary_line(mbox, root, issue[len(issues_path)+1:], formats),     # +1 for trailing /
    87                               _find_mbox_date(mbox, root, order)))
    85                               _find_mbox_date(mbox, root, order)))
    88         else:
    86         else:
    89             for lp in list_properties:
    87             for lp in list_properties:
    90                 if lp in mbox[root]:    list_properties_dict.setdefault(lp, set()).add(mbox[root][lp])
    88                 if lp in mbox[root]:    list_properties_dict.setdefault(lp, set()).add(mbox[root][lp])
    91 
    89 
    92     if not list_properties:
    90     if not list_properties:
    93         summaries.sort(lambda (s1,d1),(s2,d2): cmp(d2,d1))
    91         summaries.sort(lambda (s1,d1),(s2,d2): cmp(d2,d1))
    94         for s,d in summaries:
    92         for s,d in summaries:
    95             ui.write(s)
    93             ui.write(s + '\n')
    96     else:
    94     else:
    97         for lp in list_properties_dict.keys():
    95         for lp in list_properties_dict.keys():
    98             ui.write("%s:\n" % lp)
    96             ui.write("%s:\n" % lp)
    99             for value in sorted(list_properties_dict[lp]):
    97             for value in sorted(list_properties_dict[lp]):
   100                 ui.write("  %s\n" % value)
    98                 ui.write("  %s\n" % value)
   416         # Set the filename parameter
   414         # Set the filename parameter
   417         attachment.add_header('Content-Disposition', 'attachment', filename=filename)
   415         attachment.add_header('Content-Disposition', 'attachment', filename=filename)
   418         outer.attach(attachment)
   416         outer.attach(attachment)
   419     return outer
   417     return outer
   420 
   418 
   421 def _status_msg(msg):
   419 def _read_formats(ui):
   422     s = msg['State']
   420     formats = []
   423     if s in annotation:
   421     global default_format
   424         return '%s=%s' % (s, msg[annotation[s]])
       
   425     else:
       
   426         return s
       
   427 
       
   428 def _read_colors(ui):
       
   429     colors = {}
       
   430 
   422 
   431     for k,v in ui.configitems('artemis'):
   423     for k,v in ui.configitems('artemis'):
   432         if k == 'issues': continue
   424         if not k.startswith('format'): continue
   433         k = k.split('.')
   425         if k == 'format':
   434         s = k[0]; t = k[1]
   426             default_format = v
   435         if s not in colors: colors[s] = {}
   427             continue
   436         colors[s][t] = v
   428         formats.append((k.split(':')[1], v))
   437 
   429 
   438     return colors
   430     return formats
   439 
   431 
   440 def _color_summary(line, msg, colors):
   432 def _format_match(props, formats):
   441     s = msg['State']
   433     for k,v in formats:
   442     for alias, l in state.items():
   434         eq = k.split('&')
   443         if s in l: s = alias; break
   435         eq = [e.split('*') for e in eq]
   444     if s in colors:
   436         for e in eq:
   445         color    = colors[s]['color']         if 'color'    in colors[s] else None
   437             if props[e[0]] != e[1]:
   446         on_color = colors[s]['on_color']      if 'on_color' in colors[s] else None
   438                 break
   447         attrs    = colors[s]['attrs'].split() if 'attrs'    in colors[s] else None
   439         else:
   448         return colored(line, color, on_color, attrs)
   440             return v
   449     else:
   441 
   450         return line
   442     return default_format
   451 
   443 
   452 def _summary_line(mbox, root, issue, colors):
   444 def _summary_line(mbox, root, issue, formats):
   453     line = "%s (%3d) [%s]: %s\n" % (issue,
   445     props = PropertiesDictionary(mbox[root])
   454                                     len(mbox)-1,                # number of replies (-1 for self)
   446     props['id']  = issue
   455                                     _status_msg(mbox[root]),
   447     props['len'] = len(mbox)-1              # number of replies (-1 for self)
   456                                     mbox[root]['Subject'])
   448 
   457     return _color_summary(line, mbox[root], colors)
   449     return _format_match(props, formats) % props
       
   450 
       
   451 class PropertiesDictionary(dict):
       
   452     def __init__(self, msg):
       
   453         # Borrowed from termcolor
       
   454         for k,v in zip(['bold', 'dark', '', 'underline', 'blink', '', 'reverse', 'concealed'], range(1, 9)) + \
       
   455                    zip(['grey', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'], range(30, 38)):
       
   456             self[k] = '\033[' + str(v) + 'm'
       
   457         self['reset']  = '\033[0m'
       
   458         del self['']
       
   459 
       
   460         for k,v in msg.items():
       
   461             self[k] = v
       
   462 
       
   463     def __contains__(self, k):
       
   464         return super(PropertiesDictionary, self).__contains__(k.lower())
       
   465 
       
   466     def __getitem__(self, k):
       
   467         if k not in self: return ''
       
   468         return super(PropertiesDictionary, self).__getitem__(k.lower())
       
   469 
       
   470     def __setitem__(self, k, v):
       
   471         super(PropertiesDictionary, self).__setitem__(k.lower(), v)
       
   472 
   458 
   473 
   459 cmdtable = {
   474 cmdtable = {
   460     'ilist':    (ilist,
   475     'ilist':    (ilist,
   461                  [('a', 'all', False,
   476                  [('a', 'all', False,
   462                    'list all issues (by default only those with state new)'),
   477                    'list all issues (by default only those with state new)'),