ivoryos 0.1.8__py3-none-any.whl → 0.1.10__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.
Potentially problematic release.
This version of ivoryos might be problematic. Click here for more details.
- ivoryos/__init__.py +118 -99
- ivoryos/config.py +47 -47
- ivoryos/routes/auth/auth.py +100 -65
- ivoryos/routes/auth/templates/auth/login.html +25 -25
- ivoryos/routes/auth/templates/auth/signup.html +32 -32
- ivoryos/routes/control/control.py +400 -272
- ivoryos/routes/control/templates/control/controllers.html +75 -75
- ivoryos/routes/control/templates/control/controllers_home.html +50 -50
- ivoryos/routes/control/templates/control/controllers_new.html +89 -89
- ivoryos/routes/database/database.py +188 -114
- ivoryos/routes/database/templates/database/experiment_database.html +72 -72
- ivoryos/routes/design/design.py +542 -406
- ivoryos/routes/design/templates/design/experiment_builder.html +415 -412
- ivoryos/routes/design/templates/design/experiment_run.html +325 -325
- ivoryos/routes/main/main.py +42 -25
- ivoryos/routes/main/templates/main/help.html +141 -141
- ivoryos/routes/main/templates/main/home.html +68 -68
- ivoryos/static/.DS_Store +0 -0
- ivoryos/static/js/overlay.js +12 -12
- ivoryos/static/js/socket_handler.js +34 -34
- ivoryos/static/js/sortable_card.js +24 -24
- ivoryos/static/js/sortable_design.js +36 -36
- ivoryos/static/style.css +201 -201
- ivoryos/templates/base.html +143 -143
- ivoryos/utils/db_models.py +518 -500
- ivoryos/utils/form.py +316 -316
- ivoryos/utils/global_config.py +67 -67
- ivoryos/utils/llm_agent.py +183 -183
- ivoryos/utils/script_runner.py +165 -164
- ivoryos/utils/utils.py +425 -422
- ivoryos/version.py +1 -0
- {ivoryos-0.1.8.dist-info → ivoryos-0.1.10.dist-info}/LICENSE +21 -21
- {ivoryos-0.1.8.dist-info → ivoryos-0.1.10.dist-info}/METADATA +170 -166
- ivoryos-0.1.10.dist-info/RECORD +47 -0
- {ivoryos-0.1.8.dist-info → ivoryos-0.1.10.dist-info}/WHEEL +1 -1
- ivoryos-0.1.8.dist-info/RECORD +0 -45
- {ivoryos-0.1.8.dist-info → ivoryos-0.1.10.dist-info}/top_level.txt +0 -0
ivoryos/utils/db_models.py
CHANGED
|
@@ -1,500 +1,518 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import keyword
|
|
3
|
-
import re
|
|
4
|
-
import uuid
|
|
5
|
-
from datetime import datetime
|
|
6
|
-
|
|
7
|
-
from flask_login import UserMixin
|
|
8
|
-
from flask_sqlalchemy import SQLAlchemy
|
|
9
|
-
from sqlalchemy_utils import JSONType
|
|
10
|
-
|
|
11
|
-
db = SQLAlchemy()
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class User(db.Model, UserMixin):
|
|
15
|
-
__tablename__ = 'user'
|
|
16
|
-
# id = db.Column(db.Integer)
|
|
17
|
-
username = db.Column(db.String(50), primary_key=True, unique=True, nullable=False)
|
|
18
|
-
# email = db.Column(db.String)
|
|
19
|
-
hashPassword = db.Column(db.String(255))
|
|
20
|
-
|
|
21
|
-
# password = db.Column()
|
|
22
|
-
def __init__(self, username, password):
|
|
23
|
-
# self.id = id
|
|
24
|
-
self.username = username
|
|
25
|
-
# self.email = email
|
|
26
|
-
self.hashPassword = password
|
|
27
|
-
|
|
28
|
-
def get_id(self):
|
|
29
|
-
return self.username
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class Script(db.Model):
|
|
33
|
-
__tablename__ = 'script'
|
|
34
|
-
# id = db.Column(db.Integer, primary_key=True)
|
|
35
|
-
name = db.Column(db.String(50), primary_key=True, unique=True)
|
|
36
|
-
deck = db.Column(db.String(50), nullable=True)
|
|
37
|
-
status = db.Column(db.String(50), nullable=True)
|
|
38
|
-
script_dict = db.Column(JSONType, nullable=True)
|
|
39
|
-
time_created = db.Column(db.String(50), nullable=True)
|
|
40
|
-
last_modified = db.Column(db.String(50), nullable=True)
|
|
41
|
-
id_order = db.Column(JSONType, nullable=True)
|
|
42
|
-
editing_type = db.Column(db.String(50), nullable=True)
|
|
43
|
-
author = db.Column(db.String(50), nullable=False)
|
|
44
|
-
|
|
45
|
-
def __init__(self, name=None, deck=None, status=None, script_dict: dict = None, id_order: dict = None,
|
|
46
|
-
time_created=None, last_modified=None, editing_type=None, author: str = None):
|
|
47
|
-
if script_dict is None:
|
|
48
|
-
script_dict = {"prep": [], "script": [], "cleanup": []}
|
|
49
|
-
elif type(script_dict) is not dict:
|
|
50
|
-
script_dict = json.loads(script_dict)
|
|
51
|
-
if id_order is None:
|
|
52
|
-
id_order = {"prep": [], "script": [], "cleanup": []}
|
|
53
|
-
elif type(id_order) is not dict:
|
|
54
|
-
id_order = json.loads(id_order)
|
|
55
|
-
if status is None:
|
|
56
|
-
status = 'editing'
|
|
57
|
-
if time_created is None:
|
|
58
|
-
time_created = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
59
|
-
if last_modified is None:
|
|
60
|
-
last_modified = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
61
|
-
if editing_type is None:
|
|
62
|
-
editing_type = "script"
|
|
63
|
-
|
|
64
|
-
self.name = name
|
|
65
|
-
self.deck = deck
|
|
66
|
-
self.status = status
|
|
67
|
-
self.script_dict = script_dict
|
|
68
|
-
self.time_created = time_created
|
|
69
|
-
self.last_modified = last_modified
|
|
70
|
-
self.id_order = id_order
|
|
71
|
-
self.editing_type = editing_type
|
|
72
|
-
self.author = author
|
|
73
|
-
|
|
74
|
-
def as_dict(self):
|
|
75
|
-
dict = self.__dict__
|
|
76
|
-
dict.pop('_sa_instance_state', None)
|
|
77
|
-
return dict
|
|
78
|
-
|
|
79
|
-
def get(self):
|
|
80
|
-
workflows = db.session.query(Script).all()
|
|
81
|
-
# result = script_schema.dump(workflows)
|
|
82
|
-
return workflows
|
|
83
|
-
|
|
84
|
-
def find_by_uuid(self, uuid):
|
|
85
|
-
for stype in self.script_dict:
|
|
86
|
-
for action in self.script_dict[stype]:
|
|
87
|
-
|
|
88
|
-
if action['uuid'] == int(uuid):
|
|
89
|
-
return action
|
|
90
|
-
|
|
91
|
-
def _convert_type(self, args, arg_types):
|
|
92
|
-
if type(arg_types) is not list:
|
|
93
|
-
arg_types = [arg_types]
|
|
94
|
-
for arg_type in arg_types:
|
|
95
|
-
try:
|
|
96
|
-
args = eval(f"{arg_type}('{args}')")
|
|
97
|
-
return
|
|
98
|
-
except Exception:
|
|
99
|
-
pass
|
|
100
|
-
raise TypeError(f"Input type error: cannot convert '{args}' to {arg_type}.")
|
|
101
|
-
|
|
102
|
-
def update_by_uuid(self, uuid, args, output):
|
|
103
|
-
bool_dict = {"True": True, "False": False}
|
|
104
|
-
action = self.find_by_uuid(uuid)
|
|
105
|
-
if type(action['args']) is dict:
|
|
106
|
-
for arg in action['args']:
|
|
107
|
-
if not args[arg].startswith("#"):
|
|
108
|
-
|
|
109
|
-
if args[arg] in bool_dict.keys():
|
|
110
|
-
args[arg] = bool_dict[args[arg]]
|
|
111
|
-
elif args[arg] == "None" or args[arg] == "":
|
|
112
|
-
args[arg] = None
|
|
113
|
-
else:
|
|
114
|
-
if arg in action['arg_types']:
|
|
115
|
-
arg_types = action['arg_types'][arg]
|
|
116
|
-
self._convert_type(args[arg], arg_types)
|
|
117
|
-
else:
|
|
118
|
-
try:
|
|
119
|
-
args[arg] = eval(args[arg])
|
|
120
|
-
except Exception:
|
|
121
|
-
pass
|
|
122
|
-
else:
|
|
123
|
-
args = list(args.values())[0]
|
|
124
|
-
if not args.startswith("#"):
|
|
125
|
-
if args in bool_dict.keys():
|
|
126
|
-
args = bool_dict[args]
|
|
127
|
-
|
|
128
|
-
else:
|
|
129
|
-
if 'arg_types' in action:
|
|
130
|
-
arg_types = action['arg_types']
|
|
131
|
-
self._convert_type(args, arg_types)
|
|
132
|
-
|
|
133
|
-
# print(args)
|
|
134
|
-
action['args'] = args
|
|
135
|
-
# print(action)
|
|
136
|
-
action['return'] = output
|
|
137
|
-
|
|
138
|
-
@property
|
|
139
|
-
def stypes(self):
|
|
140
|
-
return list(self.script_dict.keys())
|
|
141
|
-
|
|
142
|
-
@property
|
|
143
|
-
def currently_editing_script(self):
|
|
144
|
-
return self.script_dict[self.editing_type]
|
|
145
|
-
|
|
146
|
-
@currently_editing_script.setter
|
|
147
|
-
def currently_editing_script(self, script):
|
|
148
|
-
self.script_dict[self.editing_type] = script
|
|
149
|
-
|
|
150
|
-
@property
|
|
151
|
-
def currently_editing_order(self):
|
|
152
|
-
return self.id_order[self.editing_type]
|
|
153
|
-
|
|
154
|
-
@currently_editing_order.setter
|
|
155
|
-
def currently_editing_order(self, script):
|
|
156
|
-
self.id_order[self.editing_type] = script
|
|
157
|
-
|
|
158
|
-
# @property
|
|
159
|
-
# def editing_type(self):
|
|
160
|
-
# return self.editing_type
|
|
161
|
-
|
|
162
|
-
# @editing_type.setter
|
|
163
|
-
# def editing_type(self, change_type):
|
|
164
|
-
# self.editing_type = change_type
|
|
165
|
-
|
|
166
|
-
def update_time_stamp(self):
|
|
167
|
-
self.last_modified = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
168
|
-
|
|
169
|
-
def get_script(self, stype: str):
|
|
170
|
-
return self.script_dict[stype]
|
|
171
|
-
|
|
172
|
-
def isEmpty(self) -> bool:
|
|
173
|
-
if not (self.script_dict['script'] or self.script_dict['prep'] or self.script_dict['cleanup']):
|
|
174
|
-
return True
|
|
175
|
-
return False
|
|
176
|
-
|
|
177
|
-
def _sort(self, script_type):
|
|
178
|
-
if len(self.id_order[script_type]) > 0:
|
|
179
|
-
for action in self.script_dict[script_type]:
|
|
180
|
-
for i in range(len(self.id_order[script_type])):
|
|
181
|
-
if action['id'] == int(self.id_order[script_type][i]):
|
|
182
|
-
# print(i+1)
|
|
183
|
-
action['id'] = i + 1
|
|
184
|
-
break
|
|
185
|
-
self.id_order[script_type].sort()
|
|
186
|
-
if not int(self.id_order[script_type][-1]) == len(self.script_dict[script_type]):
|
|
187
|
-
new_order = list(range(1, len(self.script_dict[script_type]) + 1))
|
|
188
|
-
self.id_order[script_type] = [str(i) for i in new_order]
|
|
189
|
-
self.script_dict[script_type].sort(key=lambda x: x['id'])
|
|
190
|
-
|
|
191
|
-
def sort_actions(self, script_type=None):
|
|
192
|
-
if script_type:
|
|
193
|
-
self._sort(script_type)
|
|
194
|
-
else:
|
|
195
|
-
for i in self.stypes:
|
|
196
|
-
self._sort(i)
|
|
197
|
-
|
|
198
|
-
def add_action(self, action: dict):
|
|
199
|
-
current_len = len(self.currently_editing_script)
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
self.
|
|
204
|
-
self.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
self.
|
|
213
|
-
self.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
"
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
"
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
self.
|
|
247
|
-
self.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
self.
|
|
257
|
-
self.
|
|
258
|
-
self.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
for
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
""
|
|
324
|
-
self.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
if
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
if
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
"""
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
"""
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
indent_unit
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
"""
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
1
|
+
import json
|
|
2
|
+
import keyword
|
|
3
|
+
import re
|
|
4
|
+
import uuid
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
from flask_login import UserMixin
|
|
8
|
+
from flask_sqlalchemy import SQLAlchemy
|
|
9
|
+
from sqlalchemy_utils import JSONType
|
|
10
|
+
|
|
11
|
+
db = SQLAlchemy()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class User(db.Model, UserMixin):
|
|
15
|
+
__tablename__ = 'user'
|
|
16
|
+
# id = db.Column(db.Integer)
|
|
17
|
+
username = db.Column(db.String(50), primary_key=True, unique=True, nullable=False)
|
|
18
|
+
# email = db.Column(db.String)
|
|
19
|
+
hashPassword = db.Column(db.String(255))
|
|
20
|
+
|
|
21
|
+
# password = db.Column()
|
|
22
|
+
def __init__(self, username, password):
|
|
23
|
+
# self.id = id
|
|
24
|
+
self.username = username
|
|
25
|
+
# self.email = email
|
|
26
|
+
self.hashPassword = password
|
|
27
|
+
|
|
28
|
+
def get_id(self):
|
|
29
|
+
return self.username
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Script(db.Model):
|
|
33
|
+
__tablename__ = 'script'
|
|
34
|
+
# id = db.Column(db.Integer, primary_key=True)
|
|
35
|
+
name = db.Column(db.String(50), primary_key=True, unique=True)
|
|
36
|
+
deck = db.Column(db.String(50), nullable=True)
|
|
37
|
+
status = db.Column(db.String(50), nullable=True)
|
|
38
|
+
script_dict = db.Column(JSONType, nullable=True)
|
|
39
|
+
time_created = db.Column(db.String(50), nullable=True)
|
|
40
|
+
last_modified = db.Column(db.String(50), nullable=True)
|
|
41
|
+
id_order = db.Column(JSONType, nullable=True)
|
|
42
|
+
editing_type = db.Column(db.String(50), nullable=True)
|
|
43
|
+
author = db.Column(db.String(50), nullable=False)
|
|
44
|
+
|
|
45
|
+
def __init__(self, name=None, deck=None, status=None, script_dict: dict = None, id_order: dict = None,
|
|
46
|
+
time_created=None, last_modified=None, editing_type=None, author: str = None):
|
|
47
|
+
if script_dict is None:
|
|
48
|
+
script_dict = {"prep": [], "script": [], "cleanup": []}
|
|
49
|
+
elif type(script_dict) is not dict:
|
|
50
|
+
script_dict = json.loads(script_dict)
|
|
51
|
+
if id_order is None:
|
|
52
|
+
id_order = {"prep": [], "script": [], "cleanup": []}
|
|
53
|
+
elif type(id_order) is not dict:
|
|
54
|
+
id_order = json.loads(id_order)
|
|
55
|
+
if status is None:
|
|
56
|
+
status = 'editing'
|
|
57
|
+
if time_created is None:
|
|
58
|
+
time_created = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
59
|
+
if last_modified is None:
|
|
60
|
+
last_modified = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
61
|
+
if editing_type is None:
|
|
62
|
+
editing_type = "script"
|
|
63
|
+
|
|
64
|
+
self.name = name
|
|
65
|
+
self.deck = deck
|
|
66
|
+
self.status = status
|
|
67
|
+
self.script_dict = script_dict
|
|
68
|
+
self.time_created = time_created
|
|
69
|
+
self.last_modified = last_modified
|
|
70
|
+
self.id_order = id_order
|
|
71
|
+
self.editing_type = editing_type
|
|
72
|
+
self.author = author
|
|
73
|
+
|
|
74
|
+
def as_dict(self):
|
|
75
|
+
dict = self.__dict__
|
|
76
|
+
dict.pop('_sa_instance_state', None)
|
|
77
|
+
return dict
|
|
78
|
+
|
|
79
|
+
def get(self):
|
|
80
|
+
workflows = db.session.query(Script).all()
|
|
81
|
+
# result = script_schema.dump(workflows)
|
|
82
|
+
return workflows
|
|
83
|
+
|
|
84
|
+
def find_by_uuid(self, uuid):
|
|
85
|
+
for stype in self.script_dict:
|
|
86
|
+
for action in self.script_dict[stype]:
|
|
87
|
+
|
|
88
|
+
if action['uuid'] == int(uuid):
|
|
89
|
+
return action
|
|
90
|
+
|
|
91
|
+
def _convert_type(self, args, arg_types):
|
|
92
|
+
if type(arg_types) is not list:
|
|
93
|
+
arg_types = [arg_types]
|
|
94
|
+
for arg_type in arg_types:
|
|
95
|
+
try:
|
|
96
|
+
args = eval(f"{arg_type}('{args}')")
|
|
97
|
+
return
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
100
|
+
raise TypeError(f"Input type error: cannot convert '{args}' to {arg_type}.")
|
|
101
|
+
|
|
102
|
+
def update_by_uuid(self, uuid, args, output):
|
|
103
|
+
bool_dict = {"True": True, "False": False}
|
|
104
|
+
action = self.find_by_uuid(uuid)
|
|
105
|
+
if type(action['args']) is dict:
|
|
106
|
+
for arg in action['args']:
|
|
107
|
+
if not args[arg].startswith("#"):
|
|
108
|
+
|
|
109
|
+
if args[arg] in bool_dict.keys():
|
|
110
|
+
args[arg] = bool_dict[args[arg]]
|
|
111
|
+
elif args[arg] == "None" or args[arg] == "":
|
|
112
|
+
args[arg] = None
|
|
113
|
+
else:
|
|
114
|
+
if arg in action['arg_types']:
|
|
115
|
+
arg_types = action['arg_types'][arg]
|
|
116
|
+
self._convert_type(args[arg], arg_types)
|
|
117
|
+
else:
|
|
118
|
+
try:
|
|
119
|
+
args[arg] = eval(args[arg])
|
|
120
|
+
except Exception:
|
|
121
|
+
pass
|
|
122
|
+
else:
|
|
123
|
+
args = list(args.values())[0]
|
|
124
|
+
if not args.startswith("#"):
|
|
125
|
+
if args in bool_dict.keys():
|
|
126
|
+
args = bool_dict[args]
|
|
127
|
+
|
|
128
|
+
else:
|
|
129
|
+
if 'arg_types' in action:
|
|
130
|
+
arg_types = action['arg_types']
|
|
131
|
+
self._convert_type(args, arg_types)
|
|
132
|
+
|
|
133
|
+
# print(args)
|
|
134
|
+
action['args'] = args
|
|
135
|
+
# print(action)
|
|
136
|
+
action['return'] = output
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def stypes(self):
|
|
140
|
+
return list(self.script_dict.keys())
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def currently_editing_script(self):
|
|
144
|
+
return self.script_dict[self.editing_type]
|
|
145
|
+
|
|
146
|
+
@currently_editing_script.setter
|
|
147
|
+
def currently_editing_script(self, script):
|
|
148
|
+
self.script_dict[self.editing_type] = script
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def currently_editing_order(self):
|
|
152
|
+
return self.id_order[self.editing_type]
|
|
153
|
+
|
|
154
|
+
@currently_editing_order.setter
|
|
155
|
+
def currently_editing_order(self, script):
|
|
156
|
+
self.id_order[self.editing_type] = script
|
|
157
|
+
|
|
158
|
+
# @property
|
|
159
|
+
# def editing_type(self):
|
|
160
|
+
# return self.editing_type
|
|
161
|
+
|
|
162
|
+
# @editing_type.setter
|
|
163
|
+
# def editing_type(self, change_type):
|
|
164
|
+
# self.editing_type = change_type
|
|
165
|
+
|
|
166
|
+
def update_time_stamp(self):
|
|
167
|
+
self.last_modified = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
168
|
+
|
|
169
|
+
def get_script(self, stype: str):
|
|
170
|
+
return self.script_dict[stype]
|
|
171
|
+
|
|
172
|
+
def isEmpty(self) -> bool:
|
|
173
|
+
if not (self.script_dict['script'] or self.script_dict['prep'] or self.script_dict['cleanup']):
|
|
174
|
+
return True
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
def _sort(self, script_type):
|
|
178
|
+
if len(self.id_order[script_type]) > 0:
|
|
179
|
+
for action in self.script_dict[script_type]:
|
|
180
|
+
for i in range(len(self.id_order[script_type])):
|
|
181
|
+
if action['id'] == int(self.id_order[script_type][i]):
|
|
182
|
+
# print(i+1)
|
|
183
|
+
action['id'] = i + 1
|
|
184
|
+
break
|
|
185
|
+
self.id_order[script_type].sort()
|
|
186
|
+
if not int(self.id_order[script_type][-1]) == len(self.script_dict[script_type]):
|
|
187
|
+
new_order = list(range(1, len(self.script_dict[script_type]) + 1))
|
|
188
|
+
self.id_order[script_type] = [str(i) for i in new_order]
|
|
189
|
+
self.script_dict[script_type].sort(key=lambda x: x['id'])
|
|
190
|
+
|
|
191
|
+
def sort_actions(self, script_type=None):
|
|
192
|
+
if script_type:
|
|
193
|
+
self._sort(script_type)
|
|
194
|
+
else:
|
|
195
|
+
for i in self.stypes:
|
|
196
|
+
self._sort(i)
|
|
197
|
+
|
|
198
|
+
def add_action(self, action: dict):
|
|
199
|
+
current_len = len(self.currently_editing_script)
|
|
200
|
+
action_to_add = action.copy()
|
|
201
|
+
action_to_add['id'] = current_len + 1
|
|
202
|
+
action_to_add['uuid'] = uuid.uuid4().fields[-1]
|
|
203
|
+
self.currently_editing_script.append(action_to_add)
|
|
204
|
+
self.currently_editing_order.append(str(current_len + 1))
|
|
205
|
+
self.update_time_stamp()
|
|
206
|
+
|
|
207
|
+
def add_variable(self, statement, variable):
|
|
208
|
+
current_len = len(self.currently_editing_script)
|
|
209
|
+
uid = uuid.uuid4().fields[-1]
|
|
210
|
+
action_list = [{"id": current_len + 1, "instrument": 'variable', "action": variable,
|
|
211
|
+
"args": 'None' if statement == '' else statement, "return": '', "uuid": uid, "arg_types": ''}]
|
|
212
|
+
self.currently_editing_script.extend(action_list)
|
|
213
|
+
self.currently_editing_order.extend([str(current_len + i + 1) for i in range(len(action_list))])
|
|
214
|
+
self.update_time_stamp()
|
|
215
|
+
|
|
216
|
+
def add_logic_action(self, logic_type: str, statement):
|
|
217
|
+
current_len = len(self.currently_editing_script)
|
|
218
|
+
uid = uuid.uuid4().fields[-1]
|
|
219
|
+
logic_dict = {
|
|
220
|
+
"if":
|
|
221
|
+
[
|
|
222
|
+
{"id": current_len + 1, "instrument": 'if', "action": 'if',
|
|
223
|
+
"args": 'True' if statement == '' else statement,
|
|
224
|
+
"return": '', "uuid": uid, "arg_types": ''},
|
|
225
|
+
{"id": current_len + 2, "instrument": 'if', "action": 'else', "args": '', "return": '',
|
|
226
|
+
"uuid": uid},
|
|
227
|
+
{"id": current_len + 3, "instrument": 'if', "action": 'endif', "args": '', "return": '',
|
|
228
|
+
"uuid": uid},
|
|
229
|
+
],
|
|
230
|
+
"while":
|
|
231
|
+
[
|
|
232
|
+
{"id": current_len + 1, "instrument": 'while', "action": 'while',
|
|
233
|
+
"args": 'False' if statement == '' else statement, "return": '', "uuid": uid, "arg_types": ''},
|
|
234
|
+
{"id": current_len + 2, "instrument": 'while', "action": 'endwhile', "args": '', "return": '',
|
|
235
|
+
"uuid": uid},
|
|
236
|
+
],
|
|
237
|
+
|
|
238
|
+
"wait":
|
|
239
|
+
[
|
|
240
|
+
{"id": current_len + 1, "instrument": 'wait', "action": "wait",
|
|
241
|
+
"args": '0' if statement == '' else statement,
|
|
242
|
+
"return": '', "uuid": uid, "arg_types": "float"},
|
|
243
|
+
],
|
|
244
|
+
}
|
|
245
|
+
action_list = logic_dict[logic_type]
|
|
246
|
+
self.currently_editing_script.extend(action_list)
|
|
247
|
+
self.currently_editing_order.extend([str(current_len + i + 1) for i in range(len(action_list))])
|
|
248
|
+
self.update_time_stamp()
|
|
249
|
+
|
|
250
|
+
def delete_action(self, id: int):
|
|
251
|
+
|
|
252
|
+
uid = next((action['uuid'] for action in self.currently_editing_script if action['id'] == int(id)), None)
|
|
253
|
+
id_to_be_removed = [action['id'] for action in self.currently_editing_script if action['uuid'] == uid]
|
|
254
|
+
order = self.currently_editing_order
|
|
255
|
+
script = self.currently_editing_script
|
|
256
|
+
self.currently_editing_order = [i for i in order if int(i) not in id_to_be_removed]
|
|
257
|
+
self.currently_editing_script = [action for action in script if action['id'] not in id_to_be_removed]
|
|
258
|
+
self.sort_actions()
|
|
259
|
+
self.update_time_stamp()
|
|
260
|
+
|
|
261
|
+
def duplicate_action(self, id: int):
|
|
262
|
+
action_to_duplicate = next((action for action in self.currently_editing_script if action['id'] == int(id)), None)
|
|
263
|
+
insert_id = action_to_duplicate.get("id")
|
|
264
|
+
self.add_action(action_to_duplicate)
|
|
265
|
+
# print(self.currently_editing_script)
|
|
266
|
+
if action_to_duplicate is not None:
|
|
267
|
+
# Update IDs for all subsequent actions
|
|
268
|
+
for action in self.currently_editing_script:
|
|
269
|
+
if action['id'] > insert_id:
|
|
270
|
+
action['id'] += 1
|
|
271
|
+
self.currently_editing_script[-1]['id'] = insert_id + 1
|
|
272
|
+
# Sort actions if necessary and update the time stamp
|
|
273
|
+
self.sort_actions()
|
|
274
|
+
self.update_time_stamp()
|
|
275
|
+
else:
|
|
276
|
+
raise ValueError("Action not found: Unable to duplicate the action with ID", id)
|
|
277
|
+
|
|
278
|
+
def config(self, stype):
|
|
279
|
+
"""
|
|
280
|
+
take the global script_dict
|
|
281
|
+
:return: list of variable that require input
|
|
282
|
+
"""
|
|
283
|
+
configure = []
|
|
284
|
+
config_type_dict = {}
|
|
285
|
+
for action in self.script_dict[stype]:
|
|
286
|
+
args = action['args']
|
|
287
|
+
if args is not None:
|
|
288
|
+
if type(args) is not dict:
|
|
289
|
+
if type(args) is str and args.startswith("#") and not args[1:] in configure:
|
|
290
|
+
configure.append(args[1:])
|
|
291
|
+
config_type_dict[args[1:]] = action['arg_types']
|
|
292
|
+
|
|
293
|
+
else:
|
|
294
|
+
for arg in args:
|
|
295
|
+
if type(args[arg]) is str \
|
|
296
|
+
and args[arg].startswith("#") \
|
|
297
|
+
and not args[arg][1:] in configure:
|
|
298
|
+
configure.append(args[arg][1:])
|
|
299
|
+
if arg in action['arg_types']:
|
|
300
|
+
if action['arg_types'][arg] == '':
|
|
301
|
+
config_type_dict[args[arg][1:]] = "any"
|
|
302
|
+
else:
|
|
303
|
+
config_type_dict[args[arg][1:]] = action['arg_types'][arg]
|
|
304
|
+
else:
|
|
305
|
+
config_type_dict[args[arg][1:]] = "any"
|
|
306
|
+
# todo
|
|
307
|
+
return configure, config_type_dict
|
|
308
|
+
|
|
309
|
+
def config_return(self):
|
|
310
|
+
"""
|
|
311
|
+
take the global script_dict
|
|
312
|
+
:return: list of variable that require input
|
|
313
|
+
"""
|
|
314
|
+
|
|
315
|
+
return_list = [action['return'] for action in self.script_dict['script'] if not action['return'] == '']
|
|
316
|
+
output_str = "return {"
|
|
317
|
+
for i in return_list:
|
|
318
|
+
output_str += "'" + i + "':" + i + ","
|
|
319
|
+
output_str += "}"
|
|
320
|
+
return output_str, return_list
|
|
321
|
+
|
|
322
|
+
def finalize(self):
|
|
323
|
+
self.status = "finalized"
|
|
324
|
+
self.update_time_stamp()
|
|
325
|
+
|
|
326
|
+
def save_as(self, name):
|
|
327
|
+
self.name = name
|
|
328
|
+
self.status = "editing"
|
|
329
|
+
self.update_time_stamp()
|
|
330
|
+
|
|
331
|
+
def indent(self, unit=0):
|
|
332
|
+
string = "\n"
|
|
333
|
+
for _ in range(unit):
|
|
334
|
+
string += "\t"
|
|
335
|
+
return string
|
|
336
|
+
|
|
337
|
+
def compile(self, script_path=None):
|
|
338
|
+
"""
|
|
339
|
+
Compile the current script to a Python file.
|
|
340
|
+
:return: String to write to a Python file.
|
|
341
|
+
"""
|
|
342
|
+
self.sort_actions()
|
|
343
|
+
run_name = self.name if self.name else "untitled"
|
|
344
|
+
run_name = self.validate_function_name(run_name)
|
|
345
|
+
exec_string = ''
|
|
346
|
+
|
|
347
|
+
for i in self.stypes:
|
|
348
|
+
exec_string += self._generate_function_header(run_name, i)
|
|
349
|
+
exec_string += self._generate_function_body(i)
|
|
350
|
+
|
|
351
|
+
if script_path:
|
|
352
|
+
self._write_to_file(script_path, run_name, exec_string)
|
|
353
|
+
|
|
354
|
+
return exec_string
|
|
355
|
+
|
|
356
|
+
@staticmethod
|
|
357
|
+
def validate_function_name(name):
|
|
358
|
+
"""Replace invalid characters with underscores"""
|
|
359
|
+
name = re.sub(r'\W|^(?=\d)', '_', name)
|
|
360
|
+
# Check if it's a Python keyword and adjust if necessary
|
|
361
|
+
if keyword.iskeyword(name):
|
|
362
|
+
name += '_'
|
|
363
|
+
return name
|
|
364
|
+
|
|
365
|
+
def _generate_function_header(self, run_name, stype):
|
|
366
|
+
"""
|
|
367
|
+
Generate the function header.
|
|
368
|
+
"""
|
|
369
|
+
configure, _ = self.config(stype)
|
|
370
|
+
function_header = f"\n\ndef {run_name}_{stype}("
|
|
371
|
+
|
|
372
|
+
if stype == "script":
|
|
373
|
+
function_header += ",".join(configure)
|
|
374
|
+
|
|
375
|
+
function_header += "):"
|
|
376
|
+
function_header += self.indent(1) + f"global {run_name}_{stype}"
|
|
377
|
+
return function_header
|
|
378
|
+
|
|
379
|
+
def _generate_function_body(self, stype):
|
|
380
|
+
"""
|
|
381
|
+
Generate the function body for each type in stypes.
|
|
382
|
+
"""
|
|
383
|
+
body = ''
|
|
384
|
+
indent_unit = 1
|
|
385
|
+
|
|
386
|
+
for index, action in enumerate(self.script_dict[stype]):
|
|
387
|
+
text, indent_unit = self._process_action(indent_unit, action, index, stype)
|
|
388
|
+
body += text
|
|
389
|
+
return_str, return_list = self.config_return()
|
|
390
|
+
if return_list and stype == "script":
|
|
391
|
+
body += self.indent(indent_unit) + return_str
|
|
392
|
+
|
|
393
|
+
return body
|
|
394
|
+
|
|
395
|
+
def _process_action(self, indent_unit, action, index, stype):
|
|
396
|
+
"""
|
|
397
|
+
Process each action within the script dictionary.
|
|
398
|
+
"""
|
|
399
|
+
instrument = action['instrument']
|
|
400
|
+
args = self._process_args(action['args'])
|
|
401
|
+
save_data = action['return']
|
|
402
|
+
action_name = action['action']
|
|
403
|
+
next_action = self._get_next_action(stype, index)
|
|
404
|
+
if instrument == 'if':
|
|
405
|
+
return self._process_if(indent_unit, action_name, args, next_action)
|
|
406
|
+
elif instrument == 'while':
|
|
407
|
+
return self._process_while(indent_unit, action_name, args, next_action)
|
|
408
|
+
elif instrument == 'variable':
|
|
409
|
+
return self.indent(indent_unit) + f"{action_name} = {args}", indent_unit
|
|
410
|
+
elif instrument == 'wait':
|
|
411
|
+
return f"{self.indent(indent_unit)}time.sleep({args})", indent_unit
|
|
412
|
+
else:
|
|
413
|
+
return self._process_instrument_action(indent_unit, instrument, action_name, args, save_data)
|
|
414
|
+
|
|
415
|
+
def _process_args(self, args):
|
|
416
|
+
"""
|
|
417
|
+
Process arguments, handling any specific formatting needs.
|
|
418
|
+
"""
|
|
419
|
+
if isinstance(args, str) and args.startswith("#"):
|
|
420
|
+
return args[1:]
|
|
421
|
+
return args
|
|
422
|
+
|
|
423
|
+
def _process_if(self, indent_unit, action, args, next_action):
|
|
424
|
+
"""
|
|
425
|
+
Process 'if' and 'else' actions.
|
|
426
|
+
"""
|
|
427
|
+
exec_string = ""
|
|
428
|
+
if action == 'if':
|
|
429
|
+
exec_string += self.indent(indent_unit) + f"if {args}:"
|
|
430
|
+
indent_unit += 1
|
|
431
|
+
if next_action and next_action['instrument'] == 'if' and next_action['action'] == 'else':
|
|
432
|
+
exec_string += self.indent(indent_unit) + "pass"
|
|
433
|
+
# else:
|
|
434
|
+
|
|
435
|
+
elif action == 'else':
|
|
436
|
+
indent_unit -= 1
|
|
437
|
+
exec_string += self.indent(indent_unit) + "else:"
|
|
438
|
+
indent_unit += 1
|
|
439
|
+
if next_action and next_action['instrument'] == 'if' and next_action['action'] == 'endif':
|
|
440
|
+
exec_string += self.indent(indent_unit) + "pass"
|
|
441
|
+
else:
|
|
442
|
+
indent_unit -= 1
|
|
443
|
+
return exec_string, indent_unit
|
|
444
|
+
|
|
445
|
+
def _process_while(self, indent_unit, action, args, next_action):
|
|
446
|
+
"""
|
|
447
|
+
Process 'while' and 'endwhile' actions.
|
|
448
|
+
"""
|
|
449
|
+
exec_string = ""
|
|
450
|
+
if action == 'while':
|
|
451
|
+
exec_string += self.indent(indent_unit) + f"while {args}:"
|
|
452
|
+
indent_unit += 1
|
|
453
|
+
if next_action and next_action['instrument'] == 'while':
|
|
454
|
+
exec_string += self.indent(indent_unit) + "pass"
|
|
455
|
+
elif action == 'endwhile':
|
|
456
|
+
indent_unit -= 1
|
|
457
|
+
return exec_string, indent_unit
|
|
458
|
+
|
|
459
|
+
def _process_instrument_action(self, indent_unit, instrument, action, args, save_data):
|
|
460
|
+
"""
|
|
461
|
+
Process actions related to instruments.
|
|
462
|
+
"""
|
|
463
|
+
if isinstance(args, dict):
|
|
464
|
+
args_str = self._process_dict_args(args)
|
|
465
|
+
single_line = f"{instrument}.{action}(**{args_str})"
|
|
466
|
+
elif isinstance(args, str):
|
|
467
|
+
single_line = f"{instrument}.{action} = {args}"
|
|
468
|
+
else:
|
|
469
|
+
single_line = f"{instrument}.{action}()"
|
|
470
|
+
|
|
471
|
+
if save_data:
|
|
472
|
+
save_data += " = "
|
|
473
|
+
|
|
474
|
+
return self.indent(indent_unit) + save_data + single_line, indent_unit
|
|
475
|
+
|
|
476
|
+
def _process_dict_args(self, args):
|
|
477
|
+
"""
|
|
478
|
+
Process dictionary arguments, handling special cases like variables.
|
|
479
|
+
"""
|
|
480
|
+
args_str = args.__str__()
|
|
481
|
+
for arg in args:
|
|
482
|
+
if isinstance(args[arg], str) and args[arg].startswith("#"):
|
|
483
|
+
args_str = args_str.replace(f"'#{args[arg][1:]}'", args[arg][1:])
|
|
484
|
+
elif self._is_variable(arg):
|
|
485
|
+
args_str = args_str.replace(f"'{args[arg]}'", args[arg])
|
|
486
|
+
return args_str
|
|
487
|
+
|
|
488
|
+
def _get_next_action(self, stype, index):
|
|
489
|
+
"""
|
|
490
|
+
Get the next action in the sequence if it exists.
|
|
491
|
+
"""
|
|
492
|
+
if index < (len(self.script_dict[stype]) - 1):
|
|
493
|
+
return self.script_dict[stype][index + 1]
|
|
494
|
+
return None
|
|
495
|
+
|
|
496
|
+
def _is_variable(self, arg):
|
|
497
|
+
"""
|
|
498
|
+
Check if the argument is of type 'variable'.
|
|
499
|
+
"""
|
|
500
|
+
return arg in self.script_dict and self.script_dict[arg].get("arg_types") == "variable"
|
|
501
|
+
|
|
502
|
+
def _write_to_file(self, script_path, run_name, exec_string):
|
|
503
|
+
"""
|
|
504
|
+
Write the compiled script to a file.
|
|
505
|
+
"""
|
|
506
|
+
with open(script_path + run_name + ".py", "w") as s:
|
|
507
|
+
if self.deck:
|
|
508
|
+
s.write(f"import {self.deck} as deck")
|
|
509
|
+
else:
|
|
510
|
+
s.write("deck = None")
|
|
511
|
+
s.write("\nimport time")
|
|
512
|
+
s.write(exec_string)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
if __name__ == "__main__":
|
|
516
|
+
a = Script()
|
|
517
|
+
|
|
518
|
+
print("")
|