diff --git a/build/lib/pyrediseasyio/io_group.py b/build/lib/pyrediseasyio/io_group.py index d54ff83..3a9d14a 100644 --- a/build/lib/pyrediseasyio/io_group.py +++ b/build/lib/pyrediseasyio/io_group.py @@ -1,5 +1,5 @@ from pyrediseasyio.reader_writer import ReaderWriter -from pyrediseasyio.single_io import SingleIO +from pyrediseasyio.io.base import SingleIO import json diff --git a/build/lib/tests/test_read_write.py b/build/lib/tests/test_read_write.py index 673e739..9fa7d8c 100644 --- a/build/lib/tests/test_read_write.py +++ b/build/lib/tests/test_read_write.py @@ -1,6 +1,6 @@ import unittest -from pyrediseasyio.io_group import IOGroup -from pyrediseasyio.single_io import BooleanIO, IntIO, FloatIO, StringIO +from pyrediseasyio.io.io_group import IOGroup +from pyrediseasyio.io.base import BooleanIO, IntIO, FloatIO, StringIO from assertpy import assert_that diff --git a/dist/pyrediseasyio-0.0.11-py3-none-any.whl b/dist/pyrediseasyio-0.0.11-py3-none-any.whl deleted file mode 100644 index 5a7d645..0000000 Binary files a/dist/pyrediseasyio-0.0.11-py3-none-any.whl and /dev/null differ diff --git a/dist/pyrediseasyio-0.0.11.tar.gz b/dist/pyrediseasyio-0.0.11.tar.gz deleted file mode 100644 index b6f53d7..0000000 Binary files a/dist/pyrediseasyio-0.0.11.tar.gz and /dev/null differ diff --git a/pyrediseasyio/__init__.py b/pyrediseasyio/__init__.py index e69de29..6a7d220 100644 --- a/pyrediseasyio/__init__.py +++ b/pyrediseasyio/__init__.py @@ -0,0 +1,8 @@ +from pyrediseasyio.io.io_group import IOGroup +from pyrediseasyio.html.html_io_group import HMTLIOGroup +from pyrediseasyio.html.html_io import HTMLIO +from pyrediseasyio.io.base import SingleIO +from pyrediseasyio.io.boolean_io import BooleanIO +from pyrediseasyio.io.string_io import StringIO +from pyrediseasyio.io.float_io import FloatIO +from pyrediseasyio.io.integer_io import IntIO diff --git a/pyrediseasyio/html/__init__.py b/pyrediseasyio/html/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyrediseasyio/html/html_io.py b/pyrediseasyio/html/html_io.py new file mode 100644 index 0000000..0a25191 --- /dev/null +++ b/pyrediseasyio/html/html_io.py @@ -0,0 +1,52 @@ +from pyrediseasyio.io.base import SingleIO +from dominate.tags import div, tr, td + + + +class HTMLIO: + + def __init__(self, io: SingleIO, html_id_header: str, namespace: str = 'pyredeio'): + self.io = io + self.namespace = namespace + self.html_id_header = html_id_header + + @staticmethod + def html_id_for(header: str, attr: SingleIO): + return f'{header}_{attr.addr}_io' + + @property + def html_id(self): + return self.html_id_for(self.html_id_header, self.io) + + @property + def html_class(self): + return f'{self.namespace}_io' + + def html(self, show_units: bool = True): + io = self.io + name, addr, val, units = io.name, io.addr, io.value, io.units + with div(cls=self.html_class, id=self.html_id) as container: + div(name, cls=f'{self.html_class}_name') + div(val, cls=f'{self.html_class}_value', id=f'{self.html_id}_value', onchange='OnIOValueChange(event)') + if show_units: + units = '' if units is None else units + div(units, cls=f'{self.html_class}_units') + return container + + def html_row(self, show_units: bool = True): + io = self.io + name, addr, val, units = io.name, io.addr, io.value, io.units + with tr(cls=self.html_class, id=self.html_id) as container: + td(name, cls=f'{self.html_class}_name') + td(val, cls=f'{self.html_class}_value', id=f'{self.html_id}_value', onchange='OnIOValueChange(event)') + if show_units: + units = '' if units is None else units + td(units, cls=f'{self.html_class}_units') + return container + + + + + + + diff --git a/pyrediseasyio/html/html_io_group.py b/pyrediseasyio/html/html_io_group.py new file mode 100644 index 0000000..d0f2fe3 --- /dev/null +++ b/pyrediseasyio/html/html_io_group.py @@ -0,0 +1,61 @@ +from pyrediseasyio.io.io_group import IOGroup +from pyrediseasyio.html.html_io import HTMLIO +from typing import List, Callable +from dominate.tags import div, table +import json + + +class HMTLIOGroup: + + def __init__(self, io_group: IOGroup, + html_id_header: str, + namespace: str = 'pyredeio'): + + self.html_id_header = html_id_header + self._io_group = io_group + self.namespace = namespace + + @property + def html_id(self): + return f'{self.html_id_header}_io_container' + + def html(self, by_names: List = None, by_type: List = None, + by_lambda_each: Callable = None, by_lambda_results: Callable = None, + html_classes: List = None): + + attrs = self._io_group.get_attributes(by_names, by_type, by_lambda_each, by_lambda_results) + cls = f'{self.namespace}_io_container' + classes = html_classes.append(cls) if html_classes else [cls] + classes = ' '.join(classes) + + with div(cls=classes, id=self.html_id) as container: + for attr in attrs: + HTMLIO(attr, self.html_id_header, self.namespace).html() + return container + + def html_table(self, by_names: List = None, by_type: List = None, + by_lambda_each: Callable = None, by_lambda_results: Callable = None, + html_classes: List = None, show_units: bool=True): + + attrs = self._io_group.get_attributes(by_names, by_type, by_lambda_each, by_lambda_results) + cls = f'{self.namespace}_io_container' + classes = html_classes.append(cls) if html_classes else [cls] + classes = ' '.join(classes) + + with table(cls=classes, id=self.html_id) as container: + for attr in attrs: + HTMLIO(attr, self.html_id_header, self.namespace).html_row(show_units) + return container + + def dumps(self, by_names: List = None, by_type: List = None, + by_lambda_each: Callable = None, by_lambda_results: Callable = None): + + attrs = self._io_group.get_attributes(by_names, by_type, by_lambda_each, by_lambda_results) + + def f(a): + return dict(id=HTMLIO.html_id_for(self.html_id_header, a), name=a.name, value=a.value, units=a.units) + results = [f(a) for a in attrs] + + return json.dumps(results) + + diff --git a/pyrediseasyio/io/__init__.py b/pyrediseasyio/io/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyrediseasyio/single_io.py b/pyrediseasyio/io/base.py similarity index 65% rename from pyrediseasyio/single_io.py rename to pyrediseasyio/io/base.py index c27ec55..36e8eb1 100644 --- a/pyrediseasyio/single_io.py +++ b/pyrediseasyio/io/base.py @@ -1,6 +1,6 @@ from pyrediseasyio.abstract_reader_writer import AbstractReaderWriter +from dominate.tags import div, span, tr, td import threading -from str2bool import str2bool import json lock = threading.Lock() @@ -94,49 +94,3 @@ def write(self, value): value = self._convert_type(value) self._reader_writer.write(self.addr, value) - -class BooleanIO(SingleIO): - def __init__(self, name: str, addr: str, default: bool = False, units: str = None, reader: AbstractReaderWriter = None): - super().__init__(name, addr, default, units, reader) - - @staticmethod - def _convert_type(value): - if isinstance(value, str): - return str2bool(value) - return bool(value) - - def __bool__(self): - return self.value - - @property - def value(self) -> bool: - return super().value - - -class IntIO(SingleIO): - def __init__(self, name: str, addr: str, default: int = 0, units: str = None, reader: AbstractReaderWriter = None): - super().__init__(name, addr, default, units, reader) - - @staticmethod - def _convert_type(value): - return int(value) - - -class FloatIO(SingleIO): - def __init__(self, name: str, addr: str, default: float = 0, units: str = None, reader: AbstractReaderWriter = None): - super().__init__(name, addr, default, units, reader) - - @staticmethod - def _convert_type(value): - return float(value) - - -class StringIO(SingleIO): - def __init__(self, name: str, addr: str, default: str = '', units: str = None, reader: AbstractReaderWriter = None): - super().__init__(name, addr, default, units, reader) - - @staticmethod - def _convert_type(value): - return str(value) - - diff --git a/pyrediseasyio/io/boolean_io.py b/pyrediseasyio/io/boolean_io.py new file mode 100644 index 0000000..167c181 --- /dev/null +++ b/pyrediseasyio/io/boolean_io.py @@ -0,0 +1,24 @@ +from pyrediseasyio import SingleIO +from pyrediseasyio.abstract_reader_writer import AbstractReaderWriter +from str2bool import str2bool + + +class BooleanIO(SingleIO): + def __init__(self, name: str, addr: str, default: bool = False, units: str = None, + reader: AbstractReaderWriter = None): + super().__init__(name, addr, default, units, reader) + + @staticmethod + def _convert_type(value): + if value is None: + return False + if isinstance(value, str): + return str2bool(value) + return bool(value) + + def __bool__(self): + return self.value + + @property + def value(self) -> bool: + return super().value diff --git a/pyrediseasyio/io/float_io.py b/pyrediseasyio/io/float_io.py new file mode 100644 index 0000000..be07f0d --- /dev/null +++ b/pyrediseasyio/io/float_io.py @@ -0,0 +1,13 @@ +from pyrediseasyio import SingleIO +from pyrediseasyio.abstract_reader_writer import AbstractReaderWriter + + +class FloatIO(SingleIO): + def __init__(self, name: str, addr: str, default: float = 0, units: str = None, reader: AbstractReaderWriter = None): + super().__init__(name, addr, default, units, reader) + + @staticmethod + def _convert_type(value): + return float(value) + + diff --git a/pyrediseasyio/io/integer_io.py b/pyrediseasyio/io/integer_io.py new file mode 100644 index 0000000..65aa7ba --- /dev/null +++ b/pyrediseasyio/io/integer_io.py @@ -0,0 +1,14 @@ +from pyrediseasyio import SingleIO +from pyrediseasyio.abstract_reader_writer import AbstractReaderWriter + + +class IntIO(SingleIO): + def __init__(self, name: str, addr: str, default: int = 0, units: str = None, reader: AbstractReaderWriter = None): + super().__init__(name, addr, default, units, reader) + + @staticmethod + def _convert_type(value): + return int(value) + + + diff --git a/pyrediseasyio/io_group.py b/pyrediseasyio/io/io_group.py similarity index 64% rename from pyrediseasyio/io_group.py rename to pyrediseasyio/io/io_group.py index d54ff83..898c441 100644 --- a/pyrediseasyio/io_group.py +++ b/pyrediseasyio/io/io_group.py @@ -1,5 +1,7 @@ from pyrediseasyio.reader_writer import ReaderWriter -from pyrediseasyio.single_io import SingleIO +from pyrediseasyio.io.base import SingleIO +from dominate.tags import div, span, table, tr +from typing import List, Callable import json @@ -26,6 +28,9 @@ def __init__(self, host='localhost', port=6379, db=0, except AttributeError: pass + def __len__(self): + return len(self.members) + def __setattr__(self, key, value): if hasattr(self, 'members') and key in self.members: attr = getattr(self, key) @@ -44,16 +49,31 @@ def dump(self, key: str) -> str: self.write(key, s) return s - def dumps(self) -> str: + def dumps(self, by_names: List = None, by_type: List = None, by_lambda: Callable = None) -> str: """ Returns a json string containing a list of dict(name/addr/value) of all members. """ - members = [] - for m in self.members: - attr = getattr(self, m) - members.append(dict(name=attr.name, addr=attr.addr, value=attr.value)) + attrs = self.get_attributes(by_names, by_type, by_lambda) + members = [dict(name=attr.name, addr=attr.addr, value=attr.value) for attr in attrs] return json.dumps(members) + def get_attributes(self, by_names: List = None, by_type: List = None, + by_lambda_each: Callable = None, by_lambda_results: Callable = None): + + names = self.members if by_names is None else by_names + attrs = [getattr(self, name) for name in names] + if by_type: + attrs = [a for a in attrs if type(a) in by_type] + if by_lambda_each: + attrs = [a for a in attrs if by_lambda_each(a)] + if by_lambda_results: + attrs = by_lambda_results(attrs) + + return attrs + + + + diff --git a/pyrediseasyio/io/string_io.py b/pyrediseasyio/io/string_io.py new file mode 100644 index 0000000..f006879 --- /dev/null +++ b/pyrediseasyio/io/string_io.py @@ -0,0 +1,11 @@ +from pyrediseasyio.abstract_reader_writer import AbstractReaderWriter +from pyrediseasyio import SingleIO + + +class StringIO(SingleIO): + def __init__(self, name: str, addr: str, default: str = '', units: str = None, reader: AbstractReaderWriter = None): + super().__init__(name, addr, default, units, reader) + + @staticmethod + def _convert_type(value): + return str(value) \ No newline at end of file diff --git a/tests/test_get_attributes.py b/tests/test_get_attributes.py new file mode 100644 index 0000000..da14df3 --- /dev/null +++ b/tests/test_get_attributes.py @@ -0,0 +1,41 @@ +import unittest +from pyrediseasyio.io.io_group import IOGroup +from pyrediseasyio import BooleanIO, IntIO, FloatIO +from assertpy import assert_that + + +class TestGroup(IOGroup): + length = 5 + Bool1 = BooleanIO("Boolean 1", "Bool1", False) + Bool2 = BooleanIO("Boolean 2", "Bool2", True) + Int1 = IntIO("Integer 1", "Int1") + Int2 = IntIO("Integer 2", "Int2", default=34) + Float1 = FloatIO("Float 1", "Float1", default=1.2) + + +test_group = TestGroup() + + +class TestAttributes(unittest.TestCase): + + def test_get_all_attributes(self): + attrs = test_group.get_attributes() + assert_that(len(attrs)).is_equal_to(TestGroup.length) + + def test_limit_attributes_by_names(self): + attrs = test_group.get_attributes(by_names=['Bool1','Float1']) + assert_that(len(attrs)).is_equal_to(2) + + def test_limit_attributes_by_type(self): + attrs = test_group.get_attributes(by_type=[IntIO, FloatIO]) + assert_that(len(attrs)).is_equal_to(3) + + def test_limit_by_io_filter(self): + attrs = test_group.get_attributes(by_lambda_each=lambda x: x.value == 1.2) + assert_that(len(attrs)).is_equal_to(1) + + def test_limit_by_io_result(self): + attrs = test_group.get_attributes(by_lambda_results=lambda x: x[-1:]) + assert_that(len(attrs)).is_equal_to(1) + assert_that(attrs[0].value).is_equal_to(34) + diff --git a/tests/test_html_io_conversion.py b/tests/test_html_io_conversion.py new file mode 100644 index 0000000..3fbb087 --- /dev/null +++ b/tests/test_html_io_conversion.py @@ -0,0 +1,58 @@ +import unittest +from pyrediseasyio.io.io_group import IOGroup +from pyrediseasyio.html.html_io import HTMLIO +from pyrediseasyio import BooleanIO, SingleIO, FloatIO, StringIO +from assertpy import assert_that +from htmldiffer import utils as diff_utils + + +class TestGroup(IOGroup): + Bool1 = BooleanIO("Boolean 1", "Bool1", units="On/Off") + Float1 = FloatIO("Float 1", "Float1", default=1.23, units="furlongs") + + +test_group = TestGroup() + + +def html_equals(html1, html2): + lines1 = [l for l in diff_utils.html2list(html1) if l != '\n ' and l != ' ' and l != '\n'] + lines2 = [l for l in diff_utils.html2list(html2) if l != '\n ' and l != ' ' and l != '\n'] + return lines1 == lines2 + + +class TestHTML(unittest.TestCase): + + def test_boolean_div_conversion(self): + h = HTMLIO(test_group.Bool1, html_id_header="my_id", namespace="my_ns").html().render() + expected = ''' +
+
Boolean 1
+
False
+
On/Off
+
+ ''' + assert_that(html_equals(h, expected)).is_true() + + def test_bool_row_conversion(self): + h = HTMLIO(test_group.Bool1, html_id_header="my_id", namespace="my_ns").html_row().render() + expected = ''' + + Boolean 1 + False + On/Off + + ''' + assert_that(html_equals(h, expected)).is_true() + + def test_float_div_conversion(self): + h = HTMLIO(test_group.Float1, html_id_header="my_id", namespace="my_ns").html().render() + print(h) + expected = ''' +
+
Float 1
+
1.23
+
furlongs
+
+ ''' + assert_that(html_equals(h, expected)).is_true() + diff --git a/tests/test_html_io_group_conversion.py b/tests/test_html_io_group_conversion.py new file mode 100644 index 0000000..f0f85a8 --- /dev/null +++ b/tests/test_html_io_group_conversion.py @@ -0,0 +1,73 @@ +import unittest +from pyrediseasyio.io.io_group import IOGroup +from pyrediseasyio.html.html_io_group import HMTLIOGroup +from pyrediseasyio import BooleanIO, SingleIO, FloatIO, StringIO +from assertpy import assert_that +from htmldiffer import utils as diff_utils +import json + + +class TestGroup(IOGroup): + Bool1 = BooleanIO("Boolean 1", "Bool1", units="On/Off") + Float1 = FloatIO("Float 1", "Float1", default=1.23, units="furlongs") + + +test_group = TestGroup() + + +def html_equals(html1, html2): + lines1 = [l for l in diff_utils.html2list(html1) if l != '\n ' and l != ' ' and l != '\n'] + lines2 = [l for l in diff_utils.html2list(html2) if l != '\n ' and l != ' ' and l != '\n'] + return lines1 == lines2 + + +class TestHTML(unittest.TestCase): + + def test_boolean_div_conversion(self): + h = HMTLIOGroup(test_group, "my_id", "my_namespace").html().render() + expected = ''' +
+
+
Boolean 1
+
False
+
On/Off
+
+
+
Float 1
+
1.23
+
furlongs
+
+
+ ''' + assert_that(html_equals(h, expected)).is_true() + + def test_boolean_table_conversion(self): + h = HMTLIOGroup(test_group, "my_id", "my_namespace").html_table().render() + expected = ''' + + + + + + + + + + + +
Boolean 1FalseOn/Off
Float 11.23furlongs
+ ''' + assert_that(html_equals(h, expected)).is_true() + + def test_json_dumps_basic(self): + h = HMTLIOGroup(test_group, "my_id", "my_namespace") + d = h.dumps() + as_list = json.loads(d) + assert_that(len(as_list)).is_equal_to(len(test_group)) + assert_that(as_list[0]['name']).is_equal_to('Boolean 1') + + def test_json_dumps_by_name(self): + h = HMTLIOGroup(test_group, "my_id", "my_namespace") + d = h.dumps(by_names=['Bool1']) + as_list = json.loads(d) + assert_that(len(as_list)).is_equal_to(1) diff --git a/tests/test_pub_sub.py b/tests/test_pub_sub.py index 1d82c23..94f29f4 100644 --- a/tests/test_pub_sub.py +++ b/tests/test_pub_sub.py @@ -2,13 +2,10 @@ import time import threading -from pyrediseasyio.io_group import IOGroup from pyrediseasyio.reader_writer import ReaderWriter from assertpy import assert_that - - class PubSubTests(unittest.TestCase): def _publish_many(self, count: int, wait: float = 0): diff --git a/tests/test_read_write.py b/tests/test_read_write.py index 673e739..4071ad5 100644 --- a/tests/test_read_write.py +++ b/tests/test_read_write.py @@ -1,6 +1,6 @@ import unittest -from pyrediseasyio.io_group import IOGroup -from pyrediseasyio.single_io import BooleanIO, IntIO, FloatIO, StringIO +from pyrediseasyio.io.io_group import IOGroup +from pyrediseasyio import BooleanIO, IntIO, FloatIO, StringIO from assertpy import assert_that