aboutsummaryrefslogtreecommitdiffstats
path: root/interfaces/email/interactive
diff options
context:
space:
mode:
authorW. Trevor King <wking@drexel.edu>2009-07-27 08:12:16 -0400
committerW. Trevor King <wking@drexel.edu>2009-07-27 08:12:16 -0400
commit675aa10516d1fe2a5f0502be5553e38169f444c0 (patch)
treecb19b2ee621cd1120ae9afdafe6f52ee70137a32 /interfaces/email/interactive
parent885b04b50ad3bbde81ec2eccfbfb2e516d0d3a80 (diff)
parent7f2ee356b76303edd01efad6bd049fbc7f01b5ff (diff)
downloadbugseverywhere-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-xinterfaces/email/interactive/be-handle-mail140
-rw-r--r--interfaces/email/interactive/send_pgp_mime.py41
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