Cleaned up be-handle-mail's subscriber notification emails (fewer attachments).
authorW. Trevor King <wking@drexel.edu>
Mon, 27 Jul 2009 18:42:17 +0000 (14:42 -0400)
committerW. Trevor King <wking@drexel.edu>
Mon, 27 Jul 2009 18:42:17 +0000 (14:42 -0400)
Previously, every node in the DiffTree created it's own attachment.
Now they're consolidated into a single attachment per bug.  higher
level nodes are still one attachment per node.

Also:
  * added send_pgp_mime.append_text()
  * pulled guess_encoding() out of send_pgp_mime.encodedMIMEText().
  * renamed data_string -> data_part in libbe.diff, since it needn't be a string.

interfaces/email/interactive/be-handle-mail
interfaces/email/interactive/send_pgp_mime.py
libbe/diff.py

index 47c108e000d5c30e34964fcdc40f3ed839fd7561..d491a7485b167f116ca3bfda3ad52801115d4159 100755 (executable)
@@ -53,8 +53,8 @@ import doctest
 import unittest
 
 from becommands import subscribe
-import libbe.cmdutil, libbe.encoding, libbe.utility, libbe.bugdir
-import libbe.diff
+import libbe.cmdutil, libbe.encoding, libbe.utility, libbe.diff, \
+    libbe.bugdir, libbe.bug, libbe.comment
 import send_pgp_mime
 
 THIS_SERVER = u"thor.physics.drexel.edu"
@@ -272,15 +272,53 @@ class Command (object):
         return response_generator.plain()
 
 class DiffTree (libbe.diff.DiffTree):
+    """
+    In order to avoid tons of tiny MIMEText attachments, bug-level
+    nodes set .add_child_text=True (in .join()), which is propogated
+    on to their descendents.  Instead of creating their own
+    attachement, each of these descendents appends his data_part to
+    the end of the bug-level MIMEText attachment.
+
+    For the example tree in the libbe.diff.Diff unittests:
+      bugdir
+      bugdir/settings
+      bugdir/bugs
+      bugdir/bugs/new
+      bugdir/bugs/new/c          <- sets .add_child_text
+      bugdir/bugs/rem
+      bugdir/bugs/rem/b          <- sets .add_child_text
+      bugdir/bugs/mod
+      bugdir/bugs/mod/a          <- sets .add_child_text
+      bugdir/bugs/mod/a/settings
+      bugdir/bugs/mod/a/comments
+      bugdir/bugs/mod/a/comments/new
+      bugdir/bugs/mod/a/comments/new/acom
+      bugdir/bugs/mod/a/comments/rem
+      bugdir/bugs/mod/a/comments/mod
+    """
     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)
+    def join(self, root, parent, data_part):
+        if hasattr(parent, "attach_child_text"):
+            self.attach_child_text = True
+            if data_part != None:
+                send_pgp_mime.append_text(parent.data_mime_part, u"\n\n%s" % (data_part))
+            self.data_mime_part = parent.data_mime_part
+        else:
+            self.data_mime_part = None
+            if data_part != None:
+                self.data_mime_part = send_pgp_mime.encodedMIMEText(data_part)
+            if parent != None and parent.name in ["new", "rem", "mod"]:
+                self.attach_child_text = True
+                if data_part == None: # make blank data_mime_part for children's appends
+                    self.data_mime_part = send_pgp_mime.encodedMIMEText("")
+            if self.data_mime_part != None:
+                self.data_mime_part["Content-Description"] = self.name
+                root.attach(self.data_mime_part)
+    def data_part(self, depth, indent=False):
+        return libbe.diff.DiffTree.data_part(self, depth, indent=indent)
 
 class Diff (libbe.diff.Diff):
     def bug_add_string(self, bug):
@@ -717,7 +755,7 @@ def main():
 
     if options.notify_since != None:
         if options.subscribers == True:
-            libbe.encoding.set_IO_stream_encodings(ENCODING) # _after_ reading message
+            libbe.encoding.set_IO_stream_encodings(ENCODING)
             open_logfile(options.logfile)
             if LOGFILE != None:
                 LOGFILE.write(u"Checking for subscribers to notify since revision %s\n"
index 09ac0ed8638c3304c67d87226916b1cba892fac4..55767b3d42270900c8407d8ed2bdba53ed05323d 100644 (file)
@@ -153,25 +153,35 @@ def header_from_text(text, encoding="us-ascii"):
     p = Parser()
     return p.parsestr(text, headersonly=True)
 
+def guess_encoding(text):
+    if type(text) == types.StringType:
+        encoding = "us-ascii"
+    elif type(text) == types.UnicodeType:
+        for encoding in ["us-ascii", "iso-8859-1", "utf-8"]:
+            try:
+                text.encode(encoding)
+            except UnicodeError:
+                pass
+            else:
+                break
+        assert encoding != None
+    return encoding
+
 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)
+        encoding = guess_encoding(body)
     if encoding == "us-ascii":
         return MIMEText(body)
     else:
+        # Create the message ('plain' stands for Content-Type: text/plain)
         return MIMEText(body.encode(encoding), 'plain', encoding)
 
+def append_text(text_part, new_text):
+    original_payload = text_part.get_payload(decode=True)
+    new_payload = u"%s%s" % (original_payload, new_text)
+    new_encoding = guess_encoding(new_payload)
+    text_part.set_payload(new_payload.encode(new_encoding), new_encoding)
+
 def attach_root(header, root_part):
     """
     Attach the email.Message root_part to the email.Message header
index 0956624c8503805616384c0741e8b80b46526108..69ccff88f288ff3f9e1740d3494f30db7d3aff8b 100644 (file)
@@ -59,12 +59,12 @@ class DiffTree (tree.Tree):
     >>> print all.report_string()
     target: None -> 1.0
     """
-    def __init__(self, name, data=None, data_string_fn=str,
+    def __init__(self, name, data=None, data_part_fn=str,
                  requires_children=False, masked=False):
         tree.Tree.__init__(self)
         self.name = name
         self.data = data
-        self.data_string_fn = data_string_fn
+        self.data_part_fn = data_part_fn
         self.requires_children = requires_children
         self.masked = masked
     def paths(self, parent_path=None):
@@ -98,38 +98,38 @@ class DiffTree (tree.Tree):
         raise KeyError, "%s points to child not in %s" % (names, [c.name for c in self])
     def report_string(self):
         return "\n".join(self.report())
-    def report(self, root=None, depth=0):
+    def report(self, root=None, parent=None, depth=0):
         if root == None:
             root = self.make_root()
         if self.masked == True:
             return None
-        data_string = self.data_string(depth)
-        if self.data == None:
-            pass
-        elif self.requires_children == True and len(self) == 0:
+        data_part = self.data_part(depth)
+        if self.requires_children == True and len(self) == 0:
             pass
         else:
-            self.join(root, data_string)
+            self.join(root, parent, data_part)
             depth += 1
         for child in self:
-            child.report(root, depth)
+            child.report(root, self, depth)
         return root
     def make_root(self):
         return []
-    def join(self, root, part):
-        if part != None:
-            root.append(part)
-    def data_string(self, depth, indent=True):
-        if hasattr(self, "_cached_data_string"):
-            return self._cached_data_string
-        data_string = self.data_string_fn(self.data)
+    def join(self, root, parent, data_part):
+        if data_part != None:
+            root.append(data_part)
+    def data_part(self, depth, indent=True):
+        if self.data == None:
+            return None
+        if hasattr(self, "_cached_data_part"):
+            return self._cached_data_part
+        data_part = self.data_part_fn(self.data)
         if indent == True:
-            data_string_lines = data_string.splitlines()
+            data_part_lines = data_part.splitlines()
             indent = "  "*(depth)
             line_sep = "\n"+indent
-            data_string = indent+line_sep.join(data_string_lines)
-        self._cached_data_string = data_string
-        return data_string
+            data_part = indent+line_sep.join(data_part_lines)
+        self._cached_data_part = data_part
+        return data_part
 
 class Diff (object):
     """