diff options
author | W. Trevor King <wking@drexel.edu> | 2009-11-21 10:53:04 -0500 |
---|---|---|
committer | W. Trevor King <wking@drexel.edu> | 2009-11-21 10:53:04 -0500 |
commit | 3b168403ff5e50d767476c4c0f037d1841bb2bf9 (patch) | |
tree | 1929369aed3b5b4b0b6c87b27c92732798d0714a | |
parent | 75fedab07f9e566ca1c984051d7deece4d910e2c (diff) | |
download | bugseverywhere-3b168403ff5e50d767476c4c0f037d1841bb2bf9.tar.gz |
Broke `be comment --xml` out and extended into `be import-xml`.
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.)
-rw-r--r-- | becommands/assign.py | 2 | ||||
-rw-r--r-- | becommands/close.py | 2 | ||||
-rw-r--r-- | becommands/comment.py | 80 | ||||
-rw-r--r-- | becommands/depend.py | 4 | ||||
-rw-r--r-- | becommands/email_bugs.py | 2 | ||||
-rw-r--r-- | becommands/import_xml.py | 257 | ||||
-rw-r--r-- | becommands/merge.py | 4 | ||||
-rw-r--r-- | becommands/open.py | 2 | ||||
-rw-r--r-- | becommands/remove.py | 2 | ||||
-rw-r--r-- | becommands/severity.py | 2 | ||||
-rw-r--r-- | becommands/show.py | 21 | ||||
-rw-r--r-- | becommands/status.py | 2 | ||||
-rw-r--r-- | becommands/tag.py | 2 | ||||
-rw-r--r-- | becommands/target.py | 2 | ||||
-rwxr-xr-x | interfaces/xml/be-mbox-to-xml | 8 | ||||
-rwxr-xr-x | interfaces/xml/be-xml-to-mbox | 42 | ||||
-rw-r--r-- | libbe/cmdutil.py | 59 | ||||
-rw-r--r-- | libbe/comment.py | 6 |
18 files changed, 373 insertions, 126 deletions
diff --git a/becommands/assign.py b/becommands/assign.py index fbef281..31aa607 100644 --- a/becommands/assign.py +++ b/becommands/assign.py @@ -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 diff --git a/becommands/close.py b/becommands/close.py index 2cdcb59..d65c4e0 100644 --- a/becommands/close.py +++ b/becommands/close.py @@ -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() diff --git a/becommands/comment.py b/becommands/comment.py index dad32dd..a600e9f 100644 --- a/becommands/comment.py +++ b/becommands/comment.py @@ -19,10 +19,6 @@ 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=""" diff --git a/becommands/depend.py b/becommands/depend.py index f52527e..044aaff 100644 --- a/becommands/depend.py +++ b/becommands/depend.py @@ -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 diff --git a/becommands/email_bugs.py b/becommands/email_bugs.py index 9aeccec..c188332 100644 --- a/becommands/email_bugs.py +++ b/becommands/email_bugs.py @@ -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 index 0000000..d28e97a --- /dev/null +++ b/becommands/import_xml.py @@ -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 diff --git a/becommands/merge.py b/becommands/merge.py index bc18479..766af56 100644 --- a/becommands/merge.py +++ b/becommands/merge.py @@ -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) diff --git a/becommands/open.py b/becommands/open.py index c9e55a2..1b4c23e 100644 --- a/becommands/open.py +++ b/becommands/open.py @@ -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(): diff --git a/becommands/remove.py b/becommands/remove.py index 0e61362..d265e5c 100644 --- a/becommands/remove.py +++ b/becommands/remove.py @@ -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 diff --git a/becommands/severity.py b/becommands/severity.py index e987760..f42f740 100644 --- a/becommands/severity.py +++ b/becommands/severity.py @@ -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: diff --git a/becommands/show.py b/becommands/show.py index 94e16bf..4f8c30d 100644 --- a/becommands/show.py +++ b/becommands/show.py @@ -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: diff --git a/becommands/status.py b/becommands/status.py index 827c7ce..bf66c26 100644 --- a/becommands/status.py +++ b/becommands/status.py @@ -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: diff --git a/becommands/tag.py b/becommands/tag.py index 31b43ba..e6debb4 100644 --- a/becommands/tag.py +++ b/becommands/tag.py @@ -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 diff --git a/becommands/target.py b/becommands/target.py index 7e41451..672eb06 100644 --- a/becommands/target.py +++ b/becommands/target.py @@ -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." diff --git a/interfaces/xml/be-mbox-to-xml b/interfaces/xml/be-mbox-to-xml index a740117..3af2978 100755 --- a/interfaces/xml/be-mbox-to-xml +++ b/interfaces/xml/be-mbox-to-xml @@ -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__": diff --git a/interfaces/xml/be-xml-to-mbox b/interfaces/xml/be-xml-to-mbox index 7960d56..ecc6327 100755 --- a/interfaces/xml/be-xml-to-mbox +++ b/interfaces/xml/be-xml-to-mbox @@ -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 diff --git a/libbe/cmdutil.py b/libbe/cmdutil.py index f1c8acd..96430eb 100644 --- a/libbe/cmdutil.py +++ b/libbe/cmdutil.py @@ -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 diff --git a/libbe/comment.py b/libbe/comment.py index 678d8ba..5cc43c4 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -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 = "" |