unisi 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
unisi/__init__.py CHANGED
@@ -6,4 +6,5 @@ from .tables import *
6
6
  from .containers import *
7
7
  from .proxy import *
8
8
  from .dbunits import *
9
+ from .graphs import *
9
10
  from .kdb import Database, Dbtable
unisi/autotest.py CHANGED
@@ -96,6 +96,8 @@ test_name = Edit('Name test file', '', focus = True)
96
96
  rewrite = Switch('Overwrite existing', False, type = 'check')
97
97
 
98
98
  def button_clicked(_,__):
99
+ if not os.path.exists(testdir):
100
+ os.makedirs(testdir)
99
101
  test_name.value = User.last_user.screen.name
100
102
  test_name.complete = smart_complete(os.listdir(testdir))
101
103
  return Dialog('Create autotest..', ask_create_test, test_name, rewrite)
@@ -176,11 +178,7 @@ def check_module(module):
176
178
  errors.insert(0, f"\nErrors in screen {screen.name}, file name {module.__file__}:")
177
179
  return errors
178
180
 
179
- def run_tests():
180
- if not os.path.exists(testdir):
181
- os.makedirs(testdir)
182
- user = User.type(testdir)
183
- user.load()
181
+ def run_tests(user):
184
182
  errors = []
185
183
  for module in user.screens:
186
184
  errors += check_module(module)
@@ -200,8 +198,7 @@ def run_tests():
200
198
  if not test(file,user):
201
199
  ok = False
202
200
  if process and ok:
203
- print('\n-----Autotests successfully passed.-----\n')
204
- User.last_user = None
201
+ print('\n-----Autotests successfully passed.-----\n')
205
202
  User.toolbar.append(button)
206
203
 
207
204
 
unisi/containers.py CHANGED
@@ -1,186 +1,6 @@
1
1
  from .units import *
2
- from .tables import Table
3
2
  from .common import pretty4, flatten
4
3
  from numbers import Number
5
- from collections import defaultdict
6
- from collections.abc import Iterable
7
-
8
- class Node:
9
- def __init__(self, name , color = '', size = 0, image = ''):
10
- if name:
11
- self.name = name
12
- if image:
13
- self.type = 'image'
14
- self.image = image
15
- else:
16
- self.type = ''
17
- if color:
18
- self.color = color
19
- if size:
20
- self.size = size
21
-
22
- class Edge:
23
- def __init__(self, source, target, name = '', color = '', size = 0, property = None):
24
- self.source = source
25
- self.target = target
26
- if name:
27
- self.name = name
28
- if color:
29
- self.color = color
30
- if size:
31
- self.size = size
32
- if property is not None:
33
- self.property = property
34
- def __str__(self):
35
- return f"Edge({self.source}->{self.target})"
36
- def __repr__(self):
37
- return f"Edge({self.source}->{self.target})"
38
-
39
- graph_default_value = {'nodes' : [], 'edges' : []}
40
-
41
- class Graph(Unit):
42
- '''has to contain nodes, edges, see Readme'''
43
- def __init__(self, name, *args, **kwargs):
44
- super().__init__(name, *args, **kwargs)
45
- set_defaults(self, dict(type ='graph', value = graph_default_value, nodes = [], edges = []))
46
-
47
- Topology = lambda: defaultdict(lambda: defaultdict(lambda: {}))
48
-
49
- def unit2image(unit):
50
- match unit:
51
- case Block():
52
- return 'https://img.icons8.com/fluency/48/object.png'
53
- case Button():
54
- return 'https://img.icons8.com/ios-filled/50/doorbell.png'
55
- case Edit() | Text():
56
- return 'https://img.icons8.com/fluency-systems-filled/50/123.png' if unit.type == 'number'\
57
- else 'https://img.icons8.com/sf-regular/48/abc.png'
58
- case Switch():
59
- return 'https://img.icons8.com/ios/50/toggle-on--v1.png'
60
- case TextArea():
61
- return 'https://img.icons8.com/color/48/align-cell-content-left.png'
62
- case Table():
63
- return 'https://img.icons8.com/color/48/day-view.png' if unit.type == 'table'\
64
- else 'https://img.icons8.com/ultraviolet/40/combo-chart.png'
65
- case Tree():
66
- return 'https://img.icons8.com/external-flatart-icons-outline-flatarticons/64/external-tree-nature-flatart-icons-outline-flatarticons-3.png'
67
- case Select():
68
- return 'https://img.icons8.com/cotton/64/list--v2.png'
69
- case Graph():
70
- return 'https://img.icons8.com/external-vitaliy-gorbachev-blue-vitaly-gorbachev/50/external-nodes-cryptocurrency-vitaliy-gorbachev-blue-vitaly-gorbachev.png'
71
- case Range():
72
- return 'https://img.icons8.com/ios/50/slider-control.png'
73
- case Unit():
74
- return 'https://img.icons8.com/ios-filled/50/link--v1.png'
75
- case _:
76
- return ''
77
-
78
- class Net(Graph):
79
- """Graph of Units"""
80
- def __init__(self, name, topology = Topology(), *args, **kwargs):
81
- super().__init__(name, *args, **kwargs)
82
- self.type = 'graph'
83
- self.topology = topology
84
- changed_handler = getattr(self, 'changed', None)
85
-
86
- def changed_converter(_, value):
87
- mark_changed = self._mark_changed
88
- self._mark_changed = None #turn off for 'value' diff reaction
89
- self._value = value
90
- narray = self._narray
91
- value = dict(nodes = [self._narray[i] for i in value['nodes']], edges =
92
- [Edge(narray[self._edges[i].source], narray[self._edges[i].target]) for i in value['edges']])
93
- if changed_handler:
94
- result = changed_handler(_, value)
95
- else:
96
- result = None
97
- self.value = value
98
- self._mark_changed = mark_changed #turn on
99
- return result
100
- self.changed = changed_converter
101
-
102
- def specific_changed_register(self, property = None, value = None):
103
- """ mark serial info as invalid """
104
- if property:
105
- if property.startswith('_'):
106
- return False
107
- else:
108
- self.delattr('_nodes')
109
- self.delattr('_value')
110
- return True
111
-
112
- def elements(self, stubs=True):
113
- if not hasattr(self, '_nodes'):
114
- self.__getstate__()
115
- return self.narray
116
-
117
- def __getstate__(self):
118
- if not hasattr(self, '_nodes'):
119
- nodes = []
120
- narray = []
121
- earray = []
122
- for sunit, links in self.topology.items():
123
- sindex = index_of(narray,sunit)
124
- if sindex == -1:
125
- sindex = len(narray)
126
- narray.append(sunit)
127
- nodes.append(Node(sunit.name, image = unit2image(sunit),
128
- color = 'white', size = 15))
129
- for dunit in links:
130
- dindex = index_of(narray,dunit)
131
- if dindex == -1:
132
- dindex = len(narray)
133
- narray.append(dunit)
134
- nodes.append(Node(dunit.name, image = unit2image(dunit),
135
- color = 'white', size = 15))
136
- earray.append(Edge(sindex, dindex))
137
- self._nodes = nodes
138
- self._edges = earray
139
- self._narray = narray
140
-
141
- if not hasattr(self, '_value'):
142
- self._value = dict(nodes = [index_of(self._narray,unit) for unit in self.value['nodes']],
143
- edges = [Edge(index_of(self._narray, e.source), index_of(self._narray, e.target)) for e in self.value['edges']])
144
- return dict(name = self.name, type = self.type, nodes = self._nodes, edges = self._edges, value = self._value)
145
-
146
- def make_topology(self, unit: Unit | Iterable):
147
- topo = Topology()
148
- def dive(unit):
149
- match unit:
150
- case Iterable():
151
- node = Unit('Union', type = 'union')
152
- for obj in unit:
153
- if obj:
154
- topo[node][dive(obj)] = {}
155
- return node
156
- case Block():
157
- for obj in unit.value:
158
- if obj:
159
- topo[unit][dive(obj)] = {}
160
- case _: ...
161
- return unit
162
- dive(unit)
163
- self.topology = topo
164
- self.specific_changed_register()
165
-
166
- class ContentScaler(Range):
167
- def __init__(self, *args, **kwargs):
168
- name = args[0] if args else 'Scale content'
169
- super().__init__(name, *args, **kwargs)
170
- if 'options' not in kwargs:
171
- self.options = [0.25, 3.0, 0.25]
172
- self.changed = self.scaler
173
-
174
- def scaler(self, _, val):
175
- prev = self.value
176
- elements = self.elements()
177
- self.value = val
178
- if elements:
179
- prev /= val
180
- for element in elements:
181
- element.width /= prev
182
- element.height /= prev
183
- return elements
184
4
 
185
5
  class Block(Unit):
186
6
  def __init__(self, name, *elems, **options):
unisi/graphs.py ADDED
@@ -0,0 +1,169 @@
1
+ from .units import *
2
+ from .tables import Table
3
+ from .containers import Block
4
+ from collections import defaultdict
5
+ from collections.abc import Iterable
6
+
7
+ class Node:
8
+ def __init__(self, name , color = '', size = 0, image = ''):
9
+ if name:
10
+ self.name = name
11
+ if image:
12
+ self.type = 'image'
13
+ self.image = image
14
+ else:
15
+ self.type = ''
16
+ if color:
17
+ self.color = color
18
+ if size:
19
+ self.size = size
20
+
21
+ class Edge:
22
+ def __init__(self, source, target, name = '', color = '', size = 0, property = None):
23
+ self.source = source
24
+ self.target = target
25
+ if name:
26
+ self.name = name
27
+ if color:
28
+ self.color = color
29
+ if size:
30
+ self.size = size
31
+ if property is not None:
32
+ self.property = property
33
+ def __str__(self):
34
+ return f"Edge({self.source}->{self.target})"
35
+ def __repr__(self):
36
+ return f"Edge({self.source}->{self.target})"
37
+
38
+ graph_default_value = {'nodes' : [], 'edges' : []}
39
+
40
+ class Graph(Unit):
41
+ '''has to contain nodes, edges, see Readme'''
42
+ def __init__(self, name, *args, **kwargs):
43
+ super().__init__(name, *args, **kwargs)
44
+ set_defaults(self, dict(type ='graph', value = graph_default_value, nodes = [], edges = []))
45
+
46
+ Topology = lambda: defaultdict(lambda: defaultdict(lambda: None))
47
+
48
+ def unit2image(unit):
49
+ match unit:
50
+ case Block():
51
+ return 'https://img.icons8.com/fluency/48/object.png'
52
+ case Button():
53
+ return 'https://img.icons8.com/ios-filled/50/doorbell.png'
54
+ case Edit() | Text():
55
+ return 'https://img.icons8.com/fluency-systems-filled/50/123.png' if unit.type == 'number'\
56
+ else 'https://img.icons8.com/sf-regular/48/abc.png'
57
+ case Switch():
58
+ return 'https://img.icons8.com/ios/50/toggle-on--v1.png'
59
+ case TextArea():
60
+ return 'https://img.icons8.com/color/48/align-cell-content-left.png'
61
+ case Table():
62
+ return 'https://img.icons8.com/color/48/day-view.png' if unit.type == 'table'\
63
+ else 'https://img.icons8.com/ultraviolet/40/combo-chart.png'
64
+ case Tree():
65
+ return 'https://img.icons8.com/external-flatart-icons-outline-flatarticons/64/external-tree-nature-flatart-icons-outline-flatarticons-3.png'
66
+ case Select():
67
+ return 'https://img.icons8.com/cotton/64/list--v2.png'
68
+ case Graph():
69
+ return 'https://img.icons8.com/external-vitaliy-gorbachev-blue-vitaly-gorbachev/50/external-nodes-cryptocurrency-vitaliy-gorbachev-blue-vitaly-gorbachev.png'
70
+ case Range():
71
+ return 'https://img.icons8.com/ios/50/slider-control.png'
72
+ case Unit():
73
+ return 'https://img.icons8.com/ios-filled/50/link--v1.png'
74
+ case _:
75
+ return ''
76
+
77
+ class Net(Graph):
78
+ """Graph of Units"""
79
+ replace4state = dict(nodes = '_nodes', edges = '_edges', value = '_value')
80
+ def __init__(self, name, topology = Topology(), **kwargs):
81
+ Unit.__init__(self, name, **kwargs)
82
+ if not hasattr(self, 'value'):
83
+ self.value = graph_default_value
84
+ self.type = 'graph'
85
+ self.topology = topology
86
+ self._inside_converter = False
87
+ changed_handler = getattr(self, 'changed', None)
88
+
89
+ def changed_converter(_, value):
90
+ self._inside_converter = True
91
+ self._value = value
92
+ narray = self._narray
93
+ value = dict(nodes = [self._narray[i] for i in value['nodes']], edges =
94
+ [Edge(narray[self._edges[i].source], narray[self._edges[i].target]) for i in value['edges']])
95
+ if changed_handler:
96
+ result = changed_handler(_, value)
97
+ else:
98
+ result = None
99
+ self.value = value
100
+ self._inside_converter = False
101
+ return result
102
+ self.changed = changed_converter
103
+
104
+ def specific_changed_register(self, property = None, value = None):
105
+ """ mark serial info as invalid """
106
+ if property:
107
+ if property.startswith('_'):
108
+ return False
109
+ elif property == 'value' and self._inside_converter:
110
+ return False
111
+ else:
112
+ self.delattr('_nodes')
113
+ self.delattr('_value')
114
+ return True
115
+
116
+ def elements(self, stubs=True):
117
+ if not hasattr(self, '_nodes'):
118
+ self.__getstate__()
119
+ return self.narray
120
+
121
+ def __getstate__(self):
122
+ if not hasattr(self, '_nodes'):
123
+ nodes = []
124
+ narray = []
125
+ earray = []
126
+ for sunit, links in self.topology.items():
127
+ sindex = index_of(narray,sunit)
128
+ if sindex == -1:
129
+ sindex = len(narray)
130
+ narray.append(sunit)
131
+ nodes.append(Node(sunit.name, image = unit2image(sunit),
132
+ color = 'white', size = 15))
133
+ for dunit in links:
134
+ dindex = index_of(narray,dunit)
135
+ if dindex == -1:
136
+ dindex = len(narray)
137
+ narray.append(dunit)
138
+ nodes.append(Node(dunit.name, image = unit2image(dunit),
139
+ color = 'white', size = 15))
140
+ earray.append(Edge(sindex, dindex))
141
+ self._nodes = nodes
142
+ self._edges = earray
143
+ self._narray = narray
144
+
145
+ if not hasattr(self, '_value'):
146
+ self._value = dict(nodes = [index_of(self._narray,unit) for unit in self.value['nodes']],
147
+ edges = [Edge(index_of(self._narray, e.source), index_of(self._narray, e.target)) for e in self.value['edges']])
148
+ return {name: getattr(self,Net.replace4state.get(name, name)) for name in ['nodes', 'edges', *self.__dict__.keys()]
149
+ if name != 'topology' and name[0] != '_'}
150
+
151
+ def make_topology(self, unit: Unit | Iterable):
152
+ topo = Topology()
153
+ def dive(unit):
154
+ match unit:
155
+ case Iterable():
156
+ node = Unit('Union', type = 'union')
157
+ for obj in unit:
158
+ if obj:
159
+ topo[node][dive(obj)] = {}
160
+ return node
161
+ case Block():
162
+ for obj in unit.value:
163
+ if obj:
164
+ topo[unit][dive(obj)] = {}
165
+ case _: ...
166
+ return unit
167
+ dive(unit)
168
+ self.topology = topo
169
+ self.specific_changed_register()
unisi/server.py CHANGED
@@ -105,10 +105,8 @@ def start(appname = None, user_type = User, http_handlers = []):
105
105
  if appname:
106
106
  config.appname = appname
107
107
 
108
- User.type = user_type
109
-
110
- if config.autotest:
111
- run_tests()
108
+ User.type = user_type
109
+ run_tests(User.init_user())
112
110
 
113
111
  http_handlers.insert(0, web.get('/ws', websocket_handler))
114
112
  http_handlers += [web.static(f'/{config.upload_dir}', upload_dir),
unisi/units.py CHANGED
@@ -125,6 +125,9 @@ class Unit:
125
125
  def changed_handler(obj, value):
126
126
  obj.value = value
127
127
  self.changed = compose_handlers(changed_handler, handler)
128
+
129
+ def __getstate__(self):
130
+ return {n: v for n, v in self.__dict__.items() if n[0] != '_'}
128
131
 
129
132
  def __str__(self):
130
133
  return f'{type(self).__name__}({self.name})'
@@ -177,6 +180,25 @@ class Range(Unit):
177
180
  if 'options' not in kwargs:
178
181
  self.options = [self.value - 10, self.value + 10, 1]
179
182
 
183
+ class ContentScaler(Range):
184
+ def __init__(self, *args, **kwargs):
185
+ name = args[0] if args else 'Scale content'
186
+ super().__init__(name, *args, **kwargs)
187
+ if 'options' not in kwargs:
188
+ self.options = [0.25, 3.0, 0.25]
189
+ self.changed = self.scaler
190
+
191
+ def scaler(self, _, val):
192
+ prev = self.value
193
+ elements = self.elements()
194
+ self.value = val
195
+ if elements:
196
+ prev /= val
197
+ for element in elements:
198
+ element.width /= prev
199
+ element.height /= prev
200
+ return elements
201
+
180
202
  class Button(Unit):
181
203
  def __init__(self, name, handler = None, **kwargs):
182
204
  self._mark_changed = None
unisi/users.py CHANGED
@@ -285,7 +285,7 @@ class User:
285
285
  self.log(error)
286
286
  return Error(error)
287
287
 
288
- def monitor(self, session, share):
288
+ def monitor(self, session, share = None):
289
289
  if config.share and session != testdir:
290
290
  self.log(f'User is connected, session: {session}, share: {share.session if share else None}', type = 'info')
291
291
 
@@ -306,6 +306,14 @@ class User:
306
306
  logging.info(str)
307
307
  func(level = logging.WARNING)
308
308
 
309
+ def init_user():
310
+ """make initial user for autotest and evaluating dbsharing"""
311
+ user = User.type(testdir)
312
+ user.load()
313
+ #register shared db map once
314
+ user.calc_dbsharing()
315
+ return user
316
+
309
317
  def calc_dbsharing(self):
310
318
  """calc connections db and units"""
311
319
  dbshare.clear()
@@ -362,15 +370,18 @@ def make_user(request):
362
370
  return None, Error(error)
363
371
  user = User.type(session, user)
364
372
  ok = user.screens
365
- elif config.mirror and User.last_user:
373
+ elif config.mirror and User.count:
366
374
  user = User.type(session, User.last_user)
367
375
  ok = user.screens
376
+ elif not User.count:
377
+ user = User.last_user
378
+ user.session = session
379
+ user.monitor(session)
380
+ ok = True
368
381
  else:
369
382
  user = User.type(session)
370
383
  ok = user.load()
371
- #register shared db map once
372
- if not user.count:
373
- user.calc_dbsharing()
384
+
374
385
  User.count += 1
375
386
  Unishare.sessions[session] = user
376
387
  return user, ok
@@ -0,0 +1 @@
1
+ thead tr:first-child th{-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);position:sticky;top:0;z-index:1000}:root{--scrollbar-width-height:10px;--scrollbar-thumb-hover:#2176d2;--scrollbar-thumb-dark:#2176d2;--scrollbar-thumb-hover-dark:#2176d2}::-webkit-scrollbar{height:var(--scrollbar-width-height);width:var(--scrollbar-width-height)}::-webkit-scrollbar-track{box-shadow:inset 0 0 4px var(--scrollbar-track-dark)}::-webkit-scrollbar-corner{background:var(--scrollbar-track-dark)}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb-dark);border-radius:5px}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover-dark)}#header[data-v-419ab6c8]{font-size:16px;font-weight:700;left:10px;pointer-events:none;position:absolute;top:10px;z-index:2}#graph[data-v-419ab6c8]{border:1px solid #ccc;height:600px;position:relative;width:100%}body[data-v-0f5a7837]{display:flex;justify-content:center}.custom-caption[data-v-0f5a7837]{padding:5px!important}.web-camera-container[data-v-0f5a7837]{align-items:center;border:1px solid #ccc;border-radius:4px;display:flex;flex-direction:column;justify-content:center;margin-bottom:2rem;margin-top:2rem;padding:2rem;width:500px}.web-camera-container .camera-button[data-v-0f5a7837]{margin-bottom:2rem}.web-camera-container .camera-box .camera-shutter[data-v-0f5a7837]{background-color:#fff;height:337.5px;opacity:0;position:absolute;width:450px}.web-camera-container .camera-box .camera-shutter.flash[data-v-0f5a7837]{opacity:1}.web-camera-container .camera-shoot[data-v-0f5a7837]{margin:1rem 0}.web-camera-container .camera-shoot button[data-v-0f5a7837]{align-items:center;border-radius:100%;display:flex;height:60px;justify-content:center;width:60px}.web-camera-container .camera-shoot button img[data-v-0f5a7837]{height:35px;object-fit:cover}.web-camera-container .camera-loading[data-v-0f5a7837]{height:100%;margin:3rem 0 0 -1.2rem;min-height:150px;overflow:hidden;position:absolute;width:100%}.web-camera-container .camera-loading ul[data-v-0f5a7837]{height:100%;margin:0;position:absolute;width:100%;z-index:999999}.web-camera-container .camera-loading .loader-circle[data-v-0f5a7837]{display:block;height:14px;left:100%;margin:0 auto;padding:0;position:absolute;top:50%;transform:translateY(-50%);transform:translateX(-50%);width:100%}.web-camera-container .camera-loading .loader-circle li[data-v-0f5a7837]{animation:preload-0f5a7837 1s infinite;background:#999;border-radius:100%;display:block;float:left;height:10px;line-height:10px;margin:0 0 0 4px;padding:0;position:relative;top:-50%;width:10px}.web-camera-container .camera-loading .loader-circle li[data-v-0f5a7837]:nth-child(2){animation-delay:.2s}.web-camera-container .camera-loading .loader-circle li[data-v-0f5a7837]:nth-child(3){animation-delay:.4s}@keyframes preload-0f5a7837{0%{opacity:1}50%{opacity:.4}to{opacity:1}}.container[data-v-0f5a7837]{position:relative}.frame[data-v-0f5a7837]{border:2px solid #573497;border-radius:8px;height:100%;left:0;pointer-events:none;position:absolute;top:0;width:100%;z-index:10}.container{position:relative}.frame{border:2px solid #573497;border-radius:8px;height:100%;left:0;pointer-events:none;position:absolute;top:0;width:100%;z-index:10}.q-tab__label{font-size:16px;font-weight:700}