[Refactor] Creates Tagger transformer

Move the tag rules based transformer to its own class.
Verified with unit tests.
This commit is contained in:
Luís Murta 2023-04-21 15:32:40 +01:00
parent 2843c66453
commit 2c7c527ea9
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
6 changed files with 57 additions and 34 deletions

View File

@ -29,6 +29,7 @@ from pfbudget.extract.parsers import parse_data
from pfbudget.extract.psd2 import PSD2Extractor
from pfbudget.transform.categorizer import Categorizer
from pfbudget.transform.nullifier import Nullifier
from pfbudget.transform.tagger import Tagger
class Manager:
@ -107,6 +108,9 @@ class Manager:
Categorizer().rules(uncategorized, categories, tags, params[0])
rules = [rule for tag in tags for rule in tag.rules]
Tagger(rules).transform_inplace(uncategorized)
case Operation.BankMod:
with self.db.session() as session:
session.update(Bank, params)

View File

View File

@ -31,7 +31,6 @@ class Categorizer:
categories = [cat for cat in categories if cat.name != "null"]
self._rule_based_categories(transactions, categories)
self._rule_based_tags(transactions, tags)
@Timer(name="categoryrules")
def _rule_based_categories(
@ -77,33 +76,3 @@ class Categorizer:
for k, v in d.items():
print(f"{v}: {k}")
@Timer(name="tagrules")
def _rule_based_tags(
self, transactions: Sequence[t.BankTransaction], tags: Sequence[t.Tag]
):
print(f"Tagging {len(transactions)} transactions")
d = {}
for tag in [t for t in tags if len(t.rules) > 0]:
for rule in tag.rules:
# for transaction in [t for t in transactions if not t.category]:
for transaction in [
t
for t in transactions
if tag.name not in [tag.tag for tag in t.tags]
]:
if not rule.matches(transaction):
continue
if not transaction.tags:
transaction.tags = {t.TransactionTag(tag.name)}
else:
transaction.tags.add(t.TransactionTag(tag.name))
if rule in d:
d[rule] += 1
else:
d[rule] = 1
for k, v in d.items():
print(f"{v}: {k}")

View File

@ -0,0 +1,30 @@
from copy import deepcopy
from typing import Sequence
from pfbudget.db.model import TagRule, Transaction, TransactionTag
from .transform import Transformer
class Tagger(Transformer):
def __init__(self, rules: Sequence[TagRule]):
self.rules = rules
def transform(self, transactions: Sequence[Transaction]) -> Sequence[Transaction]:
result = deepcopy(transactions)
self.transform_inplace(result)
return result
def transform_inplace(self, transactions: Sequence[Transaction]) -> None:
for rule in self.rules:
for transaction in transactions:
if rule.tag in transaction.tags:
continue
if not rule.matches(transaction):
continue
if not transaction.tags:
transaction.tags = {TransactionTag(rule.tag)}
else:
transaction.tags.add(TransactionTag(rule.tag))

View File

@ -1,11 +1,15 @@
from decimal import Decimal
from pfbudget.db.model import Category, CategoryRule
from pfbudget.db.model import Category, CategoryRule, Tag, TagRule
category_null = Category("null", None, set())
category_cat1 = Category(
category1 = Category(
"cat#1",
None,
{CategoryRule(None, None, "desc#1", None, None, None, Decimal(0), "cat#1")},
)
tag_1 = Tag(
"tag#1", {TagRule(None, None, "desc#1", None, None, None, Decimal(0), "tag#1")}
)

View File

@ -10,9 +10,11 @@ from pfbudget.db.model import (
CategorySelector,
Selector_T,
TransactionCategory,
TransactionTag,
)
from pfbudget.transform.categorizer import Categorizer
from pfbudget.transform.nullifier import Nullifier
from pfbudget.transform.tagger import Tagger
from pfbudget.transform.transform import Transformer
@ -77,6 +79,20 @@ class TestTransform:
"null", CategorySelector(Selector_T.nullifier)
)
def test_tagger(self):
transactions = [
BankTransaction(date(2023, 1, 1), "desc#1", Decimal("-10"), "Bank#1")
]
for t in transactions:
assert not t.category
categorizer = Tagger(mock.tag_1.rules)
transactions = categorizer.transform(transactions)
for t in transactions:
assert TransactionTag("tag#1") in t.tags
def test_categorize(self):
transactions = [
BankTransaction(date(2023, 1, 1), "desc#1", Decimal("-10"), "Bank#1")
@ -86,7 +102,7 @@ class TestTransform:
assert not t.category
categorizer = Categorizer()
categorizer.rules(transactions, [mock.category_cat1], [])
categorizer.rules(transactions, [mock.category1], [])
for t in transactions:
assert t.category == TransactionCategory(