pychemstation 0.5.15__py3-none-any.whl → 0.6.1__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.
@@ -0,0 +1,219 @@
1
+ """
2
+ pip install pandas
3
+ pip install aghplctools==4.8.6
4
+ """
5
+
6
+ import os
7
+ import re
8
+ from dataclasses import dataclass
9
+ from typing import List, AnyStr
10
+
11
+ import pandas as pd
12
+ from aghplctools.ingestion.text import _no_peaks_re, _area_report_re, _header_block_re, _signal_info_re, \
13
+ _signal_table_re, chunk_string
14
+ from result import Result, Err, Ok
15
+
16
+
17
+ @dataclass
18
+ class AgilentPeak:
19
+ peak_number: int
20
+ retention_time: float
21
+ peak_type: str
22
+ width: float
23
+ area: float
24
+ height: float
25
+ height_percent: float
26
+
27
+
28
+ _column_re_dictionary = { # regex matches for column and unit combinations
29
+ 'Peak': { # peak index
30
+ '#': '[ ]+(?P<Peak>[\d]+)', # number
31
+ },
32
+ 'RetTime': { # retention time
33
+ '[min]': '(?P<RetTime>[\d]+.[\d]+)', # minutes
34
+ },
35
+ 'Type': { # peak type
36
+ '': '(?P<Type>[A-Z]{1,3}(?: [A-Z]{1,2})*)', # todo this is different from <4.8.8 aghplc tools
37
+ },
38
+ 'Width': { # peak width
39
+ '[min]': '(?P<Width>[\d]+.[\d]+[e+-]*[\d]+)',
40
+ },
41
+ 'Area': { # peak area
42
+ '[mAU*s]': '(?P<Area>[\d]+.[\d]+[e+-]*[\d]+)', # area units
43
+ '%': '(?P<percent>[\d]+.[\d]+[e+-]*[\d]+)', # percent
44
+ },
45
+ 'Height': { # peak height
46
+ '[mAU]': '(?P<Height>[\d]+.[\d]+[e+-]*[\d]+)',
47
+ },
48
+ 'Name': {
49
+ '': '(?P<Name>[^\s]+(?:\s[^\s]+)*)', # peak name
50
+ },
51
+ }
52
+
53
+
54
+ def build_peak_regex(signal_table: str):
55
+ """
56
+ Builds a peak regex from a signal table
57
+
58
+ :param signal_table: block of lines associated with an area table
59
+ :return: peak line regex object (<=3.6 _sre.SRE_PATTERN, >=3.7 re.Pattern)
60
+ """
61
+ split_table = signal_table.split('\n')
62
+ if len(split_table) <= 4: # catch peak table with no values
63
+ return None
64
+ # todo verify that these indicies are always true
65
+ column_line = split_table[2] # table column line
66
+ unit_line = split_table[3] # column unit line
67
+ length_line = [len(val) + 1 for val in split_table[4].split('|')] # length line
68
+
69
+ # iterate over header values and units to build peak table regex
70
+ peak_re_string = []
71
+ for header, unit in zip(
72
+ chunk_string(column_line, length_line),
73
+ chunk_string(unit_line, length_line)
74
+ ):
75
+ if header == '': # todo create a better catch for an undefined header
76
+ continue
77
+ try:
78
+ peak_re_string.append(
79
+ _column_re_dictionary[header][unit] # append the appropriate regex
80
+ )
81
+ except KeyError: # catch for undefined regexes (need to be built)
82
+ raise KeyError(f'The header/unit combination "{header}" "{unit}" is not defined in the peak regex '
83
+ f'dictionary. Let Lars know.')
84
+ return re.compile(
85
+ '[ ]+'.join(peak_re_string) # constructed string delimited by 1 or more spaces
86
+ + '[\s]*' # and any remaining white space
87
+ )
88
+
89
+
90
+ # todo should be able to use the parse_area_report method of aghplctools v4.8.8
91
+
92
+ def parse_area_report(report_text: str) -> dict:
93
+ """
94
+ Interprets report text and parses the area report section, converting it to dictionary.
95
+
96
+ :param report_text: plain text version of the report.
97
+ :raises ValueError: if there are no peaks defined in the report text file
98
+ :return: dictionary of signals in the form
99
+ dict[wavelength][retention time (float)][Width/Area/Height/etc.]
100
+ """
101
+ if re.search(_no_peaks_re, report_text): # There are no peaks in Report.txt
102
+ raise ValueError(f'No peaks found in Report.txt')
103
+ blocks = _header_block_re.split(report_text)
104
+ signals = {} # output dictionary
105
+ for ind, block in enumerate(blocks):
106
+ # area report block
107
+ if _area_report_re.match(block): # match area report block
108
+ # break into signal blocks
109
+ signal_blocks = _signal_table_re.split(blocks[ind + 1])
110
+ # iterate over signal blocks
111
+ for table in signal_blocks:
112
+ si = _signal_info_re.match(table)
113
+ if si is not None:
114
+ # some error state (e.g. 'not found')
115
+ if si.group('error') != '':
116
+ continue
117
+ wavelength = float(si.group('wavelength'))
118
+ if wavelength in signals:
119
+ # placeholder error raise just in case (this probably won't happen)
120
+ raise KeyError(
121
+ f'The wavelength {float(si.group("wavelength"))} is already in the signals dictionary')
122
+ signals[wavelength] = {}
123
+ # build peak regex
124
+ peak_re = build_peak_regex(table)
125
+ if peak_re is None: # if there are no columns (empty table), continue
126
+ continue
127
+ for line in table.split('\n'):
128
+ peak = peak_re.match(line)
129
+ if peak is not None:
130
+ signals[wavelength][float(peak.group('RetTime'))] = {}
131
+ current = signals[wavelength][float(peak.group('RetTime'))]
132
+ for key in _column_re_dictionary:
133
+ if key in peak.re.groupindex:
134
+ try: # try float conversion, otherwise continue
135
+ value = float(peak.group(key))
136
+ except ValueError:
137
+ value = peak.group(key)
138
+ current[key] = value
139
+ else: # ensures defined
140
+ current[key] = None
141
+ return signals
142
+
143
+
144
+ def process_export_report(file_path, target_wavelengths=None, min_retention_time=0, max_retention_time=999):
145
+ # # Pull signals from the file
146
+ # from aghplctools.ingestion.text import pull_hplc_area_from_txt
147
+ # signals = pull_hplc_area_from_txt(file_path)
148
+
149
+ with open(file_path, 'r', encoding='utf-16') as openfile:
150
+ text = openfile.read()
151
+
152
+ try:
153
+ signals = parse_area_report(text)
154
+ except ValueError as e:
155
+ # value error thrown if there are no peaks found in the report
156
+ print(e)
157
+ return [], [], []
158
+
159
+ # filter wavelengths by the ones to keep
160
+ if target_wavelengths is not None:
161
+ signals = {key: signals[key] for key in target_wavelengths if key in signals}
162
+
163
+ wavelengths = []
164
+ retention_times = []
165
+ areas = []
166
+
167
+ for wavelength, wavelength_dict in signals.items():
168
+ for ret_time, ret_time_dict in wavelength_dict.items():
169
+ if min_retention_time <= ret_time <= max_retention_time:
170
+ wavelengths.append(wavelength)
171
+ retention_times.append(ret_time)
172
+ areas.append(ret_time_dict['Area'])
173
+
174
+ return wavelengths, retention_times, areas
175
+
176
+
177
+ def process_folder(folder_path, target_wavelengths=None, min_retention_time=0, max_retention_time=999):
178
+ # folder path is the path to the overall folder, and inside there should be subfolders for each LC sample
179
+ # each subfolder should have a Report.TXT file
180
+ # sample_names = []
181
+ wavelengths = []
182
+ retention_times = []
183
+ peak_areas = []
184
+
185
+ # Get a list of all items (files and directories) in the folder
186
+ items = [os.path.join(folder_path, item) for item in os.listdir(folder_path)]
187
+
188
+ # Filter only directories from the list
189
+ # folders = [item for item in items if os.path.isdir(item)]
190
+
191
+ # # Sort the folders by creation date
192
+ # sorted_folders = sorted(folders, key=lambda f: os.stat(f).st_ctime)
193
+
194
+ for filename in items:
195
+ if filename.endswith('Report.TXT'):
196
+ # file_path = os.path.join(subfolder, filename)
197
+ file_wavelengths, file_retention_times, file_peak_areas = process_export_report(filename,
198
+ target_wavelengths,
199
+ min_retention_time,
200
+ max_retention_time)
201
+ wavelengths.extend(file_wavelengths)
202
+ retention_times.extend(file_retention_times)
203
+ peak_areas.extend(file_peak_areas)
204
+
205
+ results_df = pd.DataFrame({'Wavelengths': wavelengths, 'Retention Times': retention_times, 'Areas': peak_areas})
206
+
207
+ # Save the results to a CSV file
208
+ # results_csv_path = os.path.join(folder_path, 'all_sample_data.csv') # Specify the desired file path
209
+ # results_df.to_csv(results_csv_path, index=False)
210
+ # print(f"Results saved to {results_csv_path}")
211
+ return results_df
212
+
213
+
214
+ def process_csv_report(folder_path: str, num: int) -> Result[List[AgilentPeak], AnyStr]:
215
+ potential_report = os.path.join(folder_path, f'REPORT0{num}.CSV')
216
+ if os.path.exists(potential_report):
217
+ df = pd.read_csv(potential_report, encoding="utf-16", header=None)
218
+ return Ok(df.apply(lambda row: AgilentPeak(*row), axis=1))
219
+ return Err("No report found")
File without changes
@@ -9,8 +9,8 @@ from ....utils.table_types import Table
9
9
 
10
10
  class DeviceController(TableController, abc.ABC):
11
11
 
12
- def __init__(self, controller: CommunicationController, table: Table):
13
- super().__init__(controller, None, None, table)
12
+ def __init__(self, controller: CommunicationController, table: Table, offline: bool):
13
+ super().__init__(controller, None, None, table, offline)
14
14
 
15
15
  @abc.abstractmethod
16
16
  def get_row(self, row: int):
@@ -7,8 +7,8 @@ from ....utils.table_types import Table, RegisterFlag
7
7
 
8
8
  class InjectorController(DeviceController):
9
9
 
10
- def __init__(self, controller: CommunicationController, table: Table):
11
- super().__init__(controller, table)
10
+ def __init__(self, controller: CommunicationController, table: Table, offline: bool):
11
+ super().__init__(controller, table, offline)
12
12
 
13
13
  def get_row(self, row: int) -> InjectorFunction:
14
14
  def return_tray_loc() -> Tray:
@@ -35,27 +35,14 @@ class InjectorController(DeviceController):
35
35
  def load(self) -> InjectorTable:
36
36
  rows = self.get_num_rows()
37
37
  if rows.is_ok():
38
- return InjectorTable(functions=[self.get_row(i + 1) for i in range(int(rows.ok_value.num_response))])
38
+ return InjectorTable(functions=[self.get_row(i) for i in range(int(rows.ok_value.num_response))])
39
39
 
40
40
  def edit(self, injector_table: InjectorTable):
41
- rows = self.get_num_rows()
42
- if rows.is_ok():
43
- existing_row_num = rows.value.num_response
44
- wanted_row_num = len(injector_table.functions)
45
- while existing_row_num != wanted_row_num:
46
- if wanted_row_num > existing_row_num:
47
- self.add_row()
48
- elif wanted_row_num < existing_row_num:
49
- self.delete_row(int(existing_row_num))
50
- self.send(Command.SAVE_METHOD_CMD)
51
- existing_row_num = self.get_num_rows().ok_value.num_response
52
- self.send(Command.SWITCH_METHOD_CMD)
53
-
54
41
  columns_added = set()
55
42
 
56
43
  def add_table_val(col_name: RegisterFlag, val: Union[str, int, float]):
57
44
  nonlocal columns_added
58
- if col_name in columns_added:
45
+ if True:
59
46
  if isinstance(val, str):
60
47
  self.edit_row_text(col_name=col_name, val=val)
61
48
  else:
@@ -102,21 +89,29 @@ class InjectorController(DeviceController):
102
89
  add_table_val(col_name=RegisterFlag.REMOTE, val=remote.command.value)
103
90
  add_table_val(col_name=RegisterFlag.REMOTE_DUR, val=remote.duration)
104
91
 
105
- for i, function in enumerate(injector_table.functions):
106
- self.add_row()
107
- if isinstance(function, Inject):
108
- add_inject(function)
109
- elif isinstance(function, Draw):
110
- add_draw(function)
111
- elif isinstance(function, Wait):
112
- add_wait(function)
113
- elif isinstance(function, Remote):
114
- add_remote(function)
115
- self.download()
116
92
  self.send(Command.SAVE_METHOD_CMD)
117
- self.send(Command.SWITCH_METHOD_CMD)
93
+ rows = self.get_num_rows()
94
+ if rows.is_ok():
95
+ existing_row_num = rows.value.num_response
96
+ for i, function in enumerate(injector_table.functions):
97
+ if (i+1) > existing_row_num:
98
+ self.add_row()
99
+ if isinstance(function, Inject):
100
+ add_inject(function)
101
+ elif isinstance(function, Draw):
102
+ add_draw(function)
103
+ elif isinstance(function, Wait):
104
+ add_wait(function)
105
+ elif isinstance(function, Remote):
106
+ add_remote(function)
107
+ self.download()
108
+ self.send(Command.SAVE_METHOD_CMD)
109
+ self.send(Command.SWITCH_METHOD_CMD)
110
+ existing_row_num = self.get_num_rows().ok_value.num_response
118
111
 
119
112
  def download(self):
113
+ self.send('Sleep 1')
114
+ self.sleepy_send("DownloadRCMethod WLS1")
120
115
  self.send('Sleep 1')
121
116
  self.sleepy_send("DownloadLWls 1")
122
117
  self.send('Sleep 1')
@@ -57,7 +57,8 @@ class HPLCController:
57
57
  table=self.METHOD_TIMETABLE,
58
58
  offline=offline,
59
59
  injector_controller=InjectorController(controller=self.comm,
60
- table=self.INJECTOR_TABLE))
60
+ table=self.INJECTOR_TABLE,
61
+ offline=offline))
61
62
  self.sequence_controller = SequenceController(controller=self.comm,
62
63
  src=sequence_dir,
63
64
  data_dir=data_dir,
@@ -127,9 +128,6 @@ class HPLCController:
127
128
  """
128
129
  self.sequence_controller.run(stall_while_running=stall_while_running)
129
130
 
130
- def edit_injector_program(self, injector_table: InjectorTable):
131
- self.method_controller.injector_controller.edit(injector_table=injector_table)
132
-
133
131
  def edit_method(self, updated_method: MethodDetails, save: bool = False):
134
132
  """Updated the currently loaded method in ChemStation with provided values.
135
133
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pychemstation
3
- Version: 0.5.15
3
+ Version: 0.6.1
4
4
  Summary: Library to interact with Chemstation software, primarily used in Hein lab
5
5
  Home-page: https://gitlab.com/heingroup/device-api/pychemstation
6
6
  Author: Lucy Hao
@@ -1,11 +1,12 @@
1
1
  pychemstation/__init__.py,sha256=SpTl-Tg1B1HTyjNOE-8ue-N2wGnXN_2zl7RFUSxlkiM,33
2
2
  pychemstation/analysis/__init__.py,sha256=EWoU47iyn9xGS-b44zK9eq50bSjOV4AC5dvt420YMI4,44
3
3
  pychemstation/analysis/base_spectrum.py,sha256=XPf9eJ72uz0CnxCY5uFOyu1MbVX-OTTXeN1tLzIMok4,16536
4
+ pychemstation/analysis/process_report.py,sha256=d290KgPY-cMlQg4yHaspLFxgso_yu9qNxXG6SF2qpuo,9054
4
5
  pychemstation/analysis/spec_utils.py,sha256=UOo9hJR3evJfmaohEEsyb7aq6X996ofuUfu-GKjiDi8,10201
5
6
  pychemstation/analysis/utils.py,sha256=ISupAOb_yqA4_DZRK9v18UL-XjUQccAicIJKb1VMnGg,2055
6
7
  pychemstation/control/__init__.py,sha256=4xTy8X-mkn_PPZKr7w9rnj1wZhtmTesbQptPhpYmKXs,64
7
8
  pychemstation/control/comm.py,sha256=u44g1hTluQ0yUG93Un-QAshScoDpgYRrZfFTgweP5tY,7386
8
- pychemstation/control/hplc.py,sha256=IdHH4Yho5cJDDBLcc6k0XKDFDecghaY8NaA4dIU7KB8,9898
9
+ pychemstation/control/hplc.py,sha256=8vKdm5sTGY_VnBRqcAS3EMRJAQ2wbLRbFswaSi2CHHc,9848
9
10
  pychemstation/control/controllers/__init__.py,sha256=EM6LBNSTJqYVatmnvPq0P-S3q0POA88c-y64zL79I_I,252
10
11
  pychemstation/control/controllers/comm.py,sha256=IU4I_Q42VNCNUlVi93MxCmw2EBY9hiBDkU9FxubKg3c,7441
11
12
  pychemstation/control/controllers/method.py,sha256=XUclB7lQ_SIkquR58MBmmi9drHIPEq9AR8VprTLenvI,15503
@@ -13,8 +14,9 @@ pychemstation/control/controllers/sequence.py,sha256=kYNxxck2I-q9mZDEZwG8bJ_99Ff
13
14
  pychemstation/control/controllers/table_controller.py,sha256=70ovnIjLKkJborS1ztk445Mv42TtUM9jUniaQmZuyWQ,11031
14
15
  pychemstation/control/controllers/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
16
  pychemstation/control/controllers/devices/column.py,sha256=SCpCnVFZFUM9LM51MbWkVcBRayN3WFxy7lz9gs2PYeY,348
16
- pychemstation/control/controllers/devices/device.py,sha256=SF1JK93FjmACnYrlKvldX3gEeA21qnXZegeNhc9QJGQ,738
17
- pychemstation/control/controllers/devices/injector.py,sha256=ul5q8quGcB7N3sNPBWbifIoqGdp95LJMXPAbcufHBng,5728
17
+ pychemstation/control/controllers/devices/dad.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ pychemstation/control/controllers/devices/device.py,sha256=QUFpUwmGxMzmG1CiRhNauV5cVZEJyRXyQDNQuW8Qfxk,762
19
+ pychemstation/control/controllers/devices/injector.py,sha256=Kxf4NjxPUt2xjOmJ9Pea1b9YzWyGcpW8VdOlx9Jo-Nw,5540
18
20
  pychemstation/control/controllers/devices/pump.py,sha256=DJQh4lNXEraeC1CWrsKmsITOjuYlRI3tih_XRB3F1hg,1404
19
21
  pychemstation/control/controllers/tables/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
22
  pychemstation/control/controllers/tables/method.py,sha256=3GdAQ6Cwf20QL3v7eMkwNieWbJdtr9GN12USbSVl_iE,16426
@@ -42,11 +44,12 @@ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
44
  tests/constants.py,sha256=55DOLpylLt12mb_gSgzEo-EYynwTmI2uH7PcrTM6Iq0,2455
43
45
  tests/test_comb.py,sha256=TS-CbtiPbntL4u6E1gSZ6xquNp6cQxIFdJqUr2ak7PA,5515
44
46
  tests/test_comm.py,sha256=iwl-Ey-xoytXmlNrjG84pDm82Ry_QUX6wY4gmVh4NDc,2516
45
- tests/test_inj.py,sha256=1ib-RwVouBLmWJO1KLQD8ghIqpD2xi1ePsrEw49mNIU,2045
47
+ tests/test_inj.py,sha256=Blpk-z9PQuqo4xQ7AUi0CS2czMiYm-pqZe75OFTXru4,1092
46
48
  tests/test_method.py,sha256=KB7yAtVb4gZftnYzh-VfPb9LGVZOHUIW6OljEYRtbhA,4570
49
+ tests/test_proc_rep.py,sha256=WhUiFqDD1Aey_Nc6Hvbd2zo48K4MWjwDhfO9LiwEQ7M,703
47
50
  tests/test_sequence.py,sha256=vs5-dqkItRds_tPM2-N6MNJ37FB0nLRFaDzBV8d42i8,4880
48
- pychemstation-0.5.15.dist-info/LICENSE,sha256=9bdF75gIf1MecZ7oymqWgJREVz7McXPG-mjqrTmzzD8,18658
49
- pychemstation-0.5.15.dist-info/METADATA,sha256=aAdW7pVUfyidEcFiPaEMUFzGLl4DLormW-51LsCbaU0,4371
50
- pychemstation-0.5.15.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
51
- pychemstation-0.5.15.dist-info/top_level.txt,sha256=zXfKu_4nYWwPHo3OsuhshMNC3SPkcoTGCyODjURaghY,20
52
- pychemstation-0.5.15.dist-info/RECORD,,
51
+ pychemstation-0.6.1.dist-info/LICENSE,sha256=9bdF75gIf1MecZ7oymqWgJREVz7McXPG-mjqrTmzzD8,18658
52
+ pychemstation-0.6.1.dist-info/METADATA,sha256=oGTCg13YoFF8G2T3aPXss0aKXNveIK9n_UMLpdYQVxM,4370
53
+ pychemstation-0.6.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
54
+ pychemstation-0.6.1.dist-info/top_level.txt,sha256=zXfKu_4nYWwPHo3OsuhshMNC3SPkcoTGCyODjURaghY,20
55
+ pychemstation-0.6.1.dist-info/RECORD,,
tests/test_inj.py CHANGED
@@ -2,8 +2,6 @@ import os
2
2
  import unittest
3
3
 
4
4
  from pychemstation.control import HPLCController
5
- from pychemstation.utils.injector_types import *
6
- from pychemstation.utils.tray_types import FiftyFourVialPlate, Plate, Letter, Num
7
5
  from tests.constants import *
8
6
 
9
7
 
@@ -19,36 +17,16 @@ class TestInj(unittest.TestCase):
19
17
  method_dir=path_constants[1],
20
18
  data_dir=path_constants[2],
21
19
  sequence_dir=path_constants[3])
20
+ # self.hplc_controller.switch_method(DEFAULT_METHOD)
22
21
 
23
22
  def test_load_inj(self):
24
- self.hplc_controller.switch_method(DEFAULT_METHOD)
25
23
  try:
26
24
  inj_table = self.hplc_controller.load_injector_program()
27
25
  self.assertTrue(len(inj_table.functions) == 2)
28
26
  except Exception as e:
29
27
  self.fail(f"Should have not failed, {e}")
30
28
 
31
- def test_edit_inj(self):
32
- self.hplc_controller.switch_method(DEFAULT_METHOD)
33
- try:
34
- injector_program = InjectorTable(
35
- functions=[
36
- Draw(amount=0.3, location="P1-A2"),
37
- Inject()]
38
- )
39
- self.hplc_controller.edit_injector_program(injector_program)
40
- except Exception as e:
41
- self.fail(f"Should have not failed: {e}")
42
29
 
43
- def test_edit_inj_def(self):
44
- self.hplc_controller.switch_method(DEFAULT_METHOD)
45
- try:
46
- injector_program = InjectorTable(
47
- functions=[Draw(location="P1-F2"), Inject()]
48
- )
49
- self.hplc_controller.edit_injector_program(injector_program)
50
- except Exception as e:
51
- self.fail(f"Should have not failed: {e}")
52
30
 
53
31
 
54
32
  if __name__ == '__main__':
tests/test_proc_rep.py ADDED
@@ -0,0 +1,27 @@
1
+ import unittest
2
+
3
+ from result import Result
4
+
5
+ from pychemstation.analysis.process_report import process_csv_report
6
+
7
+
8
+ class TestReport(unittest.TestCase):
9
+
10
+ def test_process_reporttxt(self):
11
+ try:
12
+ # TODO
13
+ print('yes')
14
+ except Exception as e:
15
+ self.fail(f"Should have not failed, {e}")
16
+
17
+ def test_report_csv(self):
18
+ try:
19
+ possible_peaks: Result = process_csv_report(folder_path="0_2025-03-15 19-14-35.D", num=1)
20
+ self.assertTrue(len(possible_peaks.ok_value) == 16)
21
+ print('yes')
22
+ except Exception as e:
23
+ self.fail(f"Should have not failed: {e}")
24
+
25
+
26
+ if __name__ == '__main__':
27
+ unittest.main()