countings should work
This commit is contained in:
parent
94cb08e321
commit
3891c9c0bf
Binary file not shown.
Binary file not shown.
@ -13,7 +13,8 @@ EXPECTED_INPUT_TABLE_SCHEMA = [
|
|||||||
"Bemerkung"
|
"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 = {
|
COL_INSTRUCTIONS = {
|
||||||
("Art",): {
|
("Art",): {
|
||||||
@ -22,15 +23,20 @@ COL_INSTRUCTIONS = {
|
|||||||
},
|
},
|
||||||
("Ort",): {
|
("Ort",): {
|
||||||
"command": "REGEX_MOVE_TO_COL",
|
"command": "REGEX_MOVE_TO_COL",
|
||||||
"with_input_iteration": CATCHED_BY, # Moves matching group {{% 1 %}} to "to"
|
"with_input_iteration": CATCHED_BY, # Moves matching group {{% 0 %}} to "to"
|
||||||
"match": r"^(?:[^ ]* +)*({{% 1 %}}),?(?: .*+)*$",
|
"match": r"^(?:[^ ]* +)*({{% 0 %}}),?(?: .*+)*$",
|
||||||
"to": "Fangart"
|
"to": "Fangart"
|
||||||
},
|
},
|
||||||
("Fangart",): {
|
("Anzahl",): {
|
||||||
"command": "REGEX_EDIT_COL",
|
"command": "REGEX_EDIT_COL",
|
||||||
"match": r"^ *$",
|
"match": r"^ *$",
|
||||||
"replace_with": "1"
|
"replace_with": "1"
|
||||||
},
|
},
|
||||||
|
("Anzahl",): {
|
||||||
|
"command": "REGEX_GROUP_VALUEBASED_CHOOS",
|
||||||
|
"match": r"^ *([0-9]+) *oder *([0-9]+) *$",
|
||||||
|
"highest": True
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ROW_INSTRUCTION = [
|
ROW_INSTRUCTION = [
|
||||||
@ -38,7 +44,7 @@ ROW_INSTRUCTION = [
|
|||||||
"command": "CONDITIONAL_COPY",
|
"command": "CONDITIONAL_COPY",
|
||||||
"when_regex": {
|
"when_regex": {
|
||||||
"Geschlecht": r"^(?:[^ ]* +)*(m),?(?: .*+)*$",
|
"Geschlecht": r"^(?:[^ ]* +)*(m),?(?: .*+)*$",
|
||||||
"Anzahl": r"/d"
|
"Anzahl": r"([0-9]+)"
|
||||||
},
|
},
|
||||||
"source": "Anzahl",
|
"source": "Anzahl",
|
||||||
"target": "Davon m"
|
"target": "Davon m"
|
||||||
@ -47,7 +53,7 @@ ROW_INSTRUCTION = [
|
|||||||
"command": "CONDITIONAL_COPY",
|
"command": "CONDITIONAL_COPY",
|
||||||
"when_regex": {
|
"when_regex": {
|
||||||
"Geschlecht": r"^(?:[^ ]* +)*(w),?(?: .*+)*$",
|
"Geschlecht": r"^(?:[^ ]* +)*(w),?(?: .*+)*$",
|
||||||
"Anzahl": r"/d"
|
"Anzahl": r"([0-9]+)"
|
||||||
},
|
},
|
||||||
"source": "Anzahl",
|
"source": "Anzahl",
|
||||||
"target": "Davon w"
|
"target": "Davon w"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -8,6 +8,6 @@ class Copy:
|
|||||||
|
|
||||||
def execute_on_row(self, row=dict[str, str], source_col=None):
|
def execute_on_row(self, row=dict[str, str], source_col=None):
|
||||||
return {
|
return {
|
||||||
**({} if not self.replace else {source_col: ""}),
|
**({} if not self.replace else {self.source_col: ""}),
|
||||||
self.target: row[source_col]
|
self.target: row[self.source].value
|
||||||
}
|
}
|
||||||
@ -5,7 +5,9 @@ class FindAndReplace:
|
|||||||
self.map = map
|
self.map = map
|
||||||
|
|
||||||
def execute_on_cell(self, entry) -> str:
|
def execute_on_cell(self, entry) -> str:
|
||||||
|
res = deepcopy(entry.value)
|
||||||
for species, alternatives in self.map.items():
|
for species, alternatives in self.map.items():
|
||||||
for old_version in alternatives:
|
for old_version in alternatives:
|
||||||
if old_version in entry.value:
|
if old_version in entry.value:
|
||||||
return deepcopy(entry.value).replace(old_version, species)
|
res = res.replace(old_version, species)
|
||||||
|
return res
|
||||||
@ -5,7 +5,7 @@ class RegexEditCol:
|
|||||||
self.match = match
|
self.match = match
|
||||||
self.replace_with = replace_with
|
self.replace_with = replace_with
|
||||||
|
|
||||||
def execute_on_cell(self, value) -> str:
|
def execute_on_cell(self, entry) -> str:
|
||||||
if value is None:
|
if entry.value is None:
|
||||||
return value
|
return entry.value
|
||||||
return re.sub(self.match, self.replace_with, str(value))
|
return re.sub(self.match, self.replace_with, entry.value)
|
||||||
21
table_handler/commands/regex_group_valuebased_choose.py
Normal file
21
table_handler/commands/regex_group_valuebased_choose.py
Normal file
@ -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])
|
||||||
|
|
||||||
@ -1,12 +1,20 @@
|
|||||||
import regex
|
import regex
|
||||||
|
import re
|
||||||
|
|
||||||
class RegexMoveToCol:
|
class RegexMoveToCol:
|
||||||
def __init__(self, match: str, to: str, with_input_iteration: list[str]|None = None):
|
def __init__(self, match: str, to: str, with_input_iteration: list[str]|None = None):
|
||||||
self.match = match
|
self.match = match
|
||||||
self.to = to
|
self.to = to
|
||||||
|
|
||||||
if with_input_iteration is not None and type(with_input_iteration) != list:
|
if with_input_iteration is not None:
|
||||||
with_input_iteration = list(map([e] for e in with_input_iteration))
|
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.with_input_iteration = with_input_iteration
|
||||||
self.__operation_input_depth = self.__find_operation_input_depth()
|
self.__operation_input_depth = self.__find_operation_input_depth()
|
||||||
@ -14,48 +22,54 @@ class RegexMoveToCol:
|
|||||||
def __find_operation_input_depth(self):
|
def __find_operation_input_depth(self):
|
||||||
if not self.with_input_iteration:
|
if not self.with_input_iteration:
|
||||||
return None
|
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
|
@staticmethod
|
||||||
def _get_merged_groups(regex_pattern: str, entry: str) -> str:
|
def _get_merged_groups(regex_pattern: str, value: str) -> str:
|
||||||
match = regex.search(regex_pattern, entry.value)
|
match = regex.search(regex_pattern, value)
|
||||||
if not match:
|
if not match:
|
||||||
return ""
|
return None
|
||||||
|
|
||||||
return "".join(g for g in match.groups() if g is not None)
|
return "".join(g for g in match.groups() if g is not None)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _remove_regex_groups(regex_pattern: str, entry: str) -> str:
|
def _remove_regex_groups(pattern, value):
|
||||||
match = regex.search(regex_pattern, entry.value)
|
regex = re.compile(pattern)
|
||||||
if not match:
|
|
||||||
return entry.value
|
|
||||||
|
|
||||||
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 = []
|
for start, end in spans:
|
||||||
last = 0
|
rel_start = start - m.start()
|
||||||
|
rel_end = end - m.start()
|
||||||
|
s = s[:rel_start] + s[rel_end:]
|
||||||
|
|
||||||
for start, end in sorted(spans):
|
return s
|
||||||
result.append(entry.value[last:start])
|
|
||||||
last = end
|
|
||||||
|
|
||||||
result.append(entry.value[last:])
|
return regex.sub(repl, value)
|
||||||
return "".join(result)
|
|
||||||
|
|
||||||
def do_changes(self, source_coll: str, used_regex: str, row: dict[str, str], changes={}):
|
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])
|
current_to = changes.get(self.to, row[source_coll].value)
|
||||||
changes[source_coll] = self._remove_regex_groups(used_regex, row[source_coll])
|
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
|
return changes
|
||||||
|
|
||||||
def execute_on_row(self, row: dict[str, str], source_coll: str = None):
|
def execute_on_row(self, row: dict[str, str], source_coll: str = None):
|
||||||
if not source_coll:
|
if not source_coll:
|
||||||
raise ValueError("source_col required")
|
raise ValueError("source_col required")
|
||||||
if self.with_input_iteration:
|
if self.with_input_iteration:
|
||||||
changes = None
|
changes = {}
|
||||||
used_regex = self.match
|
|
||||||
for iteration in self.with_input_iteration:
|
for iteration in self.with_input_iteration:
|
||||||
for operation_id in range(0, self.__operation_input_depth-1):
|
used_regex = self.match
|
||||||
used_regex.replace(f"{{{{ {operation_id} }}}}", iteration.get(operation_id))
|
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)
|
changes = self.do_changes(source_coll, used_regex, row, changes)
|
||||||
return changes
|
return changes
|
||||||
else:
|
else:
|
||||||
|
|||||||
Binary file not shown.
@ -6,7 +6,7 @@ class RegexCellCondition:
|
|||||||
|
|
||||||
def check_on_row(self, row: dict[str, any]):
|
def check_on_row(self, row: dict[str, any]):
|
||||||
for apply_coll, condition in self.map.items():
|
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 ""):
|
if not regex.match(condition, entry_value if entry_value else ""):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@ -9,6 +9,7 @@ from .commands.find_and_replace import FindAndReplace
|
|||||||
from .commands.regex_edit_col import RegexEditCol
|
from .commands.regex_edit_col import RegexEditCol
|
||||||
from .commands.regex_move_to_col import RegexMoveToCol
|
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 .conditions.regex_cell import RegexCellCondition
|
from .conditions.regex_cell import RegexCellCondition
|
||||||
|
|
||||||
@ -53,9 +54,9 @@ class TableSerializer:
|
|||||||
for expected_col, ws_idx in map_expected_with_ws.items():
|
for expected_col, ws_idx in map_expected_with_ws.items():
|
||||||
cell = row[ws_idx]
|
cell = row[ws_idx]
|
||||||
data[expected_col][rid] = Entry(
|
data[expected_col][rid] = Entry(
|
||||||
value=cell.value,
|
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 "00000000",
|
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 "00000000"
|
fg_color=cell.font.color.rgb if cell.font and cell.font.color else (0,0,0)
|
||||||
)
|
)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ class TableSerializer:
|
|||||||
raise ValueError("There is nothing to write, serialize first")
|
raise ValueError("There is nothing to write, serialize first")
|
||||||
|
|
||||||
# write header
|
# write header
|
||||||
owb = load_workbook(destination_file)
|
owb = Workbook()
|
||||||
ows = owb.active
|
ows = owb.active
|
||||||
|
|
||||||
if self.output_header:
|
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]):
|
def _extecute_instruct(self, source_cell: str, instruct: dict, row: dict[str, any], working_row: dict[str, any]):
|
||||||
# execute changes
|
# execute changes
|
||||||
res = ""
|
res = None
|
||||||
res_row = {}
|
res_row = {}
|
||||||
match instruct.get("command"):
|
match instruct.get("command"):
|
||||||
case "FIND_AND_REPLACE":
|
case "FIND_AND_REPLACE":
|
||||||
res = FindAndReplace(instruct["map"]).execute_on_cell(row[source_cell])
|
res = FindAndReplace(instruct["map"]).execute_on_cell(row[source_cell])
|
||||||
case "REGEX_MOVE_TO_COL":
|
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)
|
.execute_on_row(row, source_cell)
|
||||||
case "REGEX_EDIT_COL":
|
case "REGEX_EDIT_COL":
|
||||||
res = RegexEditCol(instruct["match"], instruct["replace_with"])\
|
res = RegexEditCol(instruct["match"], instruct["replace_with"])\
|
||||||
.execute_on_cell(row[source_cell])
|
.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":
|
case "CONDITIONAL_COPY":
|
||||||
res_row = Copy(instruct["source"], instruct["target"], replace=instruct.get("replace"))\
|
res_row = Copy(instruct["source"], instruct["target"], replace=instruct.get("replace"))\
|
||||||
.execute_on_row(row)
|
.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)
|
working_row[target_cel] = self._merge_change_to_cel(working_row.get(target_cel), change)
|
||||||
|
|
||||||
# Merge changes in working row
|
# 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)
|
working_row[source_cell] = self._merge_change_to_cel(working_row.get(source_cell), res)
|
||||||
return working_row
|
return working_row
|
||||||
|
|
||||||
@ -128,7 +132,7 @@ class TableSerializer:
|
|||||||
|
|
||||||
def run(self, col_instructions: dict[tuple[str], dict], row_instructions: dict[tuple[str], dict]):
|
def run(self, col_instructions: dict[tuple[str], dict], row_instructions: dict[tuple[str], dict]):
|
||||||
self.serialized_output = {}
|
self.serialized_output = {}
|
||||||
for rid in range(2, self.vertical_len + 1):
|
for rid in range(2, self.vertical_len + 2):
|
||||||
working_row = {}
|
working_row = {}
|
||||||
source_row = self.get_row_as_dict(rid)
|
source_row = self.get_row_as_dict(rid)
|
||||||
for source_cols, instruct in list(col_instructions.items()) + [
|
for source_cols, instruct in list(col_instructions.items()) + [
|
||||||
@ -143,5 +147,5 @@ class TableSerializer:
|
|||||||
for col in self.expected_output_schema:
|
for col in self.expected_output_schema:
|
||||||
if col in working_row:
|
if col in working_row:
|
||||||
continue
|
continue
|
||||||
working_row[col] = source_row.get(col)
|
working_row[col] = source_row.get(col).value
|
||||||
self.serialized_output[rid] = working_row
|
self.serialized_output[rid] = working_row
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user