diff --git a/instructions/__pycache__/countings_formater.cpython-312.pyc b/instructions/__pycache__/countings_formater.cpython-312.pyc index 62b3816..b4edbc1 100644 Binary files a/instructions/__pycache__/countings_formater.cpython-312.pyc and b/instructions/__pycache__/countings_formater.cpython-312.pyc differ diff --git a/instructions/__pycache__/mappings.cpython-312.pyc b/instructions/__pycache__/mappings.cpython-312.pyc index 01a9437..8bfee93 100644 Binary files a/instructions/__pycache__/mappings.cpython-312.pyc and b/instructions/__pycache__/mappings.cpython-312.pyc differ diff --git a/instructions/countings_formater.py b/instructions/countings_formater.py index 678790a..fefcc2c 100644 --- a/instructions/countings_formater.py +++ b/instructions/countings_formater.py @@ -13,7 +13,8 @@ EXPECTED_INPUT_TABLE_SCHEMA = [ "Bemerkung" ] -EXPECTED_OUTPUT_TABLE_SCHEMA = deepcopy(EXPECTED_INPUT_TABLE_SCHEMA).remove("Geschlecht") +EXPECTED_OUTPUT_TABLE_SCHEMA = deepcopy(EXPECTED_INPUT_TABLE_SCHEMA) +EXPECTED_OUTPUT_TABLE_SCHEMA.remove("Geschlecht") COL_INSTRUCTIONS = { ("Art",): { @@ -22,15 +23,20 @@ COL_INSTRUCTIONS = { }, ("Ort",): { "command": "REGEX_MOVE_TO_COL", - "with_input_iteration": CATCHED_BY, # Moves matching group {{% 1 %}} to "to" - "match": r"^(?:[^ ]* +)*({{% 1 %}}),?(?: .*+)*$", + "with_input_iteration": CATCHED_BY, # Moves matching group {{% 0 %}} to "to" + "match": r"^(?:[^ ]* +)*({{% 0 %}}),?(?: .*+)*$", "to": "Fangart" }, - ("Fangart",): { + ("Anzahl",): { "command": "REGEX_EDIT_COL", "match": r"^ *$", "replace_with": "1" }, + ("Anzahl",): { + "command": "REGEX_GROUP_VALUEBASED_CHOOS", + "match": r"^ *([0-9]+) *oder *([0-9]+) *$", + "highest": True + }, } ROW_INSTRUCTION = [ @@ -38,7 +44,7 @@ ROW_INSTRUCTION = [ "command": "CONDITIONAL_COPY", "when_regex": { "Geschlecht": r"^(?:[^ ]* +)*(m),?(?: .*+)*$", - "Anzahl": r"/d" + "Anzahl": r"([0-9]+)" }, "source": "Anzahl", "target": "Davon m" @@ -47,7 +53,7 @@ ROW_INSTRUCTION = [ "command": "CONDITIONAL_COPY", "when_regex": { "Geschlecht": r"^(?:[^ ]* +)*(w),?(?: .*+)*$", - "Anzahl": r"/d" + "Anzahl": r"([0-9]+)" }, "source": "Anzahl", "target": "Davon w" diff --git a/table_handler/__pycache__/serializer.cpython-312.pyc b/table_handler/__pycache__/serializer.cpython-312.pyc index 0a3b328..c693891 100644 Binary files a/table_handler/__pycache__/serializer.cpython-312.pyc and b/table_handler/__pycache__/serializer.cpython-312.pyc differ diff --git a/table_handler/commands/__pycache__/copy.cpython-312.pyc b/table_handler/commands/__pycache__/copy.cpython-312.pyc index 581bd36..2d12a12 100644 Binary files a/table_handler/commands/__pycache__/copy.cpython-312.pyc and b/table_handler/commands/__pycache__/copy.cpython-312.pyc differ diff --git a/table_handler/commands/__pycache__/find_and_replace.cpython-312.pyc b/table_handler/commands/__pycache__/find_and_replace.cpython-312.pyc index 117c77d..60bcd16 100644 Binary files a/table_handler/commands/__pycache__/find_and_replace.cpython-312.pyc and b/table_handler/commands/__pycache__/find_and_replace.cpython-312.pyc differ diff --git a/table_handler/commands/__pycache__/regex_edit_col.cpython-312.pyc b/table_handler/commands/__pycache__/regex_edit_col.cpython-312.pyc index 9230af2..087452f 100644 Binary files a/table_handler/commands/__pycache__/regex_edit_col.cpython-312.pyc and b/table_handler/commands/__pycache__/regex_edit_col.cpython-312.pyc differ diff --git a/table_handler/commands/__pycache__/regex_group_valuebased_choose.cpython-312.pyc b/table_handler/commands/__pycache__/regex_group_valuebased_choose.cpython-312.pyc new file mode 100644 index 0000000..9cb2ea2 Binary files /dev/null and b/table_handler/commands/__pycache__/regex_group_valuebased_choose.cpython-312.pyc differ diff --git a/table_handler/commands/__pycache__/regex_move_to_col.cpython-312.pyc b/table_handler/commands/__pycache__/regex_move_to_col.cpython-312.pyc index 278d5d1..3d73fd1 100644 Binary files a/table_handler/commands/__pycache__/regex_move_to_col.cpython-312.pyc and b/table_handler/commands/__pycache__/regex_move_to_col.cpython-312.pyc differ diff --git a/table_handler/commands/copy.py b/table_handler/commands/copy.py index 66be82e..3a9d273 100644 --- a/table_handler/commands/copy.py +++ b/table_handler/commands/copy.py @@ -8,6 +8,6 @@ class Copy: def execute_on_row(self, row=dict[str, str], source_col=None): return { - **({} if not self.replace else {source_col: ""}), - self.target: row[source_col] + **({} if not self.replace else {self.source_col: ""}), + self.target: row[self.source].value } \ No newline at end of file diff --git a/table_handler/commands/find_and_replace.py b/table_handler/commands/find_and_replace.py index daf4427..9289aca 100644 --- a/table_handler/commands/find_and_replace.py +++ b/table_handler/commands/find_and_replace.py @@ -5,7 +5,9 @@ class FindAndReplace: self.map = map def execute_on_cell(self, entry) -> str: + res = deepcopy(entry.value) for species, alternatives in self.map.items(): for old_version in alternatives: if old_version in entry.value: - return deepcopy(entry.value).replace(old_version, species) \ No newline at end of file + res = res.replace(old_version, species) + return res \ No newline at end of file diff --git a/table_handler/commands/regex_edit_col.py b/table_handler/commands/regex_edit_col.py index 42dfcfe..c2f1976 100644 --- a/table_handler/commands/regex_edit_col.py +++ b/table_handler/commands/regex_edit_col.py @@ -5,7 +5,7 @@ class RegexEditCol: self.match = match self.replace_with = replace_with - def execute_on_cell(self, value) -> str: - if value is None: - return value - return re.sub(self.match, self.replace_with, str(value)) \ No newline at end of file + def execute_on_cell(self, entry) -> str: + if entry.value is None: + return entry.value + return re.sub(self.match, self.replace_with, entry.value) \ No newline at end of file diff --git a/table_handler/commands/regex_group_valuebased_choose.py b/table_handler/commands/regex_group_valuebased_choose.py new file mode 100644 index 0000000..09a7de6 --- /dev/null +++ b/table_handler/commands/regex_group_valuebased_choose.py @@ -0,0 +1,21 @@ +import regex + +class RegexGroupValuebasedChoose: + def __init__(self, match: str, highest: bool): + self.match = match + self.highest = highest + + def execute_on_cell(self, entry): + matches = regex.match(self.match, entry.value) + if not matches: + return None + groups = matches.groups(default=[]) + + values = sorted(map(lambda x: int(x), groups)) + if not len(values): + return None + if self.highest: + return str(values[-1]) + else: + return str(values[0]) + diff --git a/table_handler/commands/regex_move_to_col.py b/table_handler/commands/regex_move_to_col.py index 10a8301..290297b 100644 --- a/table_handler/commands/regex_move_to_col.py +++ b/table_handler/commands/regex_move_to_col.py @@ -1,12 +1,20 @@ import regex +import re class RegexMoveToCol: def __init__(self, match: str, to: str, with_input_iteration: list[str]|None = None): self.match = match self.to = to - if with_input_iteration is not None and type(with_input_iteration) != list: - with_input_iteration = list(map([e] for e in with_input_iteration)) + if with_input_iteration is not None: + with_input_iteration = ( + [[with_input_iteration]] + if isinstance(with_input_iteration, str) + else [ + x if isinstance(x, list) else [x] + for x in (with_input_iteration if isinstance(with_input_iteration, (list, tuple)) else [with_input_iteration]) + ] + ) self.with_input_iteration = with_input_iteration self.__operation_input_depth = self.__find_operation_input_depth() @@ -14,49 +22,55 @@ class RegexMoveToCol: def __find_operation_input_depth(self): if not self.with_input_iteration: return None - return max(len(option) for option in self.__iteration_input_depth()) + return max(len(option) for option in self.with_input_iteration) @staticmethod - def _get_merged_groups(regex_pattern: str, entry: str) -> str: - match = regex.search(regex_pattern, entry.value) + def _get_merged_groups(regex_pattern: str, value: str) -> str: + match = regex.search(regex_pattern, value) if not match: - return "" + return None return "".join(g for g in match.groups() if g is not None) @staticmethod - def _remove_regex_groups(regex_pattern: str, entry: str) -> str: - match = regex.search(regex_pattern, entry.value) - if not match: - return entry.value + def _remove_regex_groups(pattern, value): + regex = re.compile(pattern) - spans = [s for s in match.spans()[1:] if s != (-1, -1)] + def repl(m): + s = m.group(0) + # remove all group spans from the full match + spans = [m.span(i) for i in range(1, len(m.groups()) + 1) if m.span(i) != (-1, -1)] + spans.sort(reverse=True) - result = [] - last = 0 + for start, end in spans: + rel_start = start - m.start() + rel_end = end - m.start() + s = s[:rel_start] + s[rel_end:] - for start, end in sorted(spans): - result.append(entry.value[last:start]) - last = end + return s - result.append(entry.value[last:]) - return "".join(result) + return regex.sub(repl, value) def do_changes(self, source_coll: str, used_regex: str, row: dict[str, str], changes={}): - changes[self.to] = self._get_merged_groups(used_regex, row[source_coll]) - changes[source_coll] = self._remove_regex_groups(used_regex, row[source_coll]) + current_to = changes.get(self.to, row[source_coll].value) + current_source = changes.get(source_coll, row[source_coll].value) + + pending_res_to = self._get_merged_groups(used_regex, current_to) + if pending_res_to is not None: + changes[self.to] = pending_res_to + changes[source_coll] = self._remove_regex_groups(used_regex, current_source) return changes def execute_on_row(self, row: dict[str, str], source_coll: str = None): if not source_coll: raise ValueError("source_col required") if self.with_input_iteration: - changes = None - used_regex = self.match + changes = {} for iteration in self.with_input_iteration: - for operation_id in range(0, self.__operation_input_depth-1): - used_regex.replace(f"{{{{ {operation_id} }}}}", iteration.get(operation_id)) - changes = self.do_changes(source_coll, used_regex, row, changes) + used_regex = self.match + for operation_id in range(0, self.__operation_input_depth): + used_regex = used_regex.replace(f"{{{{% {operation_id} %}}}}", iteration[operation_id]) + changes = self.do_changes(source_coll, used_regex, row, changes) return changes else: return self.do_changes(source_coll, self.match, row) \ No newline at end of file diff --git a/table_handler/conditions/__pycache__/regex_cell.cpython-312.pyc b/table_handler/conditions/__pycache__/regex_cell.cpython-312.pyc index f1c8aa8..72af07a 100644 Binary files a/table_handler/conditions/__pycache__/regex_cell.cpython-312.pyc and b/table_handler/conditions/__pycache__/regex_cell.cpython-312.pyc differ diff --git a/table_handler/conditions/regex_cell.py b/table_handler/conditions/regex_cell.py index 07d8db6..f256e15 100644 --- a/table_handler/conditions/regex_cell.py +++ b/table_handler/conditions/regex_cell.py @@ -6,7 +6,7 @@ class RegexCellCondition: def check_on_row(self, row: dict[str, any]): for apply_coll, condition in self.map.items(): - entry_value = str(row.get(apply_coll).value) + entry_value = row.get(apply_coll).value if not regex.match(condition, entry_value if entry_value else ""): return False return True \ No newline at end of file diff --git a/table_handler/serializer.py b/table_handler/serializer.py index 33048fe..0a3bf55 100644 --- a/table_handler/serializer.py +++ b/table_handler/serializer.py @@ -9,6 +9,7 @@ from .commands.find_and_replace import FindAndReplace from .commands.regex_edit_col import RegexEditCol from .commands.regex_move_to_col import RegexMoveToCol from .commands.copy import Copy +from .commands.regex_group_valuebased_choose import RegexGroupValuebasedChoose from .conditions.regex_cell import RegexCellCondition @@ -53,9 +54,9 @@ class TableSerializer: for expected_col, ws_idx in map_expected_with_ws.items(): cell = row[ws_idx] data[expected_col][rid] = Entry( - value=cell.value, - bg_color=cell.fill.start_color.rgb if cell.fill and cell.fill.start_color else "00000000", - fg_color=cell.font.color.rgb if cell.font and cell.font.color else "00000000" + value=str(cell.value) if not cell.value is None else None, + bg_color=cell.fill.start_color.rgb if cell.fill and cell.fill.start_color else (0,0,0), + fg_color=cell.font.color.rgb if cell.font and cell.font.color else (0,0,0) ) return data @@ -64,7 +65,7 @@ class TableSerializer: raise ValueError("There is nothing to write, serialize first") # write header - owb = load_workbook(destination_file) + owb = Workbook() ows = owb.active if self.output_header: @@ -94,17 +95,20 @@ class TableSerializer: def _extecute_instruct(self, source_cell: str, instruct: dict, row: dict[str, any], working_row: dict[str, any]): # execute changes - res = "" + res = None res_row = {} match instruct.get("command"): case "FIND_AND_REPLACE": res = FindAndReplace(instruct["map"]).execute_on_cell(row[source_cell]) case "REGEX_MOVE_TO_COL": - res_row = RegexMoveToCol(instruct["match"], instruct["to"], instruct.get("with_iteration_input")) \ + res_row = RegexMoveToCol(instruct["match"], instruct["to"], instruct.get("with_input_iteration")) \ .execute_on_row(row, source_cell) case "REGEX_EDIT_COL": res = RegexEditCol(instruct["match"], instruct["replace_with"])\ .execute_on_cell(row[source_cell]) + case "REGEX_GROUP_VALUEBASED_CHOOS": + res = RegexGroupValuebasedChoose(instruct["match"], instruct["highest"])\ + .execute_on_cell(row[source_cell]) case "CONDITIONAL_COPY": res_row = Copy(instruct["source"], instruct["target"], replace=instruct.get("replace"))\ .execute_on_row(row) @@ -114,7 +118,7 @@ class TableSerializer: working_row[target_cel] = self._merge_change_to_cel(working_row.get(target_cel), change) # Merge changes in working row - if source_cell: + if source_cell and res is not None: working_row[source_cell] = self._merge_change_to_cel(working_row.get(source_cell), res) return working_row @@ -128,7 +132,7 @@ class TableSerializer: def run(self, col_instructions: dict[tuple[str], dict], row_instructions: dict[tuple[str], dict]): self.serialized_output = {} - for rid in range(2, self.vertical_len + 1): + for rid in range(2, self.vertical_len + 2): working_row = {} source_row = self.get_row_as_dict(rid) for source_cols, instruct in list(col_instructions.items()) + [ @@ -143,5 +147,5 @@ class TableSerializer: for col in self.expected_output_schema: if col in working_row: continue - working_row[col] = source_row.get(col) + working_row[col] = source_row.get(col).value self.serialized_output[rid] = working_row