Repository: dotmesh-io/dotscience-python

Issue:
ds.summary -> ds.metric
Rename ds.summary to ds.metric (leave ds.summary as an alias for backward compatibility).

Discussion:

Patch:
diff --git a/README.md b/README.md
index f4c442e..b70bff1 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ Type "help", "copyright", "credits" or "license" for more information.
 
 ## Quick Start
 
-The most basic usage is to record what data files you read and write, and maybe to declare some summary statistics about how well the job went.
+The most basic usage is to record what data files you read and write, and maybe to declare some metrics about how well the job went.
 
 ```python
 import dotscience as ds
@@ -61,8 +61,8 @@ df = pd.read_csv(ds.input('input_file.csv'))
 # Likewise with files you write to:
 df.to_csv(ds.output('output_file.csv'))
 
-# Record a summary statistic about how well your job went
-ds.add_summary('f-score', f_score)
+# Record a metric about how well your job went
+ds.add_metric('f-score', f_score)
 
 ds.publish('Did some awesome data science!')
 ```
@@ -118,7 +118,7 @@ ds.publish("trained mnist model", deploy=True)
 
 ## All the things you can record
 
-There's a lot more than just data files and summary stats that Dotscience will keep track of for you - and there's a choice of convenient ways to specify each thing, so it can fit neatly into your code. Here's the full list:
+There's a lot more than just data files and metrics that Dotscience will keep track of for you - and there's a choice of convenient ways to specify each thing, so it can fit neatly into your code. Here's the full list:
 
 ### Start and end time
 
@@ -377,11 +377,11 @@ ds.add_labels(experimental=True, mode="test")
 ds.publish('Did some awesome data science!')
 ```
 
-### Summary statistics
+### Metrics
 
-Often, your job will be able to measure its own performance in some way - perhaps testing how well a model trained on some training data works when tested on some test data. If you declare those summary statistics to Dotscience, it can help you keep track of which runs produced the best results.
+Often, your job will be able to measure its own performance in some way - perhaps testing how well a model trained on some training data works when tested on some test data. If you declare those metrics to Dotscience, it can help you keep track of which runs produced the best results.
 
-As usual, this can be done while returning the summary value with `summary()`, explicitly with `add_summary()`, or en mass with `add_summaries()`:
+As usual, this can be done while returning the metric with `metric()`, explicitly with `add_metric()`, or en mass with `add_metrics()`:
 
 ```python
 import dotscience as ds
@@ -389,11 +389,11 @@ import dotscience as ds
 ds.script()
 ds.start()
 
-print('Fit: %f%%' % (ds.summary('fit%', fit),))
+print('Fit: %f%%' % (ds.metric('fit%', fit),))
 
-ds.add_summary('fit%', fit)
+ds.add_metric('fit%', fit)
 
-ds.add_summaries(fit=computeFit(), error=computeMeanError())
+ds.add_metrics(fit=computeFit(), error=computeMeanError())
 
 ds.publish('Did some awesome data science!')
 ```
@@ -439,7 +439,7 @@ ds.publish('Trained the model')
 ds.start()
 test_data = load_csv(ds.input('test.csv'))
 accuracy = test_model(model, test_data)
-ds.add_summary('accuracy', accuracy)
+ds.add_metric('accuracy', accuracy)
 ds.publish('Tested the model')
 ```
 
@@ -467,7 +467,7 @@ for smoothing in [1.0, 1.5, 2.0]:
     accuracy = test_model(model, test_data)
 
     # Inform Dotscience of the accuracy
-    ds.add_summary('accuracy', accuracy)
+    ds.add_metric('accuracy', accuracy)
 
     # Publish this run
     ds.publish('Tested the model with smoothing %s' % (smoothing,))
diff --git a/dotscience/__init__.py b/dotscience/__init__.py
index 00096f0..5455d5c 100644
--- a/dotscience/__init__.py
+++ b/dotscience/__init__.py
@@ -39,7 +39,7 @@ class Run:
         self._inputs = set()
         self._outputs = set()
         self._labels = {}
-        self._summary = {}
+        self._metric = {}
         self._parameters = {}
         self._start = None
         self._end = None
@@ -173,22 +173,27 @@ class Run:
         self.model(kind, name, *args, **kwargs)
         return None
 
-    def add_summary(self, label, value):
-        self._summary[str(label)] = str(value)
+    def add_metric(self, label, value):
+        self._metric[str(label)] = str(value)
 
     # Supports any combination of ("a", "val of a", "b", "val of b") and (c="val of c")
-    def add_summaries(self, *args, **kwargs):
+    def add_metrics(self, *args, **kwargs):
         if kwargs is not None:
             for key, value in kwargs.items():
-                self.add_summary(key, value)
+                self.add_metric(key, value)
         for key, value in [args[i:i+2] for i  in range(0, len(args), 2)]:
-            self.add_summary(key, value)
+            self.add_metric(key, value)
 
     # Binds a single value, but returns it unchanged
-    def summary(self, label, value):
-        self.add_summary(label, value)
+    def metric(self, label, value):
+        self.add_metric(label, value)
         return value
 
+    # Backwards compatability:
+    summary = metric
+    add_summary = add_metric
+    add_summaries = add_metrics
+
     def add_parameter(self, label, value):
         self._parameters[str(label)] = str(value)
 
@@ -220,7 +225,7 @@ class Run:
             "input": sorted(self._inputs),
             "output": sorted(expanded_outputs),
             "labels": self._labels,
-            "summary": self._summary,
+            "summary": self._metric,
             "parameters": self._parameters
         }
 
@@ -371,7 +376,7 @@ class Dotscience:
         # up with multiple runs with the same times (calling end() twice has no
         # effect the second time otherwise). If the user doesn't explicitly
         # call start(), it will get implicitly called after the next
-        # interaction with the python library anyway (e.g. ds.summary(), etc),
+        # interaction with the python library anyway (e.g. ds.metric(), etc),
         # via _check_started which does a lazy_start to cover resetting the
         # start timer in this case.
         self.currentRun.forget_times()
@@ -587,8 +592,8 @@ class Dotscience:
         run.452521f7-c069-4271-b06e-dcd55635b871.output-files: ["linear_regressor.pkl"]
         run.452521f7-c069-4271-b06e-dcd55635b871.parameters.features: finishedsqft
         run.452521f7-c069-4271-b06e-dcd55635b871.start: 20190924T112352.843016
-        run.452521f7-c069-4271-b06e-dcd55635b871.summary.lin_rmse: 855408.5050370345
-        run.452521f7-c069-4271-b06e-dcd55635b871.summary.regressor_score: 0.35677710327221
+        run.452521f7-c069-4271-b06e-dcd55635b871.metric.lin_rmse: 855408.5050370345
+        run.452521f7-c069-4271-b06e-dcd55635b871.metric.regressor_score: 0.35677710327221
         run.452521f7-c069-4271-b06e-dcd55635b871.workload-file: hello-dotscience.ipynb
         runner.cpu: ["1x Intel(R) Xeon(R) CPU @ 2.30GHz"]
         runner.name: 6ddeae52-untitled
@@ -655,7 +660,7 @@ class Dotscience:
         branch = dot.getBranch("master")
         result = branch.commit("Remote dotscience run", commit)
         # construct URL
-        runURL = f"{self._hostname}/project/{project['id']}/runs/summary/{self.currentRun._id}"
+        runURL = f"{self._hostname}/project/{project['id']}/runs/metric/{self.currentRun._id}"
         return runURL
 
     def _find_model_id(self, run_id):
@@ -888,17 +893,22 @@ class Dotscience:
         self._check_started()
         self.currentRun.add_labels(*args, **kwargs)
 
-    def add_summary(self, label, value):
+    def add_metric(self, label, value):
         self._check_started()
-        self.currentRun.add_summary(label, value)
+        self.currentRun.add_metric(label, value)
 
-    def add_summaries(self, *args, **kwargs):
+    def add_metrics(self, *args, **kwargs):
         self._check_started()
-        self.currentRun.add_summaries(*args, **kwargs)
+        self.currentRun.add_metrics(*args, **kwargs)
 
-    def summary(self, label, value):
+    def metric(self, label, value):
         self._check_started()
-        return self.currentRun.summary(label, value)
+        return self.currentRun.metric(label, value)
+
+    # Backwards compatibility:
+    add_summary = add_metric
+    add_summaries = add_metrics
+    summary = metric
 
     def add_parameter(self, label, value):
         self._check_started()
@@ -982,14 +992,14 @@ def add_labels(*args, **kwargs):
 def label(label, value):
     return _defaultDS.label(label, value)
 
-def add_summary(label, value):
-    _defaultDS.add_summary(label, value)
+def add_metric(label, value):
+    _defaultDS.add_metric(label, value)
 
-def add_summaries(*args, **kwargs):
-    _defaultDS.add_summaries(*args, **kwargs)
+def add_metrics(*args, **kwargs):
+    _defaultDS.add_metrics(*args, **kwargs)
 
-def summary(label, value):
-    return _defaultDS.summary(label, value)
+def metric(label, value):
+    return _defaultDS.metric(label, value)
 
 def model(kind, name, *args, **kwargs):
     return _defaultDS.model(kind, name, *args, **kwargs)
@@ -1006,9 +1016,9 @@ def parameter(label, value):
 def remove_prefix(text, prefix):
     return text[text.startswith(prefix) and len(prefix):]
 
-add_metric = add_summary
-add_metrics = add_summaries
-metric = summary
+add_metric = add_metric
+add_metrics = add_metrics
+metric = metric
 param = parameter
 
 def debug():
@@ -1022,3 +1032,7 @@ def connect(username, apikey, project, hostname=""):
         username, apikey, project, hostname,
     )
 
+# Backwards compatibility:
+summary = metric
+add_summary = add_metric
+add_summaries = add_metrics


Test patch:
diff --git a/dotscience/test_dotscience.py b/dotscience/test_dotscience.py
index ccf8902..9a4ff38 100644
--- a/dotscience/test_dotscience.py
+++ b/dotscience/test_dotscience.py
@@ -11,6 +11,27 @@ import shutil
 from hypothesis import given, assume, note
 from hypothesis.strategies import text, lists, sampled_from
 
+
+def test_metric_to_summary_backwards_compatibility():
+    """summary()/add_summary()/add_summaries() backwards compatibility."""
+    # Run level:
+    r = dotscience.Run("/workspace-root")
+    assert r.summary == r.metric
+    assert r.add_summary == r.add_metric
+    assert r.add_summaries == r.add_metrics
+
+    # Dotscience level:
+    d = dotscience.Dotscience()
+    assert d.summary == d.metric
+    assert d.add_summary == d.add_metric
+    assert d.add_summaries == d.add_metrics
+
+    # Module level:
+    assert dotscience.summary == dotscience.metric
+    assert dotscience.add_summary == dotscience.add_metric
+    assert dotscience.add_summaries == dotscience.add_metrics
+
+
 ###
 ### Test the Run class
 ###
@@ -28,7 +49,7 @@ def test_run_null():
 
 input_files=["test.csv","/workspace-root/test.csv","data/test.csv"]
 output_files=["test.csv","/workspace-root/test.csv","data/test.csv"]
-    
+
 @given(text(),text())
 def test_run_basics(error, description):
     r = dotscience.Run("/workspace-root")
@@ -305,7 +326,7 @@ def test_run_labels_multi(a,b,c,d):
 @given(text())
 def test_run_summary_1(x):
     r = dotscience.Run("/workspace-root")
-    assert r.summary("food", x) == x
+    assert r.metric("food", x) == x
     assert str(r) == """[[DOTSCIENCE-RUN:%s]]%s[[/DOTSCIENCE-RUN:%s]]""" % \
     (r._id, json.dumps({"version": "1",
                         "labels": {},
@@ -318,9 +339,9 @@ def test_run_summary_1(x):
 @given(text(),text(),text(),text())
 def test_run_summary_multi(a,b,c,d):
     r = dotscience.Run("/workspace-root")
-    r.add_summaries("a", a)
-    r.add_summaries(b=b)
-    r.add_summaries("c", c, d=d)
+    r.add_metrics("a", a)
+    r.add_metrics(b=b)
+    r.add_metrics("c", c, d=d)
     assert str(r) == """[[DOTSCIENCE-RUN:%s]]%s[[/DOTSCIENCE-RUN:%s]]""" % \
     (r._id, json.dumps({"version": "1",
                         "labels": {},
@@ -509,7 +530,7 @@ def test_default_script_name():
     m1 = _parse(s1.getvalue())
 
     # This might be a fragile assertion...
-    assert m1["workload-file"].endswith("/usr/local/bin/pytest")
+    assert m1["workload-file"].endswith("/bin/pytest") or m1["workload-file"].endswith("/bin/py.test")
 
 def test_explicit_script_name():
     ds = dotscience.Dotscience()
@@ -770,7 +791,7 @@ def test_label_n(a, b):
 def test_summary_1a(d):
     s=io.StringIO()
     dotscience.start()
-    dotscience.add_summary("test", d)
+    dotscience.add_metric("test", d)
     dotscience.publish(stream=s)
     m = _parse(s.getvalue())
     assert m["output"] == []
@@ -784,7 +805,7 @@ def test_summary_1a(d):
 def test_summary_1b(d):
     s=io.StringIO()
     dotscience.start()
-    assert dotscience.summary("test", d) == d
+    assert dotscience.metric("test", d) == d
     dotscience.publish(stream=s)
     m = _parse(s.getvalue())
     assert m["output"] == []
@@ -798,8 +819,8 @@ def test_summary_1b(d):
 def test_summary_n(a, b):
     s=io.StringIO()
     dotscience.start()
-    dotscience.add_summaries("a", a)
-    dotscience.add_summaries(b=b)
+    dotscience.add_metrics("a", a)
+    dotscience.add_metrics(b=b)
     dotscience.publish(stream=s)
     m = _parse(s.getvalue())
     assert m["output"] == []