diff --git a/owrx/details.py b/owrx/details.py index 5bc72531..8bdfd3ef 100644 --- a/owrx/details.py +++ b/owrx/details.py @@ -1,18 +1,21 @@ from owrx.config import Config from owrx.locator import Locator from owrx.property import PropertyFilter +from owrx.property.filter import ByPropertyName class ReceiverDetails(PropertyFilter): def __init__(self): super().__init__( Config.get(), - "receiver_name", - "receiver_location", - "receiver_asl", - "receiver_gps", - "photo_title", - "photo_desc", + ByPropertyName( + "receiver_name", + "receiver_location", + "receiver_asl", + "receiver_gps", + "photo_title", + "photo_desc", + ) ) def __dict__(self): diff --git a/owrx/property/__init__.py b/owrx/property/__init__.py index 857b4cb9..302ecc1f 100644 --- a/owrx/property/__init__.py +++ b/owrx/property/__init__.py @@ -1,5 +1,6 @@ from abc import ABC, abstractmethod from owrx.property.validators import Validator +from owrx.property.filter import Filter, ByPropertyName import logging logger = logging.getLogger(__name__) @@ -60,7 +61,7 @@ class PropertyManager(ABC): return self.__dict__().__len__() def filter(self, *props): - return PropertyFilter(self, *props) + return PropertyFilter(self, ByPropertyName(*props)) def readonly(self): return PropertyReadOnly(self) @@ -132,41 +133,41 @@ class PropertyLayer(PropertyManager): class PropertyFilter(PropertyManager): - def __init__(self, pm: PropertyManager, *props: str): + def __init__(self, pm: PropertyManager, filter: Filter): super().__init__() self.pm = pm - self.props = props + self._filter = filter self.pm.wire(self.receiveEvent) def receiveEvent(self, changes): - changesToForward = {name: value for name, value in changes.items() if name in self.props} + changesToForward = {name: value for name, value in changes.items() if self._filter.apply(name)} self._fireCallbacks(changesToForward) def __getitem__(self, item): - if item not in self.props: + if not self._filter.apply(item): raise KeyError(item) return self.pm.__getitem__(item) def __setitem__(self, key, value): - if key not in self.props: + if not self._filter.apply(key): raise KeyError(key) return self.pm.__setitem__(key, value) def __contains__(self, item): - if item not in self.props: + if not self._filter.apply(item): return False return self.pm.__contains__(item) def __dict__(self): - return {k: v for k, v in self.pm.__dict__().items() if k in self.props} + return {k: v for k, v in self.pm.__dict__().items() if self._filter.apply(k)} def __delitem__(self, key): - if key not in self.props: + if not self._filter.apply(key): raise KeyError(key) return self.pm.__delitem__(key) def keys(self): - return [k for k in self.pm.keys() if k in self.props] + return [k for k in self.pm.keys() if self._filter.apply(k)] class PropertyDelegator(PropertyManager): diff --git a/owrx/property/filter.py b/owrx/property/filter.py new file mode 100644 index 00000000..7e870d05 --- /dev/null +++ b/owrx/property/filter.py @@ -0,0 +1,23 @@ +from abc import ABC, abstractmethod + + +class Filter(ABC): + @abstractmethod + def apply(self, prop) -> bool: + pass + + +class ByPropertyName(Filter): + def __init__(self, *props): + self.props = props + + def apply(self, prop) -> bool: + return prop in self.props + + +class ByLambda(Filter): + def __init__(self, func): + self.func = func + + def apply(self, prop) -> bool: + return self.func(prop) diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py index e6ab05bb..2db99b25 100644 --- a/owrx/source/__init__.py +++ b/owrx/source/__init__.py @@ -9,7 +9,8 @@ import signal from abc import ABC, abstractmethod from owrx.command import CommandMapper from owrx.socket import getAvailablePort -from owrx.property import PropertyStack, PropertyLayer +from owrx.property import PropertyStack, PropertyLayer, PropertyFilter +from owrx.property.filter import ByLambda from owrx.form import Input, TextInput, NumberInput, CheckboxInput, ModesInput from owrx.form.converter import OptionalConverter from owrx.form.device import GainInput @@ -96,6 +97,9 @@ class SdrSource(ABC): if self.isAlwaysOn() and self.state is not SdrSourceState.DISABLED: self.start() + def _loadProfile(self, profile): + self.props.replaceLayer(0, PropertyFilter(profile, ByLambda(lambda x: x != "name"))) + def validateProfiles(self): props = PropertyStack() props.addLayer(1, self.props) @@ -155,7 +159,7 @@ class SdrSource(ABC): profile = profiles[profile_id] self.profile_id = profile_id - self.props.replaceLayer(0, profile) + self._loadProfile(profile) def getId(self): return self.id diff --git a/test/property/filter/__init__.py b/test/property/filter/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/property/filter/test_by_lambda.py b/test/property/filter/test_by_lambda.py new file mode 100644 index 00000000..14c6daf7 --- /dev/null +++ b/test/property/filter/test_by_lambda.py @@ -0,0 +1,17 @@ +from owrx.property.filter import ByLambda +from unittest import TestCase +from unittest.mock import Mock + + +class TestByLambda(TestCase): + def testPositive(self): + mock = Mock(return_value=True) + filter = ByLambda(mock) + self.assertTrue(filter.apply("test_key")) + mock.assert_called_with("test_key") + + def testNegateive(self): + mock = Mock(return_value=False) + filter = ByLambda(mock) + self.assertFalse(filter.apply("test_key")) + mock.assert_called_with("test_key") diff --git a/test/property/filter/test_by_property_name.py b/test/property/filter/test_by_property_name.py new file mode 100644 index 00000000..098f69b5 --- /dev/null +++ b/test/property/filter/test_by_property_name.py @@ -0,0 +1,12 @@ +from owrx.property.filter import ByPropertyName +from unittest import TestCase + + +class ByPropertyNameTest(TestCase): + def testNameIsInList(self): + filter = ByPropertyName("test_key") + self.assertTrue(filter.apply("test_key")) + + def testNameNotInList(self): + filter = ByPropertyName("test_key") + self.assertFalse(filter.apply("other_key")) diff --git a/test/property/test_property_filter.py b/test/property/test_property_filter.py index 37b9e211..bdea738b 100644 --- a/test/property/test_property_filter.py +++ b/test/property/test_property_filter.py @@ -7,20 +7,26 @@ class PropertyFilterTest(TestCase): def testPassesProperty(self): pm = PropertyLayer() pm["testkey"] = "testvalue" - pf = PropertyFilter(pm, "testkey") + mock = Mock() + mock.apply.return_value = True + pf = PropertyFilter(pm, mock) self.assertEqual(pf["testkey"], "testvalue") def testMissesProperty(self): pm = PropertyLayer() pm["testkey"] = "testvalue" - pf = PropertyFilter(pm, "other_key") + mock = Mock() + mock.apply.return_value = False + pf = PropertyFilter(pm, mock) self.assertFalse("testkey" in pf) with self.assertRaises(KeyError): x = pf["testkey"] def testForwardsEvent(self): pm = PropertyLayer() - pf = PropertyFilter(pm, "testkey") + mock = Mock() + mock.apply.return_value = True + pf = PropertyFilter(pm, mock) mock = Mock() pf.wire(mock.method) pm["testkey"] = "testvalue" @@ -28,7 +34,9 @@ class PropertyFilterTest(TestCase): def testForwardsPropertyEvent(self): pm = PropertyLayer() - pf = PropertyFilter(pm, "testkey") + mock = Mock() + mock.apply.return_value = True + pf = PropertyFilter(pm, mock) mock = Mock() pf.wireProperty("testkey", mock.method) pm["testkey"] = "testvalue" @@ -36,7 +44,9 @@ class PropertyFilterTest(TestCase): def testForwardsWrite(self): pm = PropertyLayer() - pf = PropertyFilter(pm, "testkey") + mock = Mock() + mock.apply.return_value = True + pf = PropertyFilter(pm, mock) pf["testkey"] = "testvalue" self.assertTrue("testkey" in pm) self.assertEqual(pm["testkey"], "testvalue") @@ -44,7 +54,9 @@ class PropertyFilterTest(TestCase): def testOverwrite(self): pm = PropertyLayer() pm["testkey"] = "old value" - pf = PropertyFilter(pm, "testkey") + mock = Mock() + mock.apply.return_value = True + pf = PropertyFilter(pm, mock) pf["testkey"] = "new value" self.assertEqual(pm["testkey"], "new value") self.assertEqual(pf["testkey"], "new value") @@ -52,7 +64,9 @@ class PropertyFilterTest(TestCase): def testRejectsWrite(self): pm = PropertyLayer() pm["testkey"] = "old value" - pf = PropertyFilter(pm, "otherkey") + mock = Mock() + mock.apply.return_value = False + pf = PropertyFilter(pm, mock) with self.assertRaises(KeyError): pf["testkey"] = "new value" self.assertEqual(pm["testkey"], "old value")