mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-02 07:18:25 +01:00
Merge pull request #933 from efiop/master
metrics: specify xpath in stage files
This commit is contained in:
+32
-2
@@ -19,8 +19,10 @@ from dvc.command.add import CmdAdd
|
||||
from dvc.command.imp import CmdImport
|
||||
from dvc.command.config import CmdConfig
|
||||
from dvc.command.checkout import CmdCheckout
|
||||
from dvc.command.remote import CmdRemoteAdd, CmdRemoteRemove, CmdRemoteModify, CmdRemoteList
|
||||
from dvc.command.metrics import CmdMetricsShow, CmdMetricsAdd, CmdMetricsRemove
|
||||
from dvc.command.remote import CmdRemoteAdd, CmdRemoteRemove
|
||||
from dvc.command.remote import CmdRemoteModify, CmdRemoteList
|
||||
from dvc.command.metrics import CmdMetricsShow, CmdMetricsAdd
|
||||
from dvc.command.metrics import CmdMetricsRemove, CmdMetricsModify
|
||||
from dvc.command.install import CmdInstall
|
||||
from dvc.command.root import CmdRoot
|
||||
from dvc.command.lock import CmdLock, CmdUnlock
|
||||
@@ -477,6 +479,12 @@ def parse_args(argv=None):
|
||||
'path',
|
||||
nargs='?',
|
||||
help='Path to metrics file')
|
||||
metrics_show_parser.add_argument('-t',
|
||||
'--type',
|
||||
help='Type of metrics(RAW/JSON/TSV/HTSV/CSV/HCSV)')
|
||||
metrics_show_parser.add_argument('-x',
|
||||
'--xpath',
|
||||
help='JSON/TSV/HTSV/CSV/HCSV path')
|
||||
metrics_show_parser_group = metrics_show_parser.add_mutually_exclusive_group()
|
||||
metrics_show_parser_group.add_argument(
|
||||
'--json-path',
|
||||
@@ -505,12 +513,34 @@ def parse_args(argv=None):
|
||||
'add',
|
||||
parents=[parent_parser],
|
||||
help='Add metrics')
|
||||
metrics_add_parser.add_argument('-t',
|
||||
'--type',
|
||||
help='Type of metrics(RAW/JSON/TSV/HTSV/CSV/HCSV)')
|
||||
metrics_add_parser.add_argument('-x',
|
||||
'--xpath',
|
||||
help='JSON/TSV/HTSV/CSV/HCSV path')
|
||||
metrics_add_parser.add_argument(
|
||||
'path',
|
||||
help='Path to metrics file')
|
||||
metrics_add_parser.set_defaults(func=CmdMetricsAdd)
|
||||
|
||||
|
||||
metrics_modify_parser = metrics_subparsers.add_parser(
|
||||
'modify',
|
||||
parents=[parent_parser],
|
||||
help='Modify metrics')
|
||||
metrics_modify_parser.add_argument('-t',
|
||||
'--type',
|
||||
help='Type of metrics(RAW/JSON/TSV/HTSV/CSV/HCSV)')
|
||||
metrics_modify_parser.add_argument('-x',
|
||||
'--xpath',
|
||||
help='JSON/TSV/HTSV/CSV/HCSV path')
|
||||
metrics_modify_parser.add_argument(
|
||||
'path',
|
||||
help='Metrics file')
|
||||
metrics_modify_parser.set_defaults(func=CmdMetricsModify)
|
||||
|
||||
|
||||
metrics_remove_parser = metrics_subparsers.add_parser(
|
||||
'remove',
|
||||
parents=[parent_parser],
|
||||
|
||||
+38
-6
@@ -5,12 +5,29 @@ from dvc.exceptions import DvcException
|
||||
class CmdMetricsShow(CmdBase):
|
||||
def run(self):
|
||||
try:
|
||||
# backward compatibility
|
||||
if self.args.json_path:
|
||||
typ = 'json'
|
||||
xpath = self.args.json_path
|
||||
elif self.args.tsv_path:
|
||||
typ = 'tsv'
|
||||
xpath = self.args.tsv_path
|
||||
elif self.args.htsv_path:
|
||||
typ = 'htsv'
|
||||
xpath = self.args.htsv_path
|
||||
elif self.args.csv_path:
|
||||
typ = 'csv'
|
||||
xpath = self.args.csv_path
|
||||
elif self.args.hcsv_path:
|
||||
typ = 'hcsv'
|
||||
xpath = self.args.hcsv_path
|
||||
else:
|
||||
typ = self.args.type
|
||||
xpath = self.args.xpath
|
||||
|
||||
self.project.metrics_show(self.args.path,
|
||||
json_path=self.args.json_path,
|
||||
tsv_path=self.args.tsv_path,
|
||||
htsv_path=self.args.htsv_path,
|
||||
csv_path=self.args.csv_path,
|
||||
hcsv_path=self.args.hcsv_path,
|
||||
typ=typ,
|
||||
xpath=xpath,
|
||||
all_branches=self.args.all_branches)
|
||||
except DvcException as exc:
|
||||
self.project.logger.error('Failed to show metrics', exc)
|
||||
@@ -19,10 +36,25 @@ class CmdMetricsShow(CmdBase):
|
||||
return 0
|
||||
|
||||
|
||||
class CmdMetricsModify(CmdBase):
|
||||
def run(self):
|
||||
try:
|
||||
self.project.metrics_modify(self.args.path,
|
||||
typ=self.args.type,
|
||||
xpath=self.args.xpath)
|
||||
except DvcException as exc:
|
||||
self.project.logger.error('Failed to modify metrics', exc)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class CmdMetricsAdd(CmdBase):
|
||||
def run(self):
|
||||
try:
|
||||
self.project.metrics_add(self.args.path)
|
||||
self.project.metrics_add(self.args.path,
|
||||
self.args.type,
|
||||
self.args.xpath)
|
||||
except DvcException as exc:
|
||||
msg = 'Failed to add metric file \'{}\''.format(self.args.path)
|
||||
self.project.logger.error(msg, exc)
|
||||
|
||||
@@ -23,7 +23,7 @@ OUTS_MAP = {'hdfs': OutputHDFS,
|
||||
'': OutputLOCAL}
|
||||
|
||||
SCHEMA[schema.Optional(OutputLOCAL.PARAM_CACHE)] = bool
|
||||
SCHEMA[schema.Optional(OutputLOCAL.PARAM_METRIC)] = bool
|
||||
SCHEMA[schema.Optional(OutputLOCAL.PARAM_METRIC)] = OutputLOCAL.METRIC_SCHEMA
|
||||
|
||||
|
||||
def _get(stage, p, info, cache, metric):
|
||||
|
||||
+15
-1
@@ -14,6 +14,12 @@ from dvc.istextfile import istextfile
|
||||
class OutputLOCAL(DependencyLOCAL):
|
||||
PARAM_CACHE = 'cache'
|
||||
PARAM_METRIC = 'metric'
|
||||
PARAM_METRIC_TYPE = 'type'
|
||||
PARAM_METRIC_XPATH = 'xpath'
|
||||
|
||||
METRIC_SCHEMA = schema.Or(None, bool,
|
||||
{schema.Optional(PARAM_METRIC_TYPE): schema.Or(str, None),
|
||||
schema.Optional(PARAM_METRIC_XPATH): schema.Or(str, None)})
|
||||
|
||||
DoesNotExistError = OutputDoesNotExistError
|
||||
IsNotFileOrDirError = OutputIsNotFileOrDirError
|
||||
@@ -38,7 +44,15 @@ class OutputLOCAL(DependencyLOCAL):
|
||||
def dumpd(self):
|
||||
ret = super(OutputLOCAL, self).dumpd()
|
||||
ret[self.PARAM_CACHE] = self.use_cache
|
||||
ret[self.PARAM_METRIC] = self.metric
|
||||
|
||||
if isinstance(self.metric, dict):
|
||||
if self.PARAM_METRIC_XPATH in self.metric and \
|
||||
not self.metric[self.PARAM_METRIC_XPATH]:
|
||||
del self.metric[self.PARAM_METRIC_XPATH]
|
||||
|
||||
if self.metric:
|
||||
ret[self.PARAM_METRIC] = self.metric
|
||||
|
||||
return ret
|
||||
|
||||
def changed(self):
|
||||
|
||||
+52
-34
@@ -426,11 +426,7 @@ class Project(object):
|
||||
reader = list(csv.reader(fd, delimiter=delimiter))
|
||||
return self._do_read_metric_xsv(reader, row, col)
|
||||
|
||||
def _read_metric(self, path, json_path=None,
|
||||
tsv_path=None,
|
||||
htsv_path=None,
|
||||
csv_path=None,
|
||||
hcsv_path=None):
|
||||
def _read_metric(self, path, typ=None, xpath=None):
|
||||
ret = None
|
||||
|
||||
if not os.path.exists(path):
|
||||
@@ -438,16 +434,16 @@ class Project(object):
|
||||
|
||||
try:
|
||||
with open(path, 'r') as fd:
|
||||
if json_path:
|
||||
ret = self._read_metric_json(fd, json_path)
|
||||
elif csv_path:
|
||||
ret = self._read_metric_xsv(fd, csv_path, ',')
|
||||
elif tsv_path:
|
||||
ret = self._read_metric_xsv(fd, tsv_path, '\t')
|
||||
elif hcsv_path:
|
||||
ret = self._read_metric_hxsv(fd, hcsv_path, ',')
|
||||
elif htsv_path:
|
||||
ret = self._read_metric_hxsv(fd, htsv_path, '\t')
|
||||
if typ == 'json':
|
||||
ret = self._read_metric_json(fd, xpath)
|
||||
elif typ == 'csv':
|
||||
ret = self._read_metric_xsv(fd, xpath, ',')
|
||||
elif typ == 'tsv':
|
||||
ret = self._read_metric_xsv(fd, xpath, '\t')
|
||||
elif typ == 'hcsv':
|
||||
ret = self._read_metric_hxsv(fd, xpath, ',')
|
||||
elif typ == 'htsv':
|
||||
ret = self._read_metric_hxsv(fd, xpath, '\t')
|
||||
else:
|
||||
ret = fd.read()
|
||||
except Exception as exc:
|
||||
@@ -470,12 +466,7 @@ class Project(object):
|
||||
|
||||
return matched[0] if matched else None
|
||||
|
||||
def metrics_show(self, path=None, json_path=None,
|
||||
tsv_path=None,
|
||||
htsv_path=None,
|
||||
csv_path=None,
|
||||
hcsv_path=None,
|
||||
all_branches=False):
|
||||
def metrics_show(self, path=None, typ=None, xpath=None, all_branches=False):
|
||||
res = {}
|
||||
for branch in self.scm.brancher(all_branches=all_branches):
|
||||
outs = [out for stage in self.active_stages() for out in stage.outs]
|
||||
@@ -483,23 +474,33 @@ class Project(object):
|
||||
if path:
|
||||
out = self._find_output_by_path(path, outs=outs)
|
||||
stage = out.stage.path if out else None
|
||||
fnames = [path]
|
||||
if out and all([out.metric, not typ, isinstance(out.metric, dict)]):
|
||||
entries = [(path,
|
||||
out.metric.get(out.PARAM_METRIC_TYPE, None),
|
||||
out.metric.get(out.PARAM_METRIC_XPATH, None))]
|
||||
else:
|
||||
entries = [(path, typ, xpath)]
|
||||
else:
|
||||
metrics = filter(lambda o: o.metric, outs)
|
||||
stage = None
|
||||
fnames = map(lambda o: o.path, metrics)
|
||||
entries = []
|
||||
for o in metrics:
|
||||
if not typ and isinstance(o.metric, dict):
|
||||
t = o.metric.get(o.PARAM_METRIC_TYPE, None)
|
||||
x = o.metric.get(o.PARAM_METRIC_XPATH, None)
|
||||
else:
|
||||
t = typ
|
||||
x = xpath
|
||||
entries.append((o.path, t, x))
|
||||
|
||||
for fname in fnames:
|
||||
for fname, t, x in entries:
|
||||
if stage:
|
||||
self.checkout(stage)
|
||||
|
||||
rel = os.path.relpath(fname)
|
||||
metric = self._read_metric(fname,
|
||||
json_path=json_path,
|
||||
tsv_path=tsv_path,
|
||||
htsv_path=htsv_path,
|
||||
csv_path=csv_path,
|
||||
hcsv_path=hcsv_path)
|
||||
typ=t,
|
||||
xpath=x)
|
||||
if not metric:
|
||||
continue
|
||||
|
||||
@@ -524,7 +525,7 @@ class Project(object):
|
||||
'Use \'dvc metrics add\' to add a metric file to track.'
|
||||
raise DvcException(msg)
|
||||
|
||||
def _metrics_modify(self, path, val):
|
||||
def _metrics_modify(self, path, typ=None, xpath=None, delete=False):
|
||||
found = False
|
||||
out = self._find_output_by_path(path)
|
||||
if not out:
|
||||
@@ -539,16 +540,33 @@ class Project(object):
|
||||
msg = 'Cached output \'{}\' is not supported for metrics'
|
||||
raise DvcException(msg.format(out.rel_path))
|
||||
|
||||
out.metric = val
|
||||
if typ:
|
||||
if not isinstance(out.metric, dict):
|
||||
out.metric = {}
|
||||
out.metric[out.PARAM_METRIC_TYPE] = typ
|
||||
|
||||
if xpath:
|
||||
if not isinstance(out.metric, dict):
|
||||
out.metric = {}
|
||||
out.metric[out.PARAM_METRIC_XPATH] = xpath
|
||||
|
||||
if delete:
|
||||
out.metric = None
|
||||
|
||||
out._verify_metric()
|
||||
|
||||
out.stage.dump()
|
||||
|
||||
def metrics_add(self, path):
|
||||
self._metrics_modify(path, True)
|
||||
def metrics_modify(self, path=None, typ=None, xpath=None):
|
||||
self._metrics_modify(path, typ, xpath)
|
||||
|
||||
def metrics_add(self, path, typ=None, xpath=None):
|
||||
if not typ:
|
||||
typ = 'raw'
|
||||
self._metrics_modify(path, typ, xpath)
|
||||
|
||||
def metrics_remove(self, path):
|
||||
self._metrics_modify(path, False)
|
||||
self._metrics_modify(path, delete=True)
|
||||
|
||||
def graph(self):
|
||||
import networkx as nx
|
||||
|
||||
+51
-7
@@ -47,31 +47,46 @@ class TestMetrics(TestDvc):
|
||||
self.assertTrue(ret['bar']['metric'] == 'bar')
|
||||
self.assertTrue(ret['baz']['metric'] == 'baz')
|
||||
|
||||
ret = self.dvc.metrics_show('metric_json', json_path='branch', all_branches=True)
|
||||
ret = self.dvc.metrics_show('metric_json',
|
||||
typ='json',
|
||||
xpath='branch',
|
||||
all_branches=True)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertTrue(ret['foo']['metric_json'] == ['foo'])
|
||||
self.assertTrue(ret['bar']['metric_json'] == ['bar'])
|
||||
self.assertTrue(ret['baz']['metric_json'] == ['baz'])
|
||||
|
||||
ret = self.dvc.metrics_show('metric_tsv', tsv_path='0,0', all_branches=True)
|
||||
ret = self.dvc.metrics_show('metric_tsv',
|
||||
typ='tsv',
|
||||
xpath='0,0',
|
||||
all_branches=True)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertTrue(ret['foo']['metric_tsv'] == ['foo'])
|
||||
self.assertTrue(ret['bar']['metric_tsv'] == ['bar'])
|
||||
self.assertTrue(ret['baz']['metric_tsv'] == ['baz'])
|
||||
|
||||
ret = self.dvc.metrics_show('metric_htsv', htsv_path='branch,0', all_branches=True)
|
||||
ret = self.dvc.metrics_show('metric_htsv',
|
||||
typ='htsv',
|
||||
xpath='branch,0',
|
||||
all_branches=True)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertTrue(ret['foo']['metric_htsv'] == ['foo'])
|
||||
self.assertTrue(ret['bar']['metric_htsv'] == ['bar'])
|
||||
self.assertTrue(ret['baz']['metric_htsv'] == ['baz'])
|
||||
|
||||
ret = self.dvc.metrics_show('metric_csv', csv_path='0,0', all_branches=True)
|
||||
ret = self.dvc.metrics_show('metric_csv',
|
||||
typ='csv',
|
||||
xpath='0,0',
|
||||
all_branches=True)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertTrue(ret['foo']['metric_csv'] == ['foo'])
|
||||
self.assertTrue(ret['bar']['metric_csv'] == ['bar'])
|
||||
self.assertTrue(ret['baz']['metric_csv'] == ['baz'])
|
||||
|
||||
ret = self.dvc.metrics_show('metric_hcsv', hcsv_path='branch,0', all_branches=True)
|
||||
ret = self.dvc.metrics_show('metric_hcsv',
|
||||
typ='hcsv',
|
||||
xpath='branch,0',
|
||||
all_branches=True)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertTrue(ret['foo']['metric_hcsv'] == ['foo'])
|
||||
self.assertTrue(ret['bar']['metric_hcsv'] == ['bar'])
|
||||
@@ -112,7 +127,7 @@ class TestMetricsReproCLI(TestDvc):
|
||||
self.dvc.run(metrics_no_cache=['metrics_bin'])
|
||||
|
||||
|
||||
class TestMetricsCLI(TestMetrics):
|
||||
class TestMetricsCLICompat(TestMetrics):
|
||||
def test(self):
|
||||
#FIXME check output
|
||||
ret = main(['metrics', 'show', '--all-branches', 'metric', '-v'])
|
||||
@@ -133,6 +148,34 @@ class TestMetricsCLI(TestMetrics):
|
||||
ret = main(['metrics', 'show', '--all-branches', 'metric_hcsv', '--hcsv-path', 'branch,0'])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
class TestMetricsCLI(TestMetrics):
|
||||
def test(self):
|
||||
#FIXME check output
|
||||
ret = main(['metrics', 'show', '-a', 'metric', '-v'])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(['metrics', 'show', '-a', 'metric_json', '-t', 'json',
|
||||
'-x', 'branch'])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(['metrics', 'show', '-a', 'metric_tsv', '-t', 'tsv',
|
||||
'-x', '0,0'])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(['metrics', 'show', '-a', 'metric_htsv', '-t', 'htsv',
|
||||
'-x', 'branch,0'])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(['metrics', 'show', '-a', 'metric_csv', '-t', 'csv',
|
||||
'-x', '0,0'])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
ret = main(['metrics', 'show', '-a', 'metric_hcsv', '-t', 'hcsv',
|
||||
'-x', 'branch,0'])
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
|
||||
def test_dir(self):
|
||||
os.mkdir('metrics_dir')
|
||||
|
||||
@@ -190,7 +233,8 @@ class TestCachedMetrics(TestDvc):
|
||||
|
||||
res = self.dvc.metrics_show('metrics.json',
|
||||
all_branches=True,
|
||||
json_path='metrics')
|
||||
typ='json',
|
||||
xpath='metrics')
|
||||
|
||||
self.assertEqual(res, {"master": {"metrics.json": ["master"]},
|
||||
"one": {"metrics.json": ["one"]},
|
||||
|
||||
Reference in New Issue
Block a user