Code Duplication    Length = 30-31 lines in 2 locations

ActionTree/__init__.py 2 locations

@@ 490-520 (lines=31) @@
487
488
489
class DependencyGraph(object):
490
    """
491
    A visual representation of the dependency graph, using `Graphviz <http://graphviz.org/>`__.
492
    """
493
494
    def __init__(self, action):
495
        self.__graphviz_graph = graphviz.Digraph("action", node_attr={"shape": "box"})
496
        nodes = {}
497
        for (i, action) in enumerate(action.get_possible_execution_order()):
498
            node = str(i)
499
            nodes[action] = node
500
            self.__graphviz_graph.node(node, str(action.label))
501
            for dependency in action.dependencies:
502
                assert dependency in nodes  # Because we are iterating a possible execution order
503
                self.__graphviz_graph.edge(node, nodes[dependency])
504
505
    def write_to_png(self, filename):  # Not unittested: too difficult
506
        """
507
        Write the graph as a PNG image to the specified file.
508
509
        See also :meth:`get_graphviz_graph` if you want to draw the graph somewhere else.
510
        """
511
        directory = os.path.dirname(filename)
512
        filename = os.path.basename(filename)
513
        filename, ext = os.path.splitext(filename)
514
        g = self.get_graphviz_graph()
515
        g.format = "png"
516
        g.render(directory=directory, filename=filename, cleanup=True)
517
518
    def get_graphviz_graph(self):
519
        """
520
        Return a :class:`graphviz.Digraph` of this dependency graph.
521
522
        See also :meth:`write_to_png` for the simplest use-case.
523
        """
@@ 522-551 (lines=30) @@
519
        """
520
        Return a :class:`graphviz.Digraph` of this dependency graph.
521
522
        See also :meth:`write_to_png` for the simplest use-case.
523
        """
524
        return self.__graphviz_graph.copy()
525
526
527
class GanttChart(object):  # Not unittested: too difficult
528
    """
529
    A visual representation of the timing of an execution.
530
    """
531
532
    def __init__(self, report):
533
        self.__actions = {
534
            id(action): self.__make_action(action, status)
535
            for (action, status) in report.get_actions_and_statuses()
536
        }
537
538
        self.__ordinates = {}
539
540
        dependents = {}
541
        for (action, _) in report.get_actions_and_statuses():
542
            dependents.setdefault(action, set())
543
            for dependency in action.dependencies:
544
                dependents.setdefault(dependency, set()).add(action)
545
546
        def compute(action, ordinate):
547
            self.__ordinates[id(action)] = len(self.__actions) - ordinate
548
            for d in sorted(
549
                action.dependencies,
550
                key=lambda d: report.get_action_status(d).success_time or report.get_action_status(d).failure_time
551
            ):
552
                if len(dependents[d]) == 1:
553
                    ordinate = compute(d, ordinate - 1)
554
                else: