@@ 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: |