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:
Luís Murta 2021-07-05 22:53:03 +01:00
parent f811b5c711
commit 37c97453a9
Signed by: satprog
GPG Key ID: DDF2EFC6179009DC
3 changed files with 83 additions and 109 deletions

View File

@ -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
)
)
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 year in [
year.date()
for year in rrule(
YEARLY, dtstart=start.replace(day=1), until=end.replace(day=1)
)
]
)
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()

View File

@ -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():

View File

@ -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