aboutsummaryrefslogtreecommitdiffstats
path: root/becommands/import_xml.py
diff options
context:
space:
mode:
Diffstat (limited to 'becommands/import_xml.py')
-rw-r--r--becommands/import_xml.py434
1 files changed, 0 insertions, 434 deletions
diff --git a/becommands/import_xml.py b/becommands/import_xml.py
deleted file mode 100644
index b985193..0000000
--- a/becommands/import_xml.py
+++ /dev/null
@@ -1,434 +0,0 @@
-# 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"""
-import libbe
-from libbe import cmdutil, bugdir, bug, comment, utility
-from becommands.comment import complete
-import copy
-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
-if libbe.TESTING == True:
- import doctest
- import unittest
-__desc__ = __doc__
-
-def execute(args, manipulate_encodings=True, restrict_file_access=False,
- dir="."):
- """
- >>> 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,
- root=dir)
- if options.comment_root != None:
- croot_bug,croot_comment = \
- cmdutil.bug_comment_from_id(bd, options.comment_root)
- croot_bug.load_comments(load_full=True)
- croot_bug.set_sync_with_disk(False)
- if croot_comment.uuid == comment.INVALID_UUID:
- croot_comment = croot_bug.comment_root
- else:
- croot_comment = croot_bug.comment_from_uuid(croot_comment.uuid)
- new_croot_bug = bug.Bug(bugdir=bd, uuid=croot_bug.uuid)
- new_croot_bug.explicit_attrs = []
- new_croot_bug.comment_root = copy.deepcopy(croot_bug.comment_root)
- if croot_comment.uuid == comment.INVALID_UUID:
- new_croot_comment = new_croot_bug.comment_root
- else:
- new_croot_comment = \
- new_croot_bug.comment_from_uuid(croot_comment.uuid)
- for new in new_croot_bug.comments():
- new.explicit_attrs = []
- else:
- croot_bug,croot_comment = (None, None)
-
- if filename == '-':
- xml = sys.stdin.read()
- else:
- if restrict_file_access == True:
- cmdutil.restrict_file_access(bd, options.body)
- 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
- if options.add_only == True:
- accept_changes = False
- accept_extra_strings = False
- else:
- accept_changes = True
- accept_extra_strings = True
- accept_comments = True
- if len(root_comments) > 0:
- if croot_bug == None:
- raise UserError(
- '--comment-root option is required for your root comments:\n%s'
- % '\n\n'.join([c.string() for c in root_comments]))
- try:
- # link new comments
- new_croot_bug.add_comments(root_comments,
- default_parent=new_croot_comment,
- ignore_missing_references= \
- options.ignore_missing_references)
- except comment.MissingReference, e:
- raise cmdutil.UserError(e)
- croot_bug.merge(new_croot_bug, accept_changes=accept_changes,
- accept_extra_strings=accept_extra_strings,
- accept_comments=accept_comments)
-
- # merge the new croot_bugs
- merged_bugs = []
- old_bugs = []
- for new in root_bugs:
- try:
- old = bd.bug_from_uuid(new.alt_id)
- except KeyError:
- old = None
- if old == None:
- bd.append(new)
- else:
- old.load_comments(load_full=True)
- old.merge(new, accept_changes=accept_changes,
- accept_extra_strings=accept_extra_strings,
- accept_comments=accept_comments)
- merged_bugs.append(new)
- old_bugs.append(old)
-
- # protect against programmer error causing data loss:
- if croot_bug != None:
- 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:
- if not new in merged_bugs:
- assert bd.has_bug(new.uuid), \
- "bug %s wasn't added" % (new.uuid)
-
- # save new information
- if croot_bug != None:
- croot_bug.save()
- for new in root_bugs:
- if not new in merged_bugs:
- new.save()
- for old in old_bugs:
- old.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>
- <branch-nick>be</branch-nick>
- <revno>446</revno>
- <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` 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, creator=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, creator=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, creator=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 48f > 48f.xml
- user$ cat 48f.xml | mail -s "Demuxulizer bug xml" devs@b.com
-or equivalently (with a slightly fancier be-handle-mail compatible
-email):
- user$ be email-bugs 48f
-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
-
-
-if libbe.TESTING == True:
- class LonghelpTestCase (unittest.TestCase):
- """
- Test import scenarios given in longhelp.
- """
- def setUp(self):
- self.bugdir = bugdir.SimpleBugDir()
- self.original_working_dir = os.getcwd()
- os.chdir(self.bugdir.root)
- bugA = self.bugdir.bug_from_uuid('a')
- self.bugdir.remove_bug(bugA)
- self.bugdir.set_sync_with_disk(False)
- bugB = self.bugdir.bug_from_uuid('b')
- bugB.creator = 'John'
- bugB.status = 'open'
- bugB.extra_strings += ["don't forget your towel"]
- bugB.extra_strings += ['helps with space travel']
- comm1 = bugB.comment_root.new_reply(body='Hello\n')
- comm1.uuid = 'c1'
- comm1.author = 'Jane'
- comm2 = bugB.comment_root.new_reply(body='World\n')
- comm2.uuid = 'c2'
- comm2.author = 'Jess'
- bugB.save()
- self.bugdir.set_sync_with_disk(True)
- self.xml = """
- <be-xml>
- <bug>
- <uuid>b</uuid>
- <status>fixed</status>
- <summary>a test bug</summary>
- <extra-string>don't forget your towel</extra-string>
- <extra-string>watch out for flying dolphins</extra-string>
- <comment>
- <uuid>c1</uuid>
- <body>So long</body>
- </comment>
- <comment>
- <uuid>c3</uuid>
- <author>Jed</author>
- <body>And thanks</body>
- </comment>
- </bug>
- </be-xml>
- """
- def tearDown(self):
- os.chdir(self.original_working_dir)
- self.bugdir.cleanup()
- def _execute(self, *args):
- import StringIO
- orig_stdin = sys.stdin
- sys.stdin = StringIO.StringIO(self.xml)
- execute(list(args)+["-"], manipulate_encodings=False,
- restrict_file_access=True)
- sys.stdin = orig_stdin
- self.bugdir._clear_bugs()
- def testCleanBugdir(self):
- uuids = list(self.bugdir.uuids())
- self.failUnless(uuids == ['b'], uuids)
- def testNotAddOnly(self):
- self._execute()
- uuids = list(self.bugdir.uuids())
- self.failUnless(uuids == ['b'], uuids)
- bugB = self.bugdir.bug_from_uuid('b')
- self.failUnless(bugB.uuid == 'b', bugB.uuid)
- self.failUnless(bugB.creator == 'John', bugB.creator)
- self.failUnless(bugB.status == 'fixed', bugB.status)
- estrs = ["don't forget your towel",
- 'helps with space travel',
- 'watch out for flying dolphins']
- self.failUnless(bugB.extra_strings == estrs, bugB.extra_strings)
- comments = list(bugB.comments())
- self.failUnless(len(comments) == 3,
- ['%s (%s, %s)' % (c.uuid, c.alt_id, c.body)
- for c in comments])
- c1 = bugB.comment_from_uuid('c1')
- comments.remove(c1)
- self.failUnless(c1.uuid == 'c1', c1.uuid)
- self.failUnless(c1.alt_id == None, c1.alt_id)
- self.failUnless(c1.author == 'Jane', c1.author)
- self.failUnless(c1.body == 'So long\n', c1.body)
- c2 = bugB.comment_from_uuid('c2')
- comments.remove(c2)
- self.failUnless(c2.uuid == 'c2', c2.uuid)
- self.failUnless(c2.alt_id == None, c2.alt_id)
- self.failUnless(c2.author == 'Jess', c2.author)
- self.failUnless(c2.body == 'World\n', c2.body)
- c4 = comments[0]
- self.failUnless(len(c4.uuid) == 36, c4.uuid)
- self.failUnless(c4.alt_id == 'c3', c4.alt_id)
- self.failUnless(c4.author == 'Jed', c4.author)
- self.failUnless(c4.body == 'And thanks\n', c4.body)
- def testAddOnly(self):
- self._execute('--add-only')
- uuids = list(self.bugdir.uuids())
- self.failUnless(uuids == ['b'], uuids)
- bugB = self.bugdir.bug_from_uuid('b')
- self.failUnless(bugB.uuid == 'b', bugB.uuid)
- self.failUnless(bugB.creator == 'John', bugB.creator)
- self.failUnless(bugB.status == 'open', bugB.status)
- estrs = ["don't forget your towel",
- 'helps with space travel']
- self.failUnless(bugB.extra_strings == estrs, bugB.extra_strings)
- comments = list(bugB.comments())
- self.failUnless(len(comments) == 3,
- ['%s (%s)' % (c.uuid, c.alt_id) for c in comments])
- c1 = bugB.comment_from_uuid('c1')
- comments.remove(c1)
- self.failUnless(c1.uuid == 'c1', c1.uuid)
- self.failUnless(c1.alt_id == None, c1.alt_id)
- self.failUnless(c1.author == 'Jane', c1.author)
- self.failUnless(c1.body == 'Hello\n', c1.body)
- c2 = bugB.comment_from_uuid('c2')
- comments.remove(c2)
- self.failUnless(c2.uuid == 'c2', c2.uuid)
- self.failUnless(c2.alt_id == None, c2.alt_id)
- self.failUnless(c2.author == 'Jess', c2.author)
- self.failUnless(c2.body == 'World\n', c2.body)
- c4 = comments[0]
- self.failUnless(len(c4.uuid) == 36, c4.uuid)
- self.failUnless(c4.alt_id == 'c3', c4.alt_id)
- self.failUnless(c4.author == 'Jed', c4.author)
- self.failUnless(c4.body == 'And thanks\n', c4.body)
-
- unitsuite =unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
- suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()])