Compare commits

...

1 Commits

Author SHA1 Message Date
6c96606ac2 pain 2026-03-16 19:08:13 +01:00
4 changed files with 95 additions and 44 deletions

View File

@ -1,5 +1,5 @@
import argparse import argparse
from table_handler.serializer import TableSerializer from table_handler.serializer import Transformer, XslxReader
from logger import logger from logger import logger
import instructions.countings_formater as countings_formater import instructions.countings_formater as countings_formater
@ -40,10 +40,11 @@ def main():
row_instructions = countings_formater.ROW_INSTRUCTION row_instructions = countings_formater.ROW_INSTRUCTION
logger.info("Reading file, building serializer...") logger.info("Reading file, building serializer...")
serializer = TableSerializer( data = XslxReader(args.input).serialize_table(formatter.EXPECTED_INPUT_TABLE_SCHEMA)
args.input,
formatter.EXPECTED_INPUT_TABLE_SCHEMA, serializer = Transformer(
formatter.EXPECTED_OUTPUT_TABLE_SCHEMA data,
formatter.EXPECTED_OUTPUT_TABLE_SCHEMA or formatter.EXPECTED_INPUT_TABLE_SCHEMA,
) )
logger.info("Starting transformation") logger.info("Starting transformation")

View File

@ -1,3 +1,10 @@
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
def lock_logger(q):
while True:
msg = q.get()
if msg == "DONE":
break
logger.info(msg)

View File

@ -3,7 +3,8 @@ from openpyxl import Workbook, load_workbook
from dataclasses import dataclass from dataclasses import dataclass
from pprint import pprint as print from pprint import pprint as print
import instructions.mappings as mappings from table_handler.utils import hex_to_rgb, delegate_tasks
from logger import lock_logger
from .commands.find_and_replace import FindAndReplace from .commands.find_and_replace import FindAndReplace
from .commands.regex_edit_col import RegexEditCol from .commands.regex_edit_col import RegexEditCol
@ -11,16 +12,10 @@ from .commands.regex_move_to_col import RegexMoveToCol
from .commands.copy import Copy from .commands.copy import Copy
from .commands.regex_group_valuebased_choose import RegexGroupValuebasedChoose from .commands.regex_group_valuebased_choose import RegexGroupValuebasedChoose
from .commands.row_color_to_value import RowColorToCalue from .commands.row_color_to_value import RowColorToCalue
from logger import logger
from .conditions.regex_cell import RegexCellCondition from .conditions.regex_cell import RegexCellCondition
def hex_to_rgb(hex): from multiprocessing import Pool, Queue, Process, Manager, cpu_count
if type(hex) != str and str("Values must be of type <class 'str'>"):
# chatch wierd openxl things
hex = "#0"
hex= "{:06}".format(str(hex).lstrip("#"))
return tuple(int(hex[i:i+2],16) for i in (0, 2, 4))
@dataclass @dataclass
class Entry: class Entry:
@ -30,22 +25,14 @@ class Entry:
class TableSerializer: class XslxReader:
def __init__(self, file: str, expected_input_schema: list[str], expected_output_schema: list[str]): def __init__(self, file):
self.wb: Workbook = load_workbook(file, read_only=True) self.wb: Workbook = load_workbook(file, read_only=True)
self.ws = self.wb.active self.ws = self.wb.active
if not self.ws: if not self.ws:
raise ValueError("No active worksheet found in the workbook.") raise ValueError("No active worksheet found in the workbook.")
self.data = self._serialize_table(expected_input_schema) def serialize_table(self, expected_cols: list[str]) -> dict[int, list[Entry]]:
self.serialized_output = {}
self.seperator = ", "
self.expected_output_schema = expected_output_schema or expected_input_schema
self.implicit_copy = True
self.output_header = True
def _serialize_table(self, expected_cols: list[str]) -> dict[int, list[Entry]]:
data = {col: {} for col in expected_cols} data = {col: {} for col in expected_cols}
map_expected_with_ws = {} map_expected_with_ws = {}
for idx, cell in enumerate(self.ws[1]): for idx, cell in enumerate(self.ws[1]):
@ -68,8 +55,19 @@ class TableSerializer:
) )
return data return data
class Transformer:
def __init__(self, data, expected_output_schema: list[str], cumulated_instructions = {}):
self.data = data
self.fully_serialized_output = {}
self.seperator = ", "
self.expected_output_schema = expected_output_schema
self.implicit_copy = True
self.output_header = True
self.cumulated_instructions = cumulated_instructions
def write_output(self, destination_file: str): def write_output(self, destination_file: str):
if not self.serialized_output: if not self.fully_serialized_output:
raise ValueError("There is nothing to write, serialize first") raise ValueError("There is nothing to write, serialize first")
# write header # write header
@ -81,7 +79,7 @@ class TableSerializer:
ows.cell(row=1, column=col_i, value=col_name) ows.cell(row=1, column=col_i, value=col_name)
# write rows # write rows
for row_i, (row_id, row_data) in enumerate(self.serialized_output.items(), start=2 if self.output_header else 1): for row_i, (row_id, row_data) in enumerate(self.fully_serialized_output.items(), start=2 if self.output_header else 1):
for col_i, col_name in enumerate(self.expected_output_schema, start=1): for col_i, col_name in enumerate(self.expected_output_schema, start=1):
ows.cell(row=row_i, column=col_i, value=row_data.get(col_name)) ows.cell(row=row_i, column=col_i, value=row_data.get(col_name))
@ -134,24 +132,15 @@ class TableSerializer:
working_row[source_cell] = self._merge_change_to_cel(working_row.get(source_cell), res) working_row[source_cell] = self._merge_change_to_cel(working_row.get(source_cell), res)
return working_row return working_row
def _run_row_resolution(self, range, worker: str = "X", q = None):
@property self.fully_serialized_output = {}
def vertical_len(self): end = list(range)[-1]
return max(len(data_row) for data_row in self.data.values()) for rid in range:
if rid % 100 == 0 and q:
def get_row_as_dict(self, rid: int): q.put(f"Worker {worker}:Working on row {rid}/{end}")
return {col: rows.get(rid) for col, rows in self.data.items()}
def run(self, col_instructions: dict[tuple[str], dict], row_instructions: dict[tuple[str], dict]):
self.serialized_output = {}
amount_working_rows = self.vertical_len +2
for rid in range(2, amount_working_rows):
logger.info(f"Working on row {rid}/{amount_working_rows} ")
working_row = {} working_row = {}
source_row = self.get_row_as_dict(rid) source_row = self.get_row_as_dict(rid)
for source_cols, instructions in list(col_instructions.items()) + [ for source_cols, instructions in self.cumulated_instructions:
((None,), instr) for instr in row_instructions
]:
for instruct in instructions if type(instructions) == list else [instructions]: for instruct in instructions if type(instructions) == list else [instructions]:
for col in source_cols: for col in source_cols:
if not self._check_conditions(instruct, source_row): if not self._check_conditions(instruct, source_row):
@ -163,4 +152,38 @@ class TableSerializer:
if col in working_row: if col in working_row:
continue continue
working_row[col] = source_row.get(col).value working_row[col] = source_row.get(col).value
self.serialized_output[rid] = working_row self.fully_serialized_output[rid] = working_row
return self.fully_serialized_output
@property
def vertical_len(self):
return max(len(data_row) for data_row in self.data.values())
def get_row_as_dict(self, rid: int):
return {col: rows.get(rid) for col, rows in self.data.items()}
def run(self, col_instructions: dict[tuple[str], dict], row_instructions: dict[tuple[str], dict]):
self.cumulated_instructions = list(col_instructions.items()) + [
((None,), instr) for instr in row_instructions
]
amount_working_rows = self.vertical_len + 2
with Manager() as manager:
q = manager.Queue()
chnucs = [[elem, worker_name, q] for worker_name, elem in enumerate(
delegate_tasks(range(2, amount_working_rows), cpu_count(), 1000)
)]
log_proc = Process(target=lock_logger, args=(q,))
log_proc.start()
with Pool(len(chnucs)) as p:
result = p.starmap(
Transformer(self.data, self.expected_output_schema, self.cumulated_instructions)._run_row_resolution,
chnucs
)
q.put("DONE")
log_proc.join(1)
self.fully_serialized_output = {k: v for d in result for k, v in d.items()}
return

20
table_handler/utils.py Normal file
View File

@ -0,0 +1,20 @@
def hex_to_rgb(hex):
if type(hex) != str and str("Values must be of type <class 'str'>"):
# chatch wierd openxl things
hex = "#0"
hex= "{:06}".format(str(hex).lstrip("#"))
return tuple(int(hex[i:i+2],16) for i in (0, 2, 4))
def delegate_tasks(tasks, threats, min_tasks_per_threat):
n = len(tasks)
# determine usable cores
usable_cores = min(threats, n // min_tasks_per_threat)
if usable_cores == 0:
usable_cores = 1
# split tasks evenly
chunk_size = (n + usable_cores - 1) // usable_cores
chunks = [tasks[i:i + chunk_size] for i in range(0, n, chunk_size)]
return chunks