artemis.py
changeset 21 5b3579dc7abf
parent 20 1630cf85c7f7
child 24 17a8293bbbbf
equal deleted inserted replaced
20:1630cf85c7f7 21:5b3579dc7abf
    41             properties += config.items(opts['filter'])
    41             properties += config.items(opts['filter'])
    42 
    42 
    43     properties += _get_properties(opts['property'])
    43     properties += _get_properties(opts['property'])
    44 
    44 
    45     for issue in issues:
    45     for issue in issues:
    46         mbox = mailbox.mbox(issue)
    46         mbox = mailbox.Maildir(issue, factory=mailbox.MaildirMessage)
       
    47         root = _find_root_key(mbox)
    47         property_match = True
    48         property_match = True
    48         for property,value in properties:
    49         for property,value in properties:
    49             property_match = property_match and (mbox[0][property] == value)
    50             property_match = property_match and (mbox[root][property] == value)
    50         if not show_all and (not properties or not property_match) and (properties or mbox[0]['State'].upper() == state['fixed'].upper()): continue
    51         if not show_all and (not properties or not property_match) and (properties or mbox[root]['State'].upper() == state['fixed'].upper()): continue
    51 
    52 
    52 
    53 
    53         if match_date and not date_match(util.parsedate(mbox[0]['date'])[0]): continue
    54         if match_date and not date_match(util.parsedate(mbox[root]['date'])[0]): continue
    54         ui.write("%s (%3d) [%s]: %s\n" % (issue[len(issues_path)+1:], # +1 for trailing /
    55         ui.write("%s (%3d) [%s]: %s\n" % (issue[len(issues_path)+1:], # +1 for trailing /
    55                                           len(mbox)-1,                # number of replies (-1 for self)
    56                                           len(mbox)-1,                # number of replies (-1 for self)
    56                                           mbox[0]['State'],
    57                                           mbox[root]['State'],
    57                                           mbox[0]['Subject']))
    58                                           mbox[root]['Subject']))
    58 
    59 
    59 
    60 
    60 def iadd(ui, repo, id = None, comment = 0):
    61 def iadd(ui, repo, id = None, comment = 0):
    61     """Adds a new issue, or comment to an existing issue ID or its comment COMMENT"""
    62     """Adds a new issue, or comment to an existing issue ID or its comment COMMENT"""
    62 
    63 
    87     if issue.strip() == default_issue_text:
    88     if issue.strip() == default_issue_text:
    88         ui.warn('Unchanged issue text, ignoring\n')
    89         ui.warn('Unchanged issue text, ignoring\n')
    89         return
    90         return
    90 
    91 
    91     # Create the message
    92     # Create the message
    92     msg = mailbox.mboxMessage(issue)
    93     msg = mailbox.MaildirMessage(issue)
    93     msg.set_from('artemis', True)
    94     #msg.set_from('artemis', True)
    94 
    95 
    95     # Pick random filename
    96     # Pick random filename
    96     if not id:
    97     if not id:
    97         issue_fn = issues_path
    98         issue_fn = issues_path
    98         while os.path.exists(issue_fn):
    99         while os.path.exists(issue_fn):
    99             issue_id = _random_id()
   100             issue_id = _random_id()
   100             issue_fn = os.path.join(issues_path, issue_id)
   101             issue_fn = os.path.join(issues_path, issue_id)
   101     # else: issue_fn already set
   102     # else: issue_fn already set
   102 
   103 
   103     # Add message to the mailbox
   104     # Add message to the mailbox
   104     mbox = mailbox.mbox(issue_fn)
   105     mbox = mailbox.Maildir(issue_fn)
   105     if id and comment not in mbox:
   106     keys = _order_keys_date(mbox)
       
   107     mbox.lock()
       
   108     if id and comment >= len(mbox):
   106         ui.warn('No such comment number in mailbox, commenting on the issue itself\n')
   109         ui.warn('No such comment number in mailbox, commenting on the issue itself\n')
       
   110 
   107     if not id:
   111     if not id:
   108         msg.add_header('Message-Id', "<%s-0-artemis@%s>" % (issue_id, socket.gethostname()))
   112         msg.add_header('Message-Id', "<%s-0-artemis@%s>" % (issue_id, socket.gethostname()))
   109     else:
   113     else:
       
   114         root = keys[0]
   110         msg.add_header('Message-Id', "<%s-%s-artemis@%s>" % (issue_id, _random_id(), socket.gethostname()))
   115         msg.add_header('Message-Id', "<%s-%s-artemis@%s>" % (issue_id, _random_id(), socket.gethostname()))
   111         msg.add_header('References', mbox[(comment < len(mbox) and comment) or 0]['Message-Id'])
   116         msg.add_header('References', mbox[(comment < len(mbox) and keys[comment]) or root]['Message-Id'])
   112         msg.add_header('In-Reply-To', mbox[(comment < len(mbox) and comment) or 0]['Message-Id'])
   117         msg.add_header('In-Reply-To', mbox[(comment < len(mbox) and keys[comment]) or root]['Message-Id'])
   113     mbox.add(msg)
   118     repo.add([issue_fn[(len(repo.root)+1):] + '/new/'  + mbox.add(msg)])   # +1 for the trailing /
   114     mbox.close()
   119     mbox.close()
   115 
   120 
   116     # If adding issue, add the new mailbox to the repository
   121     # If adding issue, add the new mailbox to the repository
   117     if not id:
   122     if not id:
   118         repo.add([issue_fn[(len(repo.root)+1):]])            # +1 for the trailing /
       
   119         ui.status('Added new issue %s\n' % issue_id)
   123         ui.status('Added new issue %s\n' % issue_id)
   120 
   124 
   121 
   125 
   122 def ishow(ui, repo, id, comment = 0, **opts):
   126 def ishow(ui, repo, id, comment = 0, **opts):
   123     """Shows issue ID, or possibly its comment COMMENT"""
   127     """Shows issue ID, or possibly its comment COMMENT"""
   124 
   128 
   125     comment = int(comment)
   129     comment = int(comment)
   126     issue, id = _find_issue(ui, repo, id)
   130     issue, id = _find_issue(ui, repo, id)
   127     if not issue: return
   131     if not issue: return
   128     mbox = mailbox.mbox(issue)
   132     mbox = mailbox.Maildir(issue, factory=mailbox.MaildirMessage)
   129 
   133 
   130     if opts['all']:
   134     if opts['all']:
   131         ui.write('='*70 + '\n')
   135         ui.write('='*70 + '\n')
   132         for i in xrange(len(mbox)):
   136         i = 0
   133             _write_message(ui, mbox[i], i)
   137         keys = _order_keys_date(mbox) 
       
   138         for k in keys:
       
   139             _write_message(ui, mbox[k], i)
   134             ui.write('-'*70 + '\n')
   140             ui.write('-'*70 + '\n')
       
   141             i += 1
   135         return
   142         return
   136 
   143 
   137     _show_mbox(ui, mbox, comment)
   144     _show_mbox(ui, mbox, comment)
   138 
   145 
   139 
   146 
   144     if not issue: return
   151     if not issue: return
   145 
   152 
   146     properties = _get_properties(opts['property'])
   153     properties = _get_properties(opts['property'])
   147 
   154 
   148     # Read the issue
   155     # Read the issue
   149     mbox = mailbox.mbox(issue)
   156     mbox = mailbox.Maildir(issue, factory=mailbox.MaildirMessage)
   150     msg = mbox[0]
   157     root = _find_root_key(mbox)
       
   158     msg = mbox[root]
   151 
   159 
   152     # Fix the properties
   160     # Fix the properties
   153     properties_text = ''
   161     properties_text = ''
   154     for property, value in properties:
   162     for property, value in properties:
   155         if property in msg:
   163         if property in msg:
   156             msg.replace_header(property, value)
   164             msg.replace_header(property, value)
   157         else:
   165         else:
   158             msg.add_header(property, value)
   166             msg.add_header(property, value)
   159         properties_text += '%s=%s\n' % (property, value)
   167         properties_text += '%s=%s\n' % (property, value)
   160     mbox[0] = msg
   168     mbox.lock()
       
   169     mbox[root] = msg
   161 
   170 
   162     # Write down a comment about updated properties
   171     # Write down a comment about updated properties
   163     if properties and not opts['no_property_comment']:
   172     if properties and not opts['no_property_comment']:
   164         user = ui.username()
   173         user = ui.username()
   165         properties_text  =     "From: %s\nDate: %s\nSubject: properties changes (%s)\n\n%s" % \
   174         properties_text  =     "From: %s\nDate: %s\nSubject: properties changes (%s)\n\n%s" % \
   168                              properties_text)
   177                              properties_text)
   169         msg = mailbox.mboxMessage(properties_text)
   178         msg = mailbox.mboxMessage(properties_text)
   170         msg.add_header('Message-Id', "<%s-%s-artemis@%s>" % (id, _random_id(), socket.gethostname()))
   179         msg.add_header('Message-Id', "<%s-%s-artemis@%s>" % (id, _random_id(), socket.gethostname()))
   171         msg.add_header('References', mbox[0]['Message-Id'])
   180         msg.add_header('References', mbox[0]['Message-Id'])
   172         msg.add_header('In-Reply-To', mbox[0]['Message-Id'])
   181         msg.add_header('In-Reply-To', mbox[0]['Message-Id'])
   173         msg.set_from('artemis', True)
   182         #msg.set_from('artemis', True)
   174         mbox.add(msg)
   183         repo.add([issue_fn[(len(repo.root)+1):] + '/new/'  + mbox.add(msg)])   # +1 for the trailing /
   175     mbox.flush()
   184     mbox.close()
   176 
   185 
   177     # Show updated message
   186     # Show updated message
   178     _show_mbox(ui, mbox, 0)
   187     _show_mbox(ui, mbox, 0)
   179 
   188 
   180 
   189 
   210 def _show_mbox(ui, mbox, comment):
   219 def _show_mbox(ui, mbox, comment):
   211     # Output the issue (or comment)
   220     # Output the issue (or comment)
   212     if comment >= len(mbox):
   221     if comment >= len(mbox):
   213         comment = 0
   222         comment = 0
   214         ui.warn('Comment out of range, showing the issue itself\n')
   223         ui.warn('Comment out of range, showing the issue itself\n')
   215     msg = mbox[comment]
   224     keys = _order_keys_date(mbox)
       
   225     root = keys[0]
       
   226     msg = mbox[keys[comment]]
   216     ui.write('='*70 + '\n')
   227     ui.write('='*70 + '\n')
   217     if comment:
   228     if comment:
   218         ui.write('Subject: %s\n' % mbox[0]['Subject'])
   229         ui.write('Subject: %s\n' % mbox[root]['Subject'])
   219         ui.write('State: %s\n' % mbox[0]['State'])
   230         ui.write('State: %s\n' % mbox[root]['State'])
   220         ui.write('-'*70 + '\n')
   231         ui.write('-'*70 + '\n')
   221     _write_message(ui, msg, comment)
   232     _write_message(ui, msg, comment)
   222     ui.write('-'*70 + '\n')
   233     ui.write('-'*70 + '\n')
   223 
   234 
   224     # Read the mailbox into the messages and children dictionaries
   235     # Read the mailbox into the messages and children dictionaries
   225     messages = {}
   236     messages = {}
   226     children = {}
   237     children = {}
   227     for i in xrange(len(mbox)):
   238     i = 0
   228         m = mbox[i]
   239     for k in keys:
       
   240         m = mbox[k]
   229         messages[m['Message-Id']] = (i,m)
   241         messages[m['Message-Id']] = (i,m)
   230         children.setdefault(m['In-Reply-To'], []).append(m['Message-Id'])
   242         children.setdefault(m['In-Reply-To'], []).append(m['Message-Id'])
       
   243         i += 1
   231     children[None] = []                # Safeguard against infinte loop on empty Message-Id
   244     children[None] = []                # Safeguard against infinte loop on empty Message-Id
   232 
   245 
   233     # Iterate over children
   246     # Iterate over children
   234     id = msg['Message-Id']
   247     id = msg['Message-Id']
   235     id_stack = (id in children and map(lambda x: (x, 1), reversed(children[id]))) or []
   248     id_stack = (id in children and map(lambda x: (x, 1), reversed(children[id]))) or []
   239         id,offset = id_stack.pop()
   252         id,offset = id_stack.pop()
   240         id_stack += (id in children and map(lambda x: (x, offset+1), reversed(children[id]))) or []
   253         id_stack += (id in children and map(lambda x: (x, offset+1), reversed(children[id]))) or []
   241         index, msg = messages[id]
   254         index, msg = messages[id]
   242         ui.write('  '*offset + ('%d: ' % index) + msg['Subject'] + '\n')
   255         ui.write('  '*offset + ('%d: ' % index) + msg['Subject'] + '\n')
   243     ui.write('-'*70 + '\n')
   256     ui.write('-'*70 + '\n')
       
   257 
       
   258 def _find_root_key(maildir):
       
   259     for k,m in maildir.iteritems():
       
   260         if 'in-reply-to' not in m:
       
   261             return k
       
   262 
       
   263 def _order_keys_date(mbox):
       
   264     keys = mbox.keys()
       
   265     root = _find_root_key(mbox)
       
   266     keys.sort(lambda k1,k2: -(k1 == root) or cmp(util.parsedate(mbox[k1]['date']), util.parsedate(mbox[k2]['date'])))
       
   267     return keys
   244 
   268 
   245 def _pretty_list(lst):
   269 def _pretty_list(lst):
   246     s = ''
   270     s = ''
   247     for i in lst:
   271     for i in lst:
   248         s += i + ', '
   272         s += i + ', '