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 |
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)'), |