pychemstation 0.10.2__py3-none-any.whl → 0.10.4__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.
- pychemstation/__init__.py +1 -1
- pychemstation/analysis/__init__.py +3 -4
- pychemstation/analysis/base_spectrum.py +7 -7
- pychemstation/{utils → analysis}/chromatogram.py +4 -4
- pychemstation/analysis/process_report.py +106 -70
- pychemstation/control/README.md +21 -53
- pychemstation/control/__init__.py +3 -2
- pychemstation/control/controllers/__init__.py +1 -5
- pychemstation/control/controllers/comm.py +20 -11
- pychemstation/control/controllers/devices/device.py +22 -12
- pychemstation/control/controllers/devices/injector.py +24 -14
- pychemstation/control/controllers/tables/method.py +233 -100
- pychemstation/control/controllers/tables/ms.py +7 -4
- pychemstation/control/controllers/tables/sequence.py +134 -54
- pychemstation/control/controllers/tables/table.py +152 -92
- pychemstation/control/hplc.py +96 -78
- pychemstation/generated/__init__.py +0 -2
- pychemstation/generated/pump_method.py +15 -19
- pychemstation/utils/macro.py +10 -9
- pychemstation/utils/method_types.py +1 -0
- pychemstation/utils/num_utils.py +2 -2
- pychemstation/utils/parsing.py +0 -11
- pychemstation/utils/sequence_types.py +2 -3
- pychemstation/utils/spec_utils.py +2 -3
- pychemstation/utils/table_types.py +10 -9
- pychemstation/utils/tray_types.py +48 -38
- {pychemstation-0.10.2.dist-info → pychemstation-0.10.4.dist-info}/METADATA +46 -20
- pychemstation-0.10.4.dist-info/RECORD +37 -0
- pychemstation-0.10.2.dist-info/RECORD +0 -37
- {pychemstation-0.10.2.dist-info → pychemstation-0.10.4.dist-info}/WHEEL +0 -0
- {pychemstation-0.10.2.dist-info → pychemstation-0.10.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import time
|
3
|
+
import warnings
|
3
4
|
from typing import Dict, List, Optional
|
4
5
|
|
5
6
|
from result import Err, Ok, Result
|
@@ -7,7 +8,7 @@ from typing_extensions import override
|
|
7
8
|
|
8
9
|
from ....analysis.process_report import AgilentReport, ReportType
|
9
10
|
from ....control.controllers.comm import CommunicationController
|
10
|
-
from
|
11
|
+
from pychemstation.analysis.chromatogram import (
|
11
12
|
SEQUENCE_TIME_FORMAT,
|
12
13
|
AgilentChannelChromatogramData,
|
13
14
|
AgilentHPLCChromatogram,
|
@@ -31,15 +32,24 @@ class SequenceController(TableController):
|
|
31
32
|
Class containing sequence related logic
|
32
33
|
"""
|
33
34
|
|
34
|
-
def __init__(
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
def __init__(
|
36
|
+
self,
|
37
|
+
controller: CommunicationController,
|
38
|
+
method_controller: MethodController,
|
39
|
+
src: str,
|
40
|
+
data_dirs: List[str],
|
41
|
+
table: Table,
|
42
|
+
offline: bool,
|
43
|
+
):
|
40
44
|
self.method_controller = method_controller
|
41
45
|
self.data_files: List[SequenceDataFiles] = []
|
42
|
-
super().__init__(
|
46
|
+
super().__init__(
|
47
|
+
controller=controller,
|
48
|
+
src=src,
|
49
|
+
data_dirs=data_dirs,
|
50
|
+
table=table,
|
51
|
+
offline=offline,
|
52
|
+
)
|
43
53
|
|
44
54
|
def load(self) -> SequenceTable:
|
45
55
|
rows = self.get_num_rows()
|
@@ -49,7 +59,10 @@ class SequenceController(TableController):
|
|
49
59
|
if rows.is_ok() and seq_name.is_ok():
|
50
60
|
self.table_state = SequenceTable(
|
51
61
|
name=seq_name.ok_value.string_response.partition(".S")[0],
|
52
|
-
rows=[
|
62
|
+
rows=[
|
63
|
+
self.get_row(r + 1) for r in range(int(rows.ok_value.num_response))
|
64
|
+
],
|
65
|
+
)
|
53
66
|
return self.table_state
|
54
67
|
raise RuntimeError(rows.err_value)
|
55
68
|
|
@@ -61,15 +74,20 @@ class SequenceController(TableController):
|
|
61
74
|
inj_vol = float(self.get_text(row, RegisterFlag.INJ_VOL))
|
62
75
|
inj_source = InjectionSource(self.get_text(row, RegisterFlag.INJ_SOR))
|
63
76
|
sample_type = SampleType(self.get_num(row, RegisterFlag.SAMPLE_TYPE))
|
64
|
-
vial_enum =
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
77
|
+
vial_enum = (
|
78
|
+
TenVialColumn(vial_location)
|
79
|
+
if vial_location <= 10
|
80
|
+
else FiftyFourVialPlate.from_int(num=vial_location)
|
81
|
+
)
|
82
|
+
return SequenceEntry(
|
83
|
+
sample_name=sample_name,
|
84
|
+
vial_location=vial_enum,
|
85
|
+
method=None if len(method) == 0 else method,
|
86
|
+
num_inj=num_inj,
|
87
|
+
inj_vol=inj_vol,
|
88
|
+
inj_source=inj_source,
|
89
|
+
sample_type=sample_type,
|
90
|
+
)
|
73
91
|
|
74
92
|
def check(self) -> str:
|
75
93
|
time.sleep(2)
|
@@ -143,7 +161,9 @@ class SequenceController(TableController):
|
|
143
161
|
loc = row.vial_location.value
|
144
162
|
elif isinstance(loc, FiftyFourVialPlate):
|
145
163
|
loc = row.vial_location.value()
|
146
|
-
self._edit_row_num(
|
164
|
+
self._edit_row_num(
|
165
|
+
row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc
|
166
|
+
)
|
147
167
|
if row.method:
|
148
168
|
method_dir = self.method_controller.src
|
149
169
|
possible_path = os.path.join(method_dir, row.method) + ".M\\"
|
@@ -152,19 +172,35 @@ class SequenceController(TableController):
|
|
152
172
|
method = os.path.join(method_dir, row.method)
|
153
173
|
self._edit_row_text(row=row_num, col_name=RegisterFlag.METHOD, val=method)
|
154
174
|
if row.num_inj:
|
155
|
-
self._edit_row_num(
|
175
|
+
self._edit_row_num(
|
176
|
+
row=row_num, col_name=RegisterFlag.NUM_INJ, val=row.num_inj
|
177
|
+
)
|
156
178
|
if row.inj_vol:
|
157
|
-
self._edit_row_text(
|
179
|
+
self._edit_row_text(
|
180
|
+
row=row_num, col_name=RegisterFlag.INJ_VOL, val=row.inj_vol
|
181
|
+
)
|
158
182
|
if row.inj_source:
|
159
|
-
self._edit_row_text(
|
183
|
+
self._edit_row_text(
|
184
|
+
row=row_num, col_name=RegisterFlag.INJ_SOR, val=row.inj_source.value
|
185
|
+
)
|
160
186
|
if row.sample_name:
|
161
|
-
self._edit_row_text(
|
187
|
+
self._edit_row_text(
|
188
|
+
row=row_num, col_name=RegisterFlag.NAME, val=row.sample_name
|
189
|
+
)
|
162
190
|
if row.data_file:
|
163
|
-
self._edit_row_text(
|
191
|
+
self._edit_row_text(
|
192
|
+
row=row_num, col_name=RegisterFlag.DATA_FILE, val=row.data_file
|
193
|
+
)
|
164
194
|
else:
|
165
|
-
self._edit_row_text(
|
195
|
+
self._edit_row_text(
|
196
|
+
row=row_num, col_name=RegisterFlag.DATA_FILE, val=row.sample_name
|
197
|
+
)
|
166
198
|
if row.sample_type:
|
167
|
-
self._edit_row_num(
|
199
|
+
self._edit_row_num(
|
200
|
+
row=row_num,
|
201
|
+
col_name=RegisterFlag.SAMPLE_TYPE,
|
202
|
+
val=row.sample_type.value,
|
203
|
+
)
|
168
204
|
|
169
205
|
self.send(Command.SAVE_SEQUENCE_CMD)
|
170
206
|
|
@@ -187,9 +223,12 @@ class SequenceController(TableController):
|
|
187
223
|
method_path = entry.method.split(sep="\\")
|
188
224
|
method_name = method_path[-1]
|
189
225
|
if loaded_method != method_name:
|
190
|
-
method_dir =
|
191
|
-
|
192
|
-
|
226
|
+
method_dir = (
|
227
|
+
"\\".join(method_path[:-1]) + "\\" if len(method_path) > 1 else None
|
228
|
+
)
|
229
|
+
self.method_controller.switch(
|
230
|
+
method_name=method_name, alt_method_dir=method_dir
|
231
|
+
)
|
193
232
|
curr_method_runtime = self.method_controller.get_total_runtime()
|
194
233
|
total_runtime += curr_method_runtime
|
195
234
|
|
@@ -199,7 +238,9 @@ class SequenceController(TableController):
|
|
199
238
|
|
200
239
|
if self.check_hplc_is_running():
|
201
240
|
folder_name = f"{self.table_state.name} {timestamp}"
|
202
|
-
data_file = SequenceDataFiles(
|
241
|
+
data_file = SequenceDataFiles(
|
242
|
+
dir=folder_name, sequence_name=self.table_state.name
|
243
|
+
)
|
203
244
|
self.data_files.append(data_file)
|
204
245
|
|
205
246
|
if stall_while_running:
|
@@ -207,24 +248,33 @@ class SequenceController(TableController):
|
|
207
248
|
if run_completed.is_ok():
|
208
249
|
self.data_files[-1] = run_completed.ok_value
|
209
250
|
else:
|
210
|
-
|
251
|
+
warnings.warn("Run may have not completed.")
|
211
252
|
else:
|
212
253
|
raise RuntimeError("Sequence run did not start.")
|
213
254
|
|
214
255
|
@override
|
215
|
-
def fuzzy_match_most_recent_folder(
|
256
|
+
def fuzzy_match_most_recent_folder(
|
257
|
+
self, most_recent_folder: SequenceDataFiles
|
258
|
+
) -> Result[SequenceDataFiles, str]:
|
216
259
|
if os.path.isdir(most_recent_folder.dir):
|
217
260
|
subdirs = [x[0] for x in os.walk(most_recent_folder.dir)]
|
218
|
-
potential_folders = sorted(
|
219
|
-
|
220
|
-
|
261
|
+
potential_folders = sorted(
|
262
|
+
list(filter(lambda d: most_recent_folder.dir in d, subdirs))
|
263
|
+
)
|
264
|
+
most_recent_folder.child_dirs = [
|
265
|
+
f
|
266
|
+
for f in potential_folders
|
267
|
+
if most_recent_folder.dir in f and ".M" not in f and ".D" in f
|
268
|
+
]
|
221
269
|
return Ok(most_recent_folder)
|
222
270
|
|
223
271
|
try:
|
224
272
|
potential_folders = []
|
225
273
|
for d in self.data_dirs:
|
226
274
|
subdirs = [x[0] for x in os.walk(d)]
|
227
|
-
potential_folders = sorted(
|
275
|
+
potential_folders = sorted(
|
276
|
+
list(filter(lambda d: most_recent_folder.dir in d, subdirs))
|
277
|
+
)
|
228
278
|
if len(potential_folders) > 0:
|
229
279
|
break
|
230
280
|
assert len(potential_folders) > 0
|
@@ -239,49 +289,79 @@ class SequenceController(TableController):
|
|
239
289
|
potential_folders = []
|
240
290
|
for d in self.data_dirs:
|
241
291
|
subdirs = [x[0] for x in os.walk(d)]
|
242
|
-
potential_folders = sorted(
|
292
|
+
potential_folders = sorted(
|
293
|
+
list(filter(lambda d: parent_dir in d, subdirs))
|
294
|
+
)
|
243
295
|
if len(potential_folders) > 0:
|
244
296
|
break
|
245
297
|
assert len(potential_folders) > 0
|
246
|
-
most_recent_folder.child_dirs = [
|
247
|
-
|
298
|
+
most_recent_folder.child_dirs = [
|
299
|
+
f
|
300
|
+
for f in potential_folders
|
301
|
+
if parent_dir in f and ".M" not in f and ".D" in f
|
302
|
+
]
|
248
303
|
return Ok(most_recent_folder)
|
249
|
-
except Exception:
|
250
|
-
|
304
|
+
except Exception as e:
|
305
|
+
error = f"Failed to get sequence folder: {e}"
|
306
|
+
return Err(error)
|
251
307
|
|
252
|
-
def get_data_uv(
|
253
|
-
|
308
|
+
def get_data_uv(
|
309
|
+
self, custom_path: Optional[str] = None
|
310
|
+
) -> List[Dict[int, AgilentHPLCChromatogram]]:
|
311
|
+
custom_path = (
|
312
|
+
SequenceDataFiles(dir=custom_path, child_dirs=[], sequence_name="")
|
313
|
+
if custom_path
|
314
|
+
else self.data_files[-1]
|
315
|
+
)
|
254
316
|
if len(custom_path.child_dirs) == 0:
|
255
|
-
self.data_files[-1] = self.fuzzy_match_most_recent_folder(
|
256
|
-
|
317
|
+
self.data_files[-1] = self.fuzzy_match_most_recent_folder(
|
318
|
+
custom_path
|
319
|
+
).ok_value
|
320
|
+
all_w_spectra: List[Dict[int, AgilentHPLCChromatogram]] = []
|
257
321
|
for row in self.data_files[-1].child_dirs:
|
258
322
|
self.get_uv_spectrum(row)
|
259
323
|
all_w_spectra.append(self.uv)
|
260
324
|
return all_w_spectra
|
261
325
|
|
262
|
-
def get_data(
|
263
|
-
|
326
|
+
def get_data(
|
327
|
+
self, custom_path: Optional[str] = None
|
328
|
+
) -> List[AgilentChannelChromatogramData]:
|
329
|
+
custom_path = (
|
330
|
+
SequenceDataFiles(dir=custom_path, child_dirs=[], sequence_name="")
|
331
|
+
if custom_path
|
332
|
+
else self.data_files[-1]
|
333
|
+
)
|
264
334
|
if len(custom_path.child_dirs) == 0:
|
265
|
-
self.data_files[-1] = self.fuzzy_match_most_recent_folder(
|
335
|
+
self.data_files[-1] = self.fuzzy_match_most_recent_folder(
|
336
|
+
custom_path
|
337
|
+
).ok_value
|
266
338
|
spectra: List[AgilentChannelChromatogramData] = []
|
267
339
|
for row in self.data_files[-1].child_dirs:
|
268
340
|
self.get_spectrum_at_channels(row)
|
269
341
|
spectra.append(AgilentChannelChromatogramData(**self.spectra))
|
270
342
|
return spectra
|
271
343
|
|
272
|
-
def get_report(
|
273
|
-
|
344
|
+
def get_report(
|
345
|
+
self,
|
346
|
+
custom_path: Optional[str] = None,
|
347
|
+
report_type: ReportType = ReportType.TXT,
|
348
|
+
) -> List[AgilentReport]:
|
274
349
|
if custom_path:
|
275
350
|
self.data_files.append(
|
276
|
-
self.fuzzy_match_most_recent_folder(
|
277
|
-
|
278
|
-
|
351
|
+
self.fuzzy_match_most_recent_folder(
|
352
|
+
most_recent_folder=SequenceDataFiles(
|
353
|
+
dir=custom_path, child_dirs=[], sequence_name="NA"
|
354
|
+
)
|
355
|
+
).ok_value
|
356
|
+
)
|
279
357
|
parent_dir = self.data_files[-1]
|
280
358
|
spectra = self.get_data()
|
281
359
|
reports = []
|
282
360
|
for i, child_dir in enumerate(parent_dir.child_dirs):
|
283
361
|
metd_report = self.get_report_details(child_dir, report_type)
|
284
|
-
child_spectra: List[AgilentHPLCChromatogram] = list(
|
362
|
+
child_spectra: List[AgilentHPLCChromatogram] = list(
|
363
|
+
spectra[i].__dict__.values()
|
364
|
+
)
|
285
365
|
for j, signal in enumerate(metd_report.signals):
|
286
366
|
possible_data = child_spectra[j]
|
287
367
|
if len(possible_data.x) > 0:
|