mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-09 23:10:24 +01:00
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:
+12
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user