Refactored report module
Bring report up-to-date with sqlite3 database and yaml configuration files. Updates report command in runnable.py. Moves date arg parsing to utils module Report and graph now share an ArgumentParser with shared date options.
This commit is contained in:
parent
f811b5c711
commit
37c97453a9
@ -1,78 +1,42 @@
|
||||
from .categories import (
|
||||
get_income_categories,
|
||||
get_fixed_expenses,
|
||||
get_required_expenses,
|
||||
get_health_expenses,
|
||||
get_discretionary_expenses,
|
||||
)
|
||||
from .transactions import load_transactions, by_year_and_category
|
||||
from __future__ import annotations
|
||||
from dateutil.rrule import rrule, YEARLY
|
||||
from typing import TYPE_CHECKING
|
||||
import datetime as dt
|
||||
|
||||
import pfbudget.categories as categories
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pfbudget.database import DBManager
|
||||
|
||||
|
||||
def net(state, start=None, end=None):
|
||||
transactions = load_transactions(state.data_dir)
|
||||
if not start:
|
||||
start = transactions[0].date
|
||||
if not end:
|
||||
end = transactions[-1].date
|
||||
def net(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
|
||||
|
||||
income, fixed, required, health, discretionary = [], [], [], [], []
|
||||
yearly_transactions_by_categories = by_year_and_category(transactions, start, end)
|
||||
for _, transactions_by_category in yearly_transactions_by_categories.items():
|
||||
income.append(
|
||||
sum(
|
||||
float(t.value)
|
||||
for category, transactions in transactions_by_category.items()
|
||||
if transactions and category in get_income_categories()
|
||||
for t in transactions
|
||||
)
|
||||
yearly_transactions = tuple(
|
||||
(
|
||||
year,
|
||||
{
|
||||
group: sum(
|
||||
transaction.value
|
||||
for transaction in transactions
|
||||
if transaction.category in categories
|
||||
and year <= transaction.date <= year.replace(month=12, day=31)
|
||||
)
|
||||
for group, categories in categories.groups.items()
|
||||
},
|
||||
)
|
||||
fixed.append(
|
||||
sum(
|
||||
-float(t.value)
|
||||
for category, transactions in transactions_by_category.items()
|
||||
if transactions and category in get_fixed_expenses()
|
||||
for t in transactions
|
||||
for year in [
|
||||
year.date()
|
||||
for year in rrule(
|
||||
YEARLY, dtstart=start.replace(day=1), until=end.replace(day=1)
|
||||
)
|
||||
)
|
||||
required.append(
|
||||
sum(
|
||||
-float(t.value)
|
||||
for category, transactions in transactions_by_category.items()
|
||||
if transactions and category in get_required_expenses()
|
||||
for t in transactions
|
||||
)
|
||||
)
|
||||
health.append(
|
||||
sum(
|
||||
-float(t.value)
|
||||
for category, transactions in transactions_by_category.items()
|
||||
if transactions and category in get_health_expenses()
|
||||
for t in transactions
|
||||
)
|
||||
)
|
||||
discretionary.append(
|
||||
sum(
|
||||
-float(t.value)
|
||||
for category, transactions in transactions_by_category.items()
|
||||
if transactions and category in get_discretionary_expenses()
|
||||
for t in transactions
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
for i, year in enumerate(yearly_transactions_by_categories.keys()):
|
||||
print(year)
|
||||
print(
|
||||
"Income: {:.2f}, Expenses: {:.2f}, Net: {:.2f}\n"
|
||||
"Fixed Expenses: {:.2f}\n"
|
||||
"Required Expenses: {:.2f}\n"
|
||||
"Health Expenses: {:.2f}\n"
|
||||
"Discretionary Expenses: {:.2f}\n".format(
|
||||
income[i],
|
||||
fixed[i] + required[i] + health[i] + discretionary[i],
|
||||
income[i] - (fixed[i] + required[i] + health[i] + discretionary[i]),
|
||||
fixed[i],
|
||||
required[i],
|
||||
health[i],
|
||||
discretionary[i],
|
||||
)
|
||||
)
|
||||
for year, groups in yearly_transactions:
|
||||
print(year.year)
|
||||
print(f"Income: {groups.pop('income'):.2f}€")
|
||||
for group, value in groups.items():
|
||||
print(f"{group.capitalize()} expenses: {value:.2f}€")
|
||||
print()
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import datetime as dt
|
||||
|
||||
from .categories import categorize_data
|
||||
from .database import DBManager
|
||||
from .graph import discrete, monthly
|
||||
from .parsers import parse_data
|
||||
from . import report
|
||||
import pfbudget.graph
|
||||
import pfbudget.report
|
||||
import pfbudget.utils
|
||||
|
||||
DEFAULT_DB = "data.db"
|
||||
|
||||
@ -67,10 +67,18 @@ def argparser() -> argparse.ArgumentParser:
|
||||
func=lambda args: categorize_data(DBManager(args.database))
|
||||
)
|
||||
|
||||
period = argparse.ArgumentParser(add_help=False).add_mutually_exclusive_group()
|
||||
period.add_argument(
|
||||
"--interval", type=str, nargs=2, help="graph interval", metavar=("START", "END")
|
||||
)
|
||||
period.add_argument("--start", type=str, nargs=1, help="graph start date")
|
||||
period.add_argument("--end", type=str, nargs=1, help="graph end date")
|
||||
period.add_argument("--year", type=str, nargs=1, help="graph year")
|
||||
|
||||
"""
|
||||
Graph
|
||||
"""
|
||||
p_graph = subparsers.add_parser("graph", parents=[help])
|
||||
p_graph = subparsers.add_parser("graph", parents=[help, period])
|
||||
p_graph.add_argument(
|
||||
"option",
|
||||
type=str,
|
||||
@ -79,20 +87,17 @@ def argparser() -> argparse.ArgumentParser:
|
||||
default="monthly",
|
||||
help="graph option help",
|
||||
)
|
||||
p_graph_interval = p_graph.add_mutually_exclusive_group()
|
||||
p_graph_interval.add_argument(
|
||||
"--interval", type=str, nargs=2, help="graph interval", metavar=("START", "END")
|
||||
)
|
||||
p_graph_interval.add_argument("--start", type=str, nargs=1, help="graph start date")
|
||||
p_graph_interval.add_argument("--end", type=str, nargs=1, help="graph end date")
|
||||
p_graph_interval.add_argument("--year", type=str, nargs=1, help="graph year")
|
||||
p_graph.set_defaults(func=graph)
|
||||
|
||||
p_report = subparsers.add_parser("report", help="report help")
|
||||
"""
|
||||
Report
|
||||
"""
|
||||
p_report = subparsers.add_parser("report", parents=[help, period])
|
||||
p_report.set_defaults(func=report)
|
||||
|
||||
p_status = subparsers.add_parser("status", help="status help")
|
||||
|
||||
p_status.set_defaults(func=status)
|
||||
p_report.set_defaults(func=f_report)
|
||||
|
||||
return parser
|
||||
|
||||
@ -133,39 +138,22 @@ def graph(args):
|
||||
state (PFState): Internal state of the program
|
||||
args (dict): argparse variables
|
||||
"""
|
||||
start, end = dt.date.min, dt.date.max
|
||||
if args.start or args.interval:
|
||||
start = dt.datetime.strptime(args.start[0], "%Y/%m/%d").date()
|
||||
|
||||
if args.end or args.interval:
|
||||
end = dt.datetime.strptime(args.end[0], "%Y/%m/%d").date()
|
||||
|
||||
if args.interval:
|
||||
start = dt.datetime.strptime(args.interval[0], "%Y/%m/%d").date()
|
||||
end = dt.datetime.strptime(args.interval[1], "%Y/%m/%d").date()
|
||||
|
||||
if args.year:
|
||||
start = dt.datetime.strptime(args.year[0], "%Y").date()
|
||||
end = dt.datetime.strptime(
|
||||
str(int(args.year[0]) + 1), "%Y"
|
||||
).date() - dt.timedelta(days=1)
|
||||
|
||||
start, end = pfbudget.utils.parse_args_period(args)
|
||||
if args.option == "monthly":
|
||||
monthly(DBManager(args.database), start, end)
|
||||
pfbudget.graph.monthly(DBManager(args.database), start, end)
|
||||
elif args.option == "discrete":
|
||||
discrete(DBManager(args.database), start, end)
|
||||
pfbudget.graph.discrete(DBManager(args.database), start, end)
|
||||
|
||||
|
||||
def f_report(state, args):
|
||||
"""Report
|
||||
|
||||
Prints a detailed report of the transactions over a period of time.
|
||||
def report(args):
|
||||
"""Prints a detailed report of the transactions over a period of time.
|
||||
|
||||
Args:
|
||||
state (PFState): Internal state of the program
|
||||
args (dict): argparse variables
|
||||
"""
|
||||
report.net(state)
|
||||
start, end = pfbudget.utils.parse_args_period(args)
|
||||
pfbudget.report.net(DBManager(args.database), start, end)
|
||||
|
||||
|
||||
def run():
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
|
||||
@ -52,3 +53,24 @@ def find_credit_institution(fn, banks, creditcards):
|
||||
raise CreditCardNotAvailableError
|
||||
|
||||
return bank, cc
|
||||
|
||||
|
||||
def parse_args_period(args):
|
||||
start, end = date.min, date.max
|
||||
if args.start or args.interval:
|
||||
start = datetime.strptime(args.start[0], "%Y/%m/%d").date()
|
||||
|
||||
if args.end or args.interval:
|
||||
end = datetime.strptime(args.end[0], "%Y/%m/%d").date()
|
||||
|
||||
if args.interval:
|
||||
start = datetime.strptime(args.interval[0], "%Y/%m/%d").date()
|
||||
end = datetime.strptime(args.interval[1], "%Y/%m/%d").date()
|
||||
|
||||
if args.year:
|
||||
start = datetime.strptime(args.year[0], "%Y").date()
|
||||
end = datetime.strptime(str(int(args.year[0]) + 1), "%Y").date() - timedelta(
|
||||
days=1
|
||||
)
|
||||
|
||||
return start, end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user