Ejemplo n.º 1
0
    def test_tree_skips_subdocuments_when_skipall_file_present(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item in a document in a subfolder.
        tree = build(cwd, root)
        tree.create_document(cwd, 'TST')

        subfolder = os.path.join(temp, 'SUBFOLDER')
        os.makedirs(os.path.join(temp, subfolder))

        sub_document = tree.create_document(subfolder, 'TST_SUB', parent='TST')

        item = Item.new(tree, sub_document, subfolder, cwd, "TST_SUB-001")
        item.save()

        # Step 2: Put a .doorstop.skip-all into the subfolder.
        path = os.path.join(subfolder, '.doorstop.skip-all')
        open(path, 'a').close()

        # Verify that building tree ignores subfolder's document.
        same_tree_again = build(cwd, root)
        self.assertEqual(1, len(same_tree_again.documents))
Ejemplo n.º 2
0
    def test_tree_finds_subdocuments(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item in a document in a subfolder.
        tree = build(cwd, root)
        document = tree.create_document(cwd, 'TST')

        subfolder = os.path.join(temp, 'SUBFOLDER')
        os.makedirs(os.path.join(temp, subfolder))

        sub_document = tree.create_document(subfolder, 'TST_SUB', parent='TST')

        item = Item.new(tree, sub_document, subfolder, cwd, "TST_SUB-001")
        item.save()

        # Step 2: Read existing tree
        same_tree_again = build(cwd, root)

        # Verify that the tree has:
        # - both root-level and subfolder documents
        # - item in a subdocument
        self.assertEqual(2, len(same_tree_again.documents))
        sub_document = same_tree_again.documents[1]
        self.assertIsNotNone(document)
        self.assertEqual(1, len(sub_document.items))
        item = sub_document.items[0]
        self.assertEqual('TST_SUB-001', item.uid)
Ejemplo n.º 3
0
 def test_palce_empty(self):
     """Verify a document can be placed in an empty tree."""
     tree = build(EMPTY)
     doc = MockDocumentSkip.new(tree,
                                os.path.join(EMPTY, 'temp'), EMPTY, 'TEMP')
     tree._place(doc)  # pylint: disable=W0212
     self.assertEqual(1, len(tree))
Ejemplo n.º 4
0
 def test_palce_empty_no_parent(self):
     """Verify a document with parent cannot be placed in an empty tree."""
     tree = build(EMPTY)
     doc = MockDocumentSkip.new(tree,
                                os.path.join(EMPTY, 'temp'), EMPTY, 'TEMP',
                                parent='REQ')
     self.assertRaises(DoorstopError, tree._place, doc)  # pylint: disable=W0212
Ejemplo n.º 5
0
def run_edit(args, cwd, error, catch=True):
    """Process arguments and run the `doorstop edit` subcommand.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param error: function to call for CLI errors
    :param catch: catch and log :class:`~doorstop.common.DoorstopError`

    """
    item = document = None
    ext = utilities.get_ext(args, '.yml', '.yml', whole_tree=False, error=error)

    with utilities.capture(catch=catch) as success:
        tree = build(cwd=cwd, root=args.project)
        # find item or document
        if not args.document:
            try:
                item = tree.find_item(args.label)
            except common.DoorstopError as exc:
                if args.item:
                    raise exc from None
        if not item:
            document = tree.find_document(args.label)
        # edit item or document
        if item:
            item.edit(tool=args.tool)
        else:
            _export_import(args, cwd, error, document, ext)
    if not success:
        return False

    if item:
        show("opened item: {} ({})".format(item.uid, item.relpath))

    return True
Ejemplo n.º 6
0
 def test_palce_empty_no_parent(self):
     """Verify a document with parent cannot be placed in an empty tree."""
     tree = build(EMPTY)
     doc = MockDocumentSkip.new(tree,
                                os.path.join(EMPTY, 'temp'), EMPTY, 'TEMP',
                                parent='REQ')
     self.assertRaises(DoorstopError, tree._place, doc)  # pylint: disable=W0212
Ejemplo n.º 7
0
def run_add(args, cwd, _, catch=True):
    """Process arguments and run the `doorstop add` subcommand.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param error: function to call for CLI errors
    :param catch: catch and log :class:`~doorstop.common.DoorstopError`

    """
    with utilities.capture(catch=catch) as success:
        tree = build(cwd=cwd, root=args.project)
        document = tree.find_document(args.prefix)
        if args.force:
            log.warn("creating items without the server...")
        else:
            server.check()
        for _ in range(args.count):
            number = 0
            while number is not None and number < document.next:
                if number:
                    log.warn("server is behind, requesting next number...")
                if args.force:
                    number = None
                else:
                    number = server.get_next_number(args.prefix)
            item = document.add_item(number=number, level=args.level)
            show("added item: {} ({})".format(item.uid, item.relpath))

    if not success:
        return False

    return True
Ejemplo n.º 8
0
 def test_palce_empty(self):
     """Verify a document can be placed in an empty tree."""
     tree = build(EMPTY)
     doc = MockDocumentSkip.new(tree, os.path.join(EMPTY, 'temp'), EMPTY,
                                'TEMP')
     tree._place(doc)  # pylint: disable=W0212
     self.assertEqual(1, len(tree))
Ejemplo n.º 9
0
 def get_doc(prefix):
     # type: (str) -> Document
     tree = build(root=settings.DOORSTOP_REPO)
     doc = None
     for _doc in tree.documents:
         if _doc.prefix == prefix:
             doc = _doc
             break
     return doc
Ejemplo n.º 10
0
 def get_context_data(self, **kwargs):
     context = super().get_context_data(**kwargs)
     self._vcs = MyPyGit2(self._user)
     self.action(self._action)
     if self._curr_file:
         tree = build(root=settings.DOORSTOP_REPO)
         context['item'] = tree.find_item(self._curr_file)
     else:
         context['item'] = None
     context['patch'] = self._vcs.diff_patch()
     context['table'] = GitFileStatus(data=self._vcs.modified_files())
     return context
Ejemplo n.º 11
0
def run_import(args, cwd, error, catch=True, _tree=None):
    """Process arguments and run the `doorstop import` subcommand.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param error: function to call for CLI errors
    :param catch: catch and log :class:`~doorstop.common.DoorstopError`

    """
    document = item = None

    # Parse arguments
    attrs = utilities.literal_eval(args.attrs, error)
    mapping = utilities.literal_eval(args.map, error)
    if args.path:
        if not args.prefix:
            error("when [path] specified, [prefix] is also required")
        elif args.document:
            error("'--document' cannot be used with [path] [prefix]")
        elif args.item:
            error("'--item' cannot be used with [path] [prefix]")
        ext = utilities.get_ext(args, None, None, False, error)
    elif not (args.document or args.item):
        error("specify [path], '--document', or '--item' to import")

    # Import document or item
    with utilities.capture(catch=catch) as success:
        if args.path:
            tree = _tree or build(cwd=cwd, root=args.project)
            document = tree.find_document(args.prefix)
            msg = "importing '{}' into document {}...".format(args.path,
                                                              document)
            show(msg, flush=True)
            importer.import_file(args.path, document, ext, mapping=mapping)
        elif args.document:
            prefix, path = args.document
            document = importer.create_document(prefix, path,
                                                parent=args.parent)
        elif args.item:
            prefix, uid = args.item
            item = importer.add_item(prefix, uid, attrs=attrs)
    if not success:
        return False

    # Display result
    if document:
        show("imported document: {} ({})".format(document.prefix,
                                                 document.relpath))
    else:
        assert item
        show("imported item: {} ({})".format(item.uid, item.relpath))

    return True
Ejemplo n.º 12
0
def _iter_items(args, cwd, error):
    """Build a tree and iterate through items.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param error: function to call for CLI errors

    Items are filtered to:

    - `args.label` == 'all': all items
    - `args.label` == document prefix: the document's items
    - `args.label` == item UID: a single item

    Documents and items are inferred unless flagged by:

    - `args.document`: `args.label` is a prefix
    - `args.item`: `args.label` is an UID

    """
    # Parse arguments
    if args.label == 'all':
        if args.item:
            error("argument -i/--item: not allowed with 'all'")
        if args.document:
            error("argument -d/--document: not allowed with 'all'")

    # Build tree
    item = None
    document = None
    tree = build(cwd=cwd, root=args.project)

    # Determine if tree, document, or item was requested
    if args.label != 'all':
        if not args.item:
            try:
                document = tree.find_document(args.label)
            except common.DoorstopError as exc:
                if args.document:
                    raise exc from None
        if not document:
            item = tree.find_item(args.label)

    # Yield items from the requested object
    if item:
        yield item
    elif document:
        for item in document:
            yield item
    else:
        for document in tree:
            for item in document:
                yield item
Ejemplo n.º 13
0
    def display_tree(self, *_):
        """Display the currently selected tree."""
        # Set the current tree
        self.tree = builder.build(root=self.stringvar_project.get())
        log.info("displaying tree...")

        # Display the documents in the tree
        values = ["{} ({})".format(document.prefix, document.relpath)
                  for document in self.tree]
        self.combobox_documents['values'] = values

        # Select the first document
        self.combobox_documents.current(0)
Ejemplo n.º 14
0
 def _openProject(self):
     dialog = QFileDialog(self)
     dialog.setFileMode(QFileDialog.DirectoryOnly)
     dialog.setOptions(QFileDialog.ShowDirsOnly)
     if dialog.exec():
         # TODO: this returns a list... should give user an error if they select multiple
         dir = dialog.selectedFiles()[0]
         self._tree = builder.build(root=dir)
         for doc in self._tree:
             self._docTree.addItem(doc)
             self._reqStack.addDoc(doc)
             self._reqView.addDoc(doc)
         self.mainContainer.show()
Ejemplo n.º 15
0
    def test_tree_finds_documents(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item.
        tree = build(cwd, root)
        document = tree.create_document(cwd, 'TST')
        item = Item.new(tree, document, cwd, cwd, "TST-001")
        item.save()

        # Step 2: Find a newly created tree
        same_tree_again = build(cwd, root)

        # Verify that that the tree, document and its item can be found.
        self.assertEqual(1, len(same_tree_again.documents))
        document = same_tree_again.document
        self.assertIsNotNone(document)
        self.assertEqual(1, len(document.items))
        item = document.items[0]
        self.assertEqual('TST-001', item.uid)
Ejemplo n.º 16
0
    def test_tree_does_not_find_documents_when_skipall_file_present(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item.
        tree = build(cwd, root)
        document = tree.create_document(cwd, 'TST')
        item = Item.new(tree, document, cwd, cwd, "TST-001")
        item.save()

        # Step 2: Put a .doorstop.skip-all to the root of the tree.
        path = os.path.join(temp, '.doorstop.skip-all')
        open(path, 'a').close()

        # Step 3: Find a newly created tree
        same_tree_again = build(cwd, root)

        # Verify that the tree does not have a document because it was ignored.
        document = same_tree_again.document
        self.assertIsNone(document)
Ejemplo n.º 17
0
    def display_tree(self, *_):
        """Display the currently selected tree."""
        # Set the current tree
        self.tree = builder.build(root=self.stringvar_project.get())
        log.info("displaying tree...")

        # Display the documents in the tree
        values = [
            "{} ({})".format(document.prefix, document.relpath)
            for document in self.tree
        ]
        self.combobox_documents['values'] = values

        # Select the first document
        self.combobox_documents.current(0)
Ejemplo n.º 18
0
def run_publish(args, cwd, error, catch=True):
    """Process arguments and run the `doorstop publish` subcommand.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param error: function to call for CLI errors
    :param catch: catch and log :class:`~doorstop.common.DoorstopError`

    """
    # Parse arguments
    whole_tree = args.prefix == 'all'
    ext = utilities.get_ext(args, '.txt', '.html', whole_tree, error)

    # Publish documents
    with utilities.capture(catch=catch) as success:
        publisher.check(ext)
        tree = build(cwd=cwd, root=args.project)
        if not whole_tree:
            document = tree.find_document(args.prefix)
    if not success:
        return False

    # Set publishing arguments
    kwargs = {}
    if args.width:
        kwargs['width'] = args.width

    # Write to output file(s)
    if args.path:
        if whole_tree:
            show("publishing tree to '{}'...".format(args.path), flush=True)
            path = publisher.publish(tree, args.path, ext, **kwargs)
        else:
            msg = "publishing document {} to '{}'...".format(document,
                                                             args.path)
            show(msg, flush=True)
            path = publisher.publish(document, args.path, ext, **kwargs)
        if path:
            show("published: {}".format(path))

    # Display to standard output
    else:
        if whole_tree:
            error("only single documents can be displayed")
        for line in publisher.publish_lines(document, ext, **kwargs):
            show(line)

    return True
Ejemplo n.º 19
0
    def display_tree(self, *_):
        """Display the currently selected tree."""
        # Set the current tree
        self.tree = builder.build(root=self.stringvar_project.get())
        log.info("displaying tree...")

        # Display the documents in the tree
        values = ["{} ({})".format(document.prefix, document.relpath)
                  for document in self.tree]
        self.combobox_documents['values'] = values

        # Select the first document
        if len(self.tree):  # pylint: disable=len-as-condition
            self.combobox_documents.current(0)
        else:
            logging.warning("no documents to display")
Ejemplo n.º 20
0
    def display_tree(self, *_):
        """Display the currently selected tree."""
        # Set the current tree
        self.tree = builder.build(root=self.stringvar_project.get())
        log.info("displaying tree...")

        # Display the documents in the tree
        values = ["{} ({})".format(document.prefix, document.relpath)
                  for document in self.tree]
        self.combobox_documents['values'] = values

        # Select the first document
        if len(self.tree):  # pylint: disable=len-as-condition
            self.combobox_documents.current(0)
        else:
            logging.warning("no documents to display")
Ejemplo n.º 21
0
def run_reorder(args, cwd, error, catch=True, _tree=None):
    """Process arguments and run the `doorstop reorder` subcommand.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param error: function to call for CLI errors
    :param catch: catch and log :class:`~doorstop.common.DoorstopError`

    """
    reordered = False

    with utilities.capture(catch=catch) as success:
        tree = _tree or build(cwd=cwd, root=args.project)
        document = tree.find_document(args.prefix)
    if not success:
        return False

    with utilities.capture(catch=catch) as success:
        # automatically order
        if args.auto:
            show("reordering document {}...".format(document), flush=True)
            document.reorder(manual=False)
            reordered = True
        # or, reorder from a previously updated index
        elif document.index:
            relpath = os.path.relpath(document.index, cwd)
            if ask("reorder from '{}'?".format(relpath)):
                show("reordering document {}...".format(document), flush=True)
                document.reorder(automatic=not args.manual)
                reordered = True
            else:
                del document.index
        # or, create a new index to update
        else:
            document.index = True  # create index
            relpath = os.path.relpath(document.index, cwd)
            editor.edit(relpath, tool=args.tool)
            get('reorder')(args, cwd, error, catch=False, _tree=tree)
    if not success:
        show("after fixing the error: doorstop reorder {}".format(document))
        return False

    if reordered:
        show("reordered document: {}".format(document))

    return True
Ejemplo n.º 22
0
def run_create(args, cwd, _, catch=True):
    """Process arguments and run the `doorstop create` subcommand.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param error: function to call for CLI errors
    :param catch: catch and log :class:`~doorstop.common.DoorstopError`

    """
    with utilities.capture(catch=catch) as success:
        tree = build(cwd=cwd, root=args.project)
        document = tree.create_document(args.path, args.prefix,
                                        parent=args.parent, digits=args.digits)
    if not success:
        return False

    show("created document: {} ({})".format(document.prefix, document.relpath))
    return True
Ejemplo n.º 23
0
def run_unlink(args, cwd, _, catch=True):
    """Process arguments and run the `doorstop unlink` subcommand.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param error: function to call for CLI errors
    :param catch: catch and log :class:`~doorstop.common.DoorstopError`

    """
    with utilities.capture(catch=catch) as success:
        tree = build(cwd=cwd, root=args.project)
        child, parent = tree.unlink_items(args.child, args.parent)
    if not success:
        return False

    msg = "unlinked items: {} ({}) -> {} ({})"
    show(msg.format(child.uid, child.relpath, parent.uid, parent.relpath))

    return True
Ejemplo n.º 24
0
def run_remove(args, cwd, _, catch=True):
    """Process arguments and run the `doorstop remove` subcommand.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param error: function to call for CLI errors
    :param catch: catch and log :class:`~doorstop.common.DoorstopError`

    """
    with utilities.capture(catch=catch) as success:
        tree = build(cwd=cwd, root=args.project)
        item = tree.find_item(args.uid)
        item.delete()
    if not success:
        return False

    show("removed item: {} ({})".format(item.uid, item.relpath))

    return True
Ejemplo n.º 25
0
def _get_tree(args, cwd, request_next_number=None, load=False):
    """Build a tree and optionally load all documents.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param request_next_number: server method to get a document's next number
    :param load: force the early loading of all documents

    :return: built :class:`~doorstop.core.tree.Tree`

    """
    utilities.show("building tree...", flush=True)
    tree = build(cwd=cwd, root=args.project,
                 request_next_number=request_next_number)

    if load:
        utilities.show("loading documents...", flush=True)
        tree.load()

    return tree
Ejemplo n.º 26
0
def _get_tree(args, cwd, request_next_number=None, load=False):
    """Build a tree and optionally load all documents.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param request_next_number: server method to get a document's next number
    :param load: force the early loading of all documents

    :return: built :class:`~doorstop.core.tree.Tree`

    """
    utilities.show("building tree...", flush=True)
    tree = build(cwd=cwd,
                 root=args.project,
                 request_next_number=request_next_number)

    if load:
        utilities.show("loading documents...", flush=True)
        tree.load()

    return tree
Ejemplo n.º 27
0
def run(args, cwd, error, catch=True):  # pylint: disable=W0613
    """Process arguments and run the `doorstop` subcommand.

    :param args: Namespace of CLI arguments
    :param cwd: current working directory
    :param error: function to call for CLI errors
    :param catch: catch and log :class:`~doorstop.common.DoorstopError`

    """
    with utilities.capture(catch=catch) as success:
        show("building tree...", flush=True)
        tree = build(cwd=cwd, root=args.project)
        show("loading documents...", flush=True)
        tree.load()
        show("validating items...", flush=True)
        valid = tree.validate()
    if not success:
        return False

    if len(tree) > 1 and valid:
        show('\n' + tree.draw() + '\n')

    return valid
Ejemplo n.º 28
0
 def test_build_with_skips(self):
     """Verify documents can be skipped while building a tree."""
     tree = build(FILES)
     self.assertEqual(0, len(tree))
Ejemplo n.º 29
0
 def test_build(self):
     """Verify a tree can be built."""
     tree = build(FILES)
     self.assertEqual(4, len(tree))
Ejemplo n.º 30
0
 def test_run_empty(self):
     """Verify an empty directory is an empty hierarchy."""
     tree = build(EMPTY)
     self.assertEqual(0, len(tree))
Ejemplo n.º 31
0
 def __init__(self):
     self._user = None  # type: Optional[User]
     self._tree = build(root=settings.DOORSTOP_REPO)  # type: Tree
     self._doc = None  # type: Optional[Document]
     self._item = None  # type: Optional[DjItem]
     self._form = None
Ejemplo n.º 32
0
 def test_run_empty(self):
     """Verify an empty directory is an empty hierarchy."""
     tree = build(EMPTY)
     self.assertEqual(0, len(tree))
Ejemplo n.º 33
0
 def test_build(self):
     """Verify a tree can be built."""
     tree = build(FILES)
     self.assertEqual(4, len(tree))
Ejemplo n.º 34
0
 def test_build_with_skips(self):
     """Verify documents can be skipped while building a tree."""
     tree = build(FILES)
     self.assertEqual(0, len(tree))