Now possible to configure each group in the categories.yaml. Only color added at this moment. The order in which the groups are listed is used by the graph module and will later be used by the report. Fixes search for vacations. The negative match is now done by groups, and the key changes from negative_regex to not_in_groups. Only updates transactions when any travel is assigned. Was previously updating all uncategorized transactions that reached the travel search. Regex match now correctly done lower letters to lower letters. Transaction __repr__ method added and updated along with __str__, to provide a more clearer output when manually categorizing.
143 lines
4.5 KiB
Python
143 lines
4.5 KiB
Python
from __future__ import annotations
|
|
from calendar import monthrange
|
|
from dateutil.rrule import rrule, MONTHLY
|
|
from typing import TYPE_CHECKING
|
|
import datetime as dt
|
|
import matplotlib.pyplot as plt
|
|
|
|
import pfbudget.categories
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
from pfbudget.database import DBManager
|
|
|
|
|
|
groups = pfbudget.categories.cfg["Groups"]
|
|
|
|
|
|
def monthly(
|
|
db: DBManager, args: dict, start: dt.date = dt.date.min, end: dt.date = dt.date.max
|
|
):
|
|
transactions = db.get_daterange(start, end)
|
|
start, end = transactions[0].date, transactions[-1].date
|
|
monthly_transactions = tuple(
|
|
(
|
|
month,
|
|
{
|
|
group: sum(
|
|
transaction.value
|
|
for transaction in transactions
|
|
if transaction.category in categories
|
|
and month
|
|
<= transaction.date
|
|
<= month
|
|
+ dt.timedelta(days=monthrange(month.year, month.month)[1] - 1)
|
|
)
|
|
for group, categories in pfbudget.categories.groups.items()
|
|
},
|
|
)
|
|
for month in [
|
|
month.date()
|
|
for month in rrule(
|
|
MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1)
|
|
)
|
|
]
|
|
)
|
|
|
|
plt.figure(tight_layout=True)
|
|
plt.plot(
|
|
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
|
[groups["income"] for _, groups in monthly_transactions],
|
|
color=groups["income"]["color"],
|
|
)
|
|
plt.stackplot(
|
|
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
|
[
|
|
[-groups[group] for _, groups in monthly_transactions]
|
|
for group in pfbudget.categories.groups
|
|
if group != "income" and group != "investment"
|
|
],
|
|
labels=[
|
|
group
|
|
for group in pfbudget.categories.groups
|
|
if group != "income" and group != "investment"
|
|
],
|
|
colors=[
|
|
groups.get(group, {"color": "gray"})["color"]
|
|
for group in pfbudget.categories.groups
|
|
if group != "income" and group != "investment"
|
|
],
|
|
)
|
|
plt.legend(loc="upper left")
|
|
if args["save"]:
|
|
plt.savefig("graph.png")
|
|
else:
|
|
plt.show()
|
|
|
|
|
|
def discrete(
|
|
db: DBManager, args: dict, start: dt.date = dt.date.min, end: dt.date = dt.date.max
|
|
):
|
|
transactions = db.get_daterange(start, end)
|
|
start, end = transactions[0].date, transactions[-1].date
|
|
monthly_transactions = tuple(
|
|
(
|
|
month,
|
|
{
|
|
category: sum(
|
|
transaction.value
|
|
for transaction in transactions
|
|
if transaction.category == category
|
|
and month
|
|
<= transaction.date
|
|
<= month
|
|
+ dt.timedelta(days=monthrange(month.year, month.month)[1] - 1)
|
|
)
|
|
for category in pfbudget.categories.categories
|
|
},
|
|
)
|
|
for month in [
|
|
month.date()
|
|
for month in rrule(
|
|
MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1)
|
|
)
|
|
]
|
|
)
|
|
|
|
plt.figure(tight_layout=True)
|
|
plt.plot(
|
|
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
|
[
|
|
sum(
|
|
value
|
|
for category, value in categories.items()
|
|
if category in pfbudget.categories.groups["income"]
|
|
)
|
|
for _, categories in monthly_transactions
|
|
],
|
|
color=groups["income"]["color"],
|
|
)
|
|
plt.stackplot(
|
|
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
|
[
|
|
[-categories[category] for _, categories in monthly_transactions]
|
|
for category in pfbudget.categories.categories
|
|
if category not in pfbudget.categories.groups["income"]
|
|
and category not in pfbudget.categories.groups["investment"]
|
|
and category != "Null"
|
|
],
|
|
labels=[
|
|
category
|
|
for category in pfbudget.categories.categories
|
|
if category not in pfbudget.categories.groups["income"]
|
|
and category not in pfbudget.categories.groups["investment"]
|
|
and category != "Null"
|
|
],
|
|
)
|
|
plt.grid()
|
|
plt.legend(loc="upper left")
|
|
if args["save"]:
|
|
plt.savefig("graph.png")
|
|
else:
|
|
plt.show()
|