dragon-ml-toolbox 1.1.2__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.
Potentially problematic release.
This version of dragon-ml-toolbox might be problematic. Click here for more details.
- dragon_ml_toolbox-1.1.2.dist-info/METADATA +114 -0
- dragon_ml_toolbox-1.1.2.dist-info/RECORD +16 -0
- dragon_ml_toolbox-1.1.2.dist-info/WHEEL +5 -0
- dragon_ml_toolbox-1.1.2.dist-info/top_level.txt +1 -0
- ml_tools/MICE_imputation.py +178 -0
- ml_tools/__init__.py +0 -0
- ml_tools/data_exploration.py +751 -0
- ml_tools/datasetmaster.py +595 -0
- ml_tools/ensemble_learning.py +701 -0
- ml_tools/handle_excel.py +310 -0
- ml_tools/logger.py +145 -0
- ml_tools/particle_swarm_optimization.py +467 -0
- ml_tools/pytorch_models.py +227 -0
- ml_tools/trainer.py +366 -0
- ml_tools/utilities.py +168 -0
- ml_tools/vision_helpers.py +218 -0
ml_tools/handle_excel.py
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from openpyxl import load_workbook, Workbook
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def unmerge_and_split_excel(filepath: str) -> None:
|
|
8
|
+
"""
|
|
9
|
+
Processes a single Excel file:
|
|
10
|
+
- Unmerges all merged cells (vertical and horizontal),
|
|
11
|
+
- Fills each merged region with the top-left cell value,
|
|
12
|
+
- Splits each sheet into a separate Excel file,
|
|
13
|
+
- Saves all results in the same directory as the input file.
|
|
14
|
+
|
|
15
|
+
Parameters:
|
|
16
|
+
filepath (str): Full path to the Excel file to process.
|
|
17
|
+
"""
|
|
18
|
+
wb = load_workbook(filepath)
|
|
19
|
+
base_dir = os.path.dirname(os.path.abspath(filepath))
|
|
20
|
+
base_name = os.path.splitext(os.path.basename(filepath))[0]
|
|
21
|
+
|
|
22
|
+
total_output_files = 0
|
|
23
|
+
|
|
24
|
+
for sheet_name in wb.sheetnames:
|
|
25
|
+
ws = wb[sheet_name]
|
|
26
|
+
new_wb = Workbook()
|
|
27
|
+
new_ws = new_wb.active
|
|
28
|
+
new_ws.title = sheet_name
|
|
29
|
+
|
|
30
|
+
# Copy all cell values
|
|
31
|
+
for row in ws.iter_rows():
|
|
32
|
+
for cell in row:
|
|
33
|
+
new_ws.cell(row=cell.row, column=cell.column, value=cell.value)
|
|
34
|
+
|
|
35
|
+
# Fill and unmerge merged regions
|
|
36
|
+
for merged_range in list(ws.merged_cells.ranges):
|
|
37
|
+
min_row, min_col, max_row, max_col = (
|
|
38
|
+
merged_range.min_row, merged_range.min_col,
|
|
39
|
+
merged_range.max_row, merged_range.max_col
|
|
40
|
+
)
|
|
41
|
+
value = ws.cell(row=min_row, column=min_col).value
|
|
42
|
+
for row in range(min_row, max_row + 1):
|
|
43
|
+
for col in range(min_col, max_col + 1):
|
|
44
|
+
new_ws.cell(row=row, column=col, value=value)
|
|
45
|
+
|
|
46
|
+
# Construct flat output file name
|
|
47
|
+
sanitized_sheet_name = sheet_name.replace("/", "_").replace("\\", "_")
|
|
48
|
+
output_filename = f"{base_name}_{sanitized_sheet_name}.xlsx"
|
|
49
|
+
output_path = os.path.join(base_dir, output_filename)
|
|
50
|
+
new_wb.save(output_path)
|
|
51
|
+
|
|
52
|
+
# print(f"Saved: {output_path}")
|
|
53
|
+
total_output_files += 1
|
|
54
|
+
|
|
55
|
+
print(f"✅ Processed file: {filepath} into {total_output_files} output file(s).")
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def unmerge_and_split_from_directory(input_dir: str, output_dir: str) -> None:
|
|
60
|
+
"""
|
|
61
|
+
Processes all Excel files in the input directory:
|
|
62
|
+
- Unmerges all merged cells (vertical and horizontal),
|
|
63
|
+
- Fills each merged region with the top-left cell value,
|
|
64
|
+
- Splits each sheet into separate Excel files,
|
|
65
|
+
- Saves all results into the output directory.
|
|
66
|
+
|
|
67
|
+
Parameters:
|
|
68
|
+
input_dir (str): Directory containing Excel files to process.
|
|
69
|
+
output_dir (str): Directory to save processed Excel files.
|
|
70
|
+
"""
|
|
71
|
+
raw_files = [f for f in os.listdir(input_dir) if f.endswith(('.xlsx', '.xls'))]
|
|
72
|
+
excel_files = [os.path.join(input_dir, f) for f in raw_files if not f.startswith('~')]
|
|
73
|
+
|
|
74
|
+
if not excel_files:
|
|
75
|
+
raise FileNotFoundError(f"No valid Excel files found in directory: {input_dir}")
|
|
76
|
+
|
|
77
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
78
|
+
total_output_files = 0
|
|
79
|
+
|
|
80
|
+
for file_path in excel_files:
|
|
81
|
+
wb = load_workbook(file_path)
|
|
82
|
+
base_name = os.path.splitext(os.path.basename(file_path))[0]
|
|
83
|
+
|
|
84
|
+
for sheet_name in wb.sheetnames:
|
|
85
|
+
ws = wb[sheet_name]
|
|
86
|
+
new_wb = Workbook()
|
|
87
|
+
new_ws = new_wb.active
|
|
88
|
+
new_ws.title = sheet_name
|
|
89
|
+
|
|
90
|
+
# Copy all cell values
|
|
91
|
+
for row in ws.iter_rows():
|
|
92
|
+
for cell in row:
|
|
93
|
+
new_ws.cell(row=cell.row, column=cell.column, value=cell.value)
|
|
94
|
+
|
|
95
|
+
# Fill and unmerge merged regions
|
|
96
|
+
for merged_range in list(ws.merged_cells.ranges):
|
|
97
|
+
min_row, min_col, max_row, max_col = (
|
|
98
|
+
merged_range.min_row, merged_range.min_col,
|
|
99
|
+
merged_range.max_row, merged_range.max_col
|
|
100
|
+
)
|
|
101
|
+
value = ws.cell(row=min_row, column=min_col).value
|
|
102
|
+
for row in range(min_row, max_row + 1):
|
|
103
|
+
for col in range(min_col, max_col + 1):
|
|
104
|
+
new_ws.cell(row=row, column=col, value=value)
|
|
105
|
+
|
|
106
|
+
# Construct flat output file name
|
|
107
|
+
sanitized_sheet_name = sheet_name.replace("/", "_").replace("\\", "_")
|
|
108
|
+
output_filename = f"{base_name}_{sanitized_sheet_name}.xlsx"
|
|
109
|
+
output_path = os.path.join(output_dir, output_filename)
|
|
110
|
+
new_wb.save(output_path)
|
|
111
|
+
|
|
112
|
+
# print(f"Saved: {output_path}")
|
|
113
|
+
total_output_files += 1
|
|
114
|
+
|
|
115
|
+
print(f"✅ Processed {len(excel_files)} input Excel file(s) with a total of {total_output_files} output Excel file(s).")
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def validate_excel_schema(
|
|
120
|
+
target_dir: str,
|
|
121
|
+
expected_columns: List[str],
|
|
122
|
+
strict: bool = False
|
|
123
|
+
) -> None:
|
|
124
|
+
"""
|
|
125
|
+
Validates that each Excel file in a directory conforms to the expected column schema.
|
|
126
|
+
|
|
127
|
+
Parameters:
|
|
128
|
+
target_dir (str): Path to the directory containing Excel files.
|
|
129
|
+
expected_columns (list[str]): List of expected column names.
|
|
130
|
+
strict (bool): If True, columns must match exactly (names and order).
|
|
131
|
+
If False, columns must contain at least all expected names.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
List[str]: List of file paths that failed the schema validation.
|
|
135
|
+
"""
|
|
136
|
+
invalid_files = []
|
|
137
|
+
expected_set = set(expected_columns)
|
|
138
|
+
|
|
139
|
+
excel_seen = 0
|
|
140
|
+
|
|
141
|
+
for filename in os.listdir(target_dir):
|
|
142
|
+
if not filename.lower().endswith(".xlsx"):
|
|
143
|
+
continue # Skip non-Excel files
|
|
144
|
+
|
|
145
|
+
if filename.startswith("~"): # Skip temporary files
|
|
146
|
+
continue
|
|
147
|
+
|
|
148
|
+
file_path = os.path.join(target_dir, filename)
|
|
149
|
+
excel_seen += 1
|
|
150
|
+
try:
|
|
151
|
+
wb = load_workbook(file_path, read_only=True)
|
|
152
|
+
ws = wb.active # Only check the first worksheet
|
|
153
|
+
|
|
154
|
+
header = [cell.value for cell in next(ws.iter_rows(max_row=1))]
|
|
155
|
+
|
|
156
|
+
if strict:
|
|
157
|
+
if header != expected_columns:
|
|
158
|
+
invalid_files.append(file_path)
|
|
159
|
+
else:
|
|
160
|
+
header_set = set(header)
|
|
161
|
+
if not expected_set.issubset(header_set):
|
|
162
|
+
invalid_files.append(file_path)
|
|
163
|
+
|
|
164
|
+
except Exception as e:
|
|
165
|
+
print(f"Error processing '{file_path}': {e}")
|
|
166
|
+
invalid_files.append(file_path)
|
|
167
|
+
|
|
168
|
+
valid_excel_number = excel_seen - len(invalid_files)
|
|
169
|
+
print(f"{valid_excel_number} out of {excel_seen} excel files conform to the schema.")
|
|
170
|
+
if invalid_files:
|
|
171
|
+
print(f"⚠️ {len(invalid_files)} excel files are invalid:")
|
|
172
|
+
for file in invalid_files:
|
|
173
|
+
print(f" - {file}")
|
|
174
|
+
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def vertical_merge_transform_excel(
|
|
179
|
+
target_dir: str,
|
|
180
|
+
csv_filename: str,
|
|
181
|
+
output_dir: str,
|
|
182
|
+
target_columns: Optional[List[str]] = None,
|
|
183
|
+
rename_columns: Optional[List[str]] = None
|
|
184
|
+
) -> None:
|
|
185
|
+
"""
|
|
186
|
+
Merges multiple Excel files in a directory vertically and saves as a single CSV file.
|
|
187
|
+
|
|
188
|
+
Constraints:
|
|
189
|
+
- Only the first worksheet of each Excel file is processed.
|
|
190
|
+
- All Excel files must have either the same column names or a common subset if `target_columns` is provided.
|
|
191
|
+
- If `rename_columns` is provided, it must match the length of `target_columns` (if used) or the original columns. Names match by position.
|
|
192
|
+
|
|
193
|
+
Parameters:
|
|
194
|
+
target_dir (str): Directory containing Excel files.
|
|
195
|
+
csv_filename (str): Output CSV filename.
|
|
196
|
+
output_dir (str): Directory to save the output CSV file.
|
|
197
|
+
target_columns (list[str] | None): Columns to select from each Excel file.
|
|
198
|
+
rename_columns (list[str] | None): Optional renaming for columns. Position-based matching.
|
|
199
|
+
"""
|
|
200
|
+
raw_excel_files = [f for f in os.listdir(target_dir) if f.endswith(('.xlsx', '.xls'))]
|
|
201
|
+
excel_files = [f for f in raw_excel_files if not f.startswith('~')] # Exclude temporary files
|
|
202
|
+
|
|
203
|
+
if not excel_files:
|
|
204
|
+
raise ValueError("No Excel files found in the target directory.")
|
|
205
|
+
|
|
206
|
+
csv_filename = csv_filename if csv_filename.endswith('.csv') else f"{csv_filename}.csv"
|
|
207
|
+
csv_path = os.path.join(output_dir, csv_filename)
|
|
208
|
+
|
|
209
|
+
dataframes = []
|
|
210
|
+
for file in excel_files:
|
|
211
|
+
file_path = os.path.join(target_dir, file)
|
|
212
|
+
df = pd.read_excel(file_path, engine='openpyxl')
|
|
213
|
+
|
|
214
|
+
if target_columns is not None:
|
|
215
|
+
missing = [col for col in target_columns if col not in df.columns]
|
|
216
|
+
if missing:
|
|
217
|
+
raise ValueError(f"Missing columns in {file}: {missing}")
|
|
218
|
+
df = df[target_columns]
|
|
219
|
+
|
|
220
|
+
dataframes.append(df)
|
|
221
|
+
|
|
222
|
+
merged_df = pd.concat(dataframes, ignore_index=True)
|
|
223
|
+
|
|
224
|
+
if rename_columns is not None:
|
|
225
|
+
expected_len = len(target_columns if target_columns is not None else merged_df.columns)
|
|
226
|
+
if len(rename_columns) != expected_len:
|
|
227
|
+
raise ValueError("Length of rename_columns must match the selected columns")
|
|
228
|
+
merged_df.columns = rename_columns
|
|
229
|
+
|
|
230
|
+
merged_df.to_csv(csv_path, index=False, encoding='utf-8')
|
|
231
|
+
print(f"✅ Merged {len(dataframes)} excel files into '{csv_filename}'.")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def horizontal_merge_transform_excel(
|
|
235
|
+
target_dir: str,
|
|
236
|
+
csv_filename: str,
|
|
237
|
+
output_dir: str,
|
|
238
|
+
drop_columns: Optional[list[str]] = None,
|
|
239
|
+
skip_duplicates: bool = False
|
|
240
|
+
) -> None:
|
|
241
|
+
"""
|
|
242
|
+
Horizontally concatenates Excel files (first sheet of each) by aligning rows and expanding columns.
|
|
243
|
+
Then saves the result as a .csv file.
|
|
244
|
+
|
|
245
|
+
Constraints:
|
|
246
|
+
- All Excel files must have the same number of rows, or shorter ones will be padded with empty rows.
|
|
247
|
+
- Only the first sheet in each Excel file is used.
|
|
248
|
+
- Columns in `drop_columns` will be excluded from the result.
|
|
249
|
+
- If `skip_duplicates` is False, duplicate columns are suffixed with "_copy", "_copy2", etc.
|
|
250
|
+
If True, only the first occurrence of each column name is kept.
|
|
251
|
+
|
|
252
|
+
Parameters:
|
|
253
|
+
target_dir (str): Directory containing Excel files.
|
|
254
|
+
csv_filename (str): Name of the output CSV file.
|
|
255
|
+
output_dir (str): Directory to save the output CSV file.
|
|
256
|
+
drop_columns (list[str] | None): Columns to exclude from each file before merging.
|
|
257
|
+
skip_duplicates (bool): Whether to skip duplicate columns or rename them.
|
|
258
|
+
"""
|
|
259
|
+
raw_excel_files = [f for f in os.listdir(target_dir) if f.endswith(('.xlsx', '.xls'))]
|
|
260
|
+
excel_files = [f for f in raw_excel_files if not f.startswith('~')] # Exclude temporary files
|
|
261
|
+
if not excel_files:
|
|
262
|
+
raise ValueError("No Excel files found in the target directory.")
|
|
263
|
+
|
|
264
|
+
csv_filename = csv_filename if csv_filename.endswith('.csv') else f"{csv_filename}.csv"
|
|
265
|
+
csv_path = os.path.join(output_dir, csv_filename)
|
|
266
|
+
|
|
267
|
+
dataframes = []
|
|
268
|
+
max_rows = 0
|
|
269
|
+
|
|
270
|
+
for file in excel_files:
|
|
271
|
+
file_path = os.path.join(target_dir, file)
|
|
272
|
+
df = pd.read_excel(file_path, engine='openpyxl')
|
|
273
|
+
|
|
274
|
+
if drop_columns is not None:
|
|
275
|
+
df = df.drop(columns=[col for col in drop_columns if col in df.columns])
|
|
276
|
+
|
|
277
|
+
max_rows = max(max_rows, len(df))
|
|
278
|
+
dataframes.append(df)
|
|
279
|
+
|
|
280
|
+
padded_dataframes = []
|
|
281
|
+
for df in dataframes:
|
|
282
|
+
padded_df = df.reindex(range(max_rows)).reset_index(drop=True)
|
|
283
|
+
padded_dataframes.append(padded_df)
|
|
284
|
+
|
|
285
|
+
merged_df = pd.concat(padded_dataframes, axis=1)
|
|
286
|
+
|
|
287
|
+
duplicate_columns = merged_df.columns[merged_df.columns.duplicated()].tolist()
|
|
288
|
+
|
|
289
|
+
if skip_duplicates:
|
|
290
|
+
merged_df = merged_df.loc[:, ~merged_df.columns.duplicated()]
|
|
291
|
+
else:
|
|
292
|
+
seen = {}
|
|
293
|
+
new_cols = []
|
|
294
|
+
for col in merged_df.columns:
|
|
295
|
+
base_col = col
|
|
296
|
+
count = seen.get(base_col, 0)
|
|
297
|
+
if count:
|
|
298
|
+
while f"{base_col}_copy{count}" in seen:
|
|
299
|
+
count += 1
|
|
300
|
+
col = f"{base_col}_copy{count}"
|
|
301
|
+
seen[col] = count + 1
|
|
302
|
+
new_cols.append(col)
|
|
303
|
+
merged_df.columns = new_cols
|
|
304
|
+
|
|
305
|
+
merged_df.to_csv(csv_path, index=False, encoding='utf-8')
|
|
306
|
+
|
|
307
|
+
print(f"✅ Merged {len(excel_files)} Excel files into '{csv_filename}'.")
|
|
308
|
+
if duplicate_columns:
|
|
309
|
+
print(f"⚠️ Duplicate columns: {duplicate_columns}")
|
|
310
|
+
|
ml_tools/logger.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Union, List, Dict, Any
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from openpyxl.styles import Font, PatternFill
|
|
6
|
+
import traceback
|
|
7
|
+
import json
|
|
8
|
+
from utilities import sanitize_filename
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def custom_logger(
|
|
12
|
+
data: Union[
|
|
13
|
+
List[Any],
|
|
14
|
+
Dict[Any, Any],
|
|
15
|
+
pd.DataFrame,
|
|
16
|
+
str,
|
|
17
|
+
BaseException
|
|
18
|
+
],
|
|
19
|
+
save_directory: str,
|
|
20
|
+
log_name: str,
|
|
21
|
+
) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Logs various data types to corresponding output formats:
|
|
24
|
+
|
|
25
|
+
- list[Any] → .txt
|
|
26
|
+
Each element is written on a new line.
|
|
27
|
+
|
|
28
|
+
- dict[str, list[Any]] → .csv
|
|
29
|
+
Dictionary is treated as tabular data; keys become columns, values become rows.
|
|
30
|
+
|
|
31
|
+
- dict[str, scalar] → .json
|
|
32
|
+
Dictionary is treated as structured data and serialized as JSON.
|
|
33
|
+
|
|
34
|
+
- pandas.DataFrame → .xlsx
|
|
35
|
+
Written to an Excel file with styled headers.
|
|
36
|
+
|
|
37
|
+
- str → .log
|
|
38
|
+
Plain text string is written to a .log file.
|
|
39
|
+
|
|
40
|
+
- BaseException → .log
|
|
41
|
+
Full traceback is logged for debugging purposes.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
data: The data to be logged. Must be one of the supported types.
|
|
45
|
+
save_directory: Directory where the log will be saved. Created if it does not exist.
|
|
46
|
+
log_name: Base name for the log file. Timestamp will be appended automatically.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
ValueError: If the data type is unsupported.
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
os.makedirs(save_directory, exist_ok=True)
|
|
53
|
+
timestamp = datetime.now().strftime(r"%Y%m%d_%H%M")
|
|
54
|
+
log_name = sanitize_filename(log_name)
|
|
55
|
+
base_path = os.path.join(save_directory, f"{log_name}_{timestamp}")
|
|
56
|
+
|
|
57
|
+
if isinstance(data, list):
|
|
58
|
+
_log_list_to_txt(data, base_path + ".txt")
|
|
59
|
+
|
|
60
|
+
elif isinstance(data, dict):
|
|
61
|
+
if all(isinstance(v, list) for v in data.values()):
|
|
62
|
+
_log_dict_to_csv(data, base_path + ".csv")
|
|
63
|
+
else:
|
|
64
|
+
_log_dict_to_json(data, base_path + ".json")
|
|
65
|
+
|
|
66
|
+
elif isinstance(data, pd.DataFrame):
|
|
67
|
+
_log_dataframe_to_xlsx(data, base_path + ".xlsx")
|
|
68
|
+
|
|
69
|
+
elif isinstance(data, str):
|
|
70
|
+
_log_string_to_log(data, base_path + ".log")
|
|
71
|
+
|
|
72
|
+
elif isinstance(data, BaseException):
|
|
73
|
+
_log_exception_to_log(data, base_path + ".log")
|
|
74
|
+
|
|
75
|
+
else:
|
|
76
|
+
raise ValueError("Unsupported data type. Must be list, dict, DataFrame, str, or BaseException.")
|
|
77
|
+
|
|
78
|
+
print(f"Log saved to: {base_path}")
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
print(f"Error in custom_logger: {e}")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _log_list_to_txt(data: List[Any], path: str) -> None:
|
|
85
|
+
log_lines = []
|
|
86
|
+
for item in data:
|
|
87
|
+
try:
|
|
88
|
+
log_lines.append(str(item).strip())
|
|
89
|
+
except Exception:
|
|
90
|
+
log_lines.append(f"(unrepresentable item of type {type(item)})")
|
|
91
|
+
|
|
92
|
+
with open(path, 'w', encoding='utf-8') as f:
|
|
93
|
+
f.write('\n'.join(log_lines))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _log_dict_to_csv(data: Dict[Any, List[Any]], path: str) -> None:
|
|
97
|
+
sanitized_dict = {}
|
|
98
|
+
max_length = max(len(v) for v in data.values()) if data else 0
|
|
99
|
+
|
|
100
|
+
for key, value in data.items():
|
|
101
|
+
if not isinstance(value, list):
|
|
102
|
+
raise ValueError(f"Dictionary value for key '{key}' must be a list.")
|
|
103
|
+
sanitized_key = str(key).strip().replace('\n', '_').replace('\r', '_')
|
|
104
|
+
padded_value = value + [None] * (max_length - len(value))
|
|
105
|
+
sanitized_dict[sanitized_key] = padded_value
|
|
106
|
+
|
|
107
|
+
df = pd.DataFrame(sanitized_dict)
|
|
108
|
+
df.to_csv(path, index=False)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _log_dataframe_to_xlsx(data: pd.DataFrame, path: str) -> None:
|
|
112
|
+
writer = pd.ExcelWriter(path, engine='openpyxl')
|
|
113
|
+
data.to_excel(writer, index=True, sheet_name='Data')
|
|
114
|
+
|
|
115
|
+
workbook = writer.book
|
|
116
|
+
worksheet = writer.sheets['Data']
|
|
117
|
+
|
|
118
|
+
header_font = Font(bold=True)
|
|
119
|
+
header_fill = PatternFill(
|
|
120
|
+
start_color="ADD8E6", # Light blue
|
|
121
|
+
end_color="ADD8E6",
|
|
122
|
+
fill_type="solid"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
for cell in worksheet[1]:
|
|
126
|
+
cell.font = header_font
|
|
127
|
+
cell.fill = header_fill
|
|
128
|
+
|
|
129
|
+
writer.close()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _log_string_to_log(data: str, path: str) -> None:
|
|
133
|
+
with open(path, 'w', encoding='utf-8') as f:
|
|
134
|
+
f.write(data.strip() + '\n')
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _log_exception_to_log(exc: BaseException, path: str) -> None:
|
|
138
|
+
with open(path, 'w', encoding='utf-8') as f:
|
|
139
|
+
f.write("Exception occurred:\n")
|
|
140
|
+
traceback.print_exception(type(exc), exc, exc.__traceback__, file=f)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _log_dict_to_json(data: Dict[Any, Any], path: str) -> None:
|
|
144
|
+
with open(path, 'w', encoding='utf-8') as f:
|
|
145
|
+
json.dump(data, f, indent=4, ensure_ascii=False)
|