Readds manual categorization
Also fixes a categorization bug in the Manager, in the DB client method.
This commit is contained in:
parent
86afa99217
commit
c37e7eb37c
@ -87,15 +87,10 @@ def argparser() -> argparse.ArgumentParser:
|
||||
parse.add_argument("--bank", nargs=1, type=str)
|
||||
parse.add_argument("--creditcard", nargs=1, type=str)
|
||||
|
||||
"""
|
||||
Categorizing
|
||||
"""
|
||||
categorize = subparsers.add_parser(
|
||||
"categorize",
|
||||
description="Categorizes the transactions in the selected database",
|
||||
parents=[universal],
|
||||
)
|
||||
categorize.set_defaults(op=Operation.Categorize)
|
||||
# Automatic/manual categorization
|
||||
categorize = subparsers.add_parser("categorize").add_subparsers(required=True)
|
||||
categorize.add_parser("auto").set_defaults(op=Operation.Categorize)
|
||||
categorize.add_parser("manual").set_defaults(op=Operation.ManualCategorization)
|
||||
|
||||
"""
|
||||
Graph
|
||||
|
||||
@ -9,6 +9,7 @@ class Operation(Enum):
|
||||
Parse = auto()
|
||||
Download = auto()
|
||||
Categorize = auto()
|
||||
ManualCategorization = auto()
|
||||
Token = auto()
|
||||
RequisitionId = auto()
|
||||
CategoryAdd = auto()
|
||||
|
||||
@ -10,6 +10,8 @@ from pfbudget.db.model import (
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
Transactions = list[Transaction]
|
||||
|
||||
|
||||
class Categorizer:
|
||||
options = {}
|
||||
@ -17,25 +19,44 @@ class Categorizer:
|
||||
def __init__(self):
|
||||
self.options["null_days"] = 4
|
||||
|
||||
def categorize(
|
||||
def rules(
|
||||
self,
|
||||
transactions: list[Transaction],
|
||||
transactions: Transactions,
|
||||
categories: list[Category],
|
||||
tags: list[Tag],
|
||||
):
|
||||
"""Overarching categorization tool
|
||||
|
||||
Receives a list of transactions (by ref) and updates their category
|
||||
Receives a list of transactions (by ref) and updates their category according
|
||||
to the rules defined for each category
|
||||
|
||||
Args:
|
||||
transactions (list[Transaction]): uncategorized transactions
|
||||
categories (list[Category]): available categories
|
||||
tags (list[Tag]): currently available tags
|
||||
"""
|
||||
|
||||
self._nullify(transactions)
|
||||
|
||||
self._rule_based_categories(transactions, categories)
|
||||
self._rule_based_tags(transactions, tags)
|
||||
|
||||
def _nullify(self, transactions: list[Transaction]):
|
||||
def manual(
|
||||
self,
|
||||
transactions: Transactions,
|
||||
categories: list[Category],
|
||||
tags: list[Tag],
|
||||
):
|
||||
"""Manual categorization input
|
||||
|
||||
Args:
|
||||
transactions (list[Transaction]): uncategorized transactions
|
||||
categories (list[Category]): available categories
|
||||
tags (list[Tag]): currently available tags
|
||||
"""
|
||||
self._manual(transactions)
|
||||
|
||||
def _nullify(self, transactions: Transactions):
|
||||
count = 0
|
||||
matching = []
|
||||
for transaction in transactions:
|
||||
@ -66,7 +87,7 @@ class Categorizer:
|
||||
print(f"Nullified {count} transactions")
|
||||
|
||||
def _rule_based_categories(
|
||||
self, transactions: list[Transaction], categories: list[Category]
|
||||
self, transactions: Transactions, categories: list[Category]
|
||||
):
|
||||
d = {}
|
||||
for category in [c for c in categories if c.rules]:
|
||||
@ -81,9 +102,17 @@ class Categorizer:
|
||||
continue
|
||||
|
||||
# passed all conditions, assign category
|
||||
transaction.category = TransactionCategory(
|
||||
category.name, CategorySelector(Selector.rules)
|
||||
)
|
||||
if transaction.category:
|
||||
if (
|
||||
input(f"Overwrite {transaction} with {category}? (y/n)")
|
||||
== "y"
|
||||
):
|
||||
transaction.category.name = category.name
|
||||
transaction.category.selector.selector = Selector.rules
|
||||
else:
|
||||
transaction.category = TransactionCategory(
|
||||
category.name, CategorySelector(Selector.rules)
|
||||
)
|
||||
|
||||
if rule in d:
|
||||
d[rule] += 1
|
||||
@ -93,7 +122,7 @@ class Categorizer:
|
||||
for k, v in d.items():
|
||||
print(f"{v}: {k}")
|
||||
|
||||
def _rule_based_tags(self, transactions: list[Transaction], tags: list[Tag]):
|
||||
def _rule_based_tags(self, transactions: Transactions, tags: list[Tag]):
|
||||
d = {}
|
||||
for tag in [t for t in tags if t.rules]:
|
||||
for rule in tag.rules:
|
||||
@ -119,3 +148,20 @@ class Categorizer:
|
||||
for k, v in d.items():
|
||||
print(f"{v}: {k}")
|
||||
|
||||
def _manual(self, transactions: Transactions):
|
||||
uncategorized = [t for t in transactions if not t.category]
|
||||
print(f"{len(uncategorized)} transactions left to categorize")
|
||||
|
||||
for transaction in uncategorized:
|
||||
while True:
|
||||
category = input(f"{transaction} category: ")
|
||||
if category == "quit":
|
||||
return
|
||||
if not category:
|
||||
print("{category} doesn't exist")
|
||||
continue
|
||||
transaction.category = TransactionCategory(
|
||||
category, CategorySelector(Selector.manual)
|
||||
)
|
||||
|
||||
break
|
||||
|
||||
@ -71,10 +71,17 @@ class Manager:
|
||||
|
||||
case Operation.Categorize:
|
||||
with self.db.session() as session:
|
||||
uncategorized = session.get(Transaction, ~Transaction.category)
|
||||
uncategorized = session.get(Transaction, ~Transaction.category.has())
|
||||
categories = session.get(Category)
|
||||
tags = session.get(Tag)
|
||||
Categorizer().categorize(uncategorized, categories, tags)
|
||||
Categorizer().rules(uncategorized, categories, tags)
|
||||
|
||||
case Operation.ManualCategorization:
|
||||
with self.db.session() as session:
|
||||
uncategorized = session.get(Transaction, ~Transaction.category.has())
|
||||
categories = session.get(Category)
|
||||
tags = session.get(Tag)
|
||||
Categorizer().manual(uncategorized, categories, tags)
|
||||
|
||||
case Operation.BankMod:
|
||||
with self.db.session() as session:
|
||||
|
||||
@ -54,11 +54,11 @@ class DbClient:
|
||||
self.__session.expunge_all()
|
||||
|
||||
def get(self, type, column=None, values=None):
|
||||
if column:
|
||||
if column is not None:
|
||||
if values:
|
||||
stmt = select(type).where(column.in_(values))
|
||||
else:
|
||||
stmt = select(type).where(column.has())
|
||||
stmt = select(type).where(column)
|
||||
else:
|
||||
stmt = select(type)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user