From 93b0cb62446c05a8605a16530ed7306aacf14e62 Mon Sep 17 00:00:00 2001 From: dvorak Date: Wed, 20 Sep 2023 11:55:18 +0800 Subject: [PATCH] init for public --- MANIFEST.in | 2 +- monthly_expenses/__init__.py | 93 ++++++------------- .../templates/MonthlyExpenses.html | 33 +++---- setup.py | 20 ++-- 4 files changed, 53 insertions(+), 95 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 12e3dbc..e161c55 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include monthly_expenses/templates/* +include monthly_expenses/templates/*.html diff --git a/monthly_expenses/__init__.py b/monthly_expenses/__init__.py index f94056a..0256fa3 100644 --- a/monthly_expenses/__init__.py +++ b/monthly_expenses/__init__.py @@ -9,12 +9,13 @@ from beancount.core.number import ZERO from fava.ext import FavaExtensionBase -from fava.helpers import FavaAPIException +#from fava.helpers import FavaAPIException from fava.template_filters import cost_or_value from beancount.loader import load_file from beancount.query.query import run_query from beancount.query.numberify import numberify_results +from beancount.core.data import Custom import os import numpy as np import pandas as pd @@ -27,20 +28,24 @@ class MonthlyExpenses(FavaExtensionBase): # pragma: no cover report_title = "Monthly Expenses" + def get_all_report(self): + return [self.build_report_for_account(account) for account in self.config['accounts'] ] + + def get_valid_month_number(self): - FileName= '/mnt/c/Users/dvorak/Dropbox/beancount/a.beancount' - if not os.path.isfile(FileName): - os.system("bean-example > {}".format(FileName)) + account = self.config['account_used_to_get_valid_month'] + print("options", self.ledger.options) + print("config", self.config) - # Load an Example beancount Journal File - entries, _, opts = load_file(FileName) - # Main currency + entries = self.ledger.all_entries + opts = self.ledger.options + cols, rows = run_query(entries, opts, "SELECT year, month, account, COUNT(position)\ - WHERE account ~ 'Expenses:Life:Food'\ + WHERE account ~ '{}'\ GROUP BY year, month, account\ - ORDER BY year, month, account" + ORDER BY year, month, account".format(account) ) cols, rows = numberify_results(cols, rows) month_count = {} @@ -49,29 +54,22 @@ def get_valid_month_number(self): for row in rows: if row[3] > 10: month_count[row[0]] = month_count[row[0]] + 1 - + print("month ", month_count) return cols, month_count - - def build_report_life(self): - FileName= '/mnt/c/Users/dvorak/Dropbox/beancount/a.beancount' - if not os.path.isfile(FileName): - os.system("bean-example > {}".format(FileName)) - - # Load an Example beancount Journal File - entries, _, opts = load_file(FileName) - # Main currency + def build_report_for_account(self, account): + print("build report for ", account) + entries = self.ledger.all_entries + opts = self.ledger.options currency = opts["operating_currency"][0] cols, rows = run_query(entries, opts, "SELECT account, YEAR(date) AS year, convert(sum(position),'CNY') AS amount\ - WHERE account ~ 'Expenses:Life'\ - GROUP BY account, year ORDER BY account, year".format(currency) + WHERE account ~ '{}'\ + GROUP BY account, year ORDER BY account, year".format(account) ) - cols, rows = numberify_results(cols, rows) - - month_count = self.get_valid_month_number()[1] + month_count = self.get_valid_month_number()[1] # Converting Result Rows to a Pandas Dataframe df = pd.DataFrame(rows, columns=[k[0] for k in cols]) @@ -80,7 +78,7 @@ def build_report_life(self): df['amount (CNY)'] = df['amount (CNY)'] / df['month_count'] df = df.drop(columns=['month_count']) df.rename(columns={"account": "Account", "year":"Year", "amount ({})".format(currency): "Amount ({})".format(currency)}, inplace=True) - df = df.astype({"Account": str, "Year": int, "Amount ({})".format(currency): np.float}); + df = df.astype({"Account": str, "Year": int, "Amount ({})".format(currency): np.float64}); #print(df[["Account", "Year", "Amount ({})".format(currency)]].fillna(0)) # Pivoting a Table by Year @@ -88,48 +86,11 @@ def build_report_life(self): # Creating Multi-Level Accounts n_levels = df["Account"].str.count(":").max() + 1 cols = ["Account_L{}".format(k) for k in range(n_levels)] - df[cols] = df["Account"].str.split(':', n_levels - 1, expand=True) + df[cols] = df["Account"].str.split(':', n=n_levels - 1, expand=True) df = df.fillna('').drop(columns="Account", level=0).set_index(cols) - return (df.groupby(["Account_L0", "Account_L1","Account_L2"]).sum().round(decimals=2)).to_html(),df.groupby(["Account_L0", "Account_L1","Account_L2"]).sum().sum().to_frame().to_html() + selected_levels = len(account.split(":")) + 1 + groups_name = ["Account_L{}".format(i) for i in range(selected_levels)] - - def build_report(self): - FileName= '/mnt/c/Users/dvorak/Dropbox/beancount/a.beancount' - if not os.path.isfile(FileName): - os.system("bean-example > {}".format(FileName)) - - # Load an Example beancount Journal File - entries, _, opts = load_file(FileName) - # Main currency - currency = opts["operating_currency"][0] - cols, rows = run_query(entries, opts, - "SELECT account, YEAR(date) AS year, convert(sum(position),'CNY') AS amount\ - WHERE account ~ 'Expenses'\ - GROUP BY account, year ORDER BY account, year".format(currency) - ) - - cols, rows = numberify_results(cols, rows) - - month_count = self.get_valid_month_number()[1] - - # Converting Result Rows to a Pandas Dataframe - df = pd.DataFrame(rows, columns=[k[0] for k in cols]) - df['month_count'] = df['year'] - month_count_html = df['year'] - df = df.replace({'month_count': month_count}) - df['amount (CNY)'] = df['amount (CNY)'] / df['month_count'] - df = df.drop(columns=['month_count']) - df.rename(columns={"account": "Account", "year":"Year", "amount ({})".format(currency): "Amount ({})".format(currency)}, inplace=True) - df = df.astype({"Account": str, "Year": int, "Amount ({})".format(currency): np.float}); - #print(df[["Account", "Year", "Amount ({})".format(currency)]].fillna(0)) - - # Pivoting a Table by Year - df = df.pivot_table(index="Account", columns=[ 'Year']).fillna(0).reset_index(); - # Creating Multi-Level Accounts - n_levels = df["Account"].str.count(":").max() + 1 - cols = ["Account_L{}".format(k) for k in range(n_levels)] - df[cols] = df["Account"].str.split(':', n_levels - 1, expand=True) - df = df.fillna('').drop(columns="Account", level=0).set_index(cols) - return (df.groupby(["Account_L0", "Account_L1"]).sum().round(decimals=2)).to_html(),df.groupby(["Account_L0", "Account_L1"]).sum().sum().to_frame().to_html(),pd.DataFrame(month_count.items(), columns=['Year', 'count']).to_html() + return (df.groupby(groups_name).sum().round(decimals=2)).to_html(),df.groupby(groups_name).sum().sum().to_frame().to_html(),pd.DataFrame(month_count.items(), columns=['Year', 'count']).to_html(),pd.DataFrame(month_count.items(), columns=['Year', 'count']).to_html() diff --git a/monthly_expenses/templates/MonthlyExpenses.html b/monthly_expenses/templates/MonthlyExpenses.html index eca29d2..683d3dc 100644 --- a/monthly_expenses/templates/MonthlyExpenses.html +++ b/monthly_expenses/templates/MonthlyExpenses.html @@ -4,23 +4,18 @@

Monthly Expenses


- {% set report1 = extension.build_report() %} - {% set report2 = extension.build_report_life() %} -
- {{report1[0] |safe}} -
-
- {{report1[1] |safe}} -
-
- {{report1[2] |safe}} -
-
-
-
- {{report2[0] |safe}} -
-
- {{report2[1] |safe}} -
+ {% set reports = extension.get_all_report() %} + {% for report in reports %} +
+
+ {{report[0] |safe}} +
+
+ {{report[1] |safe}} +
+
+ {{report[2] |safe}} +
+
+ {% endfor %}
diff --git a/setup.py b/setup.py index d824ab9..8d1e6ad 100644 --- a/setup.py +++ b/setup.py @@ -1,26 +1,28 @@ -__author__ = "Ghislain Bourgeois" +__author__ = "YANG Zhenfei" __copyright__ = "Copyright (C) 2018 Ghislain Bourgeois" __license__ = "GNU GPLv2" import setuptools +from setuptools import find_packages setuptools.setup( name="monthly_expenses", - version="0.2.0", - packages=setuptools.find_packages(), + version="0.5", + packages=find_packages(), + package_data={'monthly_expenses.templates':['*.html']}, setup_requires=['pytest-runner'], - install_requires=['beancount>=2.1.2', 'tabulate', 'pandas'], + install_requires=['beancount>=2.1.2', 'tabulate', 'pandas', 'fava'], tests_require=['pytest', 'testfixtures'], test_suite="tests", - author="Ghislain Bourgeois", - author_email="ghislain.bourgeois@gmail.com", - description="Beancount portfolio allocation report", + author="YANG Zhenfei", + author_email="yangzhenfei0@gmail.com", + description="Beancount monthly expenses report", license="GPLv2", - keywords="beancount report portfolio allocation", - url="https://github.com/ghislainbourgeois/beancount_portfolio_allocation/", + keywords="beancount monthly expenses report", + url="https://github.com/dvorak0/fava-monthly-expenses/", include_package_data = True, classifiers=(