pychemstation 0.10.4__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.
- pychemstation/analysis/chromatogram.py +20 -0
- pychemstation/analysis/process_report.py +125 -63
- pychemstation/control/__init__.py +2 -0
- pychemstation/control/controllers/__init__.py +2 -3
- pychemstation/control/controllers/comm.py +37 -22
- pychemstation/control/controllers/devices/device.py +56 -31
- pychemstation/control/controllers/devices/injector.py +19 -7
- pychemstation/control/controllers/tables/__init__.py +4 -0
- pychemstation/control/controllers/tables/method.py +50 -93
- pychemstation/control/controllers/tables/sequence.py +134 -95
- pychemstation/control/controllers/tables/table.py +102 -78
- pychemstation/control/hplc.py +11 -17
- pychemstation/utils/injector_types.py +1 -1
- pychemstation/utils/macro.py +2 -2
- pychemstation/utils/method_types.py +1 -1
- {pychemstation-0.10.4.dist-info → pychemstation-0.10.5.dist-info}/METADATA +4 -4
- pychemstation-0.10.5.dist-info/RECORD +36 -0
- pychemstation/control/controllers/tables/ms.py +0 -24
- pychemstation-0.10.4.dist-info/RECORD +0 -37
- {pychemstation-0.10.4.dist-info → pychemstation-0.10.5.dist-info}/WHEEL +0 -0
- {pychemstation-0.10.4.dist-info → pychemstation-0.10.5.dist-info}/licenses/LICENSE +0 -0
@@ -1,14 +1,14 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import os
|
2
4
|
import time
|
3
5
|
import warnings
|
4
|
-
from typing import
|
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
12
|
from pychemstation.analysis.chromatogram import (
|
13
13
|
TIME_FORMAT,
|
14
14
|
AgilentChannelChromatogramData,
|
@@ -22,7 +22,7 @@ from ....utils.method_types import (
|
|
22
22
|
PType,
|
23
23
|
TimeTableEntry,
|
24
24
|
)
|
25
|
-
from ....utils.table_types import RegisterFlag,
|
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
|
|
@@ -61,20 +61,22 @@ class MethodController(TableController):
|
|
61
61
|
return "ERROR"
|
62
62
|
|
63
63
|
def get_method_params(self) -> HPLCMethodParams:
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
+
),
|
78
|
+
)
|
79
|
+
raise ValueError("Communication controller is offline!")
|
78
80
|
|
79
81
|
def get_row(self, row: int) -> TimeTableEntry:
|
80
82
|
flow = None
|
@@ -89,6 +91,9 @@ class MethodController(TableController):
|
|
89
91
|
except RuntimeError:
|
90
92
|
pass
|
91
93
|
|
94
|
+
if om is None and flow is None:
|
95
|
+
raise ValueError("Both flow and organic modifier is None")
|
96
|
+
|
92
97
|
return TimeTableEntry(
|
93
98
|
start_time=self.get_num(row, RegisterFlag.TIME),
|
94
99
|
organic_modifer=om,
|
@@ -97,7 +102,7 @@ class MethodController(TableController):
|
|
97
102
|
|
98
103
|
def get_timetable(self, rows: int):
|
99
104
|
uncoalesced_timetable_rows = [self.get_row(r + 1) for r in range(rows)]
|
100
|
-
timetable_rows = {}
|
105
|
+
timetable_rows: Dict[str, TimeTableEntry] = {}
|
101
106
|
for row in uncoalesced_timetable_rows:
|
102
107
|
time_key = str(row.start_time)
|
103
108
|
if time_key not in timetable_rows.keys():
|
@@ -141,20 +146,24 @@ class MethodController(TableController):
|
|
141
146
|
return method_name
|
142
147
|
|
143
148
|
def get_post_time(self) -> Union[int, float]:
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
+
)
|
148
155
|
)
|
149
|
-
)
|
156
|
+
raise ValueError("Communication controller is not online!")
|
150
157
|
|
151
158
|
def get_stop_time(self) -> Union[int, float]:
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
+
)
|
156
165
|
)
|
157
|
-
)
|
166
|
+
raise ValueError("Communication controller is not online!")
|
158
167
|
|
159
168
|
def get_total_runtime(self) -> Union[int, float]:
|
160
169
|
"""Returns total method runtime in minutes."""
|
@@ -198,60 +207,6 @@ class MethodController(TableController):
|
|
198
207
|
assert parsed_response == f"{method_name}.M", "Switching Methods failed."
|
199
208
|
self.table_state = None
|
200
209
|
|
201
|
-
def load_from_disk(self, method_name: str) -> MethodDetails:
|
202
|
-
"""
|
203
|
-
Retrieve method details of an existing method. Don't need to append ".M" to the end. This assumes the
|
204
|
-
organic modifier is in Channel B and that Channel A contains the aq layer. Additionally, assumes
|
205
|
-
only two solvents are being used.
|
206
|
-
|
207
|
-
:param method_name: name of method to load details of
|
208
|
-
:raises FileNotFoundError: Method does not exist
|
209
|
-
:return: method details
|
210
|
-
"""
|
211
|
-
warnings.warn("This method is not actively maintained.")
|
212
|
-
method_folder = f"{method_name}.M"
|
213
|
-
method_path = os.path.join(
|
214
|
-
self.src, method_folder, "AgilentPumpDriver1.RapidControl.MethodXML.xml"
|
215
|
-
)
|
216
|
-
dad_path = os.path.join(
|
217
|
-
self.src,
|
218
|
-
method_folder,
|
219
|
-
"Agilent1200erDadDriver1.RapidControl.MethodXML.xml",
|
220
|
-
)
|
221
|
-
|
222
|
-
if os.path.exists(os.path.join(self.src, f"{method_name}.M")):
|
223
|
-
parser = XmlParser()
|
224
|
-
method = parser.parse(method_path, PumpMethod)
|
225
|
-
dad = parser.parse(dad_path, DadMethod)
|
226
|
-
|
227
|
-
organic_modifier: Optional[SolventElement] = None
|
228
|
-
|
229
|
-
if len(method.solvent_composition.solvent_element) == 2:
|
230
|
-
for solvent in method.solvent_composition.solvent_element:
|
231
|
-
if solvent.channel == "Channel_B":
|
232
|
-
organic_modifier = solvent
|
233
|
-
|
234
|
-
self.table_state = MethodDetails(
|
235
|
-
name=method_name,
|
236
|
-
params=HPLCMethodParams(
|
237
|
-
organic_modifier=organic_modifier.percentage, flow=method.flow
|
238
|
-
),
|
239
|
-
stop_time=method.stop_time.stop_time_value,
|
240
|
-
post_time=method.post_time.post_time_value,
|
241
|
-
timetable=[
|
242
|
-
TimeTableEntry(
|
243
|
-
start_time=tte.time,
|
244
|
-
organic_modifer=tte.percent_b,
|
245
|
-
flow=method.flow,
|
246
|
-
)
|
247
|
-
for tte in method.timetable.timetable_entry
|
248
|
-
],
|
249
|
-
dad_wavelengthes=dad.signals.signal,
|
250
|
-
)
|
251
|
-
return self.table_state
|
252
|
-
else:
|
253
|
-
raise FileNotFoundError
|
254
|
-
|
255
210
|
def edit(self, updated_method: MethodDetails, save: bool):
|
256
211
|
"""Updated the currently loaded method in ChemStation with provided values.
|
257
212
|
|
@@ -317,13 +272,13 @@ class MethodController(TableController):
|
|
317
272
|
self,
|
318
273
|
new_flow: Union[int, float],
|
319
274
|
new_initial_om: Union[int, float],
|
320
|
-
new_stop_time: Union[int, float],
|
321
|
-
new_post_time: Union[int, float],
|
275
|
+
new_stop_time: Union[int, float] | None,
|
276
|
+
new_post_time: Union[int, float] | None,
|
322
277
|
):
|
323
278
|
self.delete_table()
|
324
279
|
self.edit_initial_om(new_initial_om)
|
325
280
|
self.edit_flow(new_flow)
|
326
|
-
if
|
281
|
+
if new_stop_time:
|
327
282
|
self.edit_stop_time(new_stop_time)
|
328
283
|
else:
|
329
284
|
self._update_param(
|
@@ -333,7 +288,7 @@ class MethodController(TableController):
|
|
333
288
|
ptype=PType.STR,
|
334
289
|
)
|
335
290
|
)
|
336
|
-
if
|
291
|
+
if new_post_time:
|
337
292
|
self.edit_post_time(new_post_time)
|
338
293
|
else:
|
339
294
|
self._update_param(
|
@@ -502,21 +457,23 @@ class MethodController(TableController):
|
|
502
457
|
warning = f"Data folder {self.data_files[-1]} may not exist, returning and will check again after run is done."
|
503
458
|
warnings.warn(warning)
|
504
459
|
|
505
|
-
def fuzzy_match_most_recent_folder(self, most_recent_folder: T) -> Result[
|
506
|
-
if
|
507
|
-
|
508
|
-
|
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.")
|
509
466
|
|
510
467
|
def get_data(
|
511
468
|
self, custom_path: Optional[str] = None
|
512
469
|
) -> AgilentChannelChromatogramData:
|
513
470
|
custom_path = custom_path if custom_path else self.data_files[-1]
|
514
471
|
self.get_spectrum_at_channels(custom_path)
|
515
|
-
return AgilentChannelChromatogramData(
|
472
|
+
return AgilentChannelChromatogramData.from_dict(self.spectra)
|
516
473
|
|
517
474
|
def get_data_uv(
|
518
475
|
self, custom_path: Optional[str] = None
|
519
|
-
) ->
|
476
|
+
) -> dict[int, AgilentHPLCChromatogram]:
|
520
477
|
custom_path = custom_path if custom_path else self.data_files[-1]
|
521
478
|
self.get_uv_spectrum(custom_path)
|
522
479
|
return self.uv
|
@@ -1,18 +1,20 @@
|
|
1
1
|
import os
|
2
2
|
import time
|
3
3
|
import warnings
|
4
|
-
from typing import Dict, List, Optional
|
4
|
+
from typing import Dict, List, Optional, Any
|
5
5
|
|
6
6
|
from result import Err, Ok, Result
|
7
7
|
from typing_extensions import override
|
8
8
|
|
9
|
-
from ....analysis.process_report import AgilentReport, ReportType
|
10
|
-
from ....control.controllers.comm import CommunicationController
|
11
9
|
from pychemstation.analysis.chromatogram import (
|
12
10
|
SEQUENCE_TIME_FORMAT,
|
13
11
|
AgilentChannelChromatogramData,
|
14
12
|
AgilentHPLCChromatogram,
|
15
13
|
)
|
14
|
+
from . import MethodController
|
15
|
+
|
16
|
+
from ....analysis.process_report import AgilentReport, ReportType
|
17
|
+
from ....control.controllers.comm import CommunicationController
|
16
18
|
from ....utils.macro import Command
|
17
19
|
from ....utils.sequence_types import (
|
18
20
|
InjectionSource,
|
@@ -21,9 +23,8 @@ from ....utils.sequence_types import (
|
|
21
23
|
SequenceEntry,
|
22
24
|
SequenceTable,
|
23
25
|
)
|
24
|
-
from ....utils.table_types import RegisterFlag, Table
|
25
|
-
from ....utils.tray_types import FiftyFourVialPlate, TenVialColumn
|
26
|
-
from .. import MethodController
|
26
|
+
from ....utils.table_types import RegisterFlag, T, Table
|
27
|
+
from ....utils.tray_types import FiftyFourVialPlate, TenVialColumn, Tray
|
27
28
|
from .table import TableController
|
28
29
|
|
29
30
|
|
@@ -34,7 +35,7 @@ class SequenceController(TableController):
|
|
34
35
|
|
35
36
|
def __init__(
|
36
37
|
self,
|
37
|
-
controller: CommunicationController,
|
38
|
+
controller: Optional[CommunicationController],
|
38
39
|
method_controller: MethodController,
|
39
40
|
src: str,
|
40
41
|
data_dirs: List[str],
|
@@ -57,7 +58,7 @@ class SequenceController(TableController):
|
|
57
58
|
seq_name = self.receive()
|
58
59
|
|
59
60
|
if rows.is_ok() and seq_name.is_ok():
|
60
|
-
self.table_state = SequenceTable(
|
61
|
+
self.table_state: SequenceTable = SequenceTable(
|
61
62
|
name=seq_name.ok_value.string_response.partition(".S")[0],
|
62
63
|
rows=[
|
63
64
|
self.get_row(r + 1) for r in range(int(rows.ok_value.num_response))
|
@@ -66,19 +67,37 @@ class SequenceController(TableController):
|
|
66
67
|
return self.table_state
|
67
68
|
raise RuntimeError(rows.err_value)
|
68
69
|
|
70
|
+
def try_int(self, val: Any) -> Optional[int]:
|
71
|
+
try:
|
72
|
+
return int(val)
|
73
|
+
except ValueError:
|
74
|
+
return None
|
75
|
+
|
76
|
+
def try_float(self, val: Any) -> Optional[float]:
|
77
|
+
try:
|
78
|
+
return float(val)
|
79
|
+
except ValueError:
|
80
|
+
return None
|
81
|
+
|
82
|
+
def try_vial_location(self, val: Any) -> Tray:
|
83
|
+
try:
|
84
|
+
return (
|
85
|
+
TenVialColumn(val)
|
86
|
+
if val <= 10
|
87
|
+
else FiftyFourVialPlate.from_int(num=val)
|
88
|
+
)
|
89
|
+
except ValueError:
|
90
|
+
raise ValueError("Expected vial location, is empty.")
|
91
|
+
|
69
92
|
def get_row(self, row: int) -> SequenceEntry:
|
70
93
|
sample_name = self.get_text(row, RegisterFlag.NAME)
|
71
|
-
vial_location =
|
94
|
+
vial_location = self.try_int(self.get_num(row, RegisterFlag.VIAL_LOCATION))
|
72
95
|
method = self.get_text(row, RegisterFlag.METHOD)
|
73
|
-
num_inj =
|
74
|
-
inj_vol =
|
96
|
+
num_inj = self.try_int(self.get_num(row, RegisterFlag.NUM_INJ))
|
97
|
+
inj_vol = self.try_float(self.get_text(row, RegisterFlag.INJ_VOL))
|
75
98
|
inj_source = InjectionSource(self.get_text(row, RegisterFlag.INJ_SOR))
|
76
99
|
sample_type = SampleType(self.get_num(row, RegisterFlag.SAMPLE_TYPE))
|
77
|
-
vial_enum = (
|
78
|
-
TenVialColumn(vial_location)
|
79
|
-
if vial_location <= 10
|
80
|
-
else FiftyFourVialPlate.from_int(num=vial_location)
|
81
|
-
)
|
100
|
+
vial_enum = self.try_vial_location(vial_location)
|
82
101
|
return SequenceEntry(
|
83
102
|
sample_name=sample_name,
|
84
103
|
vial_location=vial_enum,
|
@@ -113,7 +132,7 @@ class SequenceController(TableController):
|
|
113
132
|
parsed_response = self.receive().ok_value.string_response
|
114
133
|
|
115
134
|
assert parsed_response == f"{seq_name}.S", "Switching sequence failed."
|
116
|
-
self.table_state =
|
135
|
+
self.table_state = self.load()
|
117
136
|
|
118
137
|
def edit(self, sequence_table: SequenceTable):
|
119
138
|
"""
|
@@ -157,12 +176,13 @@ class SequenceController(TableController):
|
|
157
176
|
num_rows = self.get_num_rows()
|
158
177
|
if row.vial_location:
|
159
178
|
loc = row.vial_location
|
179
|
+
loc_num = -1
|
160
180
|
if isinstance(loc, TenVialColumn):
|
161
|
-
|
181
|
+
loc_num = loc.value
|
162
182
|
elif isinstance(loc, FiftyFourVialPlate):
|
163
|
-
|
183
|
+
loc_num = loc.value()
|
164
184
|
self._edit_row_num(
|
165
|
-
row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=
|
185
|
+
row=row_num, col_name=RegisterFlag.VIAL_LOCATION, val=loc_num
|
166
186
|
)
|
167
187
|
if row.method:
|
168
188
|
method_dir = self.method_controller.src
|
@@ -177,7 +197,7 @@ class SequenceController(TableController):
|
|
177
197
|
)
|
178
198
|
if row.inj_vol:
|
179
199
|
self._edit_row_text(
|
180
|
-
row=row_num, col_name=RegisterFlag.INJ_VOL, val=row.inj_vol
|
200
|
+
row=row_num, col_name=RegisterFlag.INJ_VOL, val=str(row.inj_vol)
|
181
201
|
)
|
182
202
|
if row.inj_source:
|
183
203
|
self._edit_row_text(
|
@@ -210,26 +230,32 @@ class SequenceController(TableController):
|
|
210
230
|
under the <data_dir>/<sequence table name> folder.
|
211
231
|
Device must be ready.
|
212
232
|
"""
|
213
|
-
self.controller
|
214
|
-
|
233
|
+
if self.controller:
|
234
|
+
self.controller.send(Command.SAVE_METHOD_CMD)
|
235
|
+
self.controller.send(Command.SAVE_SEQUENCE_CMD)
|
236
|
+
else:
|
237
|
+
raise ValueError("Controller is offline!")
|
215
238
|
|
216
239
|
if not self.table_state:
|
217
240
|
self.table_state = self.load()
|
218
241
|
|
219
|
-
total_runtime = 0
|
242
|
+
total_runtime = 0.0
|
220
243
|
for entry in self.table_state.rows:
|
221
244
|
curr_method_runtime = self.method_controller.get_total_runtime()
|
222
245
|
loaded_method = self.method_controller.get_method_name().removesuffix(".M")
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
246
|
+
if entry.method:
|
247
|
+
method_path = entry.method.split(sep="\\")
|
248
|
+
method_name = method_path[-1]
|
249
|
+
if loaded_method != method_name:
|
250
|
+
method_dir = (
|
251
|
+
"\\".join(method_path[:-1]) + "\\"
|
252
|
+
if len(method_path) > 1
|
253
|
+
else None
|
254
|
+
)
|
255
|
+
self.method_controller.switch(
|
256
|
+
method_name=method_name, alt_method_dir=method_dir
|
257
|
+
)
|
258
|
+
curr_method_runtime = self.method_controller.get_total_runtime()
|
233
259
|
total_runtime += curr_method_runtime
|
234
260
|
|
235
261
|
timestamp = time.strftime(SEQUENCE_TIME_FORMAT)
|
@@ -254,91 +280,100 @@ class SequenceController(TableController):
|
|
254
280
|
|
255
281
|
@override
|
256
282
|
def fuzzy_match_most_recent_folder(
|
257
|
-
self, most_recent_folder:
|
283
|
+
self, most_recent_folder: T
|
258
284
|
) -> Result[SequenceDataFiles, str]:
|
259
|
-
if
|
260
|
-
|
261
|
-
|
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
|
-
]
|
269
|
-
return Ok(most_recent_folder)
|
270
|
-
|
271
|
-
try:
|
272
|
-
potential_folders = []
|
273
|
-
for d in self.data_dirs:
|
274
|
-
subdirs = [x[0] for x in os.walk(d)]
|
285
|
+
if isinstance(most_recent_folder, SequenceDataFiles):
|
286
|
+
if os.path.isdir(most_recent_folder.dir):
|
287
|
+
subdirs = [x[0] for x in os.walk(most_recent_folder.dir)]
|
275
288
|
potential_folders = sorted(
|
276
289
|
list(filter(lambda d: most_recent_folder.dir in d, subdirs))
|
277
290
|
)
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
split_folder = path.split(os.sep)
|
285
|
-
if most_recent_folder.dir in split_folder[-1]:
|
286
|
-
parent_dirs.append(folder)
|
287
|
-
parent_dir = sorted(parent_dirs, reverse=True)[0]
|
288
|
-
|
289
|
-
potential_folders = []
|
290
|
-
for d in self.data_dirs:
|
291
|
-
subdirs = [x[0] for x in os.walk(d)]
|
292
|
-
potential_folders = sorted(
|
293
|
-
list(filter(lambda d: parent_dir in d, subdirs))
|
294
|
-
)
|
295
|
-
if len(potential_folders) > 0:
|
296
|
-
break
|
297
|
-
assert len(potential_folders) > 0
|
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
|
-
]
|
303
|
-
return Ok(most_recent_folder)
|
304
|
-
except Exception as e:
|
305
|
-
error = f"Failed to get sequence folder: {e}"
|
306
|
-
return Err(error)
|
291
|
+
most_recent_folder.child_dirs = [
|
292
|
+
f
|
293
|
+
for f in potential_folders
|
294
|
+
if most_recent_folder.dir in f and ".M" not in f and ".D" in f
|
295
|
+
]
|
296
|
+
return Ok(most_recent_folder)
|
307
297
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
298
|
+
try:
|
299
|
+
potential_folders = []
|
300
|
+
for d in self.data_dirs:
|
301
|
+
subdirs = [x[0] for x in os.walk(d)]
|
302
|
+
potential_folders = sorted(
|
303
|
+
list(filter(lambda d: most_recent_folder.dir in d, subdirs))
|
304
|
+
)
|
305
|
+
if len(potential_folders) > 0:
|
306
|
+
break
|
307
|
+
assert len(potential_folders) > 0
|
308
|
+
parent_dirs = []
|
309
|
+
for folder in potential_folders:
|
310
|
+
path = os.path.normpath(folder)
|
311
|
+
split_folder = path.split(os.sep)
|
312
|
+
if most_recent_folder.dir in split_folder[-1]:
|
313
|
+
parent_dirs.append(folder)
|
314
|
+
parent_dir = sorted(parent_dirs, reverse=True)[0]
|
315
|
+
|
316
|
+
potential_folders = []
|
317
|
+
for d in self.data_dirs:
|
318
|
+
subdirs = [x[0] for x in os.walk(d)]
|
319
|
+
potential_folders = sorted(
|
320
|
+
list(filter(lambda d: parent_dir in d, subdirs))
|
321
|
+
)
|
322
|
+
if len(potential_folders) > 0:
|
323
|
+
break
|
324
|
+
assert len(potential_folders) > 0
|
325
|
+
most_recent_folder.child_dirs = [
|
326
|
+
f
|
327
|
+
for f in potential_folders
|
328
|
+
if parent_dir in f and ".M" not in f and ".D" in f
|
329
|
+
]
|
330
|
+
return Ok(most_recent_folder)
|
331
|
+
except Exception as e:
|
332
|
+
error = f"Failed to get sequence folder: {e}"
|
333
|
+
return Err(error)
|
334
|
+
return Err("Expected SequenceDataFile type.")
|
335
|
+
|
336
|
+
def get_data_mult_uv(self, custom_path: Optional[str] = None):
|
337
|
+
seq_data_dir = (
|
312
338
|
SequenceDataFiles(dir=custom_path, child_dirs=[], sequence_name="")
|
313
339
|
if custom_path
|
314
340
|
else self.data_files[-1]
|
315
341
|
)
|
316
|
-
if len(
|
342
|
+
if len(seq_data_dir.child_dirs) == 0:
|
317
343
|
self.data_files[-1] = self.fuzzy_match_most_recent_folder(
|
318
|
-
|
344
|
+
seq_data_dir
|
319
345
|
).ok_value
|
320
346
|
all_w_spectra: List[Dict[int, AgilentHPLCChromatogram]] = []
|
321
347
|
for row in self.data_files[-1].child_dirs:
|
322
|
-
self.
|
323
|
-
all_w_spectra.append(self.uv)
|
348
|
+
all_w_spectra.append(self.get_data_uv(custom_path=row))
|
324
349
|
return all_w_spectra
|
325
350
|
|
351
|
+
def get_data_uv(
|
352
|
+
self, custom_path: Optional[str] = None
|
353
|
+
) -> Dict[int, AgilentHPLCChromatogram]:
|
354
|
+
if isinstance(custom_path, str):
|
355
|
+
self.get_uv_spectrum(custom_path)
|
356
|
+
return self.uv
|
357
|
+
raise ValueError(
|
358
|
+
"Path should exist when calling from sequence. Provide a child path (contains the method)."
|
359
|
+
)
|
360
|
+
|
326
361
|
def get_data(
|
327
362
|
self, custom_path: Optional[str] = None
|
328
363
|
) -> List[AgilentChannelChromatogramData]:
|
329
|
-
|
364
|
+
seq_file_dir = (
|
330
365
|
SequenceDataFiles(dir=custom_path, child_dirs=[], sequence_name="")
|
331
366
|
if custom_path
|
332
367
|
else self.data_files[-1]
|
333
368
|
)
|
334
|
-
if len(
|
369
|
+
if len(seq_file_dir.child_dirs) == 0:
|
335
370
|
self.data_files[-1] = self.fuzzy_match_most_recent_folder(
|
336
|
-
|
371
|
+
seq_file_dir
|
337
372
|
).ok_value
|
338
373
|
spectra: List[AgilentChannelChromatogramData] = []
|
339
374
|
for row in self.data_files[-1].child_dirs:
|
340
375
|
self.get_spectrum_at_channels(row)
|
341
|
-
spectra.append(AgilentChannelChromatogramData(
|
376
|
+
spectra.append(AgilentChannelChromatogramData.from_dict(self.spectra))
|
342
377
|
return spectra
|
343
378
|
|
344
379
|
def get_report(
|
@@ -363,8 +398,12 @@ class SequenceController(TableController):
|
|
363
398
|
spectra[i].__dict__.values()
|
364
399
|
)
|
365
400
|
for j, signal in enumerate(metd_report.signals):
|
366
|
-
|
367
|
-
|
368
|
-
|
401
|
+
assert len(metd_report.signals) <= len(child_spectra)
|
402
|
+
try:
|
403
|
+
possible_data = child_spectra[j]
|
404
|
+
if len(possible_data.x) > 0:
|
405
|
+
signal.data = possible_data
|
406
|
+
except IndexError:
|
407
|
+
raise ValueError(j)
|
369
408
|
reports.append(metd_report)
|
370
409
|
return reports
|