[Refactor] Categorizer now implements Transform

Test adapted for new interface.
Exchanged manual input functionality for throwing an exception.
Removed timer at transformer level.
This commit is contained in:
Luís Murta 2023-04-22 20:01:10 +01:00
parent 2c7c527ea9
commit b1de4d519a
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
4 changed files with 39 additions and 76 deletions

View File

@ -103,10 +103,11 @@ class Manager:
categories = session.get(Category)
tags = session.get(Tag)
null_rules = [cat.rules for cat in categories if cat.name == "null"]
Nullifier(null_rules).transform_inplace(uncategorized)
rules = [cat.rules for cat in categories if cat.name == "null"]
Nullifier(rules).transform_inplace(uncategorized)
Categorizer().rules(uncategorized, categories, tags, params[0])
rules = [rule for cat in categories for rule in cat.rules]
Categorizer(rules).transform_inplace(uncategorized)
rules = [rule for tag in tags for rule in tag.rules]
Tagger(rules).transform_inplace(uncategorized)

View File

@ -1,78 +1,36 @@
from codetiming import Timer
from copy import deepcopy
from typing import Sequence
import pfbudget.db.model as t
from pfbudget.db.model import (
CategoryRule,
CategorySelector,
Selector_T,
Transaction,
TransactionCategory,
)
from .exceptions import TransactionCategorizedError
from .transform import Transformer
class Categorizer:
options = {}
class Categorizer(Transformer):
def __init__(self, rules: Sequence[CategoryRule]):
self.rules = rules
def __init__(self):
self.options["null_days"] = 3
def transform(self, transactions: Sequence[Transaction]) -> Sequence[Transaction]:
result = deepcopy(transactions)
self.transform_inplace(result)
def rules(
self,
transactions: Sequence[t.BankTransaction],
categories: Sequence[t.Category],
tags: Sequence[t.Tag],
nullify: bool = True
):
"""Overarching categorization tool
return result
Receives a list of transactions (by ref) and updates their category according
to the rules defined for each category
def transform_inplace(self, transactions: Sequence[Transaction]) -> None:
for rule in self.rules:
for transaction in transactions:
if transaction.category:
raise TransactionCategorizedError(transaction)
Args:
transactions (Sequence[BankTransaction]): uncategorized transactions
categories (Sequence[Category]): available categories
tags (Sequence[Tag]): currently available tags
"""
if not rule.matches(transaction):
continue
categories = [cat for cat in categories if cat.name != "null"]
self._rule_based_categories(transactions, categories)
@Timer(name="categoryrules")
def _rule_based_categories(
self,
transactions: Sequence[t.BankTransaction],
categories: Sequence[t.Category],
):
print(f"Categorizing {len(transactions)} transactions")
d = {}
for category in [c for c in categories if c.rules]:
for rule in category.rules:
# for transaction in [t for t in transactions if not t.category]:
for transaction in [
t
for t in transactions
if not t.category or t.category.name != "null"
]:
if not rule.matches(transaction):
continue
# passed all conditions, assign category
if transaction.category:
if transaction.category.name == category.name:
continue
if (
input(
f"Overwrite {transaction} with {category.name}? (y/n)"
)
== "y"
):
transaction.category.name = category.name
transaction.category.selector.selector = t.Selector_T.rules
else:
transaction.category = t.TransactionCategory(
category.name, t.CategorySelector(t.Selector_T.rules)
)
if rule in d:
d[rule] += 1
else:
d[rule] = 1
for k, v in d.items():
print(f"{v}: {k}")
transaction.category = TransactionCategory(
rule.name, CategorySelector(Selector_T.rules)
)

View File

@ -1,2 +1,6 @@
class MoreThanOneMatchError(Exception):
pass
class TransactionCategorizedError(Exception):
pass

View File

@ -71,7 +71,7 @@ class TestTransform:
assert not t.category
rules.append(CategoryRule(None, None, None, None, "Bank#2", None, None, "null"))
categorizer: Transformer = Nullifier(rules)
categorizer = Nullifier(rules)
transactions = categorizer.transform(transactions)
for t in transactions:
@ -87,7 +87,7 @@ class TestTransform:
for t in transactions:
assert not t.category
categorizer = Tagger(mock.tag_1.rules)
categorizer: Transformer = Tagger(mock.tag_1.rules)
transactions = categorizer.transform(transactions)
for t in transactions:
@ -101,8 +101,8 @@ class TestTransform:
for t in transactions:
assert not t.category
categorizer = Categorizer()
categorizer.rules(transactions, [mock.category1], [])
categorizer: Transformer = Categorizer(mock.category1.rules)
transactions: Transformer = categorizer.transform(transactions)
for t in transactions:
assert t.category == TransactionCategory(