unisi 0.2.6__py3-none-any.whl → 0.2.8__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 +1 -0
- unisi/autotest.py +4 -7
- unisi/common.py +21 -0
- unisi/containers.py +12 -185
- unisi/graphs.py +168 -0
- unisi/server.py +2 -4
- unisi/tables.py +1 -1
- unisi/units.py +24 -2
- unisi/users.py +47 -18
- unisi/utils.py +0 -3
- unisi/voicecom.py +170 -0
- unisi/web/css/748.d3c09c5d.css +1 -0
- unisi/web/index.html +1 -1
- unisi/web/js/748.213707d6.js +1 -0
- unisi/web/js/app.8df2e0ff.js +1 -0
- {unisi-0.2.6.dist-info → unisi-0.2.8.dist-info}/METADATA +2 -1
- {unisi-0.2.6.dist-info → unisi-0.2.8.dist-info}/RECORD +20 -18
- unisi/web/css/293.aa8703e6.css +0 -1
- unisi/web/js/293.de6a22d6.js +0 -1
- unisi/web/js/app.144daad7.js +0 -1
- {unisi-0.2.6.dist-info → unisi-0.2.8.dist-info}/WHEEL +0 -0
- {unisi-0.2.6.dist-info → unisi-0.2.8.dist-info}/entry_points.txt +0 -0
- {unisi-0.2.6.dist-info → unisi-0.2.8.dist-info}/licenses/LICENSE +0 -0
unisi/__init__.py
CHANGED
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/common.py
CHANGED
@@ -48,6 +48,12 @@ class ReceivedMessage(ArgObject):
|
|
48
48
|
self.__dict__.update(kwargs)
|
49
49
|
def __str__(self):
|
50
50
|
return f'{self.block}/{self.element}->{self.event}({self.value})'
|
51
|
+
@property
|
52
|
+
def screen_type(self):
|
53
|
+
return self.block == 'root' and self.element is None
|
54
|
+
@property
|
55
|
+
def voice_type(self):
|
56
|
+
return self.block == 'voice' and self.element is None
|
51
57
|
|
52
58
|
def toJson(obj):
|
53
59
|
return jsonpickle.encode(obj,unpicklable = False)
|
@@ -147,6 +153,21 @@ def Answer(type, message, result):
|
|
147
153
|
ms.message = message
|
148
154
|
return ms
|
149
155
|
|
156
|
+
def delete_unit(units, name):
|
157
|
+
"""Deletes a unit with the given name from a nested list of units.
|
158
|
+
Returns True if the unit was found and deleted, False otherwise.
|
159
|
+
"""
|
160
|
+
for i in range(len(units)):
|
161
|
+
if isinstance(units[i], list | tuple):
|
162
|
+
if delete_unit(units[i], name):
|
163
|
+
if not units[i]: # if the sublist became empty after deletion
|
164
|
+
units.pop(i) # remove sublist also
|
165
|
+
return True
|
166
|
+
elif units[i].name == name:
|
167
|
+
units.pop(i)
|
168
|
+
return True
|
169
|
+
return False
|
170
|
+
|
150
171
|
|
151
172
|
|
152
173
|
|
unisi/containers.py
CHANGED
@@ -1,186 +1,6 @@
|
|
1
1
|
from .units import *
|
2
|
-
from .
|
3
|
-
from .common import pretty4, flatten
|
2
|
+
from .common import pretty4, flatten, delete_unit
|
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):
|
@@ -213,7 +33,7 @@ class Block(Unit):
|
|
213
33
|
elif isinstance(elem.llm, dict):
|
214
34
|
if elem.type != 'table':
|
215
35
|
raise AttributeError(f'{elem.name} llm parameter is a dictionary only for tables, not for {elem.type}!')
|
216
|
-
elem.
|
36
|
+
elem._llm_dependencies = {fld: (deps if isinstance(deps, list | bool) else [deps]) for fld, deps in elem.llm.items()}
|
217
37
|
elem.llm = True
|
218
38
|
continue
|
219
39
|
else:
|
@@ -222,12 +42,19 @@ class Block(Unit):
|
|
222
42
|
elem.llm = exactly
|
223
43
|
for dependency in dependencies:
|
224
44
|
dependency.add_changed_handler(elem.emit)
|
225
|
-
elem.
|
45
|
+
elem._llm_dependencies = dependencies
|
226
46
|
else:
|
227
47
|
elem.llm = None
|
228
48
|
print(f'Empty dependency list for llm calculation for {elem.name} {elem.type}!')
|
229
|
-
|
230
|
-
|
49
|
+
|
50
|
+
user = Unishare.context_user()
|
51
|
+
if hasattr(self,'closable'):
|
52
|
+
def close(*_):
|
53
|
+
delete_unit(user.screen.blocks, self.name)
|
54
|
+
return Redesign
|
55
|
+
self.close = close
|
56
|
+
|
57
|
+
self.set_reactivity(user)
|
231
58
|
|
232
59
|
def set_reactivity(self, user, override = False):
|
233
60
|
if user:
|
unisi/graphs.py
ADDED
@@ -0,0 +1,168 @@
|
|
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, value = graph_default_value, topology = Topology(), **kwargs):
|
81
|
+
Unit.__init__(self, name, **kwargs)
|
82
|
+
self.type = 'graph'
|
83
|
+
self.value = value
|
84
|
+
self.topology = topology
|
85
|
+
self._inside_converter = False
|
86
|
+
changed_handler = getattr(self, 'changed', None)
|
87
|
+
|
88
|
+
def changed_converter(_, value):
|
89
|
+
self._inside_converter = True
|
90
|
+
self._value = value
|
91
|
+
narray = self._narray
|
92
|
+
value = dict(nodes = [self._narray[i] for i in value['nodes']], edges =
|
93
|
+
[Edge(narray[self._edges[i].source], narray[self._edges[i].target]) for i in value['edges']])
|
94
|
+
if changed_handler:
|
95
|
+
result = changed_handler(_, value)
|
96
|
+
else:
|
97
|
+
result = None
|
98
|
+
self.value = value
|
99
|
+
self._inside_converter = False
|
100
|
+
return result
|
101
|
+
self.changed = changed_converter
|
102
|
+
|
103
|
+
def specific_changed_register(self, property = None, value = None):
|
104
|
+
""" mark serial info as invalid """
|
105
|
+
if property:
|
106
|
+
if property.startswith('_'):
|
107
|
+
return False
|
108
|
+
elif property == 'value' and self._inside_converter:
|
109
|
+
return False
|
110
|
+
else:
|
111
|
+
self.delattr('_nodes')
|
112
|
+
self.delattr('_value')
|
113
|
+
return True
|
114
|
+
|
115
|
+
def elements(self, stubs=True):
|
116
|
+
if not hasattr(self, '_nodes'):
|
117
|
+
self.__getstate__()
|
118
|
+
return self.narray
|
119
|
+
|
120
|
+
def __getstate__(self):
|
121
|
+
if not hasattr(self, '_nodes'):
|
122
|
+
nodes = []
|
123
|
+
narray = []
|
124
|
+
earray = []
|
125
|
+
for sunit, links in self.topology.items():
|
126
|
+
sindex = index_of(narray,sunit)
|
127
|
+
if sindex == -1:
|
128
|
+
sindex = len(narray)
|
129
|
+
narray.append(sunit)
|
130
|
+
nodes.append(Node(sunit.name, image = unit2image(sunit),
|
131
|
+
color = 'white', size = 15))
|
132
|
+
for dunit in links:
|
133
|
+
dindex = index_of(narray,dunit)
|
134
|
+
if dindex == -1:
|
135
|
+
dindex = len(narray)
|
136
|
+
narray.append(dunit)
|
137
|
+
nodes.append(Node(dunit.name, image = unit2image(dunit),
|
138
|
+
color = 'white', size = 15))
|
139
|
+
earray.append(Edge(sindex, dindex))
|
140
|
+
self._nodes = nodes
|
141
|
+
self._edges = earray
|
142
|
+
self._narray = narray
|
143
|
+
|
144
|
+
if not hasattr(self, '_value'):
|
145
|
+
self._value = dict(nodes = [index_of(self._narray,unit) for unit in self.value['nodes']],
|
146
|
+
edges = [Edge(index_of(self._narray, e.source), index_of(self._narray, e.target)) for e in self.value['edges']])
|
147
|
+
return {name: getattr(self,Net.replace4state.get(name, name)) for name in ['nodes', 'edges', *self.__dict__.keys()]
|
148
|
+
if name != 'topology' and name[0] != '_'}
|
149
|
+
|
150
|
+
def make_topology(self, unit: Unit | Iterable):
|
151
|
+
topo = Topology()
|
152
|
+
def dive(unit):
|
153
|
+
match unit:
|
154
|
+
case Iterable():
|
155
|
+
node = Unit('Union', type = 'union')
|
156
|
+
for obj in unit:
|
157
|
+
if obj:
|
158
|
+
topo[node][dive(obj)] = {}
|
159
|
+
return node
|
160
|
+
case Block():
|
161
|
+
for obj in unit.value:
|
162
|
+
if obj:
|
163
|
+
topo[unit][dive(obj)] = {}
|
164
|
+
case _: ...
|
165
|
+
return unit
|
166
|
+
dive(unit)
|
167
|
+
self.topology = topo
|
168
|
+
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/tables.py
CHANGED
@@ -199,7 +199,7 @@ class Table(Unit):
|
|
199
199
|
tasks = []
|
200
200
|
for index in self.selected_list:
|
201
201
|
values = {field: value for field, value in zip(self.headers, self.rows[index]) if value}
|
202
|
-
for fld, deps in self.
|
202
|
+
for fld, deps in self._llm_dependencies.items():
|
203
203
|
if fld not in values:
|
204
204
|
if deps is True:
|
205
205
|
context = values
|
unisi/units.py
CHANGED
@@ -112,9 +112,9 @@ class Unit:
|
|
112
112
|
async def emit(self, *_ ):
|
113
113
|
"""calcute value by system llm, can be used as a handler"""
|
114
114
|
if Unishare.llm_model and (exactly := getattr(self, 'llm', None)) is not None:
|
115
|
-
elems = [e.compact_view for e in self.
|
115
|
+
elems = [e.compact_view for e in self._llm_dependencies if e.value != '' and e.value is not None]
|
116
116
|
#exactly is requirment that all elements have to have valid value
|
117
|
-
if not exactly or len(elems) == len(self.
|
117
|
+
if not exactly or len(elems) == len(self._llm_dependencies):
|
118
118
|
context = ','.join(elems)
|
119
119
|
self.value = await get_property(self.name, context, self.type, options = getattr(self, 'options', None))
|
120
120
|
return self
|
@@ -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
@@ -1,6 +1,7 @@
|
|
1
1
|
from .utils import *
|
2
2
|
from .units import *
|
3
3
|
from .common import *
|
4
|
+
from .voicecom import VoiceCom
|
4
5
|
from .containers import Dialog, Screen
|
5
6
|
from .multimon import notify_monitor, logging_lock, run_external_process
|
6
7
|
from .kdb import Database
|
@@ -17,6 +18,7 @@ class User:
|
|
17
18
|
self.active_dialog = None
|
18
19
|
self.last_message = None
|
19
20
|
self.changed_units = set()
|
21
|
+
self.voice = None
|
20
22
|
|
21
23
|
if share:
|
22
24
|
self.screens = share.screens
|
@@ -53,7 +55,7 @@ class User:
|
|
53
55
|
if user is not self and screen is user.screen_module])
|
54
56
|
|
55
57
|
async def reflect(self, message, result):
|
56
|
-
if self.reflections and not
|
58
|
+
if self.reflections and not message.screen_type:
|
57
59
|
if result:
|
58
60
|
await self.broadcast(result)
|
59
61
|
if message:
|
@@ -197,6 +199,8 @@ class User:
|
|
197
199
|
else:
|
198
200
|
for bl in flatten(self.blocks):
|
199
201
|
if bl.name == blname:
|
202
|
+
if not elname:
|
203
|
+
return bl
|
200
204
|
for c in flatten(bl.value):
|
201
205
|
if c.name == elname:
|
202
206
|
return c
|
@@ -214,8 +218,9 @@ class User:
|
|
214
218
|
|
215
219
|
def prepare_result(self, raw):
|
216
220
|
if raw is True or raw == Redesign:
|
217
|
-
|
218
|
-
|
221
|
+
out = self.screen
|
222
|
+
out.reload = raw == Redesign
|
223
|
+
raw = out
|
219
224
|
else:
|
220
225
|
match raw:
|
221
226
|
case None:
|
@@ -240,12 +245,15 @@ class User:
|
|
240
245
|
|
241
246
|
async def process(self, message):
|
242
247
|
screen_change_message = message.screen and self.screen.name != message.screen
|
243
|
-
if screen_change_message or
|
248
|
+
if screen_change_message or message.screen_type:
|
244
249
|
for s in self.screens:
|
245
250
|
if s.name == message.value:
|
246
|
-
self.screen_module = s
|
251
|
+
self.screen_module = s
|
247
252
|
if screen_change_message:
|
248
253
|
break
|
254
|
+
if self.voice:
|
255
|
+
self.voice.set_screen(s)
|
256
|
+
self.voice.start()
|
249
257
|
if getattr(s.screen,'prepare', None):
|
250
258
|
s.screen.prepare()
|
251
259
|
return True
|
@@ -253,14 +261,24 @@ class User:
|
|
253
261
|
error = f'Unknown screen name: {message.value}'
|
254
262
|
self.log(error)
|
255
263
|
return Error(error)
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
+
elif message.voice_type:
|
265
|
+
created = False
|
266
|
+
if not self.voice:
|
267
|
+
self.voice = VoiceCom(self)
|
268
|
+
created = True
|
269
|
+
if message.event == 'listen':
|
270
|
+
return self.voice.start() if message.value else self.voice.stop()
|
271
|
+
else:
|
272
|
+
self.voice.input_word(message.value)
|
273
|
+
if created:
|
274
|
+
return Redesign
|
275
|
+
else:
|
276
|
+
elem = self.find_element(message)
|
277
|
+
if elem:
|
278
|
+
return await self.process_element(elem, message)
|
279
|
+
error = f'Element {message.block}/{message.element} does not exist!'
|
280
|
+
self.log(error)
|
281
|
+
return Error(error)
|
264
282
|
|
265
283
|
async def process_element(self, elem, message):
|
266
284
|
event = message.event
|
@@ -285,7 +303,7 @@ class User:
|
|
285
303
|
self.log(error)
|
286
304
|
return Error(error)
|
287
305
|
|
288
|
-
def monitor(self, session, share):
|
306
|
+
def monitor(self, session, share = None):
|
289
307
|
if config.share and session != testdir:
|
290
308
|
self.log(f'User is connected, session: {session}, share: {share.session if share else None}', type = 'info')
|
291
309
|
|
@@ -306,6 +324,14 @@ class User:
|
|
306
324
|
logging.info(str)
|
307
325
|
func(level = logging.WARNING)
|
308
326
|
|
327
|
+
def init_user():
|
328
|
+
"""make initial user for autotest and evaluating dbsharing"""
|
329
|
+
user = User.type(testdir)
|
330
|
+
user.load()
|
331
|
+
#register shared db map once
|
332
|
+
user.calc_dbsharing()
|
333
|
+
return user
|
334
|
+
|
309
335
|
def calc_dbsharing(self):
|
310
336
|
"""calc connections db and units"""
|
311
337
|
dbshare.clear()
|
@@ -362,15 +388,18 @@ def make_user(request):
|
|
362
388
|
return None, Error(error)
|
363
389
|
user = User.type(session, user)
|
364
390
|
ok = user.screens
|
365
|
-
elif config.mirror and User.
|
391
|
+
elif config.mirror and User.count:
|
366
392
|
user = User.type(session, User.last_user)
|
367
393
|
ok = user.screens
|
394
|
+
elif not User.count:
|
395
|
+
user = User.last_user
|
396
|
+
user.session = session
|
397
|
+
user.monitor(session)
|
398
|
+
ok = True
|
368
399
|
else:
|
369
400
|
user = User.type(session)
|
370
401
|
ok = user.load()
|
371
|
-
|
372
|
-
if not user.count:
|
373
|
-
user.calc_dbsharing()
|
402
|
+
|
374
403
|
User.count += 1
|
375
404
|
Unishare.sessions[session] = user
|
376
405
|
return user, ok
|