pychemstation 0.10.7__py3-none-any.whl → 0.10.8__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/analysis/base_spectrum.py +14 -15
  2. pychemstation/analysis/chromatogram.py +7 -8
  3. pychemstation/analysis/process_report.py +10 -16
  4. pychemstation/control/README.md +1 -1
  5. pychemstation/control/controllers/__init__.py +2 -1
  6. pychemstation/control/controllers/comm.py +33 -27
  7. pychemstation/control/controllers/data_aq/method.py +233 -79
  8. pychemstation/control/controllers/data_aq/sequence.py +104 -84
  9. pychemstation/control/controllers/devices/injector.py +3 -3
  10. pychemstation/control/hplc.py +53 -41
  11. pychemstation/utils/__init__.py +23 -0
  12. pychemstation/{control/controllers → utils}/abc_tables/abc_comm.py +15 -13
  13. pychemstation/{control/controllers → utils}/abc_tables/device.py +9 -2
  14. pychemstation/{control/controllers → utils}/abc_tables/run.py +45 -37
  15. pychemstation/{control/controllers → utils}/abc_tables/table.py +32 -29
  16. pychemstation/utils/macro.py +7 -2
  17. pychemstation/utils/method_types.py +13 -14
  18. pychemstation/utils/mocking/mock_comm.py +25 -2
  19. pychemstation/utils/mocking/mock_hplc.py +29 -1
  20. pychemstation/utils/num_utils.py +3 -3
  21. pychemstation/utils/sequence_types.py +30 -14
  22. pychemstation/utils/spec_utils.py +42 -66
  23. pychemstation/utils/table_types.py +15 -2
  24. pychemstation/utils/tray_types.py +28 -16
  25. {pychemstation-0.10.7.dist-info → pychemstation-0.10.8.dist-info}/METADATA +1 -7
  26. pychemstation-0.10.8.dist-info/RECORD +41 -0
  27. pychemstation/utils/pump_types.py +0 -7
  28. pychemstation-0.10.7.dist-info/RECORD +0 -42
  29. /pychemstation/{control/controllers → utils}/abc_tables/__init__.py +0 -0
  30. {pychemstation-0.10.7.dist-info → pychemstation-0.10.8.dist-info}/WHEEL +0 -0
  31. {pychemstation-0.10.7.dist-info → pychemstation-0.10.8.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import time
3
5
  import warnings
4
- from typing import Any, Dict, List, Optional, Union, Set
6
+ from typing import Any, Dict, List, Optional, Union, Tuple
5
7
 
6
8
  from result import Err, Ok, Result
7
9
  from typing_extensions import override
@@ -9,10 +11,12 @@ from typing_extensions import override
9
11
  from pychemstation.analysis.chromatogram import (
10
12
  AgilentChannelChromatogramData,
11
13
  AgilentHPLCChromatogram,
14
+ SEQUENCE_TIME_FORMAT,
12
15
  )
13
16
 
14
17
  from ....analysis.process_report import AgilentReport, ReportType
15
18
  from ....control.controllers.comm import CommunicationController
19
+ from ....utils.abc_tables.run import RunController
16
20
  from ....utils.macro import Command
17
21
  from ....utils.sequence_types import (
18
22
  InjectionSource,
@@ -22,8 +26,7 @@ from ....utils.sequence_types import (
22
26
  SequenceTable,
23
27
  )
24
28
  from ....utils.table_types import RegisterFlag, T, Table
25
- from ....utils.tray_types import FiftyFourVialPlate, TenVialColumn, Tray
26
- from ..abc_tables.run import RunController
29
+ from ....utils.tray_types import FiftyFourVialPlate, VialBar, Tray
27
30
  from . import MethodController
28
31
 
29
32
 
@@ -36,8 +39,8 @@ class SequenceController(RunController):
36
39
  self,
37
40
  controller: Optional[CommunicationController],
38
41
  method_controller: MethodController,
39
- src: str,
40
- data_dirs: List[str],
42
+ src: Optional[str],
43
+ data_dirs: Optional[List[str]],
41
44
  table: Table,
42
45
  offline: bool,
43
46
  ):
@@ -80,11 +83,7 @@ class SequenceController(RunController):
80
83
 
81
84
  def try_vial_location(self, val: Any) -> Tray:
82
85
  try:
83
- return (
84
- TenVialColumn(val)
85
- if val <= 10
86
- else FiftyFourVialPlate.from_int(num=val)
87
- )
86
+ return VialBar(val) if val <= 10 else FiftyFourVialPlate.from_int(num=val)
88
87
  except ValueError:
89
88
  raise ValueError("Expected vial location, is empty.")
90
89
 
@@ -109,15 +108,6 @@ class SequenceController(RunController):
109
108
  data_file=data_file,
110
109
  )
111
110
 
112
- def check(self) -> str:
113
- time.sleep(2)
114
- self.send(Command.GET_SEQUENCE_CMD)
115
- time.sleep(2)
116
- res = self.receive()
117
- if res.is_ok():
118
- return res.ok_value.string_response
119
- return "ERROR"
120
-
121
111
  def switch(self, seq_name: str):
122
112
  """
123
113
  Switch to the specified sequence. The sequence name does not need the '.S' extension.
@@ -131,7 +121,6 @@ class SequenceController(RunController):
131
121
  parsed_response = self.get_current_sequence_name()
132
122
 
133
123
  assert parsed_response == f"{seq_name}.S", "Switching sequence failed."
134
- self.table_state = self.load()
135
124
 
136
125
  def get_current_sequence_name(self):
137
126
  self.send(Command.GET_SEQUENCE_CMD)
@@ -199,6 +188,8 @@ class SequenceController(RunController):
199
188
  def edit_sample_type(
200
189
  self, sample_type: SampleType, row_num: int, save: bool = True
201
190
  ):
191
+ if not isinstance(sample_type, SampleType):
192
+ raise ValueError("`sample_type` should be of type `SampleType`")
202
193
  self._edit_row_num(
203
194
  row=row_num,
204
195
  col_name=RegisterFlag.SAMPLE_TYPE,
@@ -220,6 +211,8 @@ class SequenceController(RunController):
220
211
  def edit_injection_source(
221
212
  self, inj_source: InjectionSource, row_num: int, save: bool = True
222
213
  ):
214
+ if not isinstance(inj_source, InjectionSource):
215
+ raise ValueError("`inj_source` should be of type `InjectionSource`")
223
216
  self._edit_row_text(
224
217
  row=row_num, col_name=RegisterFlag.INJ_SOR, val=inj_source.value
225
218
  )
@@ -237,25 +230,65 @@ class SequenceController(RunController):
237
230
 
238
231
  def edit_num_injections(self, num_inj: int, row_num: int, save: bool = True):
239
232
  self._edit_row_num(row=row_num, col_name=RegisterFlag.NUM_INJ, val=num_inj)
233
+ if save:
234
+ self.save()
240
235
 
241
- def edit_method_name(self, method: str, row_num: int, save: bool = True):
236
+ def edit_method_name(
237
+ self, method: str, row_num: int, save: bool = True, override_check: bool = False
238
+ ):
242
239
  method_dir = self.method_controller.src
243
240
  possible_path = os.path.join(method_dir, method) + ".M\\"
244
241
  if os.path.exists(possible_path):
245
242
  method = os.path.join(method_dir, method)
243
+ elif not override_check:
244
+ raise ValueError(
245
+ "Method may not exist. If you would still like to use this method, set the `override_check` flag to `True`"
246
+ )
246
247
  self._edit_row_text(row=row_num, col_name=RegisterFlag.METHOD, val=method)
247
248
  if save:
248
249
  self.save()
249
250
 
250
251
  def edit_vial_location(self, loc: Tray, row_num: int, save: bool = True):
251
252
  loc_num = -1
252
- if isinstance(loc, TenVialColumn):
253
- loc_num = loc.value
254
- elif isinstance(loc, FiftyFourVialPlate):
255
- loc_num = loc.value()
256
- self._edit_row_num(
257
- row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
258
- )
253
+ try:
254
+ previous_contents = self.get_row(row_num)
255
+ if (
256
+ isinstance(loc, VialBar)
257
+ and isinstance(previous_contents.vial_location, VialBar)
258
+ or isinstance(loc, FiftyFourVialPlate)
259
+ and isinstance(previous_contents.vial_location, FiftyFourVialPlate)
260
+ ):
261
+ if isinstance(loc, VialBar):
262
+ loc_num = loc.value
263
+ elif isinstance(loc, FiftyFourVialPlate):
264
+ loc_num = loc.value()
265
+ self._edit_row_num(
266
+ row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
267
+ )
268
+ elif isinstance(loc, VialBar) or isinstance(loc, FiftyFourVialPlate):
269
+ self.add_row()
270
+ previous_contents.vial_location = loc
271
+ num_rows = self.get_num_rows().ok_value.num_response
272
+ self._edit_row(previous_contents, num_rows)
273
+ self.move_row(int(num_rows), row_num)
274
+ self.delete_row(row_num + 1)
275
+ self.save()
276
+ else:
277
+ raise ValueError(
278
+ "`loc` should be of type `VialBar`, `FiftyFourVialPlate`"
279
+ )
280
+ except Exception:
281
+ if not (isinstance(loc, VialBar) or isinstance(loc, FiftyFourVialPlate)):
282
+ raise ValueError(
283
+ "`loc` should be of type `VialBar`, `FiftyFourVialPlate`"
284
+ )
285
+ if isinstance(loc, VialBar):
286
+ loc_num = loc.value
287
+ elif isinstance(loc, FiftyFourVialPlate):
288
+ loc_num = loc.value()
289
+ self._edit_row_num(
290
+ row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
291
+ )
259
292
  if save:
260
293
  self.save()
261
294
 
@@ -268,11 +301,6 @@ class SequenceController(RunController):
268
301
  under the <data_dir>/<sequence table name> folder.
269
302
  Device must be ready.
270
303
  """
271
- if self.controller:
272
- self.controller.send(Command.SAVE_METHOD_CMD)
273
- self.controller.send(Command.SAVE_SEQUENCE_CMD)
274
- else:
275
- raise ValueError("Controller is offline!")
276
304
 
277
305
  current_sequence_name = self.get_current_sequence_name()
278
306
  if not self.table_state or self.table_state.name not in current_sequence_name:
@@ -297,6 +325,11 @@ class SequenceController(RunController):
297
325
  curr_method_runtime = self.method_controller.get_total_runtime()
298
326
  total_runtime += curr_method_runtime
299
327
 
328
+ timestamp = time.strftime(SEQUENCE_TIME_FORMAT)
329
+ folder_name = f"{self.table_state.name} {timestamp}"
330
+
331
+ self.send(Command.SAVE_METHOD_CMD)
332
+ self.send(Command.SAVE_SEQUENCE_CMD)
300
333
  self.send(Command.RUN_SEQUENCE_CMD.value)
301
334
  self.timeout = total_runtime * 60
302
335
 
@@ -308,20 +341,12 @@ class SequenceController(RunController):
308
341
  break
309
342
 
310
343
  if hplc_running:
311
- full_path_name, current_sample_file = None, None
312
- for _ in range(5):
313
- try:
314
- full_path_name, current_sample_file = (
315
- self.get_current_run_data_dir_file()
316
- )
317
- break
318
- except ValueError:
319
- pass
344
+ full_path_name, current_sample_file = self.try_getting_run_info(folder_name)
320
345
  if full_path_name and current_sample_file:
321
346
  data_file = SequenceDataFiles(
322
347
  sequence_name=self.table_state.name,
323
348
  dir=full_path_name,
324
- _data_files=[r.data_file for r in self.table_state.rows],
349
+ child_dirs=[os.path.join(full_path_name, current_sample_file)],
325
350
  )
326
351
  self.data_files.append(data_file)
327
352
  else:
@@ -332,42 +357,38 @@ class SequenceController(RunController):
332
357
  if run_completed.is_ok():
333
358
  self.data_files[-1] = run_completed.ok_value
334
359
  else:
335
- warnings.warn("Run may have not completed.")
360
+ warnings.warn(run_completed.err_value)
336
361
  else:
337
362
  raise RuntimeError("Sequence run may not have started.")
338
363
 
364
+ def try_getting_run_info(self, folder_name: str) -> Tuple[str, str | None]:
365
+ full_path_name, current_sample_file = None, None
366
+ for _ in range(5):
367
+ try:
368
+ full_path_name, current_sample_file = (
369
+ self.get_current_run_data_dir_file()
370
+ )
371
+ except ValueError:
372
+ pass
373
+ if current_sample_file and full_path_name:
374
+ return full_path_name, current_sample_file
375
+ elif full_path_name:
376
+ return full_path_name, None
377
+ raise ValueError("Could not get sequence data folder")
378
+
339
379
  @override
340
- def fuzzy_match_most_recent_folder(
341
- self, most_recent_folder: T, child_dirs: Optional[Set[str]]
380
+ def _fuzzy_match_most_recent_folder(
381
+ self, most_recent_folder: T
342
382
  ) -> Result[SequenceDataFiles, str]:
343
383
  if isinstance(most_recent_folder, SequenceDataFiles):
344
384
  try:
345
385
  if most_recent_folder.dir and os.path.isdir(most_recent_folder.dir):
346
386
  subdirs = [x[0] for x in os.walk(most_recent_folder.dir)]
347
- if (
348
- child_dirs
349
- and len(child_dirs) > 0
350
- and set(most_recent_folder._data_files) == child_dirs
351
- ):
352
- most_recent_folder.child_dirs = [
353
- os.path.join(most_recent_folder.dir, f) for f in child_dirs
354
- ]
355
- else:
356
- potential_folders: List[str] = sorted(
357
- list(
358
- filter(
359
- lambda d: most_recent_folder.dir in d,
360
- subdirs,
361
- )
362
- )
363
- )
364
- most_recent_folder.child_dirs = [
365
- f
366
- for f in potential_folders
367
- if most_recent_folder.dir in f
368
- and ".M" not in f
369
- and ".D" in f
370
- ]
387
+ most_recent_folder.child_dirs = [
388
+ f
389
+ for f in subdirs
390
+ if most_recent_folder.dir in f and ".D" in f and f[-1] == "D"
391
+ ]
371
392
  return Ok(most_recent_folder)
372
393
  else:
373
394
  return Err("No sequence folder found, please give the full path.")
@@ -382,14 +403,11 @@ class SequenceController(RunController):
382
403
  if custom_path
383
404
  else self.data_files[-1]
384
405
  )
385
- if len(seq_data_dir.child_dirs) == 0:
386
- search_folder = self.fuzzy_match_most_recent_folder(
387
- seq_data_dir, set(seq_data_dir.child_dirs)
388
- )
389
- if search_folder.is_ok():
390
- seq_data_dir = search_folder.ok_value
391
- else:
392
- raise FileNotFoundError(search_folder.err_value)
406
+ search_folder = self._fuzzy_match_most_recent_folder(seq_data_dir)
407
+ if search_folder.is_ok():
408
+ seq_data_dir = search_folder.ok_value
409
+ else:
410
+ raise FileNotFoundError(search_folder.err_value)
393
411
  all_w_spectra: List[Dict[int, AgilentHPLCChromatogram]] = []
394
412
  for row in seq_data_dir.child_dirs:
395
413
  all_w_spectra.append(self.get_data_uv(custom_path=row))
@@ -413,10 +431,9 @@ class SequenceController(RunController):
413
431
  if custom_path
414
432
  else self.data_files[-1]
415
433
  )
416
- if len(seq_file_dir.child_dirs) == 0:
417
- self.data_files[-1] = self.fuzzy_match_most_recent_folder(
418
- seq_file_dir, set(seq_file_dir.child_dirs)
419
- ).ok_value
434
+ self.data_files[-1] = self._fuzzy_match_most_recent_folder(
435
+ seq_file_dir
436
+ ).ok_value
420
437
  spectra: List[AgilentChannelChromatogramData] = []
421
438
  for row in self.data_files[-1].child_dirs:
422
439
  self.get_spectrum_at_channels(row)
@@ -430,15 +447,18 @@ class SequenceController(RunController):
430
447
  ) -> List[AgilentReport]:
431
448
  if custom_path:
432
449
  self.data_files.append(
433
- self.fuzzy_match_most_recent_folder(
450
+ self._fuzzy_match_most_recent_folder(
434
451
  most_recent_folder=SequenceDataFiles(
435
452
  dir=custom_path,
436
453
  sequence_name="NA",
437
454
  ),
438
- child_dirs=None,
439
455
  ).ok_value
440
456
  )
441
457
  parent_dir = self.data_files[-1]
458
+ parent_dir = self._fuzzy_match_most_recent_folder(
459
+ most_recent_folder=parent_dir,
460
+ ).ok_value
461
+ assert len(parent_dir.child_dirs) != 0
442
462
  spectra = self.get_data()
443
463
  reports = []
444
464
  for i, child_dir in enumerate(parent_dir.child_dirs):
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from ..abc_tables.device import DeviceController
4
3
  from ....control.controllers import CommunicationController
4
+ from ....utils.abc_tables.device import DeviceController
5
5
  from ....utils.injector_types import (
6
6
  Draw,
7
7
  Inject,
@@ -17,7 +17,7 @@ from ....utils.injector_types import (
17
17
  )
18
18
  from ....utils.macro import Response
19
19
  from ....utils.table_types import RegisterFlag, Table
20
- from ....utils.tray_types import Tray, FiftyFourVialPlate, TenVialColumn, LocationPlus
20
+ from ....utils.tray_types import Tray, FiftyFourVialPlate, VialBar, LocationPlus
21
21
 
22
22
 
23
23
  class InjectorController(DeviceController):
@@ -31,7 +31,7 @@ class InjectorController(DeviceController):
31
31
  return FiftyFourVialPlate.from_str(val)
32
32
  except Exception:
33
33
  try:
34
- return TenVialColumn(int(val))
34
+ return VialBar(int(val))
35
35
  except Exception:
36
36
  raise ValueError("Location could not be identified.")
37
37
 
@@ -20,7 +20,7 @@ from ..control.controllers import CommunicationController
20
20
  from ..utils.injector_types import InjectorTable
21
21
  from ..utils.macro import Command, Response, Status
22
22
  from ..utils.method_types import MethodDetails
23
- from ..utils.sequence_types import SequenceTable
23
+ from ..utils.sequence_types import SequenceTable, SequenceDataFiles
24
24
  from ..utils.table_types import Table
25
25
 
26
26
 
@@ -32,8 +32,6 @@ class HPLCController:
32
32
 
33
33
  INJECTOR_TABLE = Table(register="RCWLS1Pretreatment[1]", name="InstructionTable")
34
34
 
35
- DAD_TABLE = Table(register="RCDAD1Method[1]", name="Timetable")
36
-
37
35
  MSD_TABLE = Table(register="MSACQINFO[1]", name="SprayChamber")
38
36
 
39
37
  def __init__(
@@ -63,7 +61,7 @@ class HPLCController:
63
61
  if extra_data_dirs:
64
62
  data_dirs.extend(extra_data_dirs)
65
63
  data_dirs = list(set([os.path.normpath(p) for p in data_dirs]))
66
- if method_dir and sequence_dir and data_dirs:
64
+ if (method_dir and sequence_dir and data_dirs and not offline) or offline:
67
65
  self.method_controller: MethodController = MethodController(
68
66
  controller=self.comm,
69
67
  src=method_dir,
@@ -82,14 +80,15 @@ class HPLCController:
82
80
  method_controller=self.method_controller,
83
81
  offline=offline,
84
82
  )
85
- else:
83
+ elif not offline and (not method_dir or not sequence_dir or not data_dirs):
86
84
  raise ValueError(
87
- "Expected a method dir, sequence dir and data dirs but they were None."
85
+ f"Expected a method dir: {method_dir}, sequence dir: {sequence_dir} and data dirs:{data_dirs} but one was None."
88
86
  )
87
+ else:
88
+ raise ValueError("Expected error occured, please try again.")
89
89
 
90
90
  def send(self, cmd: Union[Command, str]):
91
- """
92
- Sends any Command or string to Chemstation.
91
+ """Sends any Command or string to Chemstation.
93
92
 
94
93
  :param cmd: the macro to send to Chemstation
95
94
  """
@@ -100,20 +99,22 @@ class HPLCController:
100
99
  self.comm.send(cmd)
101
100
 
102
101
  def receive(self) -> None | Response | str:
103
- """
104
- Get the most recent response from Chemstation.
102
+ """Get the most recent response from Chemstation.
105
103
 
106
- :return: most recent response from a macro that returned a response.
104
+ :return: most recent response from the most recently sent MACRO that returned a response.
107
105
  """
108
106
  if not self.comm:
109
107
  raise RuntimeError(
110
108
  "Communication controller must be initialized before sending command. It is currently in offline mode."
111
109
  )
112
- return self.comm.receive().value
110
+ res = self.comm.receive()
111
+ if res.is_ok():
112
+ return res.ok_value
113
+ else:
114
+ return res.err_value
113
115
 
114
116
  def status(self) -> Status:
115
- """
116
- Get the current status of the HPLC machine.
117
+ """Get the current status of the HPLC machine.
117
118
 
118
119
  :return: current status of the HPLC machine; Status types can be found in `pychemstation.utils.macro`
119
120
  """
@@ -124,8 +125,7 @@ class HPLCController:
124
125
  return self.comm.get_status()
125
126
 
126
127
  def switch_method(self, method_name: str):
127
- """
128
- Allows the user to switch between pre-programmed methods. No need to append '.M'
128
+ """Allows the user to switch between pre-programmed methods. No need to append '.M'
129
129
  to the end of the method name. For example. for the method named 'General-Poroshell.M',
130
130
  only 'General-Poroshell' is needed.
131
131
 
@@ -136,8 +136,7 @@ class HPLCController:
136
136
  self.method_controller.switch(method_name)
137
137
 
138
138
  def switch_sequence(self, sequence_name: str):
139
- """
140
- Allows the user to switch between pre-programmed sequences. The sequence name does not need the '.S' extension.
139
+ """Allows the user to switch between pre-programmed sequences. The sequence name does not need the '.S' extension.
141
140
  For example: for the method named 'mySeq.S', only 'mySeq' is needed.
142
141
 
143
142
  :param sequence_name: The name of the sequence file
@@ -150,8 +149,7 @@ class HPLCController:
150
149
  add_timestamp: bool = True,
151
150
  stall_while_running: bool = True,
152
151
  ):
153
- """
154
- This is the preferred method to trigger a run.
152
+ """This is the preferred method to trigger a run.
155
153
  Starts the currently selected method, storing data
156
154
  under the <data_dir>/<experiment_name>.D folder.
157
155
  Device must be ready.
@@ -171,8 +169,7 @@ class HPLCController:
171
169
  self.method_controller.stop()
172
170
 
173
171
  def run_sequence(self, stall_while_running: bool = True):
174
- """
175
- Starts the currently loaded sequence, storing data
172
+ """Starts the currently loaded sequence, storing data
176
173
  under one of the data_dirs/<sequence table name> folder.
177
174
  Device must be ready.
178
175
 
@@ -181,24 +178,21 @@ class HPLCController:
181
178
  self.sequence_controller.run(stall_while_running=stall_while_running)
182
179
 
183
180
  def check_method_complete(self) -> Tuple[float, int]:
184
- """
185
- Check if the currently running method (if any) is done.
181
+ """Check if the currently running method (if any) is done.
186
182
 
187
- :returns the percent of the method run completed, and whether the run is complete.
183
+ :returns: the percent of the method run completed, and whether the run is complete.
188
184
  """
189
185
  return self.method_controller.check_hplc_run_finished()
190
186
 
191
187
  def check_sequence_complete(self) -> Tuple[float, int]:
192
- """
193
- Check if the currently running sequence (if any) is done.
188
+ """Check if the currently running sequence (if any) is done.
194
189
 
195
190
  :return: the percent of the sequence run completed, and whether the run is complete.
196
191
  """
197
192
  return self.sequence_controller.check_hplc_run_finished()
198
193
 
199
194
  def edit_method(self, updated_method: MethodDetails, save: bool = False):
200
- """
201
- Updated the currently loaded method in ChemStation with provided values.
195
+ """Updated the currently loaded method in ChemStation with provided values.
202
196
 
203
197
  :param updated_method: the method with updated values, to be sent to Chemstation to modify the currently loaded method.
204
198
  :param save: whether this method should be saved to disk, or just modified.
@@ -206,20 +200,28 @@ class HPLCController:
206
200
  self.method_controller.edit(updated_method, save)
207
201
 
208
202
  def edit_sequence(self, updated_sequence: SequenceTable):
209
- """
210
- Updates the currently loaded sequence table with the provided table, and saves the sequence.
203
+ """Updates the currently loaded sequence table with the provided table, and saves the sequence.
211
204
 
212
205
  :param updated_sequence: The sequence table to be written to the currently loaded sequence table.
213
206
  """
214
207
  self.sequence_controller.edit(updated_sequence)
215
208
 
209
+ def get_last_run_method_file_path(self) -> str:
210
+ """Get the folder (ending with .D) for last run method.
211
+
212
+ :return: Complete path for method run.
213
+ """
214
+ if len(self.method_controller.data_files) > 0:
215
+ return self.method_controller.data_files[-1]
216
+ else:
217
+ raise UserWarning("No data yet!")
218
+
216
219
  def get_last_run_method_report(
217
220
  self,
218
221
  custom_path: Optional[str] = None,
219
222
  report_type: ReportType = ReportType.CSV,
220
223
  ) -> AgilentReport:
221
- """
222
- Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
224
+ """Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
223
225
 
224
226
  :param custom_path: path to sequence folder
225
227
  :param report_type: read either the TXT or CSV version
@@ -232,8 +234,7 @@ class HPLCController:
232
234
  def get_last_run_method_data(
233
235
  self, read_uv: bool = False, custom_path: Optional[str] = None
234
236
  ) -> Dict[int, AgilentHPLCChromatogram] | AgilentChannelChromatogramData:
235
- """
236
- Returns the last run method data.
237
+ """Returns the last run method data.
237
238
 
238
239
  :param custom_path: If you want to just load method data but from a file path. This file path must be the complete file path.
239
240
  :param read_uv: whether to also read the UV file
@@ -243,13 +244,25 @@ class HPLCController:
243
244
  else:
244
245
  return self.method_controller.get_data(custom_path=custom_path)
245
246
 
247
+ def get_last_run_sequence_file_paths(self) -> SequenceDataFiles:
248
+ """Get the sequence folder and all run folders (ending with .D).
249
+
250
+ :return: `SequenceDataFiles` containing complete path locations for sequence folder and all runs.
251
+ """
252
+ if len(self.sequence_controller.data_files):
253
+ self.sequence_controller._fuzzy_match_most_recent_folder(
254
+ most_recent_folder=self.sequence_controller.data_files[-1],
255
+ )
256
+ return self.sequence_controller.data_files[-1]
257
+ else:
258
+ raise UserWarning("No data files yet!")
259
+
246
260
  def get_last_run_sequence_reports(
247
261
  self,
248
262
  custom_path: Optional[str] = None,
249
263
  report_type: ReportType = ReportType.CSV,
250
264
  ) -> List[AgilentReport]:
251
- """
252
- Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
265
+ """Return data contained in the REPORT files. Use `aghplctools` if you want more report processing utility.
253
266
 
254
267
  :param custom_path: path to sequence folder
255
268
  :param report_type: read either the TXT or CSV version
@@ -264,8 +277,7 @@ class HPLCController:
264
277
  ) -> (
265
278
  List[Dict[int, AgilentHPLCChromatogram]] | List[AgilentChannelChromatogramData]
266
279
  ):
267
- """
268
- Returns data for all rows in the last run sequence data.
280
+ """Returns data for all rows in the last run sequence data.
269
281
 
270
282
  :param custom_path: If you want to just load sequence data but from a file path. This file path must be the complete file path.
271
283
  :param read_uv: whether to also read the UV file
@@ -277,11 +289,11 @@ class HPLCController:
277
289
 
278
290
  def check_loaded_sequence(self) -> str:
279
291
  """Returns the name of the currently loaded sequence."""
280
- return self.sequence_controller.check()
292
+ return self.sequence_controller.get_current_sequence_name()
281
293
 
282
294
  def check_loaded_method(self) -> str:
283
295
  """Returns the name of the currently loaded method."""
284
- return self.method_controller.check()
296
+ return self.method_controller.get_current_method_name()
285
297
 
286
298
  def load_method(self) -> MethodDetails:
287
299
  """Returns details of the currently loaded method, such as its starting modifier conditions and timetable."""
@@ -0,0 +1,23 @@
1
+ from . import abc_tables
2
+ from . import injector_types
3
+ from . import macro
4
+ from . import method_types
5
+ from . import num_utils
6
+ from . import parsing
7
+ from . import sequence_types
8
+ from . import spec_utils
9
+ from . import table_types
10
+ from . import tray_types
11
+
12
+ __all__ = [
13
+ "abc_tables",
14
+ "injector_types",
15
+ "macro",
16
+ "method_types",
17
+ "num_utils",
18
+ "parsing",
19
+ "sequence_types",
20
+ "spec_utils",
21
+ "table_types",
22
+ "tray_types",
23
+ ]