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.

ivoryos/utils/form.py CHANGED
@@ -1,3 +1,4 @@
1
+ from wtforms.fields.choices import SelectField
1
2
  from wtforms.fields.core import Field
2
3
  from wtforms.validators import InputRequired
3
4
  from wtforms.widgets.core import TextInput
@@ -8,16 +9,15 @@ import inspect
8
9
 
9
10
 
10
11
  def find_variable(data, script):
11
- # TODO: needs to check for valid order of variables, important when editting
12
- added_variables: list[dict[str, str]] = [action for action in script.currently_editing_script if
13
- action["instrument"] == "variable"
14
- # or action["return"] # TODO find returns
15
- ]
16
- for added_variable in added_variables:
17
- if added_variable["action"] == data:
18
- return data, added_variable["args"]
19
- # if added_variable["return"] == data:
20
- # return data, None
12
+ """
13
+ find user defined variables and return values in the script:Script
14
+ :param data: string of input variable name
15
+ :param script:Script object
16
+ """
17
+ variables: dict[str, str] = script.get_variables()
18
+ for variable_name, variable_type in variables.items():
19
+ if variable_name == data:
20
+ return data, variable_type # variable_type int float str or "function_output"
21
21
  return None, None
22
22
 
23
23
 
@@ -36,7 +36,7 @@ class VariableOrStringField(Field):
36
36
 
37
37
  def _value(self):
38
38
  if self.script:
39
- variable, value = find_variable(self.data, self.script)
39
+ variable, variable_type = find_variable(self.data, self.script)
40
40
  if variable:
41
41
  return variable
42
42
 
@@ -52,7 +52,7 @@ class VariableOrFloatField(Field):
52
52
 
53
53
  def _value(self):
54
54
  if self.script:
55
- variable, value = find_variable(self.data, self.script)
55
+ variable, variable_type = find_variable(self.data, self.script)
56
56
  if variable:
57
57
  return variable
58
58
 
@@ -73,14 +73,15 @@ class VariableOrFloatField(Field):
73
73
  try:
74
74
  if self.script:
75
75
  try:
76
- variable, value = find_variable(valuelist[0], self.script)
76
+ variable, variable_type = find_variable(valuelist[0], self.script)
77
77
  if variable:
78
- float(value)
79
- self.data = str(variable)
78
+ if not variable_type == "function_output":
79
+ if variable_type not in ["float", "int"]:
80
+ raise ValueError("Variable is not a valid float")
81
+ self.data = variable
80
82
  return
81
83
  except ValueError:
82
84
  pass
83
-
84
85
  self.data = float(valuelist[0])
85
86
  except ValueError as exc:
86
87
  self.data = None
@@ -99,7 +100,7 @@ class VariableOrIntField(Field):
99
100
 
100
101
  def _value(self):
101
102
  if self.script:
102
- variable, value = find_variable(self.data, self.script)
103
+ variable, variable_type = find_variable(self.data, self.script)
103
104
  if variable:
104
105
  return variable
105
106
 
@@ -109,34 +110,16 @@ class VariableOrIntField(Field):
109
110
  return str(self.data)
110
111
  return ""
111
112
 
112
- # def process_data(self, value):
113
- #
114
- # if self.script:
115
- # variable, var_value = find_variable(value, self.script)
116
- # if variable:
117
- # try:
118
- # int(var_value)
119
- # self.data = str(variable)
120
- # return
121
- # except ValueError:
122
- # pass
123
- # if value is None or value is unset_value:
124
- # self.data = None
125
- # return
126
- # try:
127
- # self.data = int(value)
128
- # except (ValueError, TypeError) as exc:
129
- # self.data = None
130
- # raise ValueError(self.gettext("Not a valid integer value.")) from exc
131
-
132
113
  def process_formdata(self, valuelist):
133
114
  if not valuelist:
134
115
  return
135
116
  if self.script:
136
- variable, var_value = find_variable(valuelist[0], self.script)
117
+ variable, variable_type = find_variable(valuelist[0], self.script)
137
118
  if variable:
138
119
  try:
139
- int(var_value)
120
+ if not variable_type == "function_output":
121
+ if not variable_type == "int":
122
+ raise ValueError("Not a valid integer value")
140
123
  self.data = str(variable)
141
124
  return
142
125
  except ValueError:
@@ -155,6 +138,7 @@ class VariableOrIntField(Field):
155
138
 
156
139
  class VariableOrBoolField(BooleanField):
157
140
  widget = TextInput()
141
+ false_values = (False, "false", "", "False", "f", "F")
158
142
 
159
143
  def __init__(self, label='', validators=None, script=None, **kwargs):
160
144
  super(VariableOrBoolField, self).__init__(label, validators, **kwargs)
@@ -163,30 +147,34 @@ class VariableOrBoolField(BooleanField):
163
147
  def process_data(self, value):
164
148
 
165
149
  if self.script:
166
- variable, var_value = find_variable(value, self.script)
150
+ variable, variable_type = find_variable(value, self.script)
167
151
  if variable:
168
- try:
169
- bool(var_value)
170
- return variable
171
- except ValueError:
172
- return
152
+ if not variable_type == "function_output":
153
+ raise ValueError("Not accepting boolean variables")
154
+ return variable
173
155
 
174
156
  self.data = bool(value)
175
157
 
176
158
  def process_formdata(self, valuelist):
177
- if not valuelist or type(valuelist) is list and valuelist[0] == '':
159
+ # todo
160
+ # print(valuelist)
161
+ if not valuelist or not type(valuelist) is list:
178
162
  self.data = False
179
- elif valuelist and valuelist[0].startswith("#"):
180
- if not self.script.editing_type == "script":
181
- raise ValueError(self.gettext("Variable is not supported in prep/cleanup"))
182
- self.data = valuelist[0]
183
163
  else:
184
- self.data = True
164
+ value = valuelist[0] if type(valuelist) is list else valuelist
165
+ if value.startswith("#"):
166
+ if not self.script.editing_type == "script":
167
+ raise ValueError(self.gettext("Variable is not supported in prep/cleanup"))
168
+ self.data = valuelist[0]
169
+ elif value in self.false_values:
170
+ self.data = False
171
+ else:
172
+ self.data = True
185
173
 
186
174
  def _value(self):
187
175
 
188
176
  if self.script:
189
- variable, value = find_variable(self.raw_data, self.script)
177
+ variable, variable_type = find_variable(self.raw_data, self.script)
190
178
  if variable:
191
179
  return variable
192
180
 
@@ -202,7 +190,15 @@ def format_name(name):
202
190
  return text.capitalize()
203
191
 
204
192
 
205
- def create_form_for_method(method, method_name, autofill, script=None, design=True):
193
+ def create_form_for_method(method, autofill, script=None, design=True):
194
+ """
195
+ Create forms for each method or signature
196
+ :param method: dict(docstring, signature)
197
+ :param autofill:bool if autofill is enabled
198
+ :param script:Script object
199
+ :param design: if design is enabled
200
+ """
201
+
206
202
  class DynamicForm(FlaskForm):
207
203
  pass
208
204
 
@@ -238,48 +234,141 @@ def create_form_for_method(method, method_name, autofill, script=None, design=Tr
238
234
  return DynamicForm
239
235
 
240
236
 
241
- # Create forms for each method in DummySDLDeck
242
- def create_add_form(attr, attr_name, autofill, script=None, design=True):
243
- dynamic_form = create_form_for_method(attr, attr_name, autofill, script, design)
237
+ def create_add_form(attr, attr_name, autofill: bool, script=None, design: bool = True):
238
+ """
239
+ Create forms for each method or signature
240
+ :param attr: dict(docstring, signature)
241
+ :param attr_name: method name
242
+ :param autofill:bool if autofill is enabled
243
+ :param script:Script object
244
+ :param design: if design is enabled. Design allows string input for parameter names ("#param") for all fields
245
+ """
246
+ signature = attr.get('signature', {})
247
+ docstring = attr.get('docstring', "")
248
+ dynamic_form = create_form_for_method(signature, autofill, script, design)
244
249
  if design:
245
250
  return_value = StringField(label='Save value as', render_kw={"placeholder": "Optional"})
246
251
  setattr(dynamic_form, 'return', return_value)
247
- hidden_method_name = HiddenField(name=f'hidden_name', render_kw={"value": f'{attr_name}'})
252
+ hidden_method_name = HiddenField(name=f'hidden_name', description=docstring, render_kw={"value": f'{attr_name}'})
248
253
  setattr(dynamic_form, 'hidden_name', hidden_method_name)
249
254
  return dynamic_form
250
255
 
251
256
 
252
- def create_form_from_module(sdl_module, autofill: bool, script=None, design=True):
253
- # sdl_deck = DummySDLDeck(DummyPump("COM1"), DummyBalance("COM2"))
257
+ def create_form_from_module(sdl_module, autofill: bool = False, script=None, design: bool = False):
258
+ """
259
+ Create forms for each method, used for control routes
260
+ :param sdl_module: method module
261
+ :param autofill:bool if autofill is enabled
262
+ :param script:Script object
263
+ :param design: if design is enabled
264
+ """
254
265
  method_forms = {}
255
266
  for attr_name in dir(sdl_module):
256
- attr = getattr(sdl_module, attr_name)
257
- if inspect.ismethod(attr) and not attr_name.startswith('_'):
267
+ method = getattr(sdl_module, attr_name)
268
+ if inspect.ismethod(method) and not attr_name.startswith('_'):
269
+ signature = inspect.signature(method)
270
+ docstring = inspect.getdoc(method)
271
+ attr = dict(signature=signature, docstring=docstring)
258
272
  form_class = create_add_form(attr, attr_name, autofill, script, design)
259
273
  method_forms[attr_name] = form_class()
260
274
  return method_forms
261
275
 
262
276
 
263
277
  def create_form_from_pseudo(pseudo: dict, autofill: bool, script=None, design=True):
264
- '''{'dose_liquid': < Signature(amount_in_ml: float, rate_ml_per_minute: float) >}'''
278
+ """
279
+ Create forms for pseudo method, used for design routes
280
+ :param pseudo:{'dose_liquid': {
281
+ "docstring": "some docstring",
282
+ "signature": Signature(amount_in_ml: float, rate_ml_per_minute: float) }
283
+ }
284
+ :param autofill:bool if autofill is enabled
285
+ :param script:Script object
286
+ :param design: if design is enabled
287
+ """
265
288
  method_forms = {}
266
289
  for attr_name, signature in pseudo.items():
290
+ # signature = info.get('signature', {})
267
291
  form_class = create_add_form(signature, attr_name, autofill, script, design)
268
292
  method_forms[attr_name] = form_class()
269
293
  return method_forms
270
294
 
271
295
 
272
- def create_builtin_form(logic_type):
296
+ def create_form_from_action(action: dict, script=None, design=True):
297
+ '''
298
+ Create forms for single action, used for design routes
299
+ :param action: {'action': 'dose_solid', 'arg_types': {'amount_in_mg': 'float', 'bring_in': 'bool'},
300
+ 'args': {'amount_in_mg': 5.0, 'bring_in': False}, 'id': 9,
301
+ 'instrument': 'deck.sdl', 'return': '', 'uuid': 266929188668995}
302
+ :param script:Script object
303
+ :param design: if design is enabled
304
+
305
+ '''
306
+
307
+ arg_types = action.get("arg_types", {})
308
+ args = action.get("args", {})
309
+ save_as = action.get("return")
310
+
311
+ class DynamicForm(FlaskForm):
312
+ pass
313
+
314
+ annotation_mapping = {
315
+ "int": (VariableOrIntField if design else IntegerField, 'Enter integer value'),
316
+ "float": (VariableOrFloatField if design else FloatField, 'Enter numeric value'),
317
+ "str": (VariableOrStringField if design else StringField, 'Enter text'),
318
+ "bool": (VariableOrBoolField if design else BooleanField, 'Empty for false')
319
+ }
320
+
321
+ for name, param_type in arg_types.items():
322
+ formatted_param_name = format_name(name)
323
+ value = args.get(name, "")
324
+ if type(value) is dict:
325
+ value = next(iter(value))
326
+ field_kwargs = {
327
+ "label": formatted_param_name,
328
+ "default": f'{value}',
329
+ "validators": [InputRequired()],
330
+ **({"script": script})
331
+ }
332
+ param_type = param_type if type(param_type) is str else f"{param_type}"
333
+ field_class, placeholder_text = annotation_mapping.get(
334
+ param_type,
335
+ (VariableOrStringField if design else StringField, f'Enter {param_type} value')
336
+ )
337
+ render_kwargs = {"placeholder": placeholder_text}
338
+
339
+ # Create the field with additional rendering kwargs for placeholder text
340
+ field = field_class(**field_kwargs, render_kw=render_kwargs)
341
+ setattr(DynamicForm, name, field)
342
+
343
+ if design:
344
+ return_value = StringField(label='Save value as', default=f"{save_as}", render_kw={"placeholder": "Optional"})
345
+ setattr(DynamicForm, 'return', return_value)
346
+ return DynamicForm()
347
+
348
+
349
+ def create_builtin_form(logic_type, script):
350
+ """
351
+ Create a builtin form {if, while, variable, repeat, wait}
352
+ """
273
353
  class BuiltinFunctionForm(FlaskForm):
274
354
  pass
275
355
 
276
- placeholder_text = f'Enter numbers' if logic_type == 'wait' else f'Enter statement'
277
- description_text = f'Your variable can be numbers, boolean (True or False) or text ("text")' if logic_type == 'variable' else ''
278
- field_class = FloatField if logic_type == 'wait' else StringField # Default to StringField as a fallback
356
+ placeholder_text = {
357
+ 'wait': 'Enter second',
358
+ 'repeat': 'Enter an integer'
359
+ }.get(logic_type, 'Enter statement')
360
+ description_text = {
361
+ 'variable': 'Your variable can be numbers, boolean (True or False) or text ("text")',
362
+ }.get(logic_type, '')
363
+ field_class = {
364
+ 'wait': VariableOrFloatField,
365
+ 'repeat': VariableOrIntField
366
+ }.get(logic_type, VariableOrStringField) # Default to StringField as a fallback
279
367
  field_kwargs = {
280
368
  "label": f'statement',
281
369
  "validators": [InputRequired()] if logic_type in ['wait', "variable"] else [],
282
370
  "description": description_text,
371
+ "script": script
283
372
  }
284
373
  render_kwargs = {"placeholder": placeholder_text}
285
374
  field = field_class(**field_kwargs, render_kw=render_kwargs)
@@ -288,29 +377,63 @@ def create_builtin_form(logic_type):
288
377
  variable_field = StringField(label=f'variable', validators=[InputRequired()],
289
378
  description="Your variable name cannot include space",
290
379
  render_kw=render_kwargs)
380
+ type_field = SelectField(
381
+ 'Select Input Type',
382
+ choices=[('int', 'Integer'), ('float', 'Float'), ('str', 'String')],
383
+ default='str' # Optional default value
384
+ )
291
385
  setattr(BuiltinFunctionForm, "variable", variable_field)
386
+ setattr(BuiltinFunctionForm, "type", type_field)
292
387
  hidden_field = HiddenField(name=f'builtin_name', render_kw={"value": f'{logic_type}'})
293
388
  setattr(BuiltinFunctionForm, "builtin_name", hidden_field)
294
389
  return BuiltinFunctionForm()
295
390
 
296
391
 
297
- def create_action_button(s: dict):
298
- style = ""
299
- if s['instrument'] in ['if', 'while']:
300
- text = f"{s['action']} {s['args']}"
301
- style = "background-color: tomato"
302
- elif s['instrument'] == 'variable':
303
- text = f"{s['action']} = {s['args']}"
392
+ def create_action_button(script, stype=None):
393
+ """
394
+ Creates action buttons for design route (design canvas)
395
+ :param script: Script object
396
+ :param stype: script type (script, prep, cleanup)
397
+ """
398
+ stype = stype or script.editing_type
399
+ variables = script.get_variables()
400
+ return [_action_button(i, variables) for i in script.get_script(stype)]
401
+
402
+
403
+ def _action_button(action: dict, variables: dict):
404
+ """
405
+ Creates action button for one action
406
+ :param action: Action dict
407
+ :param variables: created variable dict
408
+ """
409
+ style = {
410
+ "repeat": "background-color: lightsteelblue",
411
+ "if": "background-color: salmon",
412
+ "while": "background-color: salmon",
413
+ }.get(action['instrument'], "")
414
+
415
+ if action['instrument'] in ['if', 'while', 'repeat']:
416
+ text = f"{action['action']} {action['args'].get('statement', '')}"
417
+ elif action['instrument'] == 'variable':
418
+ text = f"{action['action']} = {action['args'].get('statement')}"
304
419
  else:
305
420
  # regular action button
306
- prefix = f"{s['return']} = " if s['return'] else ""
307
- action_text = f"{s['instrument'].split('.')[-1] if s['instrument'].startswith('deck') else s['instrument']}.{s['action']}"
421
+ prefix = f"{action['return']} = " if action['return'] else ""
422
+ action_text = f"{action['instrument'].split('.')[-1] if action['instrument'].startswith('deck') else action['instrument']}.{action['action']}"
308
423
  arg_string = ""
309
- if s['args']:
310
- if type(s['args']) is dict:
311
- arg_string = "(" + ", ".join([f"{k} = {v}" for k, v in s['args'].items()]) + ")"
424
+ if action['args']:
425
+ if type(action['args']) is dict:
426
+ arg_list = []
427
+ for k, v in action['args'].items():
428
+ if isinstance(v, dict):
429
+ value = next(iter(v)) # Extract the first key if it's a dict
430
+ # show warning color for variable calling when there is no definition
431
+ style = "background-color: khaki" if value not in variables.keys() else ""
432
+ else:
433
+ value = v # Keep the original value if not a dict
434
+ arg_list.append(f"{k} = {value}") # Format the key-value pair
435
+ arg_string = "(" + ", ".join(arg_list) + ")"
312
436
  else:
313
- arg_string = f"= {s['args']}"
314
-
437
+ arg_string = f"= {action['args']}"
315
438
  text = f"{prefix}{action_text} {arg_string}"
316
- return dict(label=text, style=style, uuid=s["uuid"], id=s["id"], instrument=s['instrument'])
439
+ return dict(label=text, style=style, uuid=action["uuid"], id=action["id"], instrument=action['instrument'])
@@ -80,14 +80,17 @@ class ScriptRunner:
80
80
 
81
81
  def _run_actions(self, actions, section_name="", run_name=None, logger=None):
82
82
  logger.info(f'Executing {section_name} steps') if actions else logger.info(f'No {section_name} steps')
83
- for action in actions:
84
- if self.stop_event.is_set():
85
- logger.info(f"Stopping execution during {section_name} section.")
86
- break
87
- logger.info(f'Executing {action.get("action", "")} action')
88
- fname = f"{run_name}_{section_name}"
89
- function = self.globals_dict[fname]
90
- function()
83
+ if self.stop_event.is_set():
84
+ logger.info(f"Stopping execution during {section_name} section.")
85
+ return
86
+ # for action in actions:
87
+ # if self.stop_event.is_set():
88
+ # logger.info(f"Stopping execution during {section_name} section.")
89
+ # break
90
+ # logger.info(f'Executing {action.get("action", "")} action')
91
+ fname = f"{run_name}_{section_name}"
92
+ function = self.globals_dict[fname]
93
+ function()
91
94
 
92
95
  def _run_config_section(self, config, arg_type, output_list, return_list, run_name, logger, socketio):
93
96
  compiled = True
@@ -133,7 +136,8 @@ class ScriptRunner:
133
136
  function = self.globals_dict[fname]
134
137
  output = function(**parameters)
135
138
  # output = eval(f"{run_name}_script(**{parameters})")
136
- ax_client.complete_trial(trial_index=trial_index, raw_data=output)
139
+ _output = output.copy()
140
+ ax_client.complete_trial(trial_index=trial_index, raw_data=_output)
137
141
  output.update(parameters)
138
142
  except Exception as e:
139
143
  logger.info(f'Optimization error: {e}')
ivoryos/utils/utils.py CHANGED
@@ -6,7 +6,7 @@ import os
6
6
  import pickle
7
7
  import subprocess
8
8
  import sys
9
- from typing import Optional, Dict, Tuple
9
+ from collections import Counter
10
10
 
11
11
  from flask import session
12
12
  from flask_socketio import SocketIO
@@ -83,7 +83,6 @@ def available_pseudo_deck(path):
83
83
  """
84
84
  load pseudo deck (snapshot) from connection history
85
85
  """
86
- import os
87
86
  return os.listdir(path)
88
87
 
89
88
 
@@ -102,20 +101,9 @@ def _inspect_class(class_object=None, debug=False):
102
101
  if not function.startswith(under_score) and not function.isupper():
103
102
  try:
104
103
  annotation = inspect.signature(method)
105
- # if doc_string:
106
104
  docstring = inspect.getdoc(method)
107
105
  functions[function] = dict(signature=annotation, docstring=docstring)
108
106
 
109
- # handle getter setters todo
110
- # if callable(att):
111
- # functions[function] = inspect.signature(att)
112
- # else:
113
- # att = getattr(class_object.__class__, function)
114
- # if isinstance(att, property) and att.fset is not None:
115
- # setter = att.fset.__annotations__
116
- # setter.pop('return', None)
117
- # if setter:
118
- # functions[function] = setter
119
107
  except Exception:
120
108
  pass
121
109
  return functions
@@ -131,36 +119,22 @@ def _get_type_from_parameters(arg, parameters):
131
119
  if annotation is not inspect._empty:
132
120
  # print(p[arg].annotation)
133
121
  if annotation.__module__ == 'typing':
134
- if hasattr(annotation, '_name') and annotation._name in ["Optional", "Union"]:
135
- # print(p[arg].annotation.__args__)
136
- arg_type = [i.__name__ for i in annotation.__args__]
137
- elif hasattr(annotation, '__origin__'):
138
- arg_type = annotation.__origin__.__name__
139
- else:
140
- # TODO
141
- pass
122
+
123
+ if hasattr(annotation, '__origin__'):
124
+ origin = annotation.__origin__
125
+ if hasattr(origin, '_name') and origin._name in ["Optional", "Union"]:
126
+ arg_type = [i.__name__ for i in annotation.__args__]
127
+ elif hasattr(origin, '__name__'):
128
+ arg_type = origin.__name__
129
+ # todo other types
130
+ elif annotation.__module__ == 'types':
131
+ arg_type = [i.__name__ for i in annotation.__args__]
132
+
142
133
  else:
143
134
  arg_type = annotation.__name__
144
135
  return arg_type
145
136
 
146
137
 
147
- def find_variable_in_script(script: Script, args: Dict[str, str]) -> Optional[Tuple[Dict[str, str], Dict[str, str]]]:
148
- # TODO: need to search for if the variable exists
149
- added_variables: list[Dict[str, str]] = [action for action in script.currently_editing_script if
150
- action["instrument"] == "variable"]
151
-
152
- possible_variable_arguments = {}
153
- possible_variable_types = {}
154
-
155
- for arg_name, arg_val in args.items():
156
- for added_variable in added_variables:
157
- if added_variable["action"] == arg_val:
158
- possible_variable_arguments[arg_name] = added_variable["action"]
159
- possible_variable_types[arg_name] = "variable"
160
-
161
- return possible_variable_arguments, possible_variable_types
162
-
163
-
164
138
  def _convert_by_str(args, arg_types):
165
139
  """
166
140
  Converts a value to type through eval(f'{type}("{args}")')
@@ -200,9 +174,6 @@ def convert_config_type(args, arg_types, is_class: bool = False):
200
174
  """
201
175
  Converts an argument from str to an arg type
202
176
  """
203
- bool_dict = {"True": True, "False": False}
204
- # print(args, arg_types)
205
- # print(globals())
206
177
  if args:
207
178
  for arg in args:
208
179
  if arg not in arg_types.keys():
@@ -317,8 +288,9 @@ def ax_wrapper(data: dict, arg_types:list):
317
288
  is_min = True if value == "minimize" else False
318
289
 
319
290
  threshold = None if f"{obj_name}_threshold" not in data else data[f"{obj_name}_threshold"]
320
- properties = ObjectiveProperties(minimize=is_min, threshold=threshold)
291
+ properties = ObjectiveProperties(minimize=is_min)
321
292
  objectives[obj_name] = properties
293
+
322
294
  return parameter, objectives
323
295
 
324
296
 
@@ -331,13 +303,15 @@ def ax_initiation(data, arg_types):
331
303
  parameter, objectives = ax_wrapper(data, arg_types)
332
304
  from ax.service.ax_client import AxClient
333
305
  ax_client = AxClient()
334
- ax_client.create_experiment(parameter, objectives)
306
+ ax_client.create_experiment(parameter, objectives=objectives)
335
307
  return ax_client
336
308
 
337
309
 
338
310
  def get_arg_type(args, parameters):
311
+ """get argument type from signature"""
339
312
  arg_types = {}
340
313
  # print(args, parameters)
314
+ parameters = parameters.get("signature")
341
315
  if args:
342
316
  for arg in args:
343
317
  arg_types[arg] = _get_type_from_parameters(arg, parameters)
@@ -423,3 +397,13 @@ def load_deck(pkl_name: str):
423
397
  return pseudo_deck
424
398
  except FileNotFoundError:
425
399
  return None
400
+
401
+
402
+ def check_config_duplicate(config):
403
+ """
404
+ Checks if the config entry has any duplicate
405
+ :param config: [{"arg": 1}, {"arg": 1}, {"arg": 1}]
406
+ :return: [True, False]
407
+ """
408
+ hashable_data = [tuple(sorted(d.items())) for d in config]
409
+ return any(count > 1 for count in Counter(hashable_data).values())
ivoryos/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.10"
1
+ __version__ = "0.1.18"
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ivoryos
3
- Version: 0.1.10
3
+ Version: 0.1.18
4
4
  Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
5
5
  Home-page: https://gitlab.com/heingroup/ivoryos
6
6
  Author: Ivory Zhang
7
7
  Author-email: ivoryzhang@chem.ubc.ca
8
8
  License: MIT
9
+ Platform: UNKNOWN
9
10
  Description-Content-Type: text/markdown
10
11
  License-File: LICENSE
11
12
  Requires-Dist: bcrypt
@@ -168,3 +169,5 @@ https://youtu.be/dFfJv9I2-1g
168
169
  Ivory Zhang, Lucy Hao
169
170
 
170
171
  Authors acknowledge all former and current Hein Lab members for their valuable suggestions.
172
+
173
+