dvc: add dvc metrics

Fixes #592

Signed-off-by: Ruslan Kuprieiev <[email protected]>
This commit is contained in:
Ruslan Kuprieiev
2018-04-19 20:47:37 +03:00
parent 89ed2caf43
commit 1e7d548ca4
7 changed files with 167 additions and 1 deletions
+19
View File
@@ -17,6 +17,7 @@ from dvc.command.add import CmdAdd
from dvc.command.config import CmdConfig
from dvc.command.checkout import CmdCheckout
from dvc.command.remote import CmdRemoteAdd, CmdRemoteRemove, CmdRemoteModify
from dvc.command.metrics import CmdMetrics
from dvc.stage import Stage
from dvc import VERSION
@@ -321,6 +322,24 @@ def parse_args(argv=None):
help='Use local config')
remote_modify_parser.set_defaults(func=CmdRemoteModify)
# Metrics
metrics_parser = subparsers.add_parser(
'metrics',
parents=[parent_parser],
help='Get metrics from all branches')
metrics_parser.add_argument(
'path',
help='Path to metrics file')
metrics_parser.add_argument(
'--json-path',
help='JSON path')
metrics_parser.add_argument(
'--tsv-path',
help='TSV path \'row,column\'(e.g. \'1,2\')')
metrics_parser.add_argument(
'--htsv-path',
help='Headed TSV path \'row,column\'(e.g. \'Name,3\'')
metrics_parser.set_defaults(func=CmdMetrics)
args = parser.parse_args(argv)
+4 -1
View File
@@ -3,6 +3,9 @@ from dvc.command.common.base import CmdBase
class CmdMetrics(CmdBase):
def run(self):
for branch, metric in self.project.metrics(self.args.path).items():
for branch, metric in self.project.metrics(self.args.path,
json_path=self.args.json_path,
tsv_path=self.args.tsv_path,
htsv_path=self.args.htsv_path).items():
self.project.logger.info("{}: {}".format(branch, metric))
return 0
+58
View File
@@ -1,6 +1,9 @@
import os
import csv
import stat
import json
import networkx as nx
from jsonpath_rw import parse
import dvc.cloud.base as cloud
@@ -258,6 +261,61 @@ class Project(object):
return self._cloud_status(target, jobs, remote=remote)
return self._local_status(target)
def _read_metric_json(self, fd, json_path):
parser = parse(json_path)
return [x.value for x in parser.find(json.load(fd))]
def _do_read_metric_tsv(self, reader, row, col):
if col != None and row != None:
return [reader[row][col]]
elif col != None:
return [r[col] for r in reader]
elif row != None:
return reader[row]
return None
def _read_metric_htsv(self, fd, htsv_path):
col, row = htsv_path.split(',')
row = int(row)
reader = list(csv.DictReader(fd, delimiter='\t'))
return self._do_read_metric_tsv(reader, row, col)
def _read_metric_tsv(self, fd, tsv_path):
col, row = tsv_path.split(',')
row = int(row)
col = int(col)
reader = list(csv.reader(fd, delimiter='\t'))
return self._do_read_metric_tsv(reader, row, col)
def _read_metric(self, path, json_path=None, tsv_path=None, htsv_path=None):
ret = None
try:
with open(path, 'r') as fd:
if json_path:
ret = self._read_metric_json(fd, json_path)
elif tsv_path:
ret = self._read_metric_tsv(fd, tsv_path)
elif htsv_path:
ret = self._read_metric_htsv(fd, htsv_path)
else:
ret = fd.read()
except Exception as exc:
self.logger.error('Unable to read metric in \'{}\''.format(path), exc)
return ret
def metrics(self, path, json_path=None, tsv_path=None, htsv_path=None):
res = {}
saved = self.scm.active_branch()
for branch in self.scm.list_branches():
self.scm.checkout(branch)
res[branch] = self._read_metric(path,
json_path=json_path,
tsv_path=tsv_path,
htsv_path=htsv_path)
self.scm.checkout(saved)
return res
def graph(self):
G = nx.DiGraph()
+12
View File
@@ -49,6 +49,12 @@ class Base(object):
def is_tracked(self, path):
pass
def active_branch(self):
pass
def list_branches(self):
pass
class Git(Base):
GITIGNORE = '.gitignore'
@@ -133,6 +139,12 @@ class Git(Base):
def is_tracked(self, path):
return len(self.repo.git.ls_files(path)) != 0
def active_branch(self):
return self.repo.active_branch.name
def list_branches(self):
return [h.name for h in self.repo.heads]
def SCM(root_dir=os.curdir, no_scm=False):
if Git.is_repo(root_dir):
+1
View File
@@ -17,3 +17,4 @@ pyasn1>=0.4.1
schema>=0.6.7
paramiko>=2.4.1
binaryornot==0.4.4
jsonpath-rw==1.4.0
+1
View File
@@ -23,6 +23,7 @@ install_requires = [
"schema>=0.6.7",
"paramiko>=2.4.1",
"binaryornot==0.4.4",
"jsonpath-rw==1.4.0",
]
setup(
+72
View File
@@ -0,0 +1,72 @@
import os
import json
from dvc.main import main
from tests.basic_env import TestDvc
class TestMetrics(TestDvc):
def setUp(self):
super(TestMetrics, self).setUp()
for branch in ['foo', 'bar', 'baz']:
self.dvc.scm.checkout(branch, create_new=True)
with open('metric', 'w+') as fd:
fd.write(branch)
with open('metric_json', 'w+') as fd:
json.dump({'branch': branch}, fd)
with open('metric_tsv', 'w+') as fd:
fd.write(branch)
with open('metric_htsv', 'w+') as fd:
fd.write('branch\n')
fd.write(branch)
self.dvc.scm.add(['metric', 'metric_json', 'metric_tsv', 'metric_htsv'])
self.dvc.scm.commit('metric')
self.dvc.scm.checkout('master')
def test(self):
ret = self.dvc.metrics('metric')
self.assertEqual(len(ret), 4)
self.assertTrue(ret['master'] == None)
self.assertTrue(ret['foo'] == 'foo')
self.assertTrue(ret['bar'] == 'bar')
self.assertTrue(ret['baz'] == 'baz')
ret = self.dvc.metrics('metric_json', json_path='branch')
self.assertEqual(len(ret), 4)
self.assertTrue(ret['master'] == None)
self.assertTrue(ret['foo'] == ['foo'])
self.assertTrue(ret['bar'] == ['bar'])
self.assertTrue(ret['baz'] == ['baz'])
ret = self.dvc.metrics('metric_tsv', tsv_path='0,0')
self.assertEqual(len(ret), 4)
self.assertTrue(ret['master'] == None)
self.assertTrue(ret['foo'] == ['foo'])
self.assertTrue(ret['bar'] == ['bar'])
self.assertTrue(ret['baz'] == ['baz'])
ret = self.dvc.metrics('metric_htsv', htsv_path='branch,0')
self.assertEqual(len(ret), 4)
self.assertTrue(ret['master'] == None)
self.assertTrue(ret['foo'] == ['foo'])
self.assertTrue(ret['bar'] == ['bar'])
self.assertTrue(ret['baz'] == ['baz'])
def test_cli(self):
#FIXME check output
ret = main(['metrics', 'metric'])
self.assertEqual(ret, 0)
ret = main(['metrics', 'metric_json', '--json-path', 'branch'])
self.assertEqual(ret, 0)
ret = main(['metrics', 'metric_tsv', '--tsv-path', '0,0'])
self.assertEqual(ret, 0)
ret = main(['metrics', 'metric_htsv', '--htsv-path', 'branch,0'])
self.assertEqual(ret, 0)