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.
- pychemstation/analysis/base_spectrum.py +14 -15
- pychemstation/analysis/chromatogram.py +7 -8
- pychemstation/analysis/process_report.py +10 -16
- pychemstation/control/README.md +1 -1
- pychemstation/control/controllers/__init__.py +2 -1
- pychemstation/control/controllers/comm.py +33 -27
- pychemstation/control/controllers/data_aq/method.py +233 -79
- pychemstation/control/controllers/data_aq/sequence.py +104 -84
- pychemstation/control/controllers/devices/injector.py +3 -3
- pychemstation/control/hplc.py +53 -41
- pychemstation/utils/__init__.py +23 -0
- pychemstation/{control/controllers → utils}/abc_tables/abc_comm.py +15 -13
- pychemstation/{control/controllers → utils}/abc_tables/device.py +9 -2
- pychemstation/{control/controllers → utils}/abc_tables/run.py +45 -37
- pychemstation/{control/controllers → utils}/abc_tables/table.py +32 -29
- pychemstation/utils/macro.py +7 -2
- pychemstation/utils/method_types.py +13 -14
- pychemstation/utils/mocking/mock_comm.py +25 -2
- pychemstation/utils/mocking/mock_hplc.py +29 -1
- pychemstation/utils/num_utils.py +3 -3
- pychemstation/utils/sequence_types.py +30 -14
- pychemstation/utils/spec_utils.py +42 -66
- pychemstation/utils/table_types.py +15 -2
- pychemstation/utils/tray_types.py +28 -16
- {pychemstation-0.10.7.dist-info → pychemstation-0.10.8.dist-info}/METADATA +1 -7
- pychemstation-0.10.8.dist-info/RECORD +41 -0
- pychemstation/utils/pump_types.py +0 -7
- pychemstation-0.10.7.dist-info/RECORD +0 -42
- /pychemstation/{control/controllers → utils}/abc_tables/__init__.py +0 -0
- {pychemstation-0.10.7.dist-info → pychemstation-0.10.8.dist-info}/WHEEL +0 -0
- {pychemstation-0.10.7.dist-info → pychemstation-0.10.8.dist-info}/licenses/LICENSE +0 -0
@@ -2,6 +2,7 @@ import logging
|
|
2
2
|
import os
|
3
3
|
import pickle
|
4
4
|
from abc import ABC, abstractmethod
|
5
|
+
from typing import Optional, Union
|
5
6
|
|
6
7
|
import matplotlib.pyplot as plt
|
7
8
|
import numpy as np
|
@@ -43,16 +44,15 @@ class AbstractSpectrum(ABC):
|
|
43
44
|
"baseline",
|
44
45
|
}
|
45
46
|
|
46
|
-
def __init__(
|
47
|
+
def __init__(
|
48
|
+
self, path: Optional[Union[str, bool]] = None, autosaving: Optional[bool] = True
|
49
|
+
):
|
47
50
|
"""Default constructor, loads properties into instance namespace.
|
48
51
|
|
49
52
|
Can be redefined in ancestor classes.
|
50
53
|
|
51
|
-
|
52
|
-
|
53
|
-
If omitted, uses ".//spectrum". If False - no folder created.
|
54
|
-
autosaving (bool, optional): If the True (default) will save the
|
55
|
-
spectrum when the new one is loaded. Will drop otherwise.
|
54
|
+
:param path: Valid path to save data to. If omitted, uses ".//spectrum". If False - no folder created.
|
55
|
+
:param autosaving: If the True (default) will save the spectrum when the new one is loaded. Will drop otherwise.
|
56
56
|
"""
|
57
57
|
|
58
58
|
self.autosaving = autosaving
|
@@ -70,10 +70,10 @@ class AbstractSpectrum(ABC):
|
|
70
70
|
self.path = os.path.join(".", "spectrum")
|
71
71
|
os.makedirs(self.path, exist_ok=True)
|
72
72
|
else:
|
73
|
-
|
73
|
+
if isinstance(path, str):
|
74
74
|
os.makedirs(path, exist_ok=True)
|
75
75
|
self.path = path
|
76
|
-
|
76
|
+
else:
|
77
77
|
self.path = "."
|
78
78
|
|
79
79
|
# creating logger
|
@@ -86,15 +86,14 @@ class AbstractSpectrum(ABC):
|
|
86
86
|
self.__init__(path=self.path, autosaving=self.autosaving)
|
87
87
|
|
88
88
|
@abstractmethod
|
89
|
-
def load_spectrum(self, x, y, timestamp):
|
89
|
+
def load_spectrum(self, x: np.ndarray, y: np.ndarray, timestamp: float):
|
90
90
|
"""Loads the spectral data.
|
91
91
|
|
92
92
|
This method must be redefined in ancestor classes.
|
93
93
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
timestamp (float): Timestamp to the corresponding spectrum.
|
94
|
+
:param x: An array with data to be plotted as "x" axis.
|
95
|
+
:param y: An array with data to be plotted as "y" axis.
|
96
|
+
:param timestamp: Timestamp to the corresponding spectrum.
|
98
97
|
"""
|
99
98
|
|
100
99
|
try:
|
@@ -107,8 +106,8 @@ class AbstractSpectrum(ABC):
|
|
107
106
|
self.save_data()
|
108
107
|
self._dump()
|
109
108
|
|
110
|
-
self.x = x
|
111
|
-
self.y = y
|
109
|
+
self.x: np.ndarray = x
|
110
|
+
self.y: np.ndarray = y
|
112
111
|
self.timestamp = timestamp
|
113
112
|
|
114
113
|
def save_data(self, filename=None, verbose=False):
|
@@ -3,7 +3,7 @@
|
|
3
3
|
import os
|
4
4
|
import time
|
5
5
|
from dataclasses import dataclass
|
6
|
-
from typing import Dict
|
6
|
+
from typing import Dict, Tuple
|
7
7
|
|
8
8
|
import numpy as np
|
9
9
|
|
@@ -80,15 +80,14 @@ class AgilentHPLCChromatogram(AbstractSpectrum):
|
|
80
80
|
|
81
81
|
### PUBLIC METHODS TO LOAD RAW DATA ###
|
82
82
|
|
83
|
-
def extract_rawdata(
|
84
|
-
|
85
|
-
|
83
|
+
def extract_rawdata(
|
84
|
+
self, experiment_dir: str, channel: str
|
85
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
86
|
+
"""Reads raw data from Chemstation .CH files.
|
86
87
|
|
87
|
-
|
88
|
-
experiment_dir: .D directory with the .CH files
|
88
|
+
:param experiment_dir: .D directory with the .CH files
|
89
89
|
|
90
|
-
|
91
|
-
np.array(times), np.array(values) Raw chromatogram data
|
90
|
+
:returns: np.array(times), np.array(values) Raw chromatogram data
|
92
91
|
"""
|
93
92
|
filename = os.path.join(experiment_dir, f"DAD1{channel}")
|
94
93
|
npz_file = filename + ".npz"
|
@@ -65,8 +65,7 @@ class ReportProcessor(abc.ABC):
|
|
65
65
|
|
66
66
|
class CSVProcessor(ReportProcessor):
|
67
67
|
def __init__(self, path: str):
|
68
|
-
"""
|
69
|
-
Class to process reports in CSV form.
|
68
|
+
"""Class to process reports in CSV form.
|
70
69
|
|
71
70
|
:param path: the parent folder that contains the CSV report(s) to parse.
|
72
71
|
"""
|
@@ -84,7 +83,9 @@ class CSVProcessor(ReportProcessor):
|
|
84
83
|
if "00" in name and file_extension.lower() == "csv":
|
85
84
|
prefix, _, _ = name.partition("00")
|
86
85
|
return prefix
|
87
|
-
raise FileNotFoundError(
|
86
|
+
raise FileNotFoundError(
|
87
|
+
"Couldn't find the prefix for CSV, please make sure the post-run settings generate a CSV."
|
88
|
+
)
|
88
89
|
|
89
90
|
def report_contains(self, labels: List[str], want: List[str]):
|
90
91
|
for label in labels:
|
@@ -104,8 +105,7 @@ class CSVProcessor(ReportProcessor):
|
|
104
105
|
return all_labels_seen
|
105
106
|
|
106
107
|
def process_report(self) -> Result[AgilentReport, AnyStr]:
|
107
|
-
"""
|
108
|
-
Method to parse details from CSV report.
|
108
|
+
"""Method to parse details from CSV report.
|
109
109
|
|
110
110
|
:return: subset of complete report details, specifically the sample location, solvents in pumps,
|
111
111
|
and list of peaks at each wavelength channel.
|
@@ -176,9 +176,7 @@ class CSVProcessor(ReportProcessor):
|
|
176
176
|
|
177
177
|
|
178
178
|
class TXTProcessor(ReportProcessor):
|
179
|
-
"""
|
180
|
-
Regex matches for column and unit combinations, courtesy of Veronica Lai.
|
181
|
-
"""
|
179
|
+
"""Regex matches for column and unit combinations, courtesy of Veronica Lai."""
|
182
180
|
|
183
181
|
_column_re_dictionary = {
|
184
182
|
"Peak": { # peak index
|
@@ -212,8 +210,7 @@ class TXTProcessor(ReportProcessor):
|
|
212
210
|
max_ret_time: int = 999,
|
213
211
|
target_wavelength_range=None,
|
214
212
|
):
|
215
|
-
"""
|
216
|
-
Class to process reports in CSV form.
|
213
|
+
"""Class to process reports in CSV form.
|
217
214
|
|
218
215
|
:param path: the parent folder that contains the CSV report(s) to parse.
|
219
216
|
:param min_ret_time: peaks after this value (min) will be returned
|
@@ -228,8 +225,7 @@ class TXTProcessor(ReportProcessor):
|
|
228
225
|
super().__init__(path)
|
229
226
|
|
230
227
|
def process_report(self) -> Result[AgilentReport, Union[AnyStr, Exception]]:
|
231
|
-
"""
|
232
|
-
Method to parse details from CSV report.
|
228
|
+
"""Method to parse details from CSV report.
|
233
229
|
If you want more functionality, use `aghplctools`.
|
234
230
|
`from aghplctools.ingestion.text import pull_hplc_area_from_txt`
|
235
231
|
`signals = pull_hplc_area_from_txt(file_path)`
|
@@ -281,8 +277,7 @@ class TXTProcessor(ReportProcessor):
|
|
281
277
|
return Err(e)
|
282
278
|
|
283
279
|
def parse_area_report(self, report_text: str) -> Dict:
|
284
|
-
"""
|
285
|
-
Interprets report text and parses the area report section, converting it to dictionary.
|
280
|
+
"""Interprets report text and parses the area report section, converting it to dictionary.
|
286
281
|
Courtesy of Veronica Lai.
|
287
282
|
|
288
283
|
:param report_text: plain text version of the report.
|
@@ -340,8 +335,7 @@ class TXTProcessor(ReportProcessor):
|
|
340
335
|
return signals
|
341
336
|
|
342
337
|
def build_peak_regex(self, signal_table: str) -> Pattern[str] | None:
|
343
|
-
"""
|
344
|
-
Builds a peak regex from a signal table. Courtesy of Veronica Lai.
|
338
|
+
"""Builds a peak regex from a signal table. Courtesy of Veronica Lai.
|
345
339
|
|
346
340
|
:param signal_table: block of lines associated with an area table
|
347
341
|
:return: peak line regex object (<=3.6 _sre.SRE_PATTERN, >=3.7 re.Pattern)
|
pychemstation/control/README.md
CHANGED
@@ -15,18 +15,23 @@ from typing import Optional, Union, Tuple, List
|
|
15
15
|
|
16
16
|
from result import Err, Ok, Result
|
17
17
|
|
18
|
+
from ...utils.abc_tables.abc_comm import ABCCommunicationController
|
18
19
|
from ...utils.macro import (
|
19
20
|
Command,
|
20
21
|
HPLCErrorStatus,
|
21
22
|
Status,
|
22
23
|
str_to_status,
|
23
24
|
)
|
24
|
-
from .abc_tables.abc_comm import ABCCommunicationController
|
25
25
|
|
26
26
|
|
27
27
|
class CommunicationController(ABCCommunicationController):
|
28
|
-
"""
|
29
|
-
|
28
|
+
"""Class that communicates with Agilent using Macros
|
29
|
+
|
30
|
+
:param comm_dir: the complete directory path that was used in the MACRO file, common file that pychemstation and Chemstation use to communicate.
|
31
|
+
:param cmd_file: name of the write file that pychemstation writes MACROs to, in `comm_dir`
|
32
|
+
:param reply_file: name of the read file that Chemstation replies to, in `comm_dir
|
33
|
+
:param offline: whether or not communication with Chemstation is to be established
|
34
|
+
:param debug: if True, prints all send MACROs to an out.txt file
|
30
35
|
"""
|
31
36
|
|
32
37
|
def __init__(
|
@@ -37,12 +42,6 @@ class CommunicationController(ABCCommunicationController):
|
|
37
42
|
offline: bool = False,
|
38
43
|
debug: bool = False,
|
39
44
|
):
|
40
|
-
"""
|
41
|
-
:param comm_dir:
|
42
|
-
:param cmd_file: Name of command file
|
43
|
-
:param reply_file: Name of reply file
|
44
|
-
:param debug: whether to save log of sent commands
|
45
|
-
"""
|
46
45
|
super().__init__(comm_dir, cmd_file, reply_file, offline, debug)
|
47
46
|
|
48
47
|
def get_num_val(self, cmd: str) -> Union[int, float]:
|
@@ -151,21 +150,28 @@ class CommunicationController(ABCCommunicationController):
|
|
151
150
|
|
152
151
|
def get_chemstation_dirs(self) -> Tuple[str, str, List[str]]:
|
153
152
|
method_dir, sequence_dir, data_dirs = None, None, None
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
153
|
+
for _ in range(10):
|
154
|
+
self.send(Command.GET_METHOD_DIR)
|
155
|
+
res = self.receive()
|
156
|
+
if res.is_ok():
|
157
|
+
method_dir = res.ok_value.string_response
|
158
|
+
self.send(Command.GET_SEQUENCE_DIR)
|
159
|
+
res = self.receive()
|
160
|
+
if res.is_ok():
|
161
|
+
sequence_dir = res.ok_value.string_response
|
162
|
+
self.send(Command.GET_DATA_DIRS)
|
163
|
+
res = self.receive()
|
164
|
+
if res.is_ok():
|
165
|
+
data_dirs = res.ok().string_response.split("|")
|
166
|
+
if method_dir and sequence_dir and data_dirs:
|
167
|
+
if not sequence_dir[0].isalpha():
|
168
|
+
sequence_dir = "C:" + sequence_dir
|
169
|
+
if not method_dir[0].isalpha():
|
170
|
+
method_dir = "C:" + method_dir
|
171
|
+
for i, data_dir in enumerate(data_dirs):
|
172
|
+
if not data_dir[0].isalpha():
|
173
|
+
data_dirs[i] = "C:" + data_dir
|
174
|
+
return method_dir, sequence_dir, data_dirs
|
175
|
+
raise ValueError(
|
176
|
+
f"One of the method: {method_dir}, sequence: {sequence_dir} or data directories: {data_dirs} could not be found, please provide your own."
|
177
|
+
)
|