ivoryos 0.1.12__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,33 +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"}},
243
282
  ],
244
283
  "repeat":
245
284
  [
246
285
  {"id": current_len + 1, "instrument": 'repeat', "action": "repeat",
247
- "args": '1' if statement == '' else statement, "return": '', "uuid": uid, "arg_types": "int"},
286
+ "args": {"statement": 1 if statement == '' else statement}, "return": '', "uuid": uid,
287
+ "arg_types": {"statement": "int"}},
248
288
  {"id": current_len + 2, "instrument": 'repeat', "action": 'endrepeat',
249
- "args": '', "return": '', "uuid": uid},
289
+ "args": {}, "return": '', "uuid": uid},
250
290
  ],
251
291
  }
252
292
  action_list = logic_dict[logic_type]
@@ -255,7 +295,9 @@ class Script(db.Model):
255
295
  self.update_time_stamp()
256
296
 
257
297
  def delete_action(self, id: int):
258
-
298
+ """
299
+ Delete the action by id (step number)
300
+ """
259
301
  uid = next((action['uuid'] for action in self.currently_editing_script if action['id'] == int(id)), None)
260
302
  id_to_be_removed = [action['id'] for action in self.currently_editing_script if action['uuid'] == uid]
261
303
  order = self.currently_editing_order
@@ -266,7 +308,11 @@ class Script(db.Model):
266
308
  self.update_time_stamp()
267
309
 
268
310
  def duplicate_action(self, id: int):
269
- 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)
270
316
  insert_id = action_to_duplicate.get("id")
271
317
  self.add_action(action_to_duplicate)
272
318
  # print(self.currently_editing_script)
@@ -319,7 +365,7 @@ class Script(db.Model):
319
365
  :return: list of variable that require input
320
366
  """
321
367
 
322
- 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'] == ''])
323
369
  output_str = "return {"
324
370
  for i in return_list:
325
371
  output_str += "'" + i + "':" + i + ","
@@ -327,15 +373,18 @@ class Script(db.Model):
327
373
  return output_str, return_list
328
374
 
329
375
  def finalize(self):
376
+ """finalize script, disable editing"""
330
377
  self.status = "finalized"
331
378
  self.update_time_stamp()
332
379
 
333
380
  def save_as(self, name):
381
+ """resave script, enable editing"""
334
382
  self.name = name
335
383
  self.status = "editing"
336
384
  self.update_time_stamp()
337
385
 
338
386
  def indent(self, unit=0):
387
+ """helper: create _ unit of indent in code string"""
339
388
  string = "\n"
340
389
  for _ in range(unit):
341
390
  string += "\t"
@@ -360,6 +409,26 @@ class Script(db.Model):
360
409
 
361
410
  return exec_string
362
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
+
363
432
  @staticmethod
364
433
  def validate_function_name(name):
365
434
  """Replace invalid characters with underscores"""
@@ -375,7 +444,8 @@ class Script(db.Model):
375
444
  """
376
445
  configure, config_type = self.config(stype)
377
446
 
378
- configure = [param + f":{param_type}" if not param_type == "any" else "" for param, param_type in config_type.items()]
447
+ configure = [param + f":{param_type}" if not param_type == "any" else "" for param, param_type in
448
+ config_type.items()]
379
449
 
380
450
  function_header = f"\n\ndef {run_name}_{stype}("
381
451
 
@@ -401,26 +471,43 @@ class Script(db.Model):
401
471
  body += self.indent(indent_unit) + return_str
402
472
  return body
403
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
+
404
489
  def _process_action(self, indent_unit, action, index, stype):
405
490
  """
406
491
  Process each action within the script dictionary.
407
492
  """
408
493
  instrument = action['instrument']
494
+ statement = action['args'].get('statement')
409
495
  args = self._process_args(action['args'])
496
+
410
497
  save_data = action['return']
411
498
  action_name = action['action']
412
499
  next_action = self._get_next_action(stype, index)
500
+ # print(args)
413
501
  if instrument == 'if':
414
- return self._process_if(indent_unit, action_name, args, next_action)
502
+ return self._process_if(indent_unit, action_name, statement, next_action)
415
503
  elif instrument == 'while':
416
- return self._process_while(indent_unit, action_name, args, next_action)
504
+ return self._process_while(indent_unit, action_name, statement, next_action)
417
505
  elif instrument == 'variable':
418
- return self.indent(indent_unit) + f"{action_name} = {args}", indent_unit
506
+ return self.indent(indent_unit) + f"{action_name} = {statement}", indent_unit
419
507
  elif instrument == 'wait':
420
- return f"{self.indent(indent_unit)}time.sleep({args})", indent_unit
508
+ return f"{self.indent(indent_unit)}time.sleep({statement})", indent_unit
421
509
  elif instrument == 'repeat':
422
- return self._process_repeat(indent_unit, action_name, args, next_action)
423
-
510
+ return self._process_repeat(indent_unit, action_name, statement, next_action)
424
511
  else:
425
512
  return self._process_instrument_action(indent_unit, instrument, action_name, args, save_data)
426
513
 
@@ -486,6 +573,7 @@ class Script(db.Model):
486
573
  """
487
574
  Process actions related to instruments.
488
575
  """
576
+
489
577
  if isinstance(args, dict):
490
578
  args_str = self._process_dict_args(args)
491
579
  single_line = f"{instrument}.{action}(**{args_str})"
@@ -507,8 +595,16 @@ class Script(db.Model):
507
595
  for arg in args:
508
596
  if isinstance(args[arg], str) and args[arg].startswith("#"):
509
597
  args_str = args_str.replace(f"'#{args[arg][1:]}'", args[arg][1:])
510
- elif self._is_variable(arg):
511
- 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])
512
608
  return args_str
513
609
 
514
610
  def _get_next_action(self, stype, index):