diff options
author | W. Trevor King <wking@drexel.edu> | 2009-07-27 08:12:16 -0400 |
---|---|---|
committer | W. Trevor King <wking@drexel.edu> | 2009-07-27 08:12:16 -0400 |
commit | 675aa10516d1fe2a5f0502be5553e38169f444c0 (patch) | |
tree | cb19b2ee621cd1120ae9afdafe6f52ee70137a32 /interfaces | |
parent | 885b04b50ad3bbde81ec2eccfbfb2e516d0d3a80 (diff) | |
parent | 7f2ee356b76303edd01efad6bd049fbc7f01b5ff (diff) | |
download | bugseverywhere-675aa10516d1fe2a5f0502be5553e38169f444c0.tar.gz |
Merged "be subscribe" and be-handle-mail subscription support.
Also assorted other changes and fixes in the be.subscribe branch.
Highlights:
* Much more powerful libbe.diff with subclassable report generators.
* "be diff" compares working copy with last commit by default.
* comment reference text shown in "be comment" EDITOR footer
* .revision_id() for all VCSs
* meaningful comment comparison and stricter bug comparison
* stricter .sync_with_disk interpretation. See BugDir.__doc__.
* Comment.From and .time_string -> .author and .date, for better
conformance with settings_object.setting_name_to_attr_name().
Diffstat (limited to 'interfaces')
-rwxr-xr-x | interfaces/email/interactive/be-handle-mail | 140 | ||||
-rw-r--r-- | interfaces/email/interactive/send_pgp_mime.py | 41 | ||||
-rwxr-xr-x | interfaces/xml/be-mbox-to-xml | 7 | ||||
-rwxr-xr-x | interfaces/xml/be-xml-to-mbox | 6 |
4 files changed, 161 insertions, 33 deletions
diff --git a/interfaces/email/interactive/be-handle-mail b/interfaces/email/interactive/be-handle-mail index f457b6a..ed45bdd 100755 --- a/interfaces/email/interactive/be-handle-mail +++ b/interfaces/email/interactive/be-handle-mail @@ -52,10 +52,14 @@ import traceback import doctest import unittest -import libbe.cmdutil, libbe.encoding, libbe.utility +from becommands import subscribe +import libbe.cmdutil, libbe.encoding, libbe.utility, libbe.bugdir +import libbe.diff import send_pgp_mime -HANDLER_ADDRESS = u"BE Bugs <wking@thor.physics.drexel.edu>" +THIS_SERVER = u"thor.physics.drexel.edu" +THIS_ADDRESS = u"BE Bugs <wking@thor.physics.drexel.edu>" + _THIS_DIR = os.path.abspath(os.path.dirname(__file__)) BE_DIR = _THIS_DIR LOGPATH = os.path.join(_THIS_DIR, u"be-handle-mail.log") @@ -135,6 +139,12 @@ class InvalidOption (InvalidCommand): InvalidCommand.__init__(self, msg, info, command, bigmessage) self.option = option +class NotificationFailed (Exception): + def __init__(self, msg): + bigmessage = "Notification failed: %s" % msg + Exception.__init__(self, bigmessage) + self.short_msg = msg + class ID (object): """ Sometimes you want to reference the output of a command that @@ -261,6 +271,23 @@ class Command (object): send_pgp_mime.PGPMimeMessageFactory(u"\n".join(response_body)) return response_generator.plain() +class DiffTree (libbe.diff.DiffTree): + def report_string(self): + return send_pgp_mime.flatten(self.report(), to_unicode=True) + def make_root(self): + return MIMEMultipart() + def join(self, root, part): + if part != None: + root.attach(send_pgp_mime.encodedMIMEText(part)) + def data_string(self, depth, indent=False): + return libbe.diff.DiffTree.data_string(self, depth, indent=indent) + +class Diff (libbe.diff.Diff): + def bug_add_string(self, bug): + return bug.string(show_comments=True) + def comment_summary_string(self, comment): + return comment.string() + class Message (object): def __init__(self, email_text): self.text = email_text @@ -472,15 +499,15 @@ class Message (object): finally: if AUTOCOMMIT == True: tag,subject = self._split_subject() - command = Command(self, "commit", [subject]) - command.run() + self.commit_command = Command(self, "commit", [subject]) + self.commit_command.run() if LOGFILE != None: LOGFILE.write("Autocommit:\n%s\n\n" % - send_pgp_mime.flatten(command.response_msg(), - to_unicode=True)) + send_pgp_mime.flatten(self.commit_command.response_msg(), + to_unicode=True)) def _begin_response(self): tag,subject = self._split_subject() - response_header = [u"From: %s" % HANDLER_ADDRESS, + response_header = [u"From: %s" % THIS_ADDRESS, u"To: %s" % self.author_addr(), u"Date: %s" % libbe.utility.time_to_str(time.time()), u"Subject: %s Re: %s"%(SUBJECT_TAG_RESPONSE,subject) @@ -501,6 +528,88 @@ class Message (object): for message in self._response_messages: response_body.attach(message) return send_pgp_mime.attach_root(self.response_header, response_body) + def subscriber_emails(self): + if AUTOCOMMIT != True: # no way to tell what's changed + raise NotificationFailed("Autocommit dissabled") + assert len(self._response_messages) > 0 + if self.commit_command.ret != 0: + # commit failed. Error already logged. + raise NotificationFailed("Commit failed") + + # read only bugdir. + bd = libbe.bugdir.BugDir(from_disk=True, + manipulate_encodings=False) + if bd.rcs.versioned == False: # no way to tell what's changed + raise NotificationFailed("Not versioned") + + subscribers = subscribe.get_bugdir_subscribers(bd, THIS_SERVER) + if len(subscribers) == 0: + return [] + + before_bd, after_bd = self._get_before_and_after_bugdirs(bd) + diff = Diff(before_bd, after_bd) + diff_tree = diff.report_tree(diff_tree=DiffTree) + bug_index = {} + for child in diff_tree.child_by_path("/bugs/new"): + bug_index[child.name] = ("added", child) + for child in diff_tree.child_by_path("/bugs/mod"): + bug_index[child.name] = ("modified", child) + for child in diff_tree.child_by_path("/bugs/rem"): + bug_index[child.name] = ("removed", child) + header = self._subscriber_header(bd) + + emails = [] + for subscriber,subscriptions in subscribers.items(): + header.replace_header("to", subscriber) + parts = [] + for id,types in subscriptions.items(): + if id == "DIR": + if subscribe.BUGDIR_TYPE_ALL in types: + parts.append(diff_tree.report()) + break + if subscribe.BUGDIR_TYPE_NEW in types: + new = diff_tree.child_by_path("/bugs/new") + parts.append(new.report()) + continue # move on to next id + assert types == [subscribe.BUG_TYPE_ALL], types + type,bug_root = bug_index[id] + parts.append(bug_root.report()) + if len(parts) == 0: + continue # no email to this subscriber + elif len(parts) == 1: + root = parts[0] + else: # join subscription parts into a single body + root = MIMEMultipart() + for part in parts: + root.attach(part) + emails.append(send_pgp_mime.attach_root(header, root)) + if LOGFILE != None: + LOGFILE.write("Notfying %s of changes\n" % subscriber) + return emails + def _get_before_and_after_bugdirs(self, bd): + commit_msg = self.commit_command.stdout + assert commit_msg.startswith("Committed "), commit_msg + after_revision = commit_msg[len("Committed "):] + before_revision = bd.rcs.revision_id(-2) + if before_revision == None: + # this commit was the initial commit + before_bd = libbe.bugdir.BugDir(from_disk=False, + manipulate_encodings=False) + else: + before_bd = bd.duplicate_bugdir(before_revision) + #after_bd = bd.duplicate_bugdir(after_revision) + after_bd = bd # assume no changes since commit a few cycles ago + return (before_bd, after_bd) + def _subscriber_header(self, bd): + root_dir = os.path.basename(bd.root) + subject = "Changes to %s on %s by %s" \ + % (root_dir, THIS_SERVER, self.author_addr()) + header = [u"From: %s" % THIS_ADDRESS, + u"To: %s" % u"DUMMY-AUTHOR", + u"Date: %s" % libbe.utility.time_to_str(time.time()), + u"Subject: %s Re: %s" % (SUBJECT_TAG_RESPONSE, subject) + ] + return send_pgp_mime.header_from_text(text=u"\n".join(header)) def generate_global_tags(tag_base=u"be-bug"): """ @@ -571,6 +680,9 @@ def main(): parser.add_option('-a', '--disable-autocommit', dest='autocommit', default=True, action='store_false', help='Disable the autocommit after parsing the email.') + parser.add_option('-s', '--disable-subscribers', dest='subscribers', + default=True, action='store_false', + help='Disable subscriber notification emails.') parser.add_option('--test', dest='test', action='store_true', help='Run internal unit-tests and exit.') @@ -615,8 +727,22 @@ def main(): LOGFILE.write(u"\n%s\n\n" % send_pgp_mime.flatten(response, to_unicode=True)) send_pgp_mime.mail(response, send_pgp_mime.sendmail) + if options.subscribers == True: + LOGFILE.write(u"Checking for subscribers\n") + try: + emails = m.subscriber_emails() + except NotificationFailed, e: + LOGFILE.write(unicode(e) + u"\n") + else: + for msg in emails: + if options.output == True: + print send_pgp_mime.flatten(msg, to_unicode=True) + else: + send_pgp_mime.mail(msg, send_pgp_mime.sendmail) + close_logfile() + class GenerateGlobalTagsTestCase (unittest.TestCase): def setUp(self): super(GenerateGlobalTagsTestCase, self).setUp() diff --git a/interfaces/email/interactive/send_pgp_mime.py b/interfaces/email/interactive/send_pgp_mime.py index babd720..09ac0ed 100644 --- a/interfaces/email/interactive/send_pgp_mime.py +++ b/interfaces/email/interactive/send_pgp_mime.py @@ -153,6 +153,25 @@ def header_from_text(text, encoding="us-ascii"): p = Parser() return p.parsestr(text, headersonly=True) +def encodedMIMEText(body, encoding=None): + if encoding == None: + if type(body) == types.StringType: + encoding = "us-ascii" + elif type(body) == types.UnicodeType: + for encoding in ["us-ascii", "iso-8859-1", "utf-8"]: + try: + body.encode(encoding) + except UnicodeError: + pass + else: + break + assert encoding != None + # Create the message ('plain' stands for Content-Type: text/plain) + if encoding == "us-ascii": + return MIMEText(body) + else: + return MIMEText(body.encode(encoding), 'plain', encoding) + def attach_root(header, root_part): """ Attach the email.Message root_part to the email.Message header @@ -355,26 +374,8 @@ class PGPMimeMessageFactory (object): """ def __init__(self, body): self.body = body - def encodedMIMEText(self, body, encoding=None): - if encoding == None: - if type(body) == types.StringType: - encoding = "us-ascii" - elif type(body) == types.UnicodeType: - for encoding in ["us-ascii", "iso-8859-1", "utf-8"]: - try: - body.encode(encoding) - except UnicodeError: - pass - else: - break - assert encoding != None - # Create the message ('plain' stands for Content-Type: text/plain) - if encoding == "us-ascii": - return MIMEText(body) - else: - return MIMEText(body.encode(encoding), 'plain', encoding) def clearBodyPart(self): - body = self.encodedMIMEText(self.body) + body = encodedMIMEText(self.body) body.add_header('Content-Disposition', 'inline') return body def passphrase_arg(self, passphrase=None): @@ -387,7 +388,7 @@ class PGPMimeMessageFactory (object): """ text/plain """ - return self.encodedMIMEText(self.body) + return encodedMIMEText(self.body) def sign(self, header, passphrase=None): """ multipart/signed diff --git a/interfaces/xml/be-mbox-to-xml b/interfaces/xml/be-mbox-to-xml index 57de719..dc6a1c5 100755 --- a/interfaces/xml/be-mbox-to-xml +++ b/interfaces/xml/be-mbox-to-xml @@ -28,6 +28,7 @@ from libbe.encoding import get_encoding, set_IO_stream_encodings from mailbox import mbox, Message # the mailbox people really want an on-disk copy from time import asctime, gmtime import types +from xml.sax.saxutils import escape DEFAULT_ENCODING = get_encoding() set_IO_stream_encodings(DEFAULT_ENCODING) @@ -40,7 +41,7 @@ def comment_message_to_xml(message, fields=None): new_fields = {} new_fields[u'alt-id'] = message[u'message-id'] new_fields[u'in-reply-to'] = message[u'in-reply-to'] - new_fields[u'from'] = message[u'from'] + new_fields[u'author'] = message[u'from'] new_fields[u'date'] = message[u'date'] new_fields[u'content-type'] = message.get_content_type() for k,v in new_fields.items(): @@ -77,12 +78,12 @@ def comment_message_to_xml(message, fields=None): if message.is_multipart(): ret = [] alt_id = fields[u'alt-id'] - from_str = fields[u'from'] + from_str = fields[u'author'] date = fields[u'date'] for m in message.walk(): if m == message: continue - fields[u'from'] = from_str + fields[u'author'] = from_str fields[u'date'] = date if len(ret) > 0: # we've added one part already fields.pop(u'alt-id') # don't pass alt-id to other parts diff --git a/interfaces/xml/be-xml-to-mbox b/interfaces/xml/be-xml-to-mbox index ea77c34..c630447 100755 --- a/interfaces/xml/be-xml-to-mbox +++ b/interfaces/xml/be-xml-to-mbox @@ -129,7 +129,7 @@ class Comment (LimitedAttrDict): u"alt-id", u"short-name", u"in-reply-to", - u"from", + u"author", u"date", u"content-type", u"body"] @@ -137,7 +137,7 @@ class Comment (LimitedAttrDict): if bug == None: bug = Bug() bug[u"uuid"] = u"no-uuid" - name,addr = email.utils.parseaddr(self["from"]) + name,addr = email.utils.parseaddr(self["author"]) print "From %s %s" % (addr, rfc2822_to_asctime(self["date"])) if "uuid" in self: id = self["uuid"] elif "alt-id" in self: id = self["alt-id"] @@ -145,7 +145,7 @@ class Comment (LimitedAttrDict): if id != None: print "Message-ID: <%s@%s>" % (id, DEFAULT_DOMAIN) print "Date: %s" % self["date"] - print "From: %s" % self["from"] + print "From: %s" % self["author"] subject = "" if "short-name" in self: subject += self["short-name"]+u": " |