ivoryos 0.1.10__py3-none-any.whl → 0.1.18__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.

@@ -1,8 +1,11 @@
1
+ import ast
2
+ import builtins
1
3
  import json
2
4
  import keyword
3
5
  import re
4
6
  import uuid
5
7
  from datetime import datetime
8
+ from typing import Dict
6
9
 
7
10
  from flask_login import UserMixin
8
11
  from flask_sqlalchemy import SQLAlchemy
@@ -89,52 +92,58 @@ class Script(db.Model):
89
92
  return action
90
93
 
91
94
  def _convert_type(self, args, arg_types):
95
+ if arg_types in ["list", "tuple", "set"]:
96
+ try:
97
+ args = ast.literal_eval(args)
98
+ return args
99
+ except Exception:
100
+ pass
92
101
  if type(arg_types) is not list:
93
102
  arg_types = [arg_types]
94
103
  for arg_type in arg_types:
95
104
  try:
105
+ # print(arg_type)
96
106
  args = eval(f"{arg_type}('{args}')")
97
107
  return
98
108
  except Exception:
109
+
99
110
  pass
100
111
  raise TypeError(f"Input type error: cannot convert '{args}' to {arg_type}.")
101
112
 
102
113
  def update_by_uuid(self, uuid, args, output):
103
- bool_dict = {"True": True, "False": False}
104
114
  action = self.find_by_uuid(uuid)
115
+ if not action:
116
+ return
117
+ arg_types = action['arg_types']
105
118
  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
119
+ # pass
120
+ self.eval_list(args, arg_types)
122
121
  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)
122
+ pass
134
123
  action['args'] = args
135
- # print(action)
136
124
  action['return'] = output
137
125
 
126
+ @staticmethod
127
+ def eval_list(args, arg_types):
128
+ for arg in args:
129
+ arg_type = arg_types[arg]
130
+ if arg_type in ["list", "tuple", "set"]:
131
+
132
+ if type(arg) is str and not args[arg].startswith("#"):
133
+ # arg_types = arg_types[arg]
134
+ # if arg_types in ["list", "tuple", "set"]:
135
+ convert_type = getattr(builtins, arg_type) # Handle unknown types s
136
+ try:
137
+ output = ast.literal_eval(args[arg])
138
+ if type(output) not in [list, tuple, set]:
139
+ output = [output]
140
+ args[arg] = convert_type(output)
141
+ # return args
142
+ except ValueError:
143
+ _list = ''.join(args[arg]).split(',')
144
+ # convert_type = getattr(builtins, arg_types) # Handle unknown types s
145
+ args[arg] = convert_type([s.strip() for s in _list])
146
+
138
147
  @property
139
148
  def stypes(self):
140
149
  return list(self.script_dict.keys())
@@ -155,14 +164,6 @@ class Script(db.Model):
155
164
  def currently_editing_order(self, script):
156
165
  self.id_order[self.editing_type] = script
157
166
 
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
167
  def update_time_stamp(self):
167
168
  self.last_modified = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
168
169
 
@@ -204,15 +205,52 @@ class Script(db.Model):
204
205
  self.currently_editing_order.append(str(current_len + 1))
205
206
  self.update_time_stamp()
206
207
 
207
- def add_variable(self, statement, variable):
208
+ def add_variable(self, statement, variable, type):
209
+ variable = self.validate_function_name(variable)
210
+ convert_type = getattr(builtins, type)
211
+ statement = convert_type(statement)
208
212
  current_len = len(self.currently_editing_script)
209
213
  uid = uuid.uuid4().fields[-1]
210
214
  action_list = [{"id": current_len + 1, "instrument": 'variable', "action": variable,
211
- "args": 'None' if statement == '' else statement, "return": '', "uuid": uid, "arg_types": ''}]
215
+ "args": {"statement": 'None' if statement == '' else statement}, "return": '', "uuid": uid,
216
+ "arg_types": {"statement": type}}]
212
217
  self.currently_editing_script.extend(action_list)
213
218
  self.currently_editing_order.extend([str(current_len + i + 1) for i in range(len(action_list))])
214
219
  self.update_time_stamp()
215
220
 
221
+ def get_added_variables(self):
222
+ added_variables: Dict[str, str] = {action["action"]: action["arg_types"]["statement"] for action in
223
+ self.currently_editing_script if action["instrument"] == "variable"}
224
+
225
+ return added_variables
226
+
227
+ def get_output_variables(self):
228
+ output_variables: Dict[str, str] = {action["return"]: "function_output" for action in
229
+ self.currently_editing_script if action["return"]}
230
+
231
+ return output_variables
232
+
233
+ def get_variables(self):
234
+ output_variables: Dict[str, str] = self.get_output_variables()
235
+ added_variables = self.get_added_variables()
236
+ output_variables.update(added_variables)
237
+
238
+ return output_variables
239
+
240
+ def validate_variables(self, kwargs):
241
+ """
242
+ Validates the kwargs passed to the Script
243
+ """
244
+ output_variables: Dict[str, str] = self.get_variables()
245
+ # print(output_variables)
246
+ for key, value in kwargs.items():
247
+ if type(value) is str and value in output_variables:
248
+ var_type = output_variables[value]
249
+ kwargs[key] = {value: var_type}
250
+ if isinstance(value, str) and value.startswith("#"):
251
+ kwargs[key] = f"#{self.validate_function_name(value[1:])}"
252
+ return kwargs
253
+
216
254
  def add_logic_action(self, logic_type: str, statement):
217
255
  current_len = len(self.currently_editing_script)
218
256
  uid = uuid.uuid4().fields[-1]
@@ -220,26 +258,35 @@ class Script(db.Model):
220
258
  "if":
221
259
  [
222
260
  {"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": '',
261
+ "args": {"statement": 'True' if statement == '' else statement},
262
+ "return": '', "uuid": uid, "arg_types": {"statement": ''}},
263
+ {"id": current_len + 2, "instrument": 'if', "action": 'else', "args": {}, "return": '',
226
264
  "uuid": uid},
227
- {"id": current_len + 3, "instrument": 'if', "action": 'endif', "args": '', "return": '',
265
+ {"id": current_len + 3, "instrument": 'if', "action": 'endif', "args": {}, "return": '',
228
266
  "uuid": uid},
229
267
  ],
230
268
  "while":
231
269
  [
232
270
  {"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": '',
271
+ "args": {"statement": 'False' if statement == '' else statement}, "return": '', "uuid": uid,
272
+ "arg_types": {"statement": ''}},
273
+ {"id": current_len + 2, "instrument": 'while', "action": 'endwhile', "args": {}, "return": '',
235
274
  "uuid": uid},
236
275
  ],
237
276
 
238
277
  "wait":
239
278
  [
240
279
  {"id": current_len + 1, "instrument": 'wait', "action": "wait",
241
- "args": '0' if statement == '' else statement,
242
- "return": '', "uuid": uid, "arg_types": "float"},
280
+ "args": {"statement": 1 if statement == '' else statement},
281
+ "return": '', "uuid": uid, "arg_types": {"statement": "float"}},
282
+ ],
283
+ "repeat":
284
+ [
285
+ {"id": current_len + 1, "instrument": 'repeat', "action": "repeat",
286
+ "args": {"statement": 1 if statement == '' else statement}, "return": '', "uuid": uid,
287
+ "arg_types": {"statement": "int"}},
288
+ {"id": current_len + 2, "instrument": 'repeat', "action": 'endrepeat',
289
+ "args": {}, "return": '', "uuid": uid},
243
290
  ],
244
291
  }
245
292
  action_list = logic_dict[logic_type]
@@ -248,7 +295,9 @@ class Script(db.Model):
248
295
  self.update_time_stamp()
249
296
 
250
297
  def delete_action(self, id: int):
251
-
298
+ """
299
+ Delete the action by id (step number)
300
+ """
252
301
  uid = next((action['uuid'] for action in self.currently_editing_script if action['id'] == int(id)), None)
253
302
  id_to_be_removed = [action['id'] for action in self.currently_editing_script if action['uuid'] == uid]
254
303
  order = self.currently_editing_order
@@ -259,7 +308,11 @@ class Script(db.Model):
259
308
  self.update_time_stamp()
260
309
 
261
310
  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)
311
+ """
312
+ duplicate action by id (step number), available only for non logic actions
313
+ """
314
+ action_to_duplicate = next((action for action in self.currently_editing_script if action['id'] == int(id)),
315
+ None)
263
316
  insert_id = action_to_duplicate.get("id")
264
317
  self.add_action(action_to_duplicate)
265
318
  # print(self.currently_editing_script)
@@ -312,7 +365,7 @@ class Script(db.Model):
312
365
  :return: list of variable that require input
313
366
  """
314
367
 
315
- return_list = [action['return'] for action in self.script_dict['script'] if not action['return'] == '']
368
+ return_list = set([action['return'] for action in self.script_dict['script'] if not action['return'] == ''])
316
369
  output_str = "return {"
317
370
  for i in return_list:
318
371
  output_str += "'" + i + "':" + i + ","
@@ -320,15 +373,18 @@ class Script(db.Model):
320
373
  return output_str, return_list
321
374
 
322
375
  def finalize(self):
376
+ """finalize script, disable editing"""
323
377
  self.status = "finalized"
324
378
  self.update_time_stamp()
325
379
 
326
380
  def save_as(self, name):
381
+ """resave script, enable editing"""
327
382
  self.name = name
328
383
  self.status = "editing"
329
384
  self.update_time_stamp()
330
385
 
331
386
  def indent(self, unit=0):
387
+ """helper: create _ unit of indent in code string"""
332
388
  string = "\n"
333
389
  for _ in range(unit):
334
390
  string += "\t"
@@ -353,6 +409,26 @@ class Script(db.Model):
353
409
 
354
410
  return exec_string
355
411
 
412
+ def compile_steps(self, script_path=None):
413
+ """
414
+ Compile the current script to steps.
415
+ :return: {"prep":[], "script":[], "cleanup":[],}.
416
+ """
417
+ self.sort_actions()
418
+ run_name = self.name if self.name else "untitled"
419
+ run_name = self.validate_function_name(run_name)
420
+ exec_string = ''
421
+ steps = {}
422
+ for i in self.stypes:
423
+ # exec_string += self._generate_function_header(run_name, i)
424
+ exec_string += self._generate_function_body(i)
425
+
426
+ if script_path:
427
+ self._write_to_file(script_path, run_name, exec_string)
428
+
429
+ return exec_string
430
+
431
+
356
432
  @staticmethod
357
433
  def validate_function_name(name):
358
434
  """Replace invalid characters with underscores"""
@@ -366,11 +442,15 @@ class Script(db.Model):
366
442
  """
367
443
  Generate the function header.
368
444
  """
369
- configure, _ = self.config(stype)
445
+ configure, config_type = self.config(stype)
446
+
447
+ configure = [param + f":{param_type}" if not param_type == "any" else "" for param, param_type in
448
+ config_type.items()]
449
+
370
450
  function_header = f"\n\ndef {run_name}_{stype}("
371
451
 
372
452
  if stype == "script":
373
- function_header += ",".join(configure)
453
+ function_header += ", ".join(configure)
374
454
 
375
455
  function_header += "):"
376
456
  function_header += self.indent(1) + f"global {run_name}_{stype}"
@@ -389,26 +469,45 @@ class Script(db.Model):
389
469
  return_str, return_list = self.config_return()
390
470
  if return_list and stype == "script":
391
471
  body += self.indent(indent_unit) + return_str
392
-
393
472
  return body
394
473
 
474
+ # def _generate_function_body(self, stype):
475
+ # """
476
+ # Generate the function body for each type in stypes.
477
+ # """
478
+ # steps = []
479
+ # indent_unit = 1
480
+ #
481
+ # for index, action in enumerate(self.script_dict[stype]):
482
+ # text, indent_unit = self._process_action(indent_unit, action, index, stype)
483
+ # body += text
484
+ # return_str, return_list = self.config_return()
485
+ # if return_list and stype == "script":
486
+ # body += self.indent(indent_unit) + return_str
487
+ # return body
488
+
395
489
  def _process_action(self, indent_unit, action, index, stype):
396
490
  """
397
491
  Process each action within the script dictionary.
398
492
  """
399
493
  instrument = action['instrument']
494
+ statement = action['args'].get('statement')
400
495
  args = self._process_args(action['args'])
496
+
401
497
  save_data = action['return']
402
498
  action_name = action['action']
403
499
  next_action = self._get_next_action(stype, index)
500
+ # print(args)
404
501
  if instrument == 'if':
405
- return self._process_if(indent_unit, action_name, args, next_action)
502
+ return self._process_if(indent_unit, action_name, statement, next_action)
406
503
  elif instrument == 'while':
407
- return self._process_while(indent_unit, action_name, args, next_action)
504
+ return self._process_while(indent_unit, action_name, statement, next_action)
408
505
  elif instrument == 'variable':
409
- return self.indent(indent_unit) + f"{action_name} = {args}", indent_unit
506
+ return self.indent(indent_unit) + f"{action_name} = {statement}", indent_unit
410
507
  elif instrument == 'wait':
411
- return f"{self.indent(indent_unit)}time.sleep({args})", indent_unit
508
+ return f"{self.indent(indent_unit)}time.sleep({statement})", indent_unit
509
+ elif instrument == 'repeat':
510
+ return self._process_repeat(indent_unit, action_name, statement, next_action)
412
511
  else:
413
512
  return self._process_instrument_action(indent_unit, instrument, action_name, args, save_data)
414
513
 
@@ -456,10 +555,25 @@ class Script(db.Model):
456
555
  indent_unit -= 1
457
556
  return exec_string, indent_unit
458
557
 
558
+ def _process_repeat(self, indent_unit, action, args, next_action):
559
+ """
560
+ Process 'while' and 'endwhile' actions.
561
+ """
562
+ exec_string = ""
563
+ if action == 'repeat':
564
+ exec_string += self.indent(indent_unit) + f"for _ in range({args}):"
565
+ indent_unit += 1
566
+ if next_action and next_action['instrument'] == 'repeat':
567
+ exec_string += self.indent(indent_unit) + "pass"
568
+ elif action == 'endrepeat':
569
+ indent_unit -= 1
570
+ return exec_string, indent_unit
571
+
459
572
  def _process_instrument_action(self, indent_unit, instrument, action, args, save_data):
460
573
  """
461
574
  Process actions related to instruments.
462
575
  """
576
+
463
577
  if isinstance(args, dict):
464
578
  args_str = self._process_dict_args(args)
465
579
  single_line = f"{instrument}.{action}(**{args_str})"
@@ -481,8 +595,16 @@ class Script(db.Model):
481
595
  for arg in args:
482
596
  if isinstance(args[arg], str) and args[arg].startswith("#"):
483
597
  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])
598
+ elif isinstance(args[arg], dict):
599
+ # print(args[arg])
600
+ variables = self.get_variables()
601
+ value = next(iter(args[arg]))
602
+ if value not in variables:
603
+ raise ValueError(f"Variable ({value}) is not defined.")
604
+ args_str = args_str.replace(f"{args[arg]}", next(iter(args[arg])))
605
+ # elif self._is_variable(arg):
606
+ # print("is variable")
607
+ # args_str = args_str.replace(f"'{args[arg]}'", args[arg])
486
608
  return args_str
487
609
 
488
610
  def _get_next_action(self, stype, index):