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 (
|
from __future__ import annotations
|
||||||
get_income_categories,
|
from dateutil.rrule import rrule, YEARLY
|
||||||
get_fixed_expenses,
|
from typing import TYPE_CHECKING
|
||||||
get_required_expenses,
|
import datetime as dt
|
||||||
get_health_expenses,
|
|
||||||
get_discretionary_expenses,
|
import pfbudget.categories as categories
|
||||||
)
|
|
||||||
from .transactions import load_transactions, by_year_and_category
|
if TYPE_CHECKING:
|
||||||
|
from pfbudget.database import DBManager
|
||||||
|
|
||||||
|
|
||||||
def net(state, start=None, end=None):
|
def net(db: DBManager, start: dt.date = dt.date.min, end: dt.date = dt.date.max):
|
||||||
transactions = load_transactions(state.data_dir)
|
transactions = db.get_daterange(start, end)
|
||||||
if not start:
|
start, end = transactions[0].date, transactions[-1].date
|
||||||
start = transactions[0].date
|
|
||||||
if not end:
|
|
||||||
end = transactions[-1].date
|
|
||||||
|
|
||||||
income, fixed, required, health, discretionary = [], [], [], [], []
|
yearly_transactions = tuple(
|
||||||
yearly_transactions_by_categories = by_year_and_category(transactions, start, end)
|
(
|
||||||
for _, transactions_by_category in yearly_transactions_by_categories.items():
|
year,
|
||||||
income.append(
|
{
|
||||||
sum(
|
group: sum(
|
||||||
float(t.value)
|
transaction.value
|
||||||
for category, transactions in transactions_by_category.items()
|
for transaction in transactions
|
||||||
if transactions and category in get_income_categories()
|
if transaction.category in categories
|
||||||
for t in transactions
|
and year <= transaction.date <= year.replace(month=12, day=31)
|
||||||
)
|
)
|
||||||
|
for group, categories in categories.groups.items()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
fixed.append(
|
for year in [
|
||||||
sum(
|
year.date()
|
||||||
-float(t.value)
|
for year in rrule(
|
||||||
for category, transactions in transactions_by_category.items()
|
YEARLY, dtstart=start.replace(day=1), until=end.replace(day=1)
|
||||||
if transactions and category in get_fixed_expenses()
|
|
||||||
for t in transactions
|
|
||||||
)
|
)
|
||||||
)
|
]
|
||||||
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()):
|
for year, groups in yearly_transactions:
|
||||||
print(year)
|
print(year.year)
|
||||||
print(
|
print(f"Income: {groups.pop('income'):.2f}€")
|
||||||
"Income: {:.2f}, Expenses: {:.2f}, Net: {:.2f}\n"
|
for group, value in groups.items():
|
||||||
"Fixed Expenses: {:.2f}\n"
|
print(f"{group.capitalize()} expenses: {value:.2f}€")
|
||||||
"Required Expenses: {:.2f}\n"
|
print()
|
||||||
"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],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import argparse
|
import argparse
|
||||||
import datetime as dt
|
|
||||||
|
|
||||||
from .categories import categorize_data
|
from .categories import categorize_data
|
||||||
from .database import DBManager
|
from .database import DBManager
|
||||||
from .graph import discrete, monthly
|
|
||||||
from .parsers import parse_data
|
from .parsers import parse_data
|
||||||
from . import report
|
import pfbudget.graph
|
||||||
|
import pfbudget.report
|
||||||
|
import pfbudget.utils
|
||||||
|
|
||||||
DEFAULT_DB = "data.db"
|
DEFAULT_DB = "data.db"
|
||||||
|
|
||||||
@ -67,10 +67,18 @@ def argparser() -> argparse.ArgumentParser:
|
|||||||
func=lambda args: categorize_data(DBManager(args.database))
|
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
|
Graph
|
||||||
"""
|
"""
|
||||||
p_graph = subparsers.add_parser("graph", parents=[help])
|
p_graph = subparsers.add_parser("graph", parents=[help, period])
|
||||||
p_graph.add_argument(
|
p_graph.add_argument(
|
||||||
"option",
|
"option",
|
||||||
type=str,
|
type=str,
|
||||||
@ -79,20 +87,17 @@ def argparser() -> argparse.ArgumentParser:
|
|||||||
default="monthly",
|
default="monthly",
|
||||||
help="graph option help",
|
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_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 = subparsers.add_parser("status", help="status help")
|
||||||
|
|
||||||
p_status.set_defaults(func=status)
|
p_status.set_defaults(func=status)
|
||||||
p_report.set_defaults(func=f_report)
|
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@ -133,39 +138,22 @@ def graph(args):
|
|||||||
state (PFState): Internal state of the program
|
state (PFState): Internal state of the program
|
||||||
args (dict): argparse variables
|
args (dict): argparse variables
|
||||||
"""
|
"""
|
||||||
start, end = dt.date.min, dt.date.max
|
start, end = pfbudget.utils.parse_args_period(args)
|
||||||
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)
|
|
||||||
|
|
||||||
if args.option == "monthly":
|
if args.option == "monthly":
|
||||||
monthly(DBManager(args.database), start, end)
|
pfbudget.graph.monthly(DBManager(args.database), start, end)
|
||||||
elif args.option == "discrete":
|
elif args.option == "discrete":
|
||||||
discrete(DBManager(args.database), start, end)
|
pfbudget.graph.discrete(DBManager(args.database), start, end)
|
||||||
|
|
||||||
|
|
||||||
def f_report(state, args):
|
def report(args):
|
||||||
"""Report
|
"""Prints a detailed report of the transactions over a period of time.
|
||||||
|
|
||||||
Prints a detailed report of the transactions over a period of time.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
state (PFState): Internal state of the program
|
state (PFState): Internal state of the program
|
||||||
args (dict): argparse variables
|
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():
|
def run():
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
from datetime import date, datetime, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -52,3 +53,24 @@ def find_credit_institution(fn, banks, creditcards):
|
|||||||
raise CreditCardNotAvailableError
|
raise CreditCardNotAvailableError
|
||||||
|
|
||||||
return bank, cc
|
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