Broke `be comment --xml` out and extended into `be import-xml`.
authorW. Trevor King <wking@drexel.edu>
Sat, 21 Nov 2009 15:53:04 +0000 (10:53 -0500)
committerW. Trevor King <wking@drexel.edu>
Sat, 21 Nov 2009 15:53:04 +0000 (10:53 -0500)
It should currently do everything that `be comment --xml` did, but it
still has a way to go before it lives up to it's longhelp string,
mostly figuring out bug/comment merging.

The allowed XML format also changed a bit, becoming a bit more
structured.

cmdutil.bug_from_shortname() renamed to cmdutil.bug_from_id().

New functions cmdutil.parse_id() and cmdutil.bug_comment_from_id().

Additional doctests in libbe.comment.Comment.comment_shortnames() to
show example output if bug_shortname==None.

Brought be-xml-to-mbox and be-mbox-to-xml up to speed on the current
<be-xml>-rooted format.
  * Added <extra-string> handling to their comment handling.
  * Moved extra strings from email bodies to X-Extra-String headers
    (some comment bodies are not text, and we should keep the estr
    location consistent between bugs and comments.)

18 files changed:
becommands/assign.py
becommands/close.py
becommands/comment.py
becommands/depend.py
becommands/email_bugs.py
becommands/import_xml.py [new file with mode: 0644]
becommands/merge.py
becommands/open.py
becommands/remove.py
becommands/severity.py
becommands/show.py
becommands/status.py
becommands/tag.py
becommands/target.py
interfaces/xml/be-mbox-to-xml
interfaces/xml/be-xml-to-mbox
libbe/cmdutil.py
libbe/comment.py

index fbef28196108518250312563acc5edf4e2f3183d..31aa6076e834aeeb4f4876a515d6e524665cbd12 100644 (file)
@@ -57,7 +57,7 @@ def execute(args, manipulate_encodings=True):
         raise cmdutil.UsageError("Too many arguments.")
     bd = bugdir.BugDir(from_disk=True,
                        manipulate_encodings=manipulate_encodings)
-    bug = cmdutil.bug_from_shortname(bd, args[0])
+    bug = cmdutil.bug_from_id(bd, args[0])
     bug = bd.bug_from_shortname(args[0])
     if len(args) == 1:
         bug.assigned = bd.user_id
index 2cdcb598f260eae1f5cbae49631ab7a87d58d9e9..d65c4e0e9892b2ba870f7c98b52806ba3beb2838 100644 (file)
@@ -45,7 +45,7 @@ def execute(args, manipulate_encodings=True):
         raise cmdutil.UsageError("Too many arguments.")
     bd = bugdir.BugDir(from_disk=True,
                        manipulate_encodings=manipulate_encodings)
-    bug = cmdutil.bug_from_shortname(bd, args[0])
+    bug = cmdutil.bug_from_id(bd, args[0])
     bug.status = "closed"
     bd.save()
 
index dad32ddca54fc33f98b69165801cd26c2f12d2b9..a600e9fcbdc7c61272d2d735a0265cfcae3f61ec 100644 (file)
 from libbe import cmdutil, bugdir, comment, editor
 import os
 import sys
-try: # import core module, Python >= 2.5
-    from xml.etree import ElementTree
-except ImportError: # look for non-core module
-    from elementtree import ElementTree
 __desc__ = __doc__
 
 def execute(args, manipulate_encodings=True):
@@ -32,7 +28,7 @@ def execute(args, manipulate_encodings=True):
     >>> os.chdir(bd.root)
     >>> execute(["a", "This is a comment about a"], manipulate_encodings=False)
     >>> bd._clear_bugs()
-    >>> bug = cmdutil.bug_from_shortname(bd, "a")
+    >>> bug = cmdutil.bug_from_id(bd, "a")
     >>> bug.load_comments(load_full=False)
     >>> comment = bug.comment_root[0]
     >>> print comment.body
@@ -54,7 +50,7 @@ def execute(args, manipulate_encodings=True):
     >>> os.environ["EDITOR"] = "echo 'I like cheese' > "
     >>> execute(["b"], manipulate_encodings=False)
     >>> bd._clear_bugs()
-    >>> bug = cmdutil.bug_from_shortname(bd, "b")
+    >>> bug = cmdutil.bug_from_id(bd, "b")
     >>> bug.load_comments(load_full=False)
     >>> comment = bug.comment_root[0]
     >>> print comment.body
@@ -71,25 +67,10 @@ def execute(args, manipulate_encodings=True):
         raise cmdutil.UsageError("Too many arguments.")
 
     shortname = args[0]
-    if shortname.count(':') > 1:
-        raise cmdutil.UserError("Invalid id '%s'." % shortname)
-    elif shortname.count(':') == 1:
-        # Split shortname generated by Comment.comment_shortnames()
-        bugname = shortname.split(':')[0]
-        is_reply = True
-    else:
-        bugname = shortname
-        is_reply = False
 
     bd = bugdir.BugDir(from_disk=True,
                        manipulate_encodings=manipulate_encodings)
-    bug = cmdutil.bug_from_shortname(bd, bugname)
-    bug.load_comments(load_full=False)
-    if is_reply:
-        parent = bug.comment_root.comment_from_shortname(shortname,
-                                                         bug_shortname=bugname)
-    else:
-        parent = bug.comment_root
+    bug, parent = cmdutil.bug_comment_from_id(bd, shortname)
 
     if len(args) == 1: # try to launch an editor for comment-body entry
         try:
@@ -118,51 +99,11 @@ def execute(args, manipulate_encodings=True):
         if not body.endswith('\n'):
             body+='\n'
 
-    if options.XML == False:
-        new = parent.new_reply(body=body, content_type=options.content_type)
-        if options.author != None:
-            new.author = options.author
-        if options.alt_id != None:
-            new.alt_id = options.alt_id
-    else: # import XML comment [list]
-        # read in the comments
-        str_body = body.encode("unicode_escape").replace(r'\n', '\n')
-        comment_list = ElementTree.XML(str_body)
-        if comment_list.tag not in ["bug", "comment-list"]:
-            raise comment.InvalidXML(
-                comment_list, "root element must be <bug> or <comment-list>")
-        new_comments = []
-        ids = []
-        for c in bug.comment_root.traverse():
-            ids.append(c.uuid)
-            if c.alt_id != None:
-                ids.append(c.alt_id)
-        for child in comment_list.getchildren():
-            if child.tag == "comment":
-                new = comment.Comment(bug)
-                new.from_xml(unicode(ElementTree.tostring(child)).decode("unicode_escape"))
-                if new.alt_id in ids:
-                    raise cmdutil.UserError(
-                        "Clashing comment alt_id: %s" % new.alt_id)
-                ids.append(new.uuid)
-                if new.alt_id != None:
-                    ids.append(new.alt_id)
-                if new.in_reply_to == None:
-                    new.in_reply_to = parent.uuid
-                new_comments.append(new)
-            else:
-                print >> sys.stderr, "Ignoring unknown tag %s in %s" \
-                    % (child.tag, comment_list.tag)
-        try:
-            comment.list_to_root(new_comments,bug,root=parent, # link new comments
-                                 ignore_missing_references=options.ignore_missing_references)
-        except comment.MissingReference, e:
-            raise cmdutil.UserError(e)
-        # Protect against programmer error causing data loss:
-        kids = [c.uuid for c in parent.traverse()]
-        for nc in new_comments:
-            assert nc.uuid in kids, "%s wasn't added to %s" % (nc.uuid, parent.uuid)
-            nc.save()
+    new = parent.new_reply(body=body, content_type=options.content_type)
+    if options.author != None:
+        new.author = options.author
+    if options.alt_id != None:
+        new.alt_id = options.alt_id
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be comment ID [COMMENT]")
@@ -172,11 +113,6 @@ def get_parser():
                       help="Set an alternate comment ID", default=None)
     parser.add_option("-c", "--content-type", metavar="MIME", dest="content_type",
                       help="Set comment content-type (e.g. text/plain)", default=None)
-    parser.add_option("-x", "--xml", action="store_true", default=False,
-                      dest='XML', help="Use COMMENT to specify an XML comment description rather than the comment body.  The root XML element should be either <bug> or <comment-list> with one or more <comment> children.  The syntax for the <comment> elements should match that generated by 'be show --xml COMMENT-ID'.  Unrecognized tags are ignored.  Missing tags are left at the default value.  The comment UUIDs are always auto-generated, so if you set a <uuid> field, but no <alt-id> field, your <uuid> will be used as the comment's <alt-id>.  An exception is raised if <alt-id> conflicts with an existing comment.")
-    parser.add_option("-i", "--ignore-missing-references", action="store_true",
-                      dest="ignore_missing_references",
-                      help="For XML import, if any comment's <in-reply-to> refers to a non-existent comment, ignore it (instead of raising an exception).")
     return parser
 
 longhelp="""
index f52527e91d55b02032f6789d0013b833d8cbce2b..044aaffdded52b1db84489f9d92bdf21b5f87f0b 100644 (file)
@@ -95,7 +95,7 @@ def execute(args, manipulate_encodings=True):
                              for blockee,blocker in fixed])
         return 0
 
-    bugA = cmdutil.bug_from_shortname(bd, args[0])
+    bugA = cmdutil.bug_from_id(bd, args[0])
 
     if options.tree_depth != None:
         dtree = DependencyTree(bd, bugA, options.tree_depth)
@@ -112,7 +112,7 @@ def execute(args, manipulate_encodings=True):
         return 0
 
     if len(args) == 2:
-        bugB = cmdutil.bug_from_shortname(bd, args[1])
+        bugB = cmdutil.bug_from_id(bd, args[1])
         if options.remove == True:
             remove_block(bugA, bugB)
         else: # add the dependency
index 9aeccecb817143db716b8adb2c2dd36759844a1c..c188332c1b237c2329fad514c70439e28ae50a9d 100644 (file)
@@ -89,7 +89,7 @@ def execute(args, manipulate_encodings=True):
     lines = [u'<?xml version="1.0" encoding="%s" ?>' % bd.encoding,
              u'<bugs>']
     for shortname in args:
-        bug = cmdutil.bug_from_shortname(bd, shortname)
+        bug = cmdutil.bug_from_id(bd, shortname)
         lines.append(bug.xml(show_comments=True))
     lines.append(u'</bugs>')
     subject = options.subject
diff --git a/becommands/import_xml.py b/becommands/import_xml.py
new file mode 100644 (file)
index 0000000..d28e97a
--- /dev/null
@@ -0,0 +1,257 @@
+# Copyright (C) 2009 W. Trevor King <wking@drexel.edu>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+"""Import comments and bugs from XML"""
+from libbe import cmdutil, bugdir, bug, comment, utility
+from becommands.comment import complete
+import os
+import sys
+try: # import core module, Python >= 2.5
+    from xml.etree import ElementTree
+except ImportError: # look for non-core module
+    from elementtree import ElementTree
+__desc__ = __doc__
+
+def execute(args, manipulate_encodings=True):
+    """
+    >>> import time
+    >>> import StringIO
+    >>> bd = bugdir.SimpleBugDir()
+    >>> os.chdir(bd.root)
+    >>> orig_stdin = sys.stdin
+    >>> sys.stdin = StringIO.StringIO("<be-xml><comment><uuid>c</uuid><body>This is a comment about a</body></comment></be-xml>")
+    >>> execute(["-c", "a", "-"], manipulate_encodings=False)
+    >>> sys.stdin = orig_stdin
+    >>> bd._clear_bugs()
+    >>> bug = cmdutil.bug_from_id(bd, "a")
+    >>> bug.load_comments(load_full=False)
+    >>> comment = bug.comment_root[0]
+    >>> print comment.body
+    This is a comment about a
+    <BLANKLINE>
+    >>> comment.author == bd.user_id
+    True
+    >>> comment.time <= int(time.time())
+    True
+    >>> comment.in_reply_to is None
+    True
+    >>> bd.cleanup()
+    """
+    parser = get_parser()
+    options, args = parser.parse_args(args)
+    complete(options, args, parser)
+    if len(args) < 1:
+        raise cmdutil.UsageError("Please specify an XML file.")
+    if len(args) > 1:
+        raise cmdutil.UsageError("Too many arguments.")
+    filename = args[0]
+
+    bd = bugdir.BugDir(from_disk=True,
+                       manipulate_encodings=manipulate_encodings)
+    if options.comment_root != None:
+        croot_bug,croot_comment = \
+            cmdutil.bug_comment_from_id(bd, options.comment_root)
+    else:
+        croot_bug,croot_comment = (None, None)
+
+    if filename == '-':
+        xml = sys.stdin.read()
+    else:
+        xml = bd.vcs.get_file_contents(filename, allow_no_vcs=True)
+    str_xml = xml.encode('unicode_escape').replace(r'\n', '\n')
+    # unicode read + encode to string so we know the encoding,
+    # which might not be given (?) in a binary string read?
+
+    # parse the xml
+    root_bugs = []
+    root_comments = []
+    version = {}
+    be_xml = ElementTree.XML(str_xml)
+    if be_xml.tag != 'be-xml':
+        raise utility.InvalidXML(
+            'import-xml', be_xml, 'root element must be <be-xml>')
+    for child in be_xml.getchildren():
+        if child.tag == 'bug':
+            new = bug.Bug(bugdir=bd)
+            new.from_xml(unicode(ElementTree.tostring(child)).decode('unicode_escape'))
+            root_bugs.append(new)
+        elif child.tag == 'comment':
+            new = comment.Comment(croot_bug)
+            new.from_xml(unicode(ElementTree.tostring(child)).decode("unicode_escape"))
+            root_comments.append(new)
+        elif child.tag == 'version':
+            for gchild in child.getchildren():
+                if child.tag in ['tag', 'nick', 'revision', 'revision-id']:
+                    text = xml.sax.saxutils.unescape(child.text)
+                    text = text.decode('unicode_escape').strip()
+                    version[child.tag] = text
+                else:
+                    print >> sys.stderr, 'ignoring unknown tag %s in %s' \
+                        % (gchild.tag, child.tag)
+        else:
+            print >> sys.stderr, 'ignoring unknown tag %s in %s' \
+                % (child.tag, comment_list.tag)
+
+    # merge the new root_comments
+    croot_cids = []
+    for c in croot_bug.comment_root.traverse():
+        croot_cids.append(c.uuid)
+        if c.alt_id != None:
+            croot_cids.append(c.alt_id)
+    for new in root_comments:
+        if new.alt_id in croot_cids:
+            raise cmdutil.UserError(
+                'clashing comment alt_id: %s' % new.alt_id)
+        croot_cids.append(new.uuid)
+        if new.alt_id != None:
+            croot_cids.append(new.alt_id)
+        if new.in_reply_to == None:
+            new.in_reply_to = croot_comment.uuid
+    try:
+        # link new comments
+        comment.list_to_root(root_comments,croot_bug,root=croot_comment,
+                             ignore_missing_references= \
+                                 options.ignore_missing_references)
+    except comment.MissingReference, e:
+        raise cmdutil.UserError(e)
+
+    # merge the new croot_bugs
+    for new in root_bugs:
+        bd.append(new)
+
+    # protect against programmer error causing data loss:
+    comms = [c.uuid for c in croot_comment.traverse()]
+    for new in root_comments:
+        assert new.uuid in comms, \
+            "comment %s wasn't added to %s" % (new.uuid, croot_comment.uuid)
+    for new in root_bugs:
+        assert bd.has_bug(new.uuid), \
+            "bug %s wasn't added" % (new.uuid)
+
+    # save new information
+    for new in root_comments:
+        new.save()
+    for new in root_bugs:
+        new.save()
+
+def get_parser():
+    parser = cmdutil.CmdOptionParser("be import-xml XMLFILE")
+    parser.add_option("-i", "--ignore-missing-references", action="store_true",
+                      dest="ignore_missing_references", default=False,
+                      help="If any comment's <in-reply-to> refers to a non-existent comment, ignore it (instead of raising an exception).")
+    parser.add_option("-a", "--add-only", action='store_true',
+                      dest="add_only", default=False,
+                      help="If any bug or comment listed in the XML file already exists in the bug repository, do not alter the repository version.")
+    parser.add_option("-c", "--comment-root", dest="comment_root",
+                      help="Supply a bug or comment ID as the root of any <comment> elements that are direct children of the <be-xml> element.  If any such <comment> elements exist, you are required to set this option.")
+    return parser
+
+longhelp="""
+Import comments and bugs from XMLFILE.  If XMLFILE is '-', the file is
+read from stdin.
+
+This command provides a fallback mechanism for passing bugs between
+repositories, in case the repositories VCSs are incompatible.  If the
+VCSs are compatible, it's better to use their builtin merge/push/pull
+to share this information, as that will preserve a more detailed
+history.
+
+The XML file should be formatted similarly to
+  <be-xml>
+    <version>
+      <tag>1.0.0</tag>
+      <nick>be</nick>
+      <revision>446</revision>
+      <revision-id>a@b.com-20091119214553-iqyw2cpqluww3zna</revision-id>
+    <version>
+    <bug>
+      ...
+      <comment>...</comment>
+      <comment>...</comment>
+    </bug>
+    <bug>...</bug>
+    <comment>...</comment>
+    <comment>...</comment>
+  </be-xml>
+where the ellipses mark output commpatible with Bug.xml() and
+Comment.xml().  Take a look at the output of `be show --xml --version`
+for some explicit examples.  Unrecognized tags are ignored.  Missing
+tags are left at the default value.  The version tag is not required,
+but is strongly recommended.
+
+The bug and comment UUIDs are always auto-generated, so if you set a
+<uuid> field, but no <alt-id> field, your <uuid> will be used as the
+comment's <alt-id>.  An exception is raised if <alt-id> conflicts with
+an existing comment.  Bugs do not have a permantent alt-id, so they
+the <uuid>s you specify are not saved.  The <uuid>s _are_ used to
+match agains prexisting bug and comment uuids, and comment alt-ids,
+and fields explicitly given in the XML file will replace old versions
+unless the --add-only flag.
+
+*.extra_strings recieves special treatment, and if --add-only is not
+set, the resulting list concatenates both source lists and removes
+repeats.
+
+Here's an example of import activity:
+  Repository
+   bug (uuid=B, author=John, status=open)
+     estr (don't forget your towel)
+     estr (helps with space travel)
+     com (uuid=C1, author=Jane, body=Hello)
+     com (uuid=C2, author=Jess, body=World)
+  XML
+   bug (uuid=B, status=fixed)
+     estr (don't forget your towel)
+     estr (watch out for flying dolphins)
+     com (uuid=C1, body=So long)
+     com (uuid=C3, author=Jed, body=And thanks)
+  Result
+   bug (uuid=B, author=John, status=fixed)
+     estr (don't forget your towel)
+     estr (helps with space travel)
+     estr (watch out for flying dolphins)
+     com (uuid=C1, author=Jane, body=So long)
+     com (uuid=C2, author=Jess, body=World)
+     com (uuid=C4, alt-id=C3, author=Jed, body=And thanks)
+  Result, with --add-only
+   bug (uuid=B, author=John, status=open)
+     estr (don't forget your towel)
+     estr (helps with space travel)
+     com (uuid=C1, author=Jane, body=Hello)
+     com (uuid=C2, author=Jess, body=World)
+     com (uuid=C4, alt-id=C3, author=Jed, body=And thanks)
+
+Examples:
+
+Import comments (e.g. emails from an mbox) and append to bug XYZ
+  $ be-mbox-to-xml mail.mbox | be import-xml --c XYZ -
+Or you can append those emails underneath the prexisting comment XYZ-3
+  $ be-mbox-to-xml mail.mbox | be import-xml --c XYZ-3 -
+
+User creates a new bug
+  user$ be new "The demuxulizer is broken"
+  Created bug with ID 48f
+  user$ be comment 48f
+  <Describe bug>
+  ...
+User exports bug as xml and emails it to the developers
+  user$ be show --xml --version 48f > 48f.xml
+  user$ cat 48f.xml | mail -s "Demuxulizer bug xml" devs@b.com
+Devs recieve email, and save it's contents as demux-bug.xml
+  dev$ cat demux-bug.xml | be import-xml -
+"""
+
+def help():
+    return get_parser().help_str() + longhelp
index bc184793f861742b1955533e24365f0c93891cac..766af56c51fcd6db4ca653c1ba24148d1d325222 100644 (file)
@@ -137,9 +137,9 @@ def execute(args, manipulate_encodings=True):
     
     bd = bugdir.BugDir(from_disk=True,
                        manipulate_encodings=manipulate_encodings)
-    bugA = cmdutil.bug_from_shortname(bd, args[0])
+    bugA = cmdutil.bug_from_id(bd, args[0])
     bugA.load_comments()
-    bugB = cmdutil.bug_from_shortname(bd, args[1])
+    bugB = cmdutil.bug_from_id(bd, args[1])
     bugB.load_comments()
     mergeA = bugA.new_comment("Merged from bug %s" % bugB.uuid)
     newCommTree = copy.deepcopy(bugB.comment_root)
index c9e55a2639827fe90a94af91debe58f72589d91b..1b4c23e173c66f1738aeb9337e7fb1a7fb4bfe31 100644 (file)
@@ -44,7 +44,7 @@ def execute(args, manipulate_encodings=True):
         raise cmdutil.UsageError, "Too many arguments."
     bd = bugdir.BugDir(from_disk=True,
                        manipulate_encodings=manipulate_encodings)
-    bug = cmdutil.bug_from_shortname(bd, args[0])
+    bug = cmdutil.bug_from_id(bd, args[0])
     bug.status = "open"
 
 def get_parser():
index 0e6136274fa37bcca5432d92055ebe4f04839dd3..d265e5cc3b0327198577606dbfffaa3f2ea16275 100644 (file)
@@ -44,7 +44,7 @@ def execute(args, manipulate_encodings=True):
         raise cmdutil.UsageError, "Please specify a bug id."
     bd = bugdir.BugDir(from_disk=True,
                        manipulate_encodings=manipulate_encodings)
-    bug = cmdutil.bug_from_shortname(bd, args[0])
+    bug = cmdutil.bug_from_id(bd, args[0])
     bd.remove_bug(bug)
     print "Removed bug %s" % bug.uuid
 
index e987760bd961b5851eff5a97d7bcb832221d734a..f42f740bee2e39b9e2b032be79f07678785c833a 100644 (file)
@@ -43,7 +43,7 @@ def execute(args, manipulate_encodings=True):
         raise cmdutil.UsageError
     bd = bugdir.BugDir(from_disk=True,
                        manipulate_encodings=manipulate_encodings)
-    bug = cmdutil.bug_from_shortname(bd, args[0])
+    bug = cmdutil.bug_from_id(bd, args[0])
     if len(args) == 1:
         print bug.severity
     elif len(args) == 2:
index 94e16bf3b34cc79c7b2cccc15f0c23ec8383e860..4f8c30d41025d740cf019b45f92f6930b63cf020 100644 (file)
@@ -63,24 +63,17 @@ def execute(args, manipulate_encodings=True):
     if options.XML:
         print '<?xml version="1.0" encoding="%s" ?>' % bd.encoding
     for shortname in args:
-        if shortname.count(':') > 1:
-            raise cmdutil.UserError("Invalid id '%s'." % shortname)        
-        elif shortname.count(':') == 1:
-            # Split shortname generated by Comment.comment_shortnames()
-            bugname = shortname.split(':')[0]
-            is_comment = True
-        else:
-            bugname = shortname
-            is_comment = False
-        if is_comment == True and options.comments == False:
-            continue
-        bug = cmdutil.bug_from_shortname(bd, bugname)
-        if is_comment == False:
+        bugname,commname = cmdutil.parse_id(shortname)
+        if commname == None: # bug shortname
+            bug = cmdutil.bug_from_id(bd, shortname)
             if options.XML:
                 print bug.xml(show_comments=options.comments)
             else:
                 print bug.string(show_comments=options.comments)
-        else:
+        elif options.comments == False:
+            continue
+        else: # comment shortname
+            bug,comment = cmdutil.bug_comment_from_id(shortname)
             comment = bug.comment_root.comment_from_shortname(
                 shortname, bug_shortname=bugname)
             if options.XML:
index 827c7ce10f7aceb42cfbc626aeeb9fa8c5b68646..bf66c2649f61b30c1d9765f08acc1a349633fc9d 100644 (file)
@@ -40,7 +40,7 @@ def execute(args, manipulate_encodings=True):
         raise cmdutil.UsageError
     bd = bugdir.BugDir(from_disk=True,
                        manipulate_encodings=manipulate_encodings)
-    bug = cmdutil.bug_from_shortname(bd, args[0])
+    bug = cmdutil.bug_from_id(bd, args[0])
     if len(args) == 1:
         print bug.status
     else:
index 31b43bac3dd3dcfca937b2cf2aa054a5286e2c2d..e6debb41c84b32183b16aeeed5447ae0d4763d02 100644 (file)
@@ -95,7 +95,7 @@ def execute(args, manipulate_encodings=True):
         if len(tags) > 0:
             print '\n'.join(tags)
         return
-    bug = cmdutil.bug_from_shortname(bd, args[0])
+    bug = cmdutil.bug_from_id(bd, args[0])
     if len(args) == 2:
         given_tag = args[1]
         estrs = bug.extra_strings
index 7e41451dc9c412cb528f7e70736d5fb299ea000a..672eb06f4a05f6ecd69534fab431b0b19d4cbb72 100644 (file)
@@ -55,7 +55,7 @@ def execute(args, manipulate_encodings=True):
             if target and isinstance(target,str):
                 print target
         return
-    bug = cmdutil.bug_from_shortname(bd, args[0])
+    bug = cmdutil.bug_from_id(bd, args[0])
     if len(args) == 1:
         if bug.target is None:
             print "No target assigned."
index a740117e32b5600c1846f84275e7651f47e34d10..3af29789146dd0fd620b2a5a974ddd1018e38b94 100755 (executable)
@@ -15,8 +15,8 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 """
-Convert an mbox into xml suitable for imput into be.
-  $ cat mbox | be-mbox-to-xml | be comment --xml <ID> -
+Convert an mbox into xml suitable for input into be.
+  $ be-mbox-to-xml file.mbox | be import-xml -c <ID> -
 mbox is a flat-file format, consisting of a series of messages.
 Messages begin with a a From_ line, followed by RFC 822 email,
 followed by a blank line.
@@ -140,10 +140,10 @@ def comment_message_to_xml(message, fields=None):
 def main(mbox_filename):
     mb = mbox(mbox_filename)
     print u'<?xml version="1.0" encoding="%s" ?>' % DEFAULT_ENCODING
-    print u"<comment-list>"
+    print u"<be-xml>"
     for message in mb:
         print comment_message_to_xml(message)
-    print u"</comment-list>"
+    print u"</be-xml>"
 
 
 if __name__ == "__main__":
index 7960d5672bf803064763154dc7ec20abd64b0371..ecc6327c87291448fb844fd8e746efc0f17be887 100755 (executable)
@@ -88,19 +88,18 @@ class Bug (LimitedAttrDict):
     def print_to_mbox(self):
         name,addr = email.utils.parseaddr(self["creator"])
         print "From %s %s" % (addr, rfc2822_to_asctime(self["created"]))
-        print "Message-ID: <%s@%s>" % (self["uuid"], DEFAULT_DOMAIN)
+        print "Message-id: <%s@%s>" % (self["uuid"], DEFAULT_DOMAIN)
         print "Date: %s" % self["created"]
         print "From: %s" % self["creator"]
         print "Content-Type: %s; charset=%s" % ("text/plain", DEFAULT_ENCODING)
         print "Content-Transfer-Encoding: 8bit"
         print "Subject: %s: %s" % (self["short-name"], self["summary"])
+        if "extra-strings" in self:
+            for estr in self["extra_strings"]:
+                print "X-Extra-String: %s" % estr
         print ""
         print self["summary"]
         print ""
-        if "extra-strings" in self:
-            print "extra strings:\n  ",
-            print '\n  '.join(self["extra_strings"])
-        print ""
         if "comments" in self:
             for comment in self["comments"]:
                 comment.print_to_mbox(self)            
@@ -131,7 +130,8 @@ class Comment (LimitedAttrDict):
               u"author",
               u"date",
               u"content-type",
-              u"body"]
+              u"body",
+              u"extra-strings"]
     def print_to_mbox(self, bug=None):
         if bug == None:
             bug = Bug()
@@ -142,7 +142,7 @@ class Comment (LimitedAttrDict):
         elif "alt-id" in self: id = self["alt-id"]
         else:                  id = None
         if id != None:
-            print "Message-ID: <%s@%s>" % (id, DEFAULT_DOMAIN)
+            print "Message-id: <%s@%s>" % (id, DEFAULT_DOMAIN)
         print "Date: %s" % self["date"]
         print "From: %s" % self["author"]
         subject = ""
@@ -156,6 +156,9 @@ class Comment (LimitedAttrDict):
         if "in-reply-to" not in self.keys():
             self["in-reply-to"] = bug["uuid"]
         print "In-Reply-To: <%s@%s>" % (self["in-reply-to"], DEFAULT_DOMAIN)
+        if "extra-strings" in self:
+            for estr in self["extra_strings"]:
+                print "X-Extra-String: %s" % estr
         if self["content-type"].startswith("text/"):
             print "Content-Transfer-Encoding: 8bit"
             print "Content-Type: %s; charset=%s" % (self["content-type"], DEFAULT_ENCODING)
@@ -168,9 +171,15 @@ class Comment (LimitedAttrDict):
         assert element.tag == "comment", element.tag
         for field in element.getchildren():
             text = unescape(unicode(field.text).decode("unicode_escape").strip())
-            if field.tag == "body":
-                text+="\n"
-            self[field.tag] = text
+            if field.tag == "extra-string":
+                if "extra-strings" in self:
+                    self["extra-strings"].append(text)
+                else:
+                    self["extra-strings"] = [text]
+            else:
+                if field.tag == "body":
+                    text+="\n"
+                self[field.tag] = text
 
 def print_to_mbox(element):
     if element.tag == "bug":
@@ -181,16 +190,9 @@ def print_to_mbox(element):
         c = Comment()
         c.init_from_etree(element)
         c.print_to_mbox()
-    elif element.tag in ["bugs", "bug-list"]:
-        for b_elt in element.getchildren():
-            b = Bug()
-            b.init_from_etree(b_elt)
-            b.print_to_mbox()
-    elif element.tag in ["comments", "comment-list"]:
-        for c_elt in element.getchildren():
-            c = Comment()
-            c.init_from_etree(c_elt)
-            c.print_to_mbox()
+    elif element.tag in ["be-xml"]:
+        for elt in element.getchildren():
+            print_to_mbox(elt)
 
 if __name__ == "__main__":
     import sys
index f1c8acdc892a3cc1a2a3484d18ed23349aa6b193..96430ebdab7c7b94c4d3c4e2188526e247505c48 100644 (file)
@@ -30,10 +30,10 @@ import sys
 import doctest
 
 import bugdir
+import comment
 import plugin
 import encoding
 
-
 class UserError(Exception):
     def __init__(self, msg):
         Exception.__init__(self, msg)
@@ -213,16 +213,69 @@ def underlined(instring):
     
     return "%s\n%s" % (instring, "="*len(instring))
 
-def bug_from_shortname(bdir, shortname):
+def parse_id(id):
+    """
+    Return (bug_id, comment_id) tuple.
+    Basically inverts Comment.comment_shortnames()
+    >>> parse_id('XYZ')
+    ('XYZ', None)
+    >>> parse_id('XYZ:123')
+    ('XYZ', ':123')
+    >>> parse_id('')
+    Traceback (most recent call last):
+      ...
+    UserError: invalid id ''.
+    >>> parse_id('::')
+    Traceback (most recent call last):
+      ...
+    UserError: invalid id '::'.
+    """
+    if len(id) == 0:
+        raise UserError("invalid id '%s'." % id)
+    if id.count(':') > 1:
+        raise UserError("invalid id '%s'." % id)
+    elif id.count(':') == 1:
+        # Split shortname generated by Comment.comment_shortnames()
+        bug_id,comment_id = id.split(':')
+        comment_id = ':'+comment_id
+    else:
+        bug_id = id
+        comment_id = None
+    return (bug_id, comment_id)
+
+def bug_from_id(bdir, id):
     """
     Exception translation for the command-line interface.
+    id can be either the bug shortname or the full uuid.
     """
     try:
-        bug = bdir.bug_from_shortname(shortname)
+        bug = bdir.bug_from_shortname(id)
     except (bugdir.MultipleBugMatches, bugdir.NoBugMatches), e:
         raise UserError(e.message)
     return bug
 
+def bug_comment_from_id(bdir, id):
+    """
+    Return (bug,comment) tuple matching shortname.  id can be either
+    the bug/comment shortname or the full uuid.  If there is no
+    comment part to the id, the returned comment is the bug's
+    .comment_root.
+    """
+    bug_id,comment_id = parse_id(id)
+    try:
+        bug = bdir.bug_from_shortname(bug_id)
+    except (bugdir.MultipleBugMatches, bugdir.NoBugMatches), e:
+        raise UserError(e.message)
+    if comment_id == None:
+        comm = bug.comment_root
+    else:
+        #bug.load_comments(load_full=False)
+        try:
+            comm = bug.comment_root.comment_from_shortname(comment_id)
+        except comment.InvalidShortname, e:
+            raise UserError(e.message)
+    return (bug, comm)
+
 def _test():
     import doctest
     import sys
index 678d8badb8ffd71e8c6519f9f4068d839f2d69f4..5cc43c402e7e23f2c6d0f2509c0cd176250ed185 100644 (file)
@@ -641,6 +641,12 @@ class Comment(Tree, settings_object.SavedSettingsObject):
         bug-1:2 b
         bug-1:3 c
         bug-1:4 d
+        >>> for id,name in a.comment_shortnames():
+        ...     print id, name.uuid
+        :1 a
+        :2 b
+        :3 c
+        :4 d
         """
         if bug_shortname == None:
             bug_shortname = ""