changeset 1551:60cc3a504377

more settings.py coverage. Zeros are now dropped from Settings lists. Ignore-this: cc61e2f12ad9b41daa126ccfb58b4f09
author Drew Perttula <drewp@bigasterisk.com>
date Mon, 22 May 2017 04:37:28 +0000
parents cbf4fc71d8d8
children 0aad247a1168
files light9/collector/collector.py light9/effect/settings.py light9/effect/settings_test.py light9/paint/capture.py light9/paint/solve.py light9/rdfdb/localsyncedgraph.py
diffstat 6 files changed, 135 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/light9/collector/collector.py	Fri May 19 07:54:20 2017 +0000
+++ b/light9/collector/collector.py	Mon May 22 04:37:28 2017 +0000
@@ -49,7 +49,7 @@
         
 class Collector(Generic[ClientType, ClientSessionType]):
     def __init__(self, graph, outputs, listeners=None, clientTimeoutSec=10):
-        # type: (Graph, List[Output], float) -> None
+        # type: (Graph, List[Output], List[Listener], float) -> None
         self.graph = graph
         self.outputs = outputs
         self.listeners = listeners
--- a/light9/effect/settings.py	Fri May 19 07:54:20 2017 +0000
+++ b/light9/effect/settings.py	Mon May 22 04:37:28 2017 +0000
@@ -17,13 +17,51 @@
     return ret
 
 class _Settings(object):
+    """
+    default values are 0. Internal rep must not store zeros or some
+    comparisons will break.
+    """
     def __init__(self, graph, settingsList):
         self.graph = graph # for looking up all possible attrs
         self._compiled = {} # dev: { attr: val }
         for row in settingsList:
             self._compiled.setdefault(row[0], {})[row[1]] = row[2]
         # self._compiled may not be final yet- see _fromCompiled
+        self._delZeros()
+        
+    @classmethod
+    def _fromCompiled(cls, graph, compiled):
+        obj = cls(graph, [])
+        obj._compiled = compiled
+        obj._delZeros()
+        return obj
+            
+    @classmethod
+    def fromResource(cls, graph, subj):
+        settingsList = []
+        with graph.currentState() as g:
+            for s in g.objects(subj, L9['setting']):
+                d = g.value(s, L9['device'])
+                da = g.value(s, L9['deviceAttr'])
+                v = getVal(g, s)
+                settingsList.append((d, da, v))
+        return cls(graph, settingsList)
 
+    @classmethod
+    def fromVector(cls, graph, vector):
+        compiled = {}
+        for (d, a), v in zip(cls(graph, [])._vectorKeys(), vector):
+            compiled.setdefault(d, {})[a] = v
+        return cls._fromCompiled(graph, compiled)
+
+    def _delZeros(self):
+        for dev, av in self._compiled.items():
+            for attr, val in av.items():
+                if val == 0:
+                    del av[attr]
+            if not av:
+                del self._compiled[dev]
+        
     def __hash__(self):
         itemed = tuple([(d, tuple([(a, v) for a, v in sorted(av.items())]))
                         for d, av in sorted(self._compiled.items())])
@@ -54,30 +92,6 @@
         
     def getValue(self, dev, attr):
         return self._compiled.get(dev, {}).get(attr, 0)
-        
-    @classmethod
-    def _fromCompiled(cls, graph, compiled):
-        obj = cls(graph, [])
-        obj._compiled = compiled
-        return obj
-            
-    @classmethod
-    def fromResource(cls, graph, subj):
-        settingsList = []
-        with graph.currentState() as g:
-            for s in g.objects(subj, L9['setting']):
-                d = g.value(s, L9['device'])
-                da = g.value(s, L9['deviceAttr'])
-                v = getVal(g, s)
-                settingsList.append((d, da, v))
-        return cls(graph, settingsList)
-
-    @classmethod
-    def fromVector(cls, graph, vector):
-        compiled = {}
-        for (d, a), v in zip(cls(graph, [])._vectorKeys(), vector):
-            compiled.setdefault(d, {})[a] = v
-        return cls._fromCompiled(graph, compiled)
 
     def _vectorKeys(self):
         """stable order of all the dev,attr pairs for this type of settings"""
@@ -118,7 +132,7 @@
                 dist += abs(attrs1[key] - attrs2[key])
         return dist
 
-    def addStatements(self, subj, ctx, settingRoot, settingsSubgraphCache):
+    def statements(self, subj, ctx, settingRoot, settingsSubgraphCache):
         """
         settingRoot can be shared across images (or even wider if you want)
         """
@@ -141,7 +155,7 @@
                 ])
             settingsSubgraphCache.add(setting)
             
-        self.graph.patch(Patch(addQuads=add))
+        return add
 
 
 class DeviceSettings(_Settings):
--- a/light9/effect/settings_test.py	Fri May 19 07:54:20 2017 +0000
+++ b/light9/effect/settings_test.py	Mon May 22 04:37:28 2017 +0000
@@ -1,4 +1,6 @@
 import unittest
+from rdflib import Literal
+from light9.rdfdb.patch import Patch
 from light9.rdfdb.localsyncedgraph import LocalSyncedGraph
 from light9.namespaces import RDF, L9, DEV
 from light9.effect.settings import DeviceSettings
@@ -11,3 +13,84 @@
     def testToVectorZero(self):
         ds = DeviceSettings(self.graph, [])
         self.assertEqual([0] * 20, ds.toVector())
+
+    def testEq(self):
+        s1 = DeviceSettings(self.graph, [
+            (L9['light1'], L9['attr1'], 0.5),
+            (L9['light1'], L9['attr2'], 0.3),
+        ])
+        s2 = DeviceSettings(self.graph, [
+            (L9['light1'], L9['attr2'], 0.3),
+            (L9['light1'], L9['attr1'], 0.5),
+        ])
+        self.assertTrue(s1 == s2)
+        self.assertFalse(s1 != s2)
+
+    def testMissingFieldsEqZero(self):
+        self.assertEqual(
+            DeviceSettings(self.graph, [(L9['aura1'], L9['rx'], 0),]),
+            DeviceSettings(self.graph, []))
+        
+    def testFromResource(self):
+        ctx = L9['']
+        self.graph.patch(Patch(addQuads=[
+            (L9['foo'], L9['setting'], L9['foo_set0'], ctx),
+            (L9['foo_set0'], L9['device'], L9['light1'], ctx),
+            (L9['foo_set0'], L9['deviceAttr'], L9['brightness'], ctx),
+            (L9['foo_set0'], L9['value'], Literal(0.1), ctx),
+            (L9['foo'], L9['setting'], L9['foo_set1'], ctx),
+            (L9['foo_set1'], L9['device'], L9['light1'], ctx),
+            (L9['foo_set1'], L9['deviceAttr'], L9['speed'], ctx),
+            (L9['foo_set1'], L9['scaledValue'], Literal(0.2), ctx),
+        ]))
+        s = DeviceSettings.fromResource(self.graph, L9['foo'])
+
+        self.assertEqual(DeviceSettings(self.graph, [
+            (L9['light1'], L9['brightness'], 0.1),
+            (L9['light1'], L9['speed'], 0.2),
+        ]), s)
+
+    def testToVector(self):
+        v = DeviceSettings(self.graph, [
+            (DEV['aura1'], L9['rx'], 0.5),
+        ]).toVector()
+        self.assertEqual(
+            [0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], v)
+        
+    def testFromVector(self):
+        s = DeviceSettings.fromVector(
+            self.graph,
+            [0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+        
+        self.assertEqual(DeviceSettings(self.graph, [
+            (DEV['aura1'], L9['rx'], 0.5),
+        ]), s)                            
+
+    def testAsList(self):
+        sets = [
+            (L9['light1'], L9['attr2'], 0.3),
+            (L9['light1'], L9['attr1'], 0.5),
+        ]
+        self.assertItemsEqual(sets, DeviceSettings(self.graph, sets).asList())
+
+    def testDevices(self):
+        s = DeviceSettings(self.graph, [
+            (L9['aura1'], L9['rx'], 0),
+            (L9['aura2'], L9['rx'], 0.1),
+            ])
+        # aura1 is all defaults (zeros), so it doesn't get listed
+        self.assertItemsEqual([L9['aura2']], s.devices())
+
+    def testAddStatements(self):
+        s = DeviceSettings(self.graph, [
+            (L9['aura2'], L9['rx'], 0.1),
+            ])
+        stmts = s.statements(L9['foo'], L9['ctx1'], L9['s_'], set())
+        self.maxDiff=None
+        self.assertItemsEqual([
+            (L9['foo'], L9['setting'], L9['s_set8011962'], L9['ctx1']),
+            (L9['s_set8011962'], L9['device'], L9['aura2'], L9['ctx1']),
+            (L9['s_set8011962'], L9['deviceAttr'], L9['rx'], L9['ctx1']),
+            (L9['s_set8011962'], L9['value'], Literal(0.1), L9['ctx1']),
+        ], stmts)
+        
--- a/light9/paint/capture.py	Fri May 19 07:54:20 2017 +0000
+++ b/light9/paint/capture.py	Mon May 22 04:37:28 2017 +0000
@@ -6,10 +6,10 @@
 from light9.paint.solve import loadNumPy
 
 def writeCaptureDescription(graph, ctx, uri, dev, relOutPath, settingsSubgraphCache, settings):
-    settings.addStatements(
+    graph.patch(Patch(addQuads=settings.statements(
         uri, ctx=ctx,
         settingRoot=URIRef('/'.join([showconfig.showUri(), 'capture', dev.rsplit('/')[1]])),
-        settingsSubgraphCache=settingsSubgraphCache)
+        settingsSubgraphCache=settingsSubgraphCache)))
     graph.patch(Patch(addQuads=[
         (dev, L9['capture'], uri, ctx),
         (uri, L9['imagePath'], URIRef('/'.join([showconfig.showUri(), relOutPath])), ctx),
--- a/light9/paint/solve.py	Fri May 19 07:54:20 2017 +0000
+++ b/light9/paint/solve.py	Mon May 22 04:37:28 2017 +0000
@@ -149,7 +149,7 @@
                     settings.append((dev, attr, toHex(rgb)))
                 else:
                     settings.append((dev, attr, xLeft.pop()))
-            return settings
+            return DeviceSettings(self.graph, settings)
 
         
         def drawError(x):
--- a/light9/rdfdb/localsyncedgraph.py	Fri May 19 07:54:20 2017 +0000
+++ b/light9/rdfdb/localsyncedgraph.py	Mon May 22 04:37:28 2017 +0000
@@ -3,6 +3,7 @@
 from light9.rdfdb.currentstategraphapi import CurrentStateGraphApi
 from light9.rdfdb.autodepgraphapi import AutoDepGraphApi
 from light9.rdfdb.grapheditapi import GraphEditApi
+from light9.rdfdb.rdflibpatch import patchQuads
 
 class LocalSyncedGraph(CurrentStateGraphApi, AutoDepGraphApi, GraphEditApi):
     """for tests"""
@@ -11,3 +12,10 @@
         for f in files or []:
             self._graph.parse(f, format='n3')
             
+
+    def patch(self, p):
+        patchQuads(self._graph,
+                   deleteQuads=p.delQuads,
+                   addQuads=p.addQuads,
+                   perfect=True)
+        # no deps