feat(rules): Add extended mode
This commit is contained in:
@@ -1,21 +1,39 @@
|
|||||||
|
import re
|
||||||
from typing import Self
|
from typing import Self
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
class Rule:
|
class Rule:
|
||||||
__slots__ = ("conditions", "values")
|
__slots__ = ("conditions", "values", "extended")
|
||||||
|
|
||||||
def __init__(self, conditions: dict[str, str], values: dict[str, str]):
|
def __init__(
|
||||||
|
self, conditions: dict[str, str], values: dict[str, str], extended=False
|
||||||
|
):
|
||||||
self.conditions = conditions
|
self.conditions = conditions
|
||||||
self.values = values
|
self.values = values
|
||||||
|
self.extended = extended
|
||||||
|
|
||||||
def check(self, record: dict[str, str]) -> bool:
|
def check(self, record: dict[str, str]) -> bool:
|
||||||
"""
|
"""
|
||||||
Check whether this rule applies to the given record fields.
|
Check whether this rule applies to the given record fields.
|
||||||
"""
|
"""
|
||||||
return all(
|
if self.extended:
|
||||||
record.get(key, None) == value for key, value in self.conditions.items()
|
match = True
|
||||||
)
|
for key, pattern in self.conditions.items():
|
||||||
|
actualvalue = record.get(key, None)
|
||||||
|
if isinstance(pattern, str) and isinstance(actualvalue, str):
|
||||||
|
match = match and bool(re.match(pattern, actualvalue))
|
||||||
|
else:
|
||||||
|
# Fallback to equality
|
||||||
|
match = match and (pattern == actualvalue)
|
||||||
|
if not match:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
match = all(
|
||||||
|
record.get(key, None) == value for key, value in self.conditions.items()
|
||||||
|
)
|
||||||
|
|
||||||
|
return match
|
||||||
|
|
||||||
|
|
||||||
class RuleInterface:
|
class RuleInterface:
|
||||||
@@ -71,6 +89,7 @@ class JSONRuleInterface(RuleInterface):
|
|||||||
rule = Rule(
|
rule = Rule(
|
||||||
conditions=rule_data["record_fields"],
|
conditions=rule_data["record_fields"],
|
||||||
values=rule_data["transaction_fields"],
|
values=rule_data["transaction_fields"],
|
||||||
|
extended=rule_data.get("extended", False),
|
||||||
)
|
)
|
||||||
rules.append(rule)
|
rules.append(rule)
|
||||||
|
|
||||||
@@ -85,6 +104,7 @@ class JSONRuleInterface(RuleInterface):
|
|||||||
{
|
{
|
||||||
"record_fields": rule.conditions,
|
"record_fields": rule.conditions,
|
||||||
"transaction_fields": rule.values,
|
"transaction_fields": rule.values,
|
||||||
|
"extended": rule.extended,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -167,9 +167,9 @@ class MainWindow(ThemedTk):
|
|||||||
|
|
||||||
def rule_created(self, event):
|
def rule_created(self, event):
|
||||||
logger.debug(f"Received RowEditor rulecreated event {event!r}")
|
logger.debug(f"Received RowEditor rulecreated event {event!r}")
|
||||||
conditions, values = self.editor.make_rule()
|
conditions, values, extended = self.editor.make_rule()
|
||||||
|
|
||||||
rule = Rule(conditions, values)
|
rule = Rule(conditions, values, extended)
|
||||||
|
|
||||||
affected = sum(rule.check(record.match_fields()) for record in self.rows)
|
affected = sum(rule.check(record.match_fields()) for record in self.rows)
|
||||||
self.ruleset.add_rule(rule)
|
self.ruleset.add_rule(rule)
|
||||||
|
|||||||
@@ -59,13 +59,29 @@ class RowEditor(ttk.Frame):
|
|||||||
)
|
)
|
||||||
self.txn_frame.columnconfigure(2, weight=1)
|
self.txn_frame.columnconfigure(2, weight=1)
|
||||||
|
|
||||||
|
self.matchopt_frame = ttk.Frame(self)
|
||||||
|
self.matchopt_frame.grid(row=0, column=1, sticky="ew")
|
||||||
|
self.matchopt_frame.columnconfigure(0, weight=0)
|
||||||
|
self.matchopt_frame.columnconfigure(1, weight=1)
|
||||||
|
self.matchopt_frame.columnconfigure(2, weight=0)
|
||||||
|
self.matchopt_frame.columnconfigure(3, weight=0)
|
||||||
|
|
||||||
self.match_var = BooleanVar()
|
self.match_var = BooleanVar()
|
||||||
self.match_toggle = ttk.Checkbutton(
|
self.match_toggle = ttk.Checkbutton(
|
||||||
self, variable=self.match_var, command=self._display_matchboxes
|
self, variable=self.match_var, command=self._display_matchboxes
|
||||||
)
|
)
|
||||||
self.match_label = ttk.Label(self, text="Rule creation mode")
|
|
||||||
self.match_toggle.grid(row=0, column=0, padx=11, pady=10, sticky="nw")
|
self.match_toggle.grid(row=0, column=0, padx=11, pady=10, sticky="nw")
|
||||||
self.match_label.grid(row=0, column=1, padx=7, pady=10, sticky="nw")
|
|
||||||
|
self.match_label = ttk.Label(self.matchopt_frame, text="Rule creation mode")
|
||||||
|
self.match_label.grid(row=0, column=0, padx=7, pady=10, sticky="nw")
|
||||||
|
|
||||||
|
self.extended_var = BooleanVar()
|
||||||
|
self.extended_toggle = ttk.Checkbutton(
|
||||||
|
self.matchopt_frame, variable=self.extended_var
|
||||||
|
)
|
||||||
|
self.extended_label = ttk.Label(self.matchopt_frame, text="Extended")
|
||||||
|
self.extended_toggle.grid(row=0, column=2, padx=11, pady=10, sticky="nw")
|
||||||
|
self.extended_label.grid(row=0, column=3, padx=7, pady=10, sticky="nw")
|
||||||
|
|
||||||
self.button_frame = ttk.Frame(self, padding=(2, 2, 3, 3))
|
self.button_frame = ttk.Frame(self, padding=(2, 2, 3, 3))
|
||||||
self.button_frame.grid(row=3, column=0, columnspan=2, sticky="s")
|
self.button_frame.grid(row=3, column=0, columnspan=2, sticky="s")
|
||||||
@@ -236,6 +252,8 @@ class RowEditor(ttk.Frame):
|
|||||||
label.grid_remove()
|
label.grid_remove()
|
||||||
entry.grid(row=i, column=2, sticky="ew", padx=5)
|
entry.grid(row=i, column=2, sticky="ew", padx=5)
|
||||||
self.rule_button.configure(state="enabled")
|
self.rule_button.configure(state="enabled")
|
||||||
|
self.extended_toggle.grid(row=0, column=2, padx=11, pady=10, sticky="nw")
|
||||||
|
self.extended_label.grid(row=0, column=3, padx=7, pady=10, sticky="nw")
|
||||||
else:
|
else:
|
||||||
# Disable match boxes
|
# Disable match boxes
|
||||||
for i, (box, _, _) in enumerate(self.txn_rows.values()):
|
for i, (box, _, _) in enumerate(self.txn_rows.values()):
|
||||||
@@ -249,6 +267,8 @@ class RowEditor(ttk.Frame):
|
|||||||
entry.grid_remove()
|
entry.grid_remove()
|
||||||
# Also disable rule button
|
# Also disable rule button
|
||||||
self.rule_button.configure(state="disabled")
|
self.rule_button.configure(state="disabled")
|
||||||
|
self.extended_toggle.grid_remove()
|
||||||
|
self.extended_label.grid_remove()
|
||||||
|
|
||||||
def do_save_txn(self):
|
def do_save_txn(self):
|
||||||
if not self.rows:
|
if not self.rows:
|
||||||
@@ -299,7 +319,7 @@ class RowEditor(ttk.Frame):
|
|||||||
}
|
}
|
||||||
update_fields = txn.parse_input(input_fields)
|
update_fields = txn.parse_input(input_fields)
|
||||||
|
|
||||||
return (rule_record_fields, update_fields)
|
return (rule_record_fields, update_fields, self.extended_var.get())
|
||||||
|
|
||||||
def _display_txns(self, txns: list[PartialTXN]):
|
def _display_txns(self, txns: list[PartialTXN]):
|
||||||
if txns and not self.showing_txn:
|
if txns and not self.showing_txn:
|
||||||
|
|||||||
Reference in New Issue
Block a user