chromaquant 0.4.0__py3-none-any.whl → 0.5.0__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.
- chromaquant/__init__.py +9 -2
- chromaquant/data/__init__.py +14 -0
- chromaquant/data/breakdown.py +430 -0
- chromaquant/data/dataset.py +195 -0
- chromaquant/data/table.py +412 -0
- chromaquant/data/value.py +215 -0
- chromaquant/formula/__init__.py +13 -0
- chromaquant/formula/base_formulas.py +168 -0
- chromaquant/formula/formula.py +507 -0
- chromaquant/import_local_packages.py +55 -0
- chromaquant/logging_and_handling.py +76 -0
- chromaquant/match/__init__.py +13 -0
- chromaquant/match/match.py +184 -0
- chromaquant/match/match_config.py +296 -0
- chromaquant/match/match_tools.py +154 -0
- chromaquant/{Quant → results}/__init__.py +2 -2
- chromaquant/results/reporting_tools.py +190 -0
- chromaquant/results/results.py +250 -0
- chromaquant/utils/__init__.py +14 -0
- chromaquant/utils/categories.py +127 -0
- chromaquant/utils/chemical_formulas.py +104 -0
- chromaquant/utils/dataframe_processing.py +222 -0
- chromaquant/utils/file_tools.py +100 -0
- chromaquant/utils/formula_tools.py +119 -0
- chromaquant-0.5.0.dist-info/METADATA +61 -0
- chromaquant-0.5.0.dist-info/RECORD +29 -0
- {chromaquant-0.4.0.dist-info → chromaquant-0.5.0.dist-info}/WHEEL +1 -1
- {chromaquant-0.4.0.dist-info → chromaquant-0.5.0.dist-info}/licenses/LICENSE.txt +1 -1
- chromaquant-0.5.0.dist-info/licenses/LICENSES_bundled.txt +251 -0
- chromaquant/Handle/__init__.py +0 -13
- chromaquant/Handle/fileChecks.py +0 -172
- chromaquant/Handle/handleDirectories.py +0 -89
- chromaquant/Hydro/__init__.py +0 -12
- chromaquant/Hydro/hydroMain.py +0 -496
- chromaquant/Manual/HydroUI.py +0 -418
- chromaquant/Manual/QuantUPP.py +0 -373
- chromaquant/Manual/Quantification.py +0 -1305
- chromaquant/Manual/__init__.py +0 -10
- chromaquant/Manual/duplicateMatch.py +0 -211
- chromaquant/Manual/fpm_match.py +0 -798
- chromaquant/Manual/label-type.py +0 -179
- chromaquant/Match/AutoFpmMatch.py +0 -1133
- chromaquant/Match/MatchSub/__init__.py +0 -13
- chromaquant/Match/MatchSub/matchTools.py +0 -282
- chromaquant/Match/MatchSub/peakTools.py +0 -259
- chromaquant/Match/__init__.py +0 -13
- chromaquant/Match/matchMain.py +0 -233
- chromaquant/Quant/AutoQuantification.py +0 -1329
- chromaquant/Quant/QuantSub/__init__.py +0 -15
- chromaquant/Quant/QuantSub/gasFID.py +0 -241
- chromaquant/Quant/QuantSub/gasTCD.py +0 -425
- chromaquant/Quant/QuantSub/liquidFID.py +0 -310
- chromaquant/Quant/QuantSub/parseTools.py +0 -162
- chromaquant/Quant/quantMain.py +0 -417
- chromaquant/UAPP/__init__.py +0 -12
- chromaquant/UAPP/uappMain.py +0 -427
- chromaquant/__main__.py +0 -526
- chromaquant/oldui.py +0 -492
- chromaquant/properties.json +0 -4
- chromaquant-0.4.0.dist-info/METADATA +0 -189
- chromaquant-0.4.0.dist-info/RECORD +0 -38
- chromaquant-0.4.0.dist-info/entry_points.txt +0 -2
- chromaquant-0.4.0.dist-info/licenses/LICENSES_bundled.txt +0 -1035
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
COPYRIGHT STATEMENT:
|
|
5
|
+
|
|
6
|
+
ChromaQuant – A quantification software for complex gas chromatographic data
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026, by Julia Hancock
|
|
9
|
+
Affiliation: Dr. Julie Elaine Rorrer
|
|
10
|
+
URL: https://www.rorrerlab.com/
|
|
11
|
+
|
|
12
|
+
License: BSD 3-Clause License
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
CLASS DEFINITION FOR CATEGORIES
|
|
17
|
+
|
|
18
|
+
Julia Hancock
|
|
19
|
+
Started 2-02-2026
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
""" CLASS """
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Define categories class
|
|
27
|
+
class Categories:
|
|
28
|
+
|
|
29
|
+
# Initialize
|
|
30
|
+
def __init__(self):
|
|
31
|
+
|
|
32
|
+
# Create attributes
|
|
33
|
+
# Category attribute
|
|
34
|
+
# Expected format is:
|
|
35
|
+
# {'category 1': ['keyword 1', 'keyword 2', ...], ...}
|
|
36
|
+
self._categories = {}
|
|
37
|
+
|
|
38
|
+
# Categorizer attribute
|
|
39
|
+
self.categorizer = self.IS_EQUAL
|
|
40
|
+
|
|
41
|
+
# Attribute denoting whether to ignore string case
|
|
42
|
+
self.ignore_case = True
|
|
43
|
+
|
|
44
|
+
# Define get_item to get internal categories
|
|
45
|
+
def __getitem__(self, key):
|
|
46
|
+
# Get category
|
|
47
|
+
return self._categories[key]
|
|
48
|
+
|
|
49
|
+
# Define set_item to set internal categories
|
|
50
|
+
def __setitem__(self, key, value):
|
|
51
|
+
# Set category
|
|
52
|
+
self._categories[key] = value
|
|
53
|
+
|
|
54
|
+
""" METHODS """
|
|
55
|
+
# Method to categorize by finding when tested value is equal to keyword,
|
|
56
|
+
# selecting first applicable category if more than one apply
|
|
57
|
+
def IS_EQUAL(self, test_value: str | int | float) -> str:
|
|
58
|
+
|
|
59
|
+
# For every category in categories...
|
|
60
|
+
for category in self._categories:
|
|
61
|
+
|
|
62
|
+
# For every keyword in the category...
|
|
63
|
+
for keyword in self._categories[category]:
|
|
64
|
+
|
|
65
|
+
# If the keyword is equal to the test value...
|
|
66
|
+
if keyword == test_value:
|
|
67
|
+
|
|
68
|
+
# Return the category
|
|
69
|
+
return category
|
|
70
|
+
|
|
71
|
+
# Otherwise, pass
|
|
72
|
+
else:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
return ''
|
|
76
|
+
|
|
77
|
+
# Method to categorize by finding when keyword is a substring of a tested
|
|
78
|
+
# value, selecting first applicable category if more than one apply
|
|
79
|
+
def IS_IN(self, test_value: str) -> str:
|
|
80
|
+
|
|
81
|
+
# Try to get the lowercase of the test_value and all keywords
|
|
82
|
+
try:
|
|
83
|
+
test_value.lower()
|
|
84
|
+
test_keyword_list = [keyword.lower() for keyword_list in
|
|
85
|
+
list(self._categories.values()) for
|
|
86
|
+
keyword in keyword_list]
|
|
87
|
+
del test_keyword_list
|
|
88
|
+
|
|
89
|
+
# Raise error if AttributeError occurs
|
|
90
|
+
except AttributeError as e:
|
|
91
|
+
raise AttributeError(('Cannot use IS_IN for non-string'
|
|
92
|
+
f'values and keywords: {e}'))
|
|
93
|
+
|
|
94
|
+
# For every category in categories...
|
|
95
|
+
for category in self._categories:
|
|
96
|
+
|
|
97
|
+
# For every keyword in the category...
|
|
98
|
+
for keyword in self._categories[category]:
|
|
99
|
+
|
|
100
|
+
# If ignoring case...
|
|
101
|
+
if self.ignore_case is True:
|
|
102
|
+
|
|
103
|
+
# If the lowercase keyword is in
|
|
104
|
+
# the lowercase test value...
|
|
105
|
+
if keyword.lower() in test_value.lower():
|
|
106
|
+
|
|
107
|
+
# Return the category
|
|
108
|
+
return category
|
|
109
|
+
|
|
110
|
+
# Otherwise, pass
|
|
111
|
+
else:
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
# If not ignoring case...
|
|
115
|
+
else:
|
|
116
|
+
|
|
117
|
+
# If the keyword is in the test value...
|
|
118
|
+
if keyword in test_value:
|
|
119
|
+
|
|
120
|
+
# Return the category
|
|
121
|
+
return category
|
|
122
|
+
|
|
123
|
+
# Otherwise, pass
|
|
124
|
+
else:
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
return ''
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
COPYRIGHT STATEMENT:
|
|
5
|
+
|
|
6
|
+
ChromaQuant – A quantification software for complex gas chromatographic data
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026, by Julia Hancock
|
|
9
|
+
Affiliation: Dr. Julie Elaine Rorrer
|
|
10
|
+
URL: https://www.rorrerlab.com/
|
|
11
|
+
|
|
12
|
+
License: BSD 3-Clause License
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
TOOLS FOR FILE IMPORTING AND EXPORTING
|
|
17
|
+
|
|
18
|
+
Julia Hancock
|
|
19
|
+
Started 1-16-2026
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from chemformula import ChemFormula
|
|
24
|
+
|
|
25
|
+
""" FUNCTIONS """
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Function to get the number of atoms of a given element from a formula
|
|
29
|
+
def get_number_element_atoms(formula: str | list[str],
|
|
30
|
+
element: str) -> list[int] | int:
|
|
31
|
+
|
|
32
|
+
# Try...
|
|
33
|
+
try:
|
|
34
|
+
# ...to split formula (checks if list or string)
|
|
35
|
+
formula.split()
|
|
36
|
+
|
|
37
|
+
# If it is a string, get a dictionary with
|
|
38
|
+
# key:value pairs as element:number
|
|
39
|
+
chemFormDict = ChemFormula(formula).element
|
|
40
|
+
|
|
41
|
+
# Get the number of atoms of the passed element
|
|
42
|
+
number_element_atoms: int | list[int] = chemFormDict[element]
|
|
43
|
+
|
|
44
|
+
# If an AttributeError occurs (formula is a list)...
|
|
45
|
+
except AttributeError:
|
|
46
|
+
|
|
47
|
+
# Create a new list to return
|
|
48
|
+
number_element_atoms = []
|
|
49
|
+
|
|
50
|
+
# For every formula within the list...
|
|
51
|
+
for single_formula in formula:
|
|
52
|
+
|
|
53
|
+
# Try...
|
|
54
|
+
try:
|
|
55
|
+
|
|
56
|
+
# Get a dictionary with key:value pairs as element:number
|
|
57
|
+
chemFormDict = ChemFormula(single_formula).element
|
|
58
|
+
|
|
59
|
+
# Get the number of atoms of the passed element from this
|
|
60
|
+
# dictionary and append this to the return list
|
|
61
|
+
number_element_atoms.append(int(chemFormDict[element]))
|
|
62
|
+
|
|
63
|
+
# If there is a ValueError (e.g., formula contains None or NaN)...
|
|
64
|
+
except ValueError:
|
|
65
|
+
|
|
66
|
+
# Append zero
|
|
67
|
+
number_element_atoms.append(0)
|
|
68
|
+
|
|
69
|
+
return number_element_atoms
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Function to get the molecular weight from a chemical formula
|
|
73
|
+
def get_molecular_weight(formula: str | list[str]) -> float | list[float]:
|
|
74
|
+
|
|
75
|
+
# Try...
|
|
76
|
+
try:
|
|
77
|
+
# ...to split formula (checks if list or string)
|
|
78
|
+
formula.split()
|
|
79
|
+
|
|
80
|
+
# If it is a string, get the weight
|
|
81
|
+
weight: float | list[float] = ChemFormula(formula).formula_weight
|
|
82
|
+
|
|
83
|
+
# If an AttributeError occurs (formula is a list)...
|
|
84
|
+
except AttributeError:
|
|
85
|
+
|
|
86
|
+
# Create a new list to return
|
|
87
|
+
weight = []
|
|
88
|
+
|
|
89
|
+
# For every formula within the list...
|
|
90
|
+
for single_formula in formula:
|
|
91
|
+
|
|
92
|
+
# Try...
|
|
93
|
+
try:
|
|
94
|
+
|
|
95
|
+
# Append the weight of the current formula
|
|
96
|
+
weight.append(ChemFormula(single_formula).formula_weight)
|
|
97
|
+
|
|
98
|
+
# If there is a ValueError (e.g., formula contains None or NaN)...
|
|
99
|
+
except ValueError:
|
|
100
|
+
|
|
101
|
+
# Append zero
|
|
102
|
+
weight.append(0)
|
|
103
|
+
|
|
104
|
+
return weight
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
COPYRIGHT STATEMENT:
|
|
5
|
+
|
|
6
|
+
ChromaQuant – A quantification software for complex gas chromatographic data
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026, by Julia Hancock
|
|
9
|
+
Affiliation: Dr. Julie Elaine Rorrer
|
|
10
|
+
URL: https://www.rorrerlab.com/
|
|
11
|
+
|
|
12
|
+
License: BSD 3-Clause License
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
SUBPACKAGE FOR ADJUSTING AND TESTING DATAFRAMES
|
|
17
|
+
|
|
18
|
+
Julia Hancock
|
|
19
|
+
Started 11-12-2025
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
""" FUNCTIONS """
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Function to loop through keys in a passed dictionary and
|
|
27
|
+
# check them against a list of permitted keys or dictionary
|
|
28
|
+
# with permitted keys
|
|
29
|
+
def check_dict_keys(dictionary, permitted):
|
|
30
|
+
|
|
31
|
+
# For every key-value pair in the dictionary...
|
|
32
|
+
for key, value in dictionary.items():
|
|
33
|
+
|
|
34
|
+
# If the current key is not permitted, raise an error
|
|
35
|
+
if key not in permitted:
|
|
36
|
+
raise ValueError(f'{key} is not a valid argument')
|
|
37
|
+
|
|
38
|
+
# Otherwise, pass
|
|
39
|
+
else:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# Function to add, remove, or rename columns in a DataFrame
|
|
46
|
+
def column_adjust(dataframe, add_col=[], remove_col=[], rename_dict={}):
|
|
47
|
+
"""
|
|
48
|
+
Function used to add, remove, or rename
|
|
49
|
+
columns in a passed DataFrame
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
dataframe : pandas.DataFrame
|
|
54
|
+
DataFrame to have columns adjusted.
|
|
55
|
+
add_col : list, optional
|
|
56
|
+
List of column headers to add, by default [].
|
|
57
|
+
remove_col : list, optional
|
|
58
|
+
List of column headers to remove, by default [].
|
|
59
|
+
rename_dict : list, optional
|
|
60
|
+
Dictionary of headers to rename as keys and
|
|
61
|
+
new headers as values, by default {}.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
pandas.DataFrame
|
|
66
|
+
DataFrame post-adjustments.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
# Define a new dataframe
|
|
70
|
+
new_dataframe = dataframe.copy()
|
|
71
|
+
|
|
72
|
+
# For each column in add_col...
|
|
73
|
+
if add_col:
|
|
74
|
+
for column in add_col:
|
|
75
|
+
# Create a new column and assign all values to NaN
|
|
76
|
+
# NOTE: if the column already exists, this will
|
|
77
|
+
# clear existing data
|
|
78
|
+
new_dataframe[column] = None
|
|
79
|
+
|
|
80
|
+
# Rename columns using key-value pairs in rename_dict
|
|
81
|
+
if rename_dict:
|
|
82
|
+
new_dataframe = new_dataframe.rename(columns=rename_dict)
|
|
83
|
+
|
|
84
|
+
# Remove dataframe columns as specified in remove_col...
|
|
85
|
+
if remove_col:
|
|
86
|
+
new_dataframe = dataframe.drop(remove_col, axis=1)
|
|
87
|
+
|
|
88
|
+
return new_dataframe
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# Function to filter a DataFrame based on certain values in rows
|
|
92
|
+
def row_filter(dataframe, filter_dict):
|
|
93
|
+
"""
|
|
94
|
+
Function used to filter a passed DataFrame
|
|
95
|
+
such that it only contains certain values
|
|
96
|
+
in specific columns
|
|
97
|
+
...
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
dataframe : pandas.DataFrame
|
|
102
|
+
DataFrame to have rows filtered.
|
|
103
|
+
filter_dict : _type_
|
|
104
|
+
Dictionary containing column names as keys and desired cell values
|
|
105
|
+
as values
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
pandas.dataframe
|
|
110
|
+
Dataframe post-filtering.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
# Define a new dataframe
|
|
114
|
+
new_dataframe = dataframe.copy()
|
|
115
|
+
|
|
116
|
+
# Filter the new dataframe to only include rows where the value
|
|
117
|
+
# in some column is equal to the value associated with the key
|
|
118
|
+
# in filter_dict that matches that column's header
|
|
119
|
+
for key, value in filter_dict.items():
|
|
120
|
+
new_dataframe = new_dataframe.loc[new_dataframe[key] == value]
|
|
121
|
+
|
|
122
|
+
return new_dataframe
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# Function to check whether a column is empty
|
|
126
|
+
def verify_column_not_empty(dataframe, column):
|
|
127
|
+
"""
|
|
128
|
+
Function used to test whether a column in a passed
|
|
129
|
+
DataFrame is empty
|
|
130
|
+
|
|
131
|
+
...
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
dataframe : pandas.DataFrame
|
|
136
|
+
DataFrame containing column to be tested
|
|
137
|
+
column : str
|
|
138
|
+
Header of a column which may or may not contain values
|
|
139
|
+
|
|
140
|
+
Returns
|
|
141
|
+
-------
|
|
142
|
+
bool
|
|
143
|
+
Result of the conditional test: True if the column is
|
|
144
|
+
not empty and False if the column is empty
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
# Define a default test result
|
|
148
|
+
test_result = False
|
|
149
|
+
|
|
150
|
+
# If there is any value in the passed column...
|
|
151
|
+
if dataframe[column].any():
|
|
152
|
+
|
|
153
|
+
# Redefine test result to True
|
|
154
|
+
test_result = True
|
|
155
|
+
|
|
156
|
+
# Otherwise, set to False
|
|
157
|
+
else:
|
|
158
|
+
test_result = False
|
|
159
|
+
|
|
160
|
+
return test_result
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# Function to test whether certain values exist in a column
|
|
164
|
+
def test_for_column_values(dataframe, column, test_values):
|
|
165
|
+
"""
|
|
166
|
+
Function used to test for the presence or absence of
|
|
167
|
+
passed values in some column of a given DataFrame
|
|
168
|
+
|
|
169
|
+
...
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
dataframe :
|
|
173
|
+
_description_
|
|
174
|
+
column : _type_
|
|
175
|
+
_description_
|
|
176
|
+
test_values : _type_
|
|
177
|
+
_description_
|
|
178
|
+
|
|
179
|
+
Returns
|
|
180
|
+
-------
|
|
181
|
+
_type_
|
|
182
|
+
_description_
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
# Define a default test result dictionary
|
|
186
|
+
test_result_dict = {test_value: False for test_value in test_values}
|
|
187
|
+
|
|
188
|
+
# If the dataframe is not empty...
|
|
189
|
+
if verify_column_not_empty(dataframe, column):
|
|
190
|
+
|
|
191
|
+
# For each test value in the test_values list...
|
|
192
|
+
for value in test_values:
|
|
193
|
+
|
|
194
|
+
# If the column contains the test value at at least one row...
|
|
195
|
+
if value in dataframe.loc[column].values.tolist():
|
|
196
|
+
|
|
197
|
+
# Set the test_result_dict entry to True
|
|
198
|
+
test_result_dict[value] = True
|
|
199
|
+
|
|
200
|
+
# Otherwise, pass
|
|
201
|
+
else:
|
|
202
|
+
pass
|
|
203
|
+
|
|
204
|
+
# Otherwise, pass
|
|
205
|
+
else:
|
|
206
|
+
pass
|
|
207
|
+
|
|
208
|
+
return test_result_dict
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# Function that adds data from one row to another
|
|
212
|
+
def add_columns_from_one_row_to_another(first_row, second_row, add_columns):
|
|
213
|
+
|
|
214
|
+
# Create a copy of the passed first_row
|
|
215
|
+
new_first = first_row.copy()
|
|
216
|
+
|
|
217
|
+
# For every column in add_columns...
|
|
218
|
+
for column in add_columns:
|
|
219
|
+
# Set the first row's entry to the second row's entry
|
|
220
|
+
new_first.at[column] = second_row[column]
|
|
221
|
+
|
|
222
|
+
return new_first
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
COPYRIGHT STATEMENT:
|
|
6
|
+
|
|
7
|
+
ChromaQuant – A quantification software for complex gas chromatographic data
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2026, by Julia Hancock
|
|
10
|
+
Affiliation: Dr. Julie Elaine Rorrer
|
|
11
|
+
URL: https://www.rorrerlab.com/
|
|
12
|
+
|
|
13
|
+
License: BSD 3-Clause License
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
TOOLS FOR FILE IMPORTING AND EXPORTING
|
|
18
|
+
|
|
19
|
+
Julia Hancock
|
|
20
|
+
Started 1-16-2026
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import logging
|
|
25
|
+
import os
|
|
26
|
+
import pandas as pd
|
|
27
|
+
from ..logging_and_handling import setup_logger, setup_error_logging
|
|
28
|
+
|
|
29
|
+
""" LOGGING AND HANDLING """
|
|
30
|
+
|
|
31
|
+
# Create a logger
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
# Format the logger
|
|
35
|
+
logger = setup_logger(logger)
|
|
36
|
+
|
|
37
|
+
# Get an error logging decorator
|
|
38
|
+
error_logging = setup_error_logging(logger)
|
|
39
|
+
|
|
40
|
+
""" FUNCTIONS """
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Function to export data to a .csv file
|
|
44
|
+
def export_to_csv(dataframe, parent, name=''):
|
|
45
|
+
"""export_to_csv _summary_
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
dataframe : dataframe
|
|
50
|
+
Passed Pandas dataframe.
|
|
51
|
+
parent : string
|
|
52
|
+
Name of parent directory for exported file.
|
|
53
|
+
name : string
|
|
54
|
+
Name of file to be exported.
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
None
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
# If the name argument is passed...
|
|
62
|
+
if name:
|
|
63
|
+
# Construct a full path from the passed
|
|
64
|
+
# parent directory and file name
|
|
65
|
+
full_path = os.path.join(parent, name, '.csv')
|
|
66
|
+
# Otherwise...
|
|
67
|
+
else:
|
|
68
|
+
# Set the full path to the parent
|
|
69
|
+
full_path = parent
|
|
70
|
+
|
|
71
|
+
# Export the dataframe to the full path
|
|
72
|
+
dataframe.to_csv(full_path, index=False)
|
|
73
|
+
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# Function to try to open a file using a passed function
|
|
78
|
+
def try_open_file(func, path, *args, **kwargs):
|
|
79
|
+
|
|
80
|
+
# Predefine the conditional Boolean result
|
|
81
|
+
tf = False
|
|
82
|
+
|
|
83
|
+
# Try opening the file, if it works then set tf to True
|
|
84
|
+
try:
|
|
85
|
+
data = func(path, *args, **kwargs)
|
|
86
|
+
tf = True
|
|
87
|
+
|
|
88
|
+
# If cannot open file, log and set tf to False
|
|
89
|
+
except Exception as e:
|
|
90
|
+
# logger.info(f'Could not open {path}, got {e}')
|
|
91
|
+
data = []
|
|
92
|
+
tf = False
|
|
93
|
+
|
|
94
|
+
return tf, data
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# Function to try to open a file with pandas.read_csv
|
|
98
|
+
def try_open_csv(path, *args, **kwargs):
|
|
99
|
+
tf, data = try_open_file(pd.read_csv, path, *args, **kwargs)
|
|
100
|
+
return tf, data
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
COPYRIGHT STATEMENT:
|
|
5
|
+
|
|
6
|
+
ChromaQuant – A quantification software for complex gas chromatographic data
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026, by Julia Hancock
|
|
9
|
+
Affiliation: Dr. Julie Elaine Rorrer
|
|
10
|
+
URL: https://www.rorrerlab.com/
|
|
11
|
+
|
|
12
|
+
License: BSD 3-Clause License
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
COLLECTION OF VARIOUS FORMULA TOOLS
|
|
17
|
+
|
|
18
|
+
Julia Hancock
|
|
19
|
+
Started 1-07-2026
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import re
|
|
24
|
+
from openpyxl.utils import get_column_letter, coordinate_to_tuple
|
|
25
|
+
|
|
26
|
+
""" FUNCTIONS """
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Function for checking that passed formula starts with '='
|
|
30
|
+
def check_formula_starts_with_equals(formula):
|
|
31
|
+
|
|
32
|
+
# Initialize tf
|
|
33
|
+
tf = False
|
|
34
|
+
|
|
35
|
+
# If a match exists, set tf to True
|
|
36
|
+
if not re.search('^=', formula) is None:
|
|
37
|
+
tf = True
|
|
38
|
+
# Otherwise, pass
|
|
39
|
+
else:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
return tf
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# Function to get a column letter based on table
|
|
46
|
+
# starting column and column's index within a table
|
|
47
|
+
def get_column_letter_from_table(
|
|
48
|
+
table_reference, column_name, start_cell_column):
|
|
49
|
+
|
|
50
|
+
# Find the column's index within the DataFrame
|
|
51
|
+
column_index = \
|
|
52
|
+
table_reference['columns'].index(column_name)
|
|
53
|
+
|
|
54
|
+
# Add the column's index to the starting column
|
|
55
|
+
# and get the column letter equivalent
|
|
56
|
+
column_letter = \
|
|
57
|
+
get_column_letter(start_cell_column + column_index)
|
|
58
|
+
|
|
59
|
+
return column_letter
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Function to construct value reference string
|
|
63
|
+
def get_value_reference_string(value_reference):
|
|
64
|
+
|
|
65
|
+
# Get value's sheet string
|
|
66
|
+
value_sheet = \
|
|
67
|
+
f"'{value_reference['sheet']}'!"
|
|
68
|
+
# Get value's cell string
|
|
69
|
+
value_cell = \
|
|
70
|
+
f"{value_reference['cell']}"
|
|
71
|
+
|
|
72
|
+
# Get the reference string
|
|
73
|
+
reference = value_sheet + value_cell
|
|
74
|
+
|
|
75
|
+
return reference
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# Function to get the starting row and column of a table
|
|
79
|
+
def get_table_start_coords(table_reference):
|
|
80
|
+
|
|
81
|
+
# Get the table's starting cell
|
|
82
|
+
table_start_cell = table_reference['start_cell']
|
|
83
|
+
|
|
84
|
+
# Get the starting cell's row and column
|
|
85
|
+
start_row, start_column = \
|
|
86
|
+
coordinate_to_tuple(table_start_cell)
|
|
87
|
+
|
|
88
|
+
return start_row, start_column
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# Function to replace formula raw insert with reference
|
|
92
|
+
def replace_insert(formula, raw, reference):
|
|
93
|
+
|
|
94
|
+
# Replace pointer substring with value's reference
|
|
95
|
+
new_formula = \
|
|
96
|
+
formula.replace(raw, reference)
|
|
97
|
+
|
|
98
|
+
return new_formula
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# Function to get a range from passed table pointers
|
|
102
|
+
def table_column_to_range(table_reference, column_key):
|
|
103
|
+
|
|
104
|
+
# Get the starting coordinates
|
|
105
|
+
start_row, start_column = get_table_start_coords(table_reference)
|
|
106
|
+
|
|
107
|
+
# Get letter for output column
|
|
108
|
+
column_letter = \
|
|
109
|
+
get_column_letter_from_table(table_reference, column_key, start_column)
|
|
110
|
+
|
|
111
|
+
# Get the final row of the table
|
|
112
|
+
end_row = start_row + table_reference['length']
|
|
113
|
+
|
|
114
|
+
# Construct the range string
|
|
115
|
+
column_range = (
|
|
116
|
+
f'${column_letter}${start_row + 1}:'
|
|
117
|
+
f'${column_letter}${end_row + 1}')
|
|
118
|
+
|
|
119
|
+
return column_range
|