pychemstation 0.10.3__py3-none-any.whl → 0.10.5__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.
Files changed (31) hide show
  1. pychemstation/__init__.py +1 -1
  2. pychemstation/analysis/__init__.py +1 -6
  3. pychemstation/analysis/base_spectrum.py +7 -7
  4. pychemstation/{utils → analysis}/chromatogram.py +24 -4
  5. pychemstation/analysis/process_report.py +189 -90
  6. pychemstation/control/__init__.py +5 -2
  7. pychemstation/control/controllers/__init__.py +2 -7
  8. pychemstation/control/controllers/comm.py +56 -32
  9. pychemstation/control/controllers/devices/device.py +59 -24
  10. pychemstation/control/controllers/devices/injector.py +33 -10
  11. pychemstation/control/controllers/tables/__init__.py +4 -0
  12. pychemstation/control/controllers/tables/method.py +241 -151
  13. pychemstation/control/controllers/tables/sequence.py +226 -107
  14. pychemstation/control/controllers/tables/table.py +216 -132
  15. pychemstation/control/hplc.py +89 -75
  16. pychemstation/generated/__init__.py +0 -2
  17. pychemstation/generated/pump_method.py +15 -19
  18. pychemstation/utils/injector_types.py +1 -1
  19. pychemstation/utils/macro.py +11 -10
  20. pychemstation/utils/method_types.py +2 -1
  21. pychemstation/utils/parsing.py +0 -11
  22. pychemstation/utils/sequence_types.py +2 -3
  23. pychemstation/utils/spec_utils.py +2 -3
  24. pychemstation/utils/table_types.py +10 -9
  25. pychemstation/utils/tray_types.py +45 -36
  26. {pychemstation-0.10.3.dist-info → pychemstation-0.10.5.dist-info}/METADATA +5 -4
  27. pychemstation-0.10.5.dist-info/RECORD +36 -0
  28. pychemstation/control/controllers/tables/ms.py +0 -21
  29. pychemstation-0.10.3.dist-info/RECORD +0 -37
  30. {pychemstation-0.10.3.dist-info → pychemstation-0.10.5.dist-info}/WHEEL +0 -0
  31. {pychemstation-0.10.3.dist-info → pychemstation-0.10.5.dist-info}/licenses/LICENSE +0 -0
@@ -1,15 +1,15 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import time
3
5
  import warnings
4
- from typing import Dict, List, Optional, Union
6
+ from typing import List, Optional, Union, Dict
5
7
 
6
8
  from result import Err, Ok, Result
7
- from xsdata.formats.dataclass.parsers import XmlParser
8
9
 
9
10
  from ....analysis.process_report import AgilentReport, ReportType
10
11
  from ....control.controllers import CommunicationController
11
- from ....generated import DadMethod, PumpMethod, SolventElement
12
- from ....utils.chromatogram import (
12
+ from pychemstation.analysis.chromatogram import (
13
13
  TIME_FORMAT,
14
14
  AgilentChannelChromatogramData,
15
15
  AgilentHPLCChromatogram,
@@ -22,7 +22,7 @@ from ....utils.method_types import (
22
22
  PType,
23
23
  TimeTableEntry,
24
24
  )
25
- from ....utils.table_types import RegisterFlag, T, Table, TableOperation
25
+ from ....utils.table_types import RegisterFlag, Table, TableOperation, T
26
26
  from ..devices.injector import InjectorController
27
27
  from .table import TableController
28
28
 
@@ -32,15 +32,24 @@ class MethodController(TableController):
32
32
  Class containing method related logic
33
33
  """
34
34
 
35
- def __init__(self, controller: CommunicationController,
36
- src: str,
37
- data_dirs: List[str],
38
- table: Table,
39
- offline: bool,
40
- injector_controller: InjectorController):
35
+ def __init__(
36
+ self,
37
+ controller: CommunicationController,
38
+ src: str,
39
+ data_dirs: List[str],
40
+ table: Table,
41
+ offline: bool,
42
+ injector_controller: InjectorController,
43
+ ):
41
44
  self.injector_controller = injector_controller
42
45
  self.data_files: List[str] = []
43
- super().__init__(controller=controller, src=src, data_dirs=data_dirs, table=table, offline=offline)
46
+ super().__init__(
47
+ controller=controller,
48
+ src=src,
49
+ data_dirs=data_dirs,
50
+ table=table,
51
+ offline=offline,
52
+ )
44
53
 
45
54
  def check(self) -> str:
46
55
  time.sleep(2)
@@ -52,19 +61,22 @@ class MethodController(TableController):
52
61
  return "ERROR"
53
62
 
54
63
  def get_method_params(self) -> HPLCMethodParams:
55
- return HPLCMethodParams(organic_modifier=self.controller.get_num_val(
56
- cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
57
- register=self.table_locator.register,
58
- register_flag=RegisterFlag.SOLVENT_B_COMPOSITION
64
+ if self.controller:
65
+ return HPLCMethodParams(
66
+ organic_modifier=self.controller.get_num_val(
67
+ cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
68
+ register=self.table_locator.register,
69
+ register_flag=RegisterFlag.SOLVENT_B_COMPOSITION,
70
+ )
71
+ ),
72
+ flow=self.controller.get_num_val(
73
+ cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
74
+ register=self.table_locator.register,
75
+ register_flag=RegisterFlag.FLOW,
76
+ )
77
+ ),
59
78
  )
60
- ),
61
- flow=self.controller.get_num_val(
62
- cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
63
- register=self.table_locator.register,
64
- register_flag=RegisterFlag.FLOW
65
- )
66
- ),
67
- )
79
+ raise ValueError("Communication controller is offline!")
68
80
 
69
81
  def get_row(self, row: int) -> TimeTableEntry:
70
82
  flow = None
@@ -79,19 +91,26 @@ class MethodController(TableController):
79
91
  except RuntimeError:
80
92
  pass
81
93
 
82
- return TimeTableEntry(start_time=self.get_num(row, RegisterFlag.TIME),
83
- organic_modifer=om,
84
- flow=flow)
94
+ if om is None and flow is None:
95
+ raise ValueError("Both flow and organic modifier is None")
96
+
97
+ return TimeTableEntry(
98
+ start_time=self.get_num(row, RegisterFlag.TIME),
99
+ organic_modifer=om,
100
+ flow=flow,
101
+ )
85
102
 
86
103
  def get_timetable(self, rows: int):
87
104
  uncoalesced_timetable_rows = [self.get_row(r + 1) for r in range(rows)]
88
- timetable_rows = {}
105
+ timetable_rows: Dict[str, TimeTableEntry] = {}
89
106
  for row in uncoalesced_timetable_rows:
90
107
  time_key = str(row.start_time)
91
108
  if time_key not in timetable_rows.keys():
92
- timetable_rows[time_key] = TimeTableEntry(start_time=row.start_time,
93
- flow=row.flow,
94
- organic_modifer=row.organic_modifer)
109
+ timetable_rows[time_key] = TimeTableEntry(
110
+ start_time=row.start_time,
111
+ flow=row.flow,
112
+ organic_modifer=row.organic_modifer,
113
+ )
95
114
  else:
96
115
  if row.flow:
97
116
  timetable_rows[time_key].flow = row.flow
@@ -109,11 +128,13 @@ class MethodController(TableController):
109
128
  params = self.get_method_params()
110
129
  stop_time = self.get_stop_time()
111
130
  post_time = self.get_post_time()
112
- self.table_state = MethodDetails(name=method_name,
113
- timetable=timetable_rows,
114
- stop_time=stop_time,
115
- post_time=post_time,
116
- params=params)
131
+ self.table_state = MethodDetails(
132
+ name=method_name,
133
+ timetable=timetable_rows,
134
+ stop_time=stop_time,
135
+ post_time=post_time,
136
+ params=params,
137
+ )
117
138
  return self.table_state
118
139
  else:
119
140
  raise RuntimeError(rows.err_value)
@@ -125,16 +146,24 @@ class MethodController(TableController):
125
146
  return method_name
126
147
 
127
148
  def get_post_time(self) -> Union[int, float]:
128
- return self.controller.get_num_val(
129
- cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
130
- register=self.table_locator.register,
131
- register_flag=RegisterFlag.POST_TIME))
149
+ if self.controller:
150
+ return self.controller.get_num_val(
151
+ cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
152
+ register=self.table_locator.register,
153
+ register_flag=RegisterFlag.POST_TIME,
154
+ )
155
+ )
156
+ raise ValueError("Communication controller is not online!")
132
157
 
133
158
  def get_stop_time(self) -> Union[int, float]:
134
- return self.controller.get_num_val(
135
- cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
136
- register=self.table_locator.register,
137
- register_flag=RegisterFlag.MAX_TIME))
159
+ if self.controller:
160
+ return self.controller.get_num_val(
161
+ cmd=TableOperation.GET_OBJ_HDR_VAL.value.format(
162
+ register=self.table_locator.register,
163
+ register_flag=RegisterFlag.MAX_TIME,
164
+ )
165
+ )
166
+ raise ValueError("Communication controller is not online!")
138
167
 
139
168
  def get_total_runtime(self) -> Union[int, float]:
140
169
  """Returns total method runtime in minutes."""
@@ -163,7 +192,11 @@ class MethodController(TableController):
163
192
  :raise AssertionError: The desired method is not selected. Try again.
164
193
  """
165
194
  method_dir = self.src if not alt_method_dir else alt_method_dir
166
- self.send(Command.SWITCH_METHOD_CMD_SPECIFIC.value.format(method_dir=method_dir, method_name=method_name))
195
+ self.send(
196
+ Command.SWITCH_METHOD_CMD_SPECIFIC.value.format(
197
+ method_dir=method_dir, method_name=method_name
198
+ )
199
+ )
167
200
 
168
201
  time.sleep(2)
169
202
  self.send(Command.GET_METHOD_CMD)
@@ -174,50 +207,6 @@ class MethodController(TableController):
174
207
  assert parsed_response == f"{method_name}.M", "Switching Methods failed."
175
208
  self.table_state = None
176
209
 
177
- def load_from_disk(self, method_name: str) -> MethodDetails:
178
- """
179
- Retrieve method details of an existing method. Don't need to append ".M" to the end. This assumes the
180
- organic modifier is in Channel B and that Channel A contains the aq layer. Additionally, assumes
181
- only two solvents are being used.
182
-
183
- :param method_name: name of method to load details of
184
- :raises FileNotFoundError: Method does not exist
185
- :return: method details
186
- """
187
- warnings.warn("This method is not actively maintained.")
188
- method_folder = f"{method_name}.M"
189
- method_path = os.path.join(self.src, method_folder, "AgilentPumpDriver1.RapidControl.MethodXML.xml")
190
- dad_path = os.path.join(self.src, method_folder, "Agilent1200erDadDriver1.RapidControl.MethodXML.xml")
191
-
192
- if os.path.exists(os.path.join(self.src, f"{method_name}.M")):
193
- parser = XmlParser()
194
- method = parser.parse(method_path, PumpMethod)
195
- dad = parser.parse(dad_path, DadMethod)
196
-
197
- organic_modifier: Optional[SolventElement] = None
198
- aq_modifier: Optional[SolventElement] = None
199
-
200
- if len(method.solvent_composition.solvent_element) == 2:
201
- for solvent in method.solvent_composition.solvent_element:
202
- if solvent.channel == "Channel_A":
203
- aq_modifier = solvent
204
- elif solvent.channel == "Channel_B":
205
- organic_modifier = solvent
206
-
207
- self.table_state = MethodDetails(name=method_name,
208
- params=HPLCMethodParams(organic_modifier=organic_modifier.percentage,
209
- flow=method.flow),
210
- stop_time=method.stop_time.stop_time_value,
211
- post_time=method.post_time.post_time_value,
212
- timetable=[TimeTableEntry(start_time=tte.time,
213
- organic_modifer=tte.percent_b,
214
- flow=method.flow
215
- ) for tte in method.timetable.timetable_entry],
216
- dad_wavelengthes=dad.signals.signal)
217
- return self.table_state
218
- else:
219
- raise FileNotFoundError
220
-
221
210
  def edit(self, updated_method: MethodDetails, save: bool):
222
211
  """Updated the currently loaded method in ChemStation with provided values.
223
212
 
@@ -225,43 +214,90 @@ class MethodController(TableController):
225
214
  :param save: if false only modifies the method, otherwise saves to disk
226
215
  """
227
216
  self.table_state = updated_method
228
- initial_organic_modifier: Param = Param(val=updated_method.params.organic_modifier,
229
- chemstation_key=RegisterFlag.SOLVENT_B_COMPOSITION,
230
- ptype=PType.NUM)
231
- max_time: Param = Param(val=updated_method.stop_time,
232
- chemstation_key=RegisterFlag.MAX_TIME,
233
- ptype=PType.NUM)
234
- post_time: Param = Param(val=updated_method.post_time,
235
- chemstation_key=RegisterFlag.POST_TIME,
236
- ptype=PType.NUM)
237
- flow: Param = Param(val=updated_method.params.flow,
238
- chemstation_key=RegisterFlag.FLOW,
239
- ptype=PType.NUM)
240
-
241
217
  # Method settings required for all runs
242
- self.update_method_params(flow, initial_organic_modifier, max_time, post_time)
243
- self._update_method_timetable(updated_method.timetable)
218
+ self.update_method_params(
219
+ new_flow=updated_method.params.flow,
220
+ new_initial_om=updated_method.params.organic_modifier,
221
+ new_stop_time=updated_method.stop_time,
222
+ new_post_time=updated_method.post_time,
223
+ )
224
+ self.edit_method_timetable(updated_method.timetable)
244
225
 
245
226
  if save:
246
- self.send(Command.SAVE_METHOD_CMD.value.format(
247
- commit_msg=f"saved method at {str(time.time())}"
248
- ))
227
+ self.send(
228
+ Command.SAVE_METHOD_CMD.value.format(
229
+ commit_msg=f"saved method at {str(time.time())}"
230
+ )
231
+ )
249
232
 
250
- def update_method_params(self, flow, initial_organic_modifier, max_time, post_time):
251
- self.delete_table()
233
+ def edit_initial_om(self, new_om: Union[int, float]):
234
+ initial_organic_modifier: Param = Param(
235
+ val=new_om,
236
+ chemstation_key=RegisterFlag.SOLVENT_B_COMPOSITION,
237
+ ptype=PType.NUM,
238
+ )
252
239
  self._update_param(initial_organic_modifier)
240
+
241
+ def edit_flow(self, new_flow: Union[int, float]):
242
+ flow: Param = Param(
243
+ val=new_flow, chemstation_key=RegisterFlag.FLOW, ptype=PType.NUM
244
+ )
253
245
  self._update_param(flow)
254
- if self.table_state.stop_time:
255
- self._update_param(Param(val="Set", chemstation_key=RegisterFlag.STOPTIME_MODE, ptype=PType.STR))
256
- self._update_param(max_time)
246
+
247
+ def edit_stop_time(self, new_stop_time: Union[int, float]):
248
+ stop_time: Param = Param(
249
+ val=new_stop_time,
250
+ chemstation_key=RegisterFlag.MAX_TIME,
251
+ ptype=PType.NUM,
252
+ )
253
+ self._update_param(
254
+ Param(
255
+ val="Set", chemstation_key=RegisterFlag.STOPTIME_MODE, ptype=PType.STR
256
+ )
257
+ )
258
+ self._update_param(stop_time)
259
+
260
+ def edit_post_time(self, new_post_time: Union[int, float]):
261
+ post_time: Param = Param(
262
+ val=new_post_time,
263
+ chemstation_key=RegisterFlag.POST_TIME,
264
+ ptype=PType.NUM,
265
+ )
266
+ self._update_param(
267
+ Param(val="Set", chemstation_key=RegisterFlag.POSTIME_MODE, ptype=PType.STR)
268
+ )
269
+ self._update_param(post_time)
270
+
271
+ def update_method_params(
272
+ self,
273
+ new_flow: Union[int, float],
274
+ new_initial_om: Union[int, float],
275
+ new_stop_time: Union[int, float] | None,
276
+ new_post_time: Union[int, float] | None,
277
+ ):
278
+ self.delete_table()
279
+ self.edit_initial_om(new_initial_om)
280
+ self.edit_flow(new_flow)
281
+ if new_stop_time:
282
+ self.edit_stop_time(new_stop_time)
257
283
  else:
258
- self._update_param(Param(val="Off", chemstation_key=RegisterFlag.STOPTIME_MODE, ptype=PType.STR))
259
- if self.table_state.post_time:
260
- self._update_param(Param(val="Set", chemstation_key=RegisterFlag.POSTIME_MODE, ptype=PType.STR))
261
- self._update_param(post_time)
284
+ self._update_param(
285
+ Param(
286
+ val="Off",
287
+ chemstation_key=RegisterFlag.STOPTIME_MODE,
288
+ ptype=PType.STR,
289
+ )
290
+ )
291
+ if new_post_time:
292
+ self.edit_post_time(new_post_time)
262
293
  else:
263
- self._update_param(Param(val="Off", chemstation_key=RegisterFlag.POSTIME_MODE, ptype=PType.STR))
264
- self.download()
294
+ self._update_param(
295
+ Param(
296
+ val="Off",
297
+ chemstation_key=RegisterFlag.POSTIME_MODE,
298
+ ptype=PType.STR,
299
+ )
300
+ )
265
301
 
266
302
  def _update_param(self, method_param: Param):
267
303
  """Change a method parameter, changes what is visibly seen in Chemstation GUI.
@@ -270,34 +306,55 @@ class MethodController(TableController):
270
306
  :param method_param: a parameter to update for currently loaded method.
271
307
  """
272
308
  register = self.table_locator.register
273
- setting_command = TableOperation.UPDATE_OBJ_HDR_VAL if method_param.ptype == PType.NUM else TableOperation.UPDATE_OBJ_HDR_TEXT
309
+ setting_command = (
310
+ TableOperation.UPDATE_OBJ_HDR_VAL
311
+ if method_param.ptype == PType.NUM
312
+ else TableOperation.UPDATE_OBJ_HDR_TEXT
313
+ )
274
314
  if isinstance(method_param.chemstation_key, list):
275
315
  for register_flag in method_param.chemstation_key:
276
- self.send(setting_command.value.format(register=register,
277
- register_flag=register_flag,
278
- val=method_param.val))
316
+ self.send(
317
+ setting_command.value.format(
318
+ register=register,
319
+ register_flag=register_flag,
320
+ val=method_param.val,
321
+ )
322
+ )
279
323
  else:
280
- self.send(setting_command.value.format(register=register,
281
- register_flag=method_param.chemstation_key,
282
- val=method_param.val))
324
+ self.send(
325
+ setting_command.value.format(
326
+ register=register,
327
+ register_flag=method_param.chemstation_key,
328
+ val=method_param.val,
329
+ )
330
+ )
283
331
  time.sleep(2)
332
+ self.download()
284
333
 
285
334
  def download(self):
286
- self.send('Sleep 1')
335
+ self.send("Sleep 1")
287
336
  self.sleepy_send("DownloadRCMethod PMP1")
288
- self.send('Sleep 1')
337
+ self.send("Sleep 1")
289
338
 
290
- def edit_row(self, row: TimeTableEntry, first_row: bool = False):
339
+ def _edit_row(self, row: TimeTableEntry, first_row: bool = False):
291
340
  if first_row:
292
341
  if row.organic_modifer:
293
342
  self.add_row()
294
- self.add_new_col_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.SOLVENT_COMPOSITION.value)
343
+ self.add_new_col_text(
344
+ col_name=RegisterFlag.FUNCTION,
345
+ val=RegisterFlag.SOLVENT_COMPOSITION.value,
346
+ )
295
347
  self.add_new_col_num(col_name=RegisterFlag.TIME, val=row.start_time)
296
- self.add_new_col_num(col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION, val=row.organic_modifer)
348
+ self.add_new_col_num(
349
+ col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION,
350
+ val=row.organic_modifer,
351
+ )
297
352
  if row.flow:
298
353
  self.add_row()
299
354
  self.get_num_rows()
300
- self._edit_row_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value)
355
+ self._edit_row_text(
356
+ col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value
357
+ )
301
358
  self.add_new_col_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
302
359
  self._edit_row_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
303
360
  self.download()
@@ -305,19 +362,27 @@ class MethodController(TableController):
305
362
  if row.organic_modifer:
306
363
  self.add_row()
307
364
  self.get_num_rows()
308
- self._edit_row_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.SOLVENT_COMPOSITION.value)
365
+ self._edit_row_text(
366
+ col_name=RegisterFlag.FUNCTION,
367
+ val=RegisterFlag.SOLVENT_COMPOSITION.value,
368
+ )
309
369
  self._edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
310
- self._edit_row_num(col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION, val=row.organic_modifer)
370
+ self._edit_row_num(
371
+ col_name=RegisterFlag.TIMETABLE_SOLVENT_B_COMPOSITION,
372
+ val=row.organic_modifer,
373
+ )
311
374
  self.download()
312
375
  if row.flow:
313
376
  self.add_row()
314
377
  self.get_num_rows()
315
- self._edit_row_text(col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value)
378
+ self._edit_row_text(
379
+ col_name=RegisterFlag.FUNCTION, val=RegisterFlag.FLOW.value
380
+ )
316
381
  self._edit_row_num(col_name=RegisterFlag.TIMETABLE_FLOW, val=row.flow)
317
382
  self._edit_row_num(col_name=RegisterFlag.TIME, val=row.start_time)
318
383
  self.download()
319
384
 
320
- def _update_method_timetable(self, timetable_rows: List[TimeTableEntry]):
385
+ def edit_method_timetable(self, timetable_rows: List[TimeTableEntry]):
321
386
  self.get_num_rows()
322
387
  self.delete_table()
323
388
  res = self.get_num_rows()
@@ -329,7 +394,7 @@ class MethodController(TableController):
329
394
  self.get_num_rows()
330
395
 
331
396
  for i, row in enumerate(timetable_rows):
332
- self.edit_row(row=row, first_row=i == 0)
397
+ self._edit_row(row=row, first_row=i == 0)
333
398
 
334
399
  def stop(self):
335
400
  """
@@ -337,7 +402,12 @@ class MethodController(TableController):
337
402
  """
338
403
  self.send(Command.STOP_METHOD_CMD)
339
404
 
340
- def run(self, experiment_name: str, add_timestamp: bool = True, stall_while_running: bool = True):
405
+ def run(
406
+ self,
407
+ experiment_name: str,
408
+ add_timestamp: bool = True,
409
+ stall_while_running: bool = True,
410
+ ):
341
411
  """
342
412
  :param experiment_name: Name of the experiment
343
413
  :param stall_while_running: whether to stall or immediately return
@@ -349,9 +419,19 @@ class MethodController(TableController):
349
419
  tries = 0
350
420
  while tries < 10 and not hplc_is_running:
351
421
  timestamp = time.strftime(TIME_FORMAT)
352
- self.send(Command.RUN_METHOD_CMD.value.format(data_dir=self.data_dirs[0],
353
- experiment_name=f"{experiment_name}_{timestamp}" if add_timestamp else experiment_name))
354
- folder_name = f"{experiment_name}_{timestamp}.D" if add_timestamp else f"{experiment_name}.D"
422
+ self.send(
423
+ Command.RUN_METHOD_CMD.value.format(
424
+ data_dir=self.data_dirs[0],
425
+ experiment_name=f"{experiment_name}_{timestamp}"
426
+ if add_timestamp
427
+ else experiment_name,
428
+ )
429
+ )
430
+ folder_name = (
431
+ f"{experiment_name}_{timestamp}.D"
432
+ if add_timestamp
433
+ else f"{experiment_name}.D"
434
+ )
355
435
  hplc_is_running = self.check_hplc_is_running()
356
436
  tries += 1
357
437
 
@@ -377,27 +457,37 @@ class MethodController(TableController):
377
457
  warning = f"Data folder {self.data_files[-1]} may not exist, returning and will check again after run is done."
378
458
  warnings.warn(warning)
379
459
 
460
+ def fuzzy_match_most_recent_folder(self, most_recent_folder: T) -> Result[str, str]:
461
+ if isinstance(most_recent_folder, str) or isinstance(most_recent_folder, bytes):
462
+ if os.path.exists(most_recent_folder):
463
+ return Ok(most_recent_folder)
464
+ return Err("Folder not found!")
465
+ raise ValueError("Folder is not a str or byte type.")
380
466
 
381
- def fuzzy_match_most_recent_folder(self, most_recent_folder: T) -> Result[T, str]:
382
- if os.path.exists(most_recent_folder):
383
- return Ok(most_recent_folder)
384
- return Err("Folder not found!")
385
-
386
- def get_data(self, custom_path: Optional[str] = None) -> AgilentChannelChromatogramData:
467
+ def get_data(
468
+ self, custom_path: Optional[str] = None
469
+ ) -> AgilentChannelChromatogramData:
387
470
  custom_path = custom_path if custom_path else self.data_files[-1]
388
471
  self.get_spectrum_at_channels(custom_path)
389
- return AgilentChannelChromatogramData(**self.spectra)
472
+ return AgilentChannelChromatogramData.from_dict(self.spectra)
390
473
 
391
- def get_data_uv(self, custom_path: Optional[str] = None) -> Dict[str, AgilentHPLCChromatogram]:
474
+ def get_data_uv(
475
+ self, custom_path: Optional[str] = None
476
+ ) -> dict[int, AgilentHPLCChromatogram]:
392
477
  custom_path = custom_path if custom_path else self.data_files[-1]
393
478
  self.get_uv_spectrum(custom_path)
394
479
  return self.uv
395
480
 
396
- def get_report(self, custom_path: Optional[str] = None,
397
- report_type: ReportType = ReportType.TXT) -> List[AgilentReport]:
481
+ def get_report(
482
+ self,
483
+ custom_path: Optional[str] = None,
484
+ report_type: ReportType = ReportType.TXT,
485
+ ) -> List[AgilentReport]:
398
486
  custom_path = self.data_files[-1] if not custom_path else custom_path
399
487
  metd_report = self.get_report_details(custom_path, report_type)
400
- chrom_data: List[AgilentHPLCChromatogram] = list(self.get_data(custom_path).__dict__.values())
488
+ chrom_data: List[AgilentHPLCChromatogram] = list(
489
+ self.get_data(custom_path).__dict__.values()
490
+ )
401
491
  for i, signal in enumerate(metd_report.signals):
402
492
  possible_data = chrom_data[i]
403
493
  if len(possible_data.x) > 0: