From 94cb08e321d09c8d79ed0642032ed4f8ae7e0149 Mon Sep 17 00:00:00 2001 From: Kyattsukuro Date: Fri, 13 Mar 2026 16:47:31 +0100 Subject: [PATCH] runable --- .gitignore | 1 + .vscode/launch.json | 16 ++ .vscode/settings.json | 3 + __init__.py | 17 ++ data/zählungen_short.xlsx | Bin 0 -> 26809 bytes instructions/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 156 bytes .../countings_formater.cpython-312.pyc | Bin 0 -> 1082 bytes .../__pycache__/mappings.cpython-312.pyc | Bin 0 -> 3574 bytes instructions/countings_formater.py | 35 ++--- instructions/ring_formater.py | 12 +- requirements.txt | 7 + shell.nix | 16 ++ table_handler/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 157 bytes .../__pycache__/serializer.cpython-312.pyc | Bin 0 -> 9002 bytes .../commands/__pycache__/copy.cpython-312.pyc | Bin 0 -> 897 bytes .../find_and_replace.cpython-312.pyc | Bin 0 -> 1110 bytes .../regex_edit_col.cpython-312.pyc | Bin 0 -> 908 bytes .../regex_move_to_col.cpython-312.pyc | Bin 0 -> 4617 bytes table_handler/commands/copy.py | 13 ++ table_handler/commands/find_and_replace.py | 11 ++ table_handler/commands/regex_edit_col.py | 11 ++ table_handler/commands/regex_move_to_col.py | 62 ++++++++ .../__pycache__/regex_cell.cpython-312.pyc | Bin 0 -> 1110 bytes table_handler/conditions/regex_cell.py | 12 ++ table_handler/serializer.py | 147 ++++++++++++++++++ 27 files changed, 337 insertions(+), 26 deletions(-) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 __init__.py create mode 100644 data/zählungen_short.xlsx create mode 100644 instructions/__init__.py create mode 100644 instructions/__pycache__/__init__.cpython-312.pyc create mode 100644 instructions/__pycache__/countings_formater.cpython-312.pyc create mode 100644 instructions/__pycache__/mappings.cpython-312.pyc create mode 100644 requirements.txt create mode 100644 shell.nix create mode 100644 table_handler/__init__.py create mode 100644 table_handler/__pycache__/__init__.cpython-312.pyc create mode 100644 table_handler/__pycache__/serializer.cpython-312.pyc create mode 100644 table_handler/commands/__pycache__/copy.cpython-312.pyc create mode 100644 table_handler/commands/__pycache__/find_and_replace.cpython-312.pyc create mode 100644 table_handler/commands/__pycache__/regex_edit_col.cpython-312.pyc create mode 100644 table_handler/commands/__pycache__/regex_move_to_col.cpython-312.pyc create mode 100644 table_handler/commands/copy.py create mode 100644 table_handler/commands/find_and_replace.py create mode 100644 table_handler/commands/regex_edit_col.py create mode 100644 table_handler/commands/regex_move_to_col.py create mode 100644 table_handler/conditions/__pycache__/regex_cell.cpython-312.pyc create mode 100644 table_handler/conditions/regex_cell.py create mode 100644 table_handler/serializer.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b694934 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.venv \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..1f0c528 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/__init__.py", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..786d4d3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix" +} \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..93a44a0 --- /dev/null +++ b/__init__.py @@ -0,0 +1,17 @@ + +from table_handler.serializer import TableSerializer +from pprint import pprint as print + + +if __name__ == "__main__": + # Example usage + import instructions.countings_formater as countings_formater + + file = "data/zählungen_short.xlsx" + serializer = TableSerializer(file, countings_formater.EXPECTED_INPUT_TABLE_SCHEMA, countings_formater.EXPECTED_OUTPUT_TABLE_SCHEMA) + serializer.run( + countings_formater.COL_INSTRUCTIONS, + countings_formater.ROW_INSTRUCTION + ) + print(serializer.serialized_output) + serializer.write_output("zählungen_test_out.xlsx") \ No newline at end of file diff --git a/data/zählungen_short.xlsx b/data/zählungen_short.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c1a69393ba4d9312c3c899f0423c83d161709aab GIT binary patch literal 26809 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 S#eVdE&ap^@e-XrUw9{YwL@DP0 literal 0 HcmV?d00001 diff --git a/instructions/__pycache__/mappings.cpython-312.pyc b/instructions/__pycache__/mappings.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..01a9437682b37eac5eb8d6afe3e987183636b2d1 GIT binary patch literal 3574 zcmZ8jTXQ2v6`s59vK+6Sz2u^V5D<`Y5(u{}0ZEqEjxDW_FJKo`7-?p;vz{4E=HisP zyj6bSVIiRi6^eaH6|j{QMe)i%$RmHi)jU!B0j-uNp7^?3mc6*j=Y0LmIj6hNIo+*) zluCICfBz}|-6hMC^lvse|67=WCqI%U>7nFGwltJ%`H}pIY%4%2M;egMkpYz9C<~P1 zC=XQNs0dWzs0_5gQ3Ys`qi2AYIJyF~%+U(avm9Loy2jCUpc@=L2lPBgF95yBQPqCQ zetD>1eP6O)87lUdhcdps`iRM|aQRg(Yg~Sf%dd0!4K82f(&TcL%hw;t_8Xs;K9>*K z-rsyAAIkPO?Qad){@=p4Z`k1o_Xv~zVu%s#s;p2~Egvzu{fW%}m8K8Q* zeXz4jb)SvM;w&5+SOYh}x`ZL094rFC9+Pw24c)M&6QgSaz=Uh6b-SBgs)gXlwXPew zaS~y}X9cII%lG0x*o@QGhyWu){te zsnY6D#R-sF+lpB_+a>_dI5gG1N&>O4^B5>p?OoEX&3hyEZAi`1X+Dn(4co(=rsrU za*Z&=kz%cn8leoRQH&bxphm11ww0dODCOw}0eY>z$7__5XXZ6Z`%aLsYE!M_Ic}{) zjE)HaH?Bc%w>GI3fFp~Y@gT9-DZwcAc$r{@`5~#&*``Y1kqiqIgB6itYOoMH+f-*k zu)Z#;%(mJ!0WcBQpm{rltR0>%BN!BgH6$!C86;J@=!q~O#u1LfdYddBA$H(Y*XP9` zIzFwpY5szu8qc+{<>7v_YAVg8LEkyDf*>Ipph@$4AmYUJ{aM3W9!@DPHE3k8zTHFy zf#;VRFjhBmSij=>Qz6l>b@Z!M%d%tQ_~KTCMt~MRX+H401i;bI#THpHVa{VXh|v_V z|Io;Pon31o@TEHhBNt1n71*^b?ksGIr%`XS+luPGNUCTlwq=ZY&Y-bn7-7r6q&(|7 zX3y>=xs5JB*uFEs0fQq0RBtz0%?*TQqj{Rx0B@4Z?j>Q#rvQy9;D9NBSy!<~W5ho~ z<74~aHa-HA(rkXA2(rztX-H%n*R=18raVfq8YBf8N!i-lKvICo(o=T}7*Dk15rV)P zExCo3JQ6{08<_Bh;)&hiiH#9scAr=AC5c=MH!iMo6o5Z?0#F^LU=JQ@5hr*ua+rCR z{Omczld{?61;~VM$Wls+mlue)K{&DJLLh1g7itI(a^IQho4LbV5?hQ=Grj=)7~u8biSHsq(TF{mY<~6~7pB^|Gof zMZaOF=3%2rPVk=X4wC3k2-?GT;1iWK2{xnp~Ik!c@v*e1nf)pO4?*<8_%61`KY=oY@r& zbJ-INYN(eHv|>z-1k+IFVVZo_$xj8t;v5L(&V>zrYO}SRT-e+f%)9D5&X!p3F9ef8 z97{0ah0XhdS=TPstcf)Xrp(qXm`E^e7qMVAbV)CrWpA8i7bd#8U`#f3?%UI)-11qr z46-t&l^nzs1fRZu{Y}fovN65RKm4CShNkalS=7_abUT+R zE2sI%ij*s!WlIR3H>O#mXiO@cDRSoVwMs@gRj03Nd1F$<2TTq-1H;El<%)7zd{Wir I3U7h`0mX@D?f?J) literal 0 HcmV?d00001 diff --git a/instructions/countings_formater.py b/instructions/countings_formater.py index ce5e8d3..678790a 100644 --- a/instructions/countings_formater.py +++ b/instructions/countings_formater.py @@ -1,4 +1,5 @@ -from mappings import * +from .mappings import * +from copy import deepcopy EXPECTED_INPUT_TABLE_SCHEMA = [ "Anzahl", @@ -12,29 +13,23 @@ EXPECTED_INPUT_TABLE_SCHEMA = [ "Bemerkung" ] -EXPECTED_OUTPUT_TABLE_SCHEMA = EXPECTED_INPUT_TABLE_SCHEMA.remove("Geschlecht") +EXPECTED_OUTPUT_TABLE_SCHEMA = deepcopy(EXPECTED_INPUT_TABLE_SCHEMA).remove("Geschlecht") COL_INSTRUCTIONS = { - ["Fangart"]: { - "command": { - "name": "FIND_AND_REPLACE", - "map": CATCHED_BY - } + ("Art",): { + "command": "FIND_AND_REPLACE", + "map": SPICIES }, - ["Ort"]: { - "command": { - "name": "REGEX_MOVE_TO_COL", - "with_input_iteration": flatten_dict(CATCHED_BY), # Moves matching group {{% 1 %}} to "to" - "match": r"^(?:[^ ]* +)*({{% 1 %}}),?(?: .*+)*$", - "to": "Fangart" - } + ("Ort",): { + "command": "REGEX_MOVE_TO_COL", + "with_input_iteration": CATCHED_BY, # Moves matching group {{% 1 %}} to "to" + "match": r"^(?:[^ ]* +)*({{% 1 %}}),?(?: .*+)*$", + "to": "Fangart" }, - ["Fangart"]: { - "command": { - "name": "REGEX_EDIT_COL", - "match": r"^ *$", - "to": "1" - } + ("Fangart",): { + "command": "REGEX_EDIT_COL", + "match": r"^ *$", + "replace_with": "1" }, } diff --git a/instructions/ring_formater.py b/instructions/ring_formater.py index bde2d55..560bb3f 100644 --- a/instructions/ring_formater.py +++ b/instructions/ring_formater.py @@ -21,16 +21,16 @@ EXPECTED_OUTPUT_TABLE_SCHEMA = EXPECTED_INPUT_TABLE_SCHEMA COL_INSTRUCTIONS = { ["Geschlecht"]: { + "name": "REGEX_MOVE_TO_COL", "command": { - "name": "REGEX_MOVE_TO_COL", "with_input_iteration": flatten_dict(REPRODUCTION), # Moves matching group {{% 1 %}} to "to" "match": r"^(?:[^ ]* +)*({{% 1 %}}),?(?: .*+)*$", "to": "Reproduktionsstatus" } }, ["Geschlecht"]: { + "name": "REGEX_MOVE_TO_COL", "command": { - "name": "REGEX_MOVE_TO_COL", "with_input_iteration": flatten_dict(AGE), # Moves matching group {{% 1 %}} to "to" "match": r"^(?:[^ ]* +)*({{% 1 %}}),?(?: .*+)*$", "to": "Alter" @@ -38,28 +38,28 @@ COL_INSTRUCTIONS = { }, ["Geschlecht"]: { # Special case ma -> Geschlecht a | Alter a + "name": "REGEX_MOVE_TO_COL", "command": { - "name": "REGEX_MOVE_TO_COL", "match": r"^(?:[^ ]* +)*m(a),?(?: .*+)*$", "to": "Alter" } }, ["Ringnummer"]: { + "name": "REGEX_EDIT_COL", "command": { - "name": "REGEX_EDIT_COL", "match": r"^[^a-zA-Z\d]*([a-zA-Z]*)[^a-zA-Z\d]*(\d+)$", "to": "{{% 1 %}}{{% 2 %}}" } }, ["Art"]: { + "name": "FIND_AND_REPLACE", "command": { - "name": "FIND_AND_REPLACE", "map": SPICIES } }, ["Ort"]: { + "name": "REGEX_MOVE_TO_COL", "command": { - "name": "REGEX_MOVE_TO_COL", "with_input_iteration": flatten_dict(CATCHED_BY), # Moves matching group {{% 1 %}} to "to" "match": r"^(?:[^ ]* +)*({{% 1 %}}),?(?: .*+)*$", "to": "Fangart" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4294cd5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +et_xmlfile==2.0.0 +numpy==2.4.2 +openpyxl==3.1.5 +pandas==3.0.1 +python-dateutil==2.9.0.post0 +regex==2026.2.28 +six==1.17.0 diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..22acaa4 --- /dev/null +++ b/shell.nix @@ -0,0 +1,16 @@ +let + pkgs = import { + overlays = [ ]; + }; +in +pkgs.mkShell { + buildInputs = with pkgs; [ + python3 + ]; + shellHook = '' + if [ ! -d .venv ]; then + python -m venv .venv + fi + source .venv/bin/activate + ''; +} \ No newline at end of file diff --git a/table_handler/__init__.py b/table_handler/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/table_handler/__pycache__/__init__.cpython-312.pyc b/table_handler/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8121bd93168a7b5f20f681648e31239c5b77bec GIT binary patch literal 157 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 literal 0 HcmV?d00001 diff --git a/table_handler/commands/__pycache__/find_and_replace.cpython-312.pyc b/table_handler/commands/__pycache__/find_and_replace.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..117c77defe2d050a4582190732bc0ae32a50640f GIT binary patch literal 1110 zcmZuwO=uHA6n?WmX>6)OD=j4wBU)jpHiAD?5JCJqh$-l$gzB*FOzf8ZiL;wvN(vr& zC{n>&Jtn6py?XHI!JDUAQ5p5*$y+so;K?_;>7l^~^X+@{-h8~jeYb!AFregSUb#;Q zz;|WriqThk$5Gh;28?*%lL{fA!3|*ADlmOhOQR}U)3D4R%CLycGR|vGSZg||H$}5= z^v)cRh67Q>8ib!u%Ooq2rjTxwCnx>VJ2d=vTyBx6Fyyn>sm%4A) zoPG`oz0!3&y-|b>Xpt7s#G#-GFG<-VB7?f6Nh9K3H8CRftXTYetF##Uyc8jX0WVz% z886j@+bC5%&baXHdQ^(-`yQu@cECI?N>1qes79qKCPq=CB28_k)|6RF-N210U7pyR z_sp(CH96Xc@EAUijjtI`vpqCGW(1GxBG^-!$~r0SEh<bDX@6t^^a5R0Z9ifY_(c_c?{FXke!W4D1; zF7y~}a1ps-5UE`&DiLbITu$`7(vRiHKZ0JuzgOc9H!I{Ssgcu$`MK*)@`rvF_`PbQjcOGa`;S) zBfNx!A}LC!8WT{>iY$ax*wKhN@%(Cgtj%7W>A3B}*5T7Vz`LD{Va|~k`fpI*ltmGg xEnOPwS4~=;avoO1D%G@k*^6n0mqY>Y)U=~R2s=6<F|uTi`~ro^tAAJ)`e6V7 literal 0 HcmV?d00001 diff --git a/table_handler/commands/__pycache__/regex_edit_col.cpython-312.pyc b/table_handler/commands/__pycache__/regex_edit_col.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9230af2d2bdca8e1237824b65b2fc69d215c35e0 GIT binary patch literal 908 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 str: + 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 diff --git a/table_handler/commands/regex_edit_col.py b/table_handler/commands/regex_edit_col.py new file mode 100644 index 0000000..42dfcfe --- /dev/null +++ b/table_handler/commands/regex_edit_col.py @@ -0,0 +1,11 @@ +import re + +class RegexEditCol: + def __init__(self, match, replace_with): + 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 diff --git a/table_handler/commands/regex_move_to_col.py b/table_handler/commands/regex_move_to_col.py new file mode 100644 index 0000000..10a8301 --- /dev/null +++ b/table_handler/commands/regex_move_to_col.py @@ -0,0 +1,62 @@ +import regex + +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)) + + self.with_input_iteration = with_input_iteration + self.__operation_input_depth = self.__find_operation_input_depth() + + 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()) + + @staticmethod + def _get_merged_groups(regex_pattern: str, entry: str) -> str: + match = regex.search(regex_pattern, entry.value) + if not match: + return "" + + 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 + + spans = [s for s in match.spans()[1:] if s != (-1, -1)] + + result = [] + last = 0 + + for start, end in sorted(spans): + result.append(entry.value[last:start]) + last = end + + result.append(entry.value[last:]) + return "".join(result) + + 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]) + 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 + 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) + 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 new file mode 100644 index 0000000000000000000000000000000000000000..f1c8aa8fadaa3ff6d0b077eee17f857397e9bc17 GIT binary patch literal 1110 zcmZuw%}*0S6o0cHw1uLE5G#nHXj2A$o#+s-HY?R5mJNTOXd-L{PZ*Mnnxj+6U zzls393Z)}Tn=})gRKWxjS&#+^u7N2ngDG!GEsr$vk*0hTO3G-NTDTu!lIv9uC`8FfC%8Q$@n}j~HRZtCwQp6pHRP*W7wfx2vgFw`v8-vwHuGNIv6)=dOGZ0^ z;5NZ!{x$nJRH1?@fP)PIK4>VVA*yn&NlbBxl?fDA97`mAT~FqmB1yU&9GfJk9g`$W zG{2C{Si~f>sF&TOr$4d?&gr&k5t=kQsklih(!&PNZ>;3AFvfX1?_s<+{BPc4olVgj zH^SjLe2N`kQC7oEt^kv%^YpK}y0|r{Ynu~Z7ZilwAt?xtL$cM@XW-#1zyn_BlC&gy zq6L9eP$AUe2Wv&z;)5Wh5N@r1VrOqj@p?M;LbSsU){0zFs8msGWTMWPQ1oIJfiEfG z%bJp6aP^)R3Lb%}d5;ubCc7SGa+Y{ZUC^yEVQSD>rWSS2$Z0AS`BHH)>()yp%g2Ue zSuC>u6s(8X9`$i>+7*>1giuiliyAp%JjRZVd5afM{pW4|bP!j$w>j|l8PzuC*5@|n z*XKWsUfhYEUb#_=>AO9Gi*ojZ<4o$Ay+=~rXr(RCg{I|oK7q-v5 zkKOt_aIz7CbC()nIMz?a50}!yVoYqkNHAvI7#AJ0%x4R;D8^69y4BuM%)H^zqx=LF zqrzm}_UQm;n-Iha3Zr6_0uV#sp69R(4GF0OwV8UXZqx_YQcd7$qf1r$>q`CZ>$CN_ z^&>xktF161f+kV%=d_}Y2qrRZwJv dict[int, list[Entry]]: + data = {col: {} for col in expected_cols} + map_expected_with_ws = {} + for idx, cell in enumerate(self.ws[1]): + cell_value = cell.value if cell.value else "" + if cell_value in expected_cols: + map_expected_with_ws[cell_value] = idx + else: + print(f"Skipping unexpected Col {cell_value}") + + if set(map_expected_with_ws.keys()) != set(expected_cols): + raise ValueError("Missing Col in input") + + for rid, row in enumerate(self.ws.iter_rows(min_row=2, values_only=False), start=2): + 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" + ) + return data + + def write_output(self, destination_file: str): + if not self.serialized_output: + raise ValueError("There is nothing to write, serialize first") + + # write header + owb = load_workbook(destination_file) + ows = owb.active + + if self.output_header: + for col_i, col_name in enumerate(self.expected_output_schema, start=1): + ows.cell(row=1, column=col_i, value=col_name) + + # write rows + for row_i, (row_id, row_data) in enumerate(self.serialized_output.items(), start=2 if self.output_header else 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)) + + owb.save(destination_file) + + + def _check_conditions(self, instruct: dict[str, str], row: dict[str, any]): + when_regex_condition_map = instruct.get("when_regex") + if when_regex_condition_map: + return RegexCellCondition(when_regex_condition_map).check_on_row(row) + return True + + def _merge_change_to_cel(self, value, changes): + if not value and changes: + value = changes + elif changes: + value = f"{value}{self.seperator}{changes}" + return value + + def _extecute_instruct(self, source_cell: str, instruct: dict, row: dict[str, any], working_row: dict[str, any]): + # execute changes + res = "" + 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")) \ + .execute_on_row(row, source_cell) + case "REGEX_EDIT_COL": + res = RegexEditCol(instruct["match"], instruct["replace_with"])\ + .execute_on_cell(row[source_cell]) + case "CONDITIONAL_COPY": + res_row = Copy(instruct["source"], instruct["target"], replace=instruct.get("replace"))\ + .execute_on_row(row) + + # Merge row_changes to working_row + for target_cel, change in res_row.items(): + working_row[target_cel] = self._merge_change_to_cel(working_row.get(target_cel), change) + + # Merge changes in working row + if source_cell: + working_row[source_cell] = self._merge_change_to_cel(working_row.get(source_cell), res) + return working_row + + + @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.serialized_output = {} + for rid in range(2, self.vertical_len + 1): + working_row = {} + source_row = self.get_row_as_dict(rid) + for source_cols, instruct in list(col_instructions.items()) + [ + ((None,), instr) for instr in row_instructions + ]: + for col in source_cols: + if not self._check_conditions(instruct, source_row): + continue + working_row = self._extecute_instruct(col, instruct, source_row, working_row) + if not self.implicit_copy: + continue + for col in self.expected_output_schema: + if col in working_row: + continue + working_row[col] = source_row.get(col) + self.serialized_output[rid] = working_row