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/email/interactive | |
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/email/interactive')
-rwxr-xr-x | interfaces/email/interactive/be-handle-mail | 140 | ||||
-rw-r--r-- | interfaces/email/interactive/send_pgp_mime.py | 41 |
2 files changed, 154 insertions, 27 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 |