[Refactor] Creates Tagger transformer
Move the tag rules based transformer to its own class. Verified with unit tests.
This commit is contained in:
parent
2843c66453
commit
2c7c527ea9
@ -29,6 +29,7 @@ from pfbudget.extract.parsers import parse_data
|
|||||||
from pfbudget.extract.psd2 import PSD2Extractor
|
from pfbudget.extract.psd2 import PSD2Extractor
|
||||||
from pfbudget.transform.categorizer import Categorizer
|
from pfbudget.transform.categorizer import Categorizer
|
||||||
from pfbudget.transform.nullifier import Nullifier
|
from pfbudget.transform.nullifier import Nullifier
|
||||||
|
from pfbudget.transform.tagger import Tagger
|
||||||
|
|
||||||
|
|
||||||
class Manager:
|
class Manager:
|
||||||
@ -107,6 +108,9 @@ class Manager:
|
|||||||
|
|
||||||
Categorizer().rules(uncategorized, categories, tags, params[0])
|
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:
|
case Operation.BankMod:
|
||||||
with self.db.session() as session:
|
with self.db.session() as session:
|
||||||
session.update(Bank, params)
|
session.update(Bank, params)
|
||||||
|
|||||||
0
pfbudget/transform/__init__.py
Normal file
0
pfbudget/transform/__init__.py
Normal file
@ -31,7 +31,6 @@ class Categorizer:
|
|||||||
categories = [cat for cat in categories if cat.name != "null"]
|
categories = [cat for cat in categories if cat.name != "null"]
|
||||||
|
|
||||||
self._rule_based_categories(transactions, categories)
|
self._rule_based_categories(transactions, categories)
|
||||||
self._rule_based_tags(transactions, tags)
|
|
||||||
|
|
||||||
@Timer(name="categoryrules")
|
@Timer(name="categoryrules")
|
||||||
def _rule_based_categories(
|
def _rule_based_categories(
|
||||||
@ -77,33 +76,3 @@ class Categorizer:
|
|||||||
|
|
||||||
for k, v in d.items():
|
for k, v in d.items():
|
||||||
print(f"{v}: {k}")
|
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}")
|
|
||||||
|
|||||||
30
pfbudget/transform/tagger.py
Normal file
30
pfbudget/transform/tagger.py
Normal 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))
|
||||||
@ -1,11 +1,15 @@
|
|||||||
from decimal import Decimal
|
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_null = Category("null", None, set())
|
||||||
|
|
||||||
category_cat1 = Category(
|
category1 = Category(
|
||||||
"cat#1",
|
"cat#1",
|
||||||
None,
|
None,
|
||||||
{CategoryRule(None, None, "desc#1", None, None, None, Decimal(0), "cat#1")},
|
{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")}
|
||||||
|
)
|
||||||
|
|||||||
@ -10,9 +10,11 @@ from pfbudget.db.model import (
|
|||||||
CategorySelector,
|
CategorySelector,
|
||||||
Selector_T,
|
Selector_T,
|
||||||
TransactionCategory,
|
TransactionCategory,
|
||||||
|
TransactionTag,
|
||||||
)
|
)
|
||||||
from pfbudget.transform.categorizer import Categorizer
|
from pfbudget.transform.categorizer import Categorizer
|
||||||
from pfbudget.transform.nullifier import Nullifier
|
from pfbudget.transform.nullifier import Nullifier
|
||||||
|
from pfbudget.transform.tagger import Tagger
|
||||||
from pfbudget.transform.transform import Transformer
|
from pfbudget.transform.transform import Transformer
|
||||||
|
|
||||||
|
|
||||||
@ -77,6 +79,20 @@ class TestTransform:
|
|||||||
"null", CategorySelector(Selector_T.nullifier)
|
"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):
|
def test_categorize(self):
|
||||||
transactions = [
|
transactions = [
|
||||||
BankTransaction(date(2023, 1, 1), "desc#1", Decimal("-10"), "Bank#1")
|
BankTransaction(date(2023, 1, 1), "desc#1", Decimal("-10"), "Bank#1")
|
||||||
@ -86,7 +102,7 @@ class TestTransform:
|
|||||||
assert not t.category
|
assert not t.category
|
||||||
|
|
||||||
categorizer = Categorizer()
|
categorizer = Categorizer()
|
||||||
categorizer.rules(transactions, [mock.category_cat1], [])
|
categorizer.rules(transactions, [mock.category1], [])
|
||||||
|
|
||||||
for t in transactions:
|
for t in transactions:
|
||||||
assert t.category == TransactionCategory(
|
assert t.category == TransactionCategory(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user