dvc: add 'dvc visual'

This command generates dependency graph for specified target,
saves it in .dot file and also draws it into .svg file.

Fixes #39

Signed-off-by: Ruslan Kuprieiev <[email protected]>
This commit is contained in:
Ruslan Kuprieiev
2017-08-26 09:18:45 +03:00
parent 30bacf7f95
commit b707dbd0fe
4 changed files with 104 additions and 0 deletions
+12
View File
@@ -14,6 +14,7 @@ from dvc.command.import_file import CmdImportFile
from dvc.command.target import CmdTarget
from dvc.command.instance_create import CmdInstanceCreate
from dvc.command.config import CmdConfig
from dvc.command.visual import CmdVisual
from dvc.command.test import CmdTest
from dvc.config import Config
from dvc import VERSION
@@ -388,6 +389,17 @@ def parse_args(argv=None):
help='Option value')
config_parser.set_defaults(func=CmdConfig)
# Visual
vz_parser = subparsers.add_parser(
'visual',
parents=[parent_parser],
help='Create a dependency graph for data')
vz_parser.add_argument(
'target',
nargs='*',
help='Target data')
vz_parser.set_defaults(func=CmdVisual)
if isinstance(argv, str):
argv = argv.split()
+90
View File
@@ -0,0 +1,90 @@
import os
import networkx as nx
from dvc.command.traverse import Traverse
from dvc.command.init import CmdInit
from dvc.logger import Logger
from dvc.state_file import StateFile
class CmdVisual(Traverse):
def __init__(self, settings):
super(CmdVisual, self).__init__(settings, "collect", do_not_start_from_root=False)
self.g = nx.DiGraph()
self.subs = []
def draw(self, g, target, fname):
try:
A = nx.nx_agraph.to_agraph(g)
A.write(fname + '.dot')
A.draw(fname + '.svg', prog='dot')
except Exception as exc:
Logger.error('Failed to draw dependency graph for {}: {}'.format(target, exc))
return 1
return 0
def find_sub(self, fname):
for s in self.subs:
if fname in s.nodes():
return s
return self.g
def draw_targets(self):
if self.parsed_args.target == None:
target = self.settings.config.data_dir
return self.draw(self.g, target, target)
for t in self.parsed_args.target:
fname = os.path.basename(os.path.normpath(t))
s = self.find_sub(t)
ret = self.draw(s, t, fname)
if ret != 0:
return ret
return 0
def run(self):
saved_targets = self.settings.parsed_args.target
self.settings.parsed_args.target = [self.settings.config.data_dir]
ret = super(CmdVisual, self).run()
self.settings.parsed_args.target = saved_targets
if ret != 0:
Logger.error('Failed to build dependency graph for the project')
return 1
# Try to find independent clusters which might occure
# when a bunch of data items were used independently.
self.subs = nx.weakly_connected_component_subgraphs(self.g)
return self.draw_targets()
def process_file(self, target):
data_item = self._get_data_item(target)
name = data_item.data.relative
state = StateFile.load(data_item.state.relative, self.git)
self.g.add_node(name)
for i in state.input_files:
self.g.add_node(i)
self.g.add_edge(i, name)
for o in state.output_files:
if o == name:
continue
self.g.add_node(o)
self.g.add_edge(name, o)
@property
def no_git_actions(self):
return True
@staticmethod
def not_committed_changes_warning():
pass
+1
View File
@@ -87,3 +87,4 @@ wcwidth==0.1.7
colorama==0.3.9
google-compute-engine==2.4.1
configobj==5.0.6
networkx==1.11
+1
View File
@@ -93,6 +93,7 @@ install_requires = [
'colorama==0.3.9',
'google-compute-engine==2.4.1',
'configobj==5.0.6',
'networkx==1.11',
]
if platform.system() == 'Darwin':