Investments are not normally reported on the receiving end in transaction logs, and they introduce too much visual noise in monthly expense graphs. As such they will later appear in relevant invesment graphs, but are removed for now from the available choices.
122 lines
4.0 KiB
Python
122 lines
4.0 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
|
|
|
|
|
|
def monthly(db: DBManager, 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(figsize=(30, 10))
|
|
plt.plot(
|
|
list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))),
|
|
[groups["income"] for _, groups in monthly_transactions],
|
|
)
|
|
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"
|
|
],
|
|
)
|
|
plt.legend(loc="upper left")
|
|
plt.tight_layout()
|
|
plt.savefig("graph.png")
|
|
|
|
|
|
def discrete(db: DBManager, 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(figsize=(30, 10))
|
|
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
|
|
],
|
|
)
|
|
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"]
|
|
],
|
|
labels=[
|
|
category
|
|
for category in pfbudget.categories.categories
|
|
if category not in pfbudget.categories.groups["income"]
|
|
and category not in pfbudget.categories.groups["investment"]
|
|
],
|
|
)
|
|
plt.legend(loc="upper left")
|
|
plt.tight_layout()
|
|
plt.savefig("graph.png")
|