From 05a6e2f526748c8d951961a18297318f20a603a6 Mon Sep 17 00:00:00 2001 From: spla Date: Wed, 6 Apr 2022 20:08:38 +0200 Subject: [PATCH] Refactor --- addbill.py | 145 -------------- addincome.py | 132 ------------ budget.py | 552 ++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 435 insertions(+), 394 deletions(-) delete mode 100644 addbill.py delete mode 100644 addincome.py diff --git a/addbill.py b/addbill.py deleted file mode 100644 index fe9ca06..0000000 --- a/addbill.py +++ /dev/null @@ -1,145 +0,0 @@ -from datetime import datetime, timezone, timedelta -import time -import threading -import os -import sys -import os.path -import psycopg2 -import pytz -import dateutil -from dateutil.parser import parse -from decimal import * -getcontext().prec = 2 - -############################################################################### -# get bills rows -############################################################################### - -def print_bills(): - - try: - - conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") - - cur = conn.cursor() - - cur.execute("SELECT datetime, domain, server, backup, fileserver, setup FROM bills") - - rows = cur.fetchall() - - for row in rows: - - date = row[0].date().strftime('%d.%m.%Y') - print(date, ', domain: ' + str(row[1]), ', server: ' + str(row[2]), 'backup: ' + str(row[3]), ', fileserver: ' + str(row[4]), ' setup: ' + str(row[5])) - - cur.close() - - except (Exception, psycopg2.DatabaseError) as error: - - print(error) - - finally: - - if conn is not None: - - conn.close() - -################################################################################### -# add bills to database -################################################################################### - -def insert_bills(billdate, domainbill, serverbill, backupbill, fileserverbill, setupbill): - - sql = "INSERT INTO bills(datetime, domain, server, backup, fileserver, setup) VALUES(%s,%s,%s,%s,%s,%s)" - - try: - - conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") - - cur = conn.cursor() - - cur.execute(sql, (billdate, domainbill, serverbill, backupbill, fileserverbill, setupbill)) - print("\n") - print("Updating bills...") - - conn.commit() - - cur.close() - - except (Exception, psycopg2.DatabaseError) as error: - - print(error) - - finally: - - if conn is not None: - - conn.close() - -############################################################################### -# INITIALISATION -############################################################################### - -# Returns the parameter from the specified file -def get_parameter( parameter, file_path ): - # Check if db_config.txt file exists - if not os.path.isfile(file_path): - if file_path == "config/db_config.txt": - print("File %s not found, exiting. Run db-setup.py."%file_path) - sys.exit(0) - - # Find parameter in file - with open( file_path ) as f: - for line in f: - if line.startswith( parameter ): - return line.replace(parameter + ":", "").strip() - - # Cannot find parameter, exit - print(file_path + " Missing parameter %s "%parameter) - print("Run setup.py") - sys.exit(0) - -# Load configuration from config file -config_filepath = "config/db_config.txt" -budget_db = get_parameter("budget_db", config_filepath) # E.g., budget -budget_db_user = get_parameter("budget_db_user", config_filepath) # E.g., mastodon - -############################################################################### - -while True: - - domainbill = input("Domain bill? (q to quit) ") - if domainbill == '': - domainbill = '0.00' - elif domainbill == 'q': - sys.exit("Bye") - domainbill = round(float(domainbill),2) - - serverbill = input("Server bill? ") - if serverbill == '': - serverbill = '0.00' - serverbill = round(float(serverbill),2) - - backupbill = input("Backup bill? ") - if backupbill == '': - backupbill = '0.00' - backupbill = round(float(backupbill),2) - - fileserverbill = input("Fileserver bill? ") - if fileserverbill == '': - fileserverbill = 0.00 - fileserverbill = round(float(fileserverbill),2) - - setupbill = input("Setup bill? ") - if setupbill == '': - setupbill = 0.00 - setupbill = round(float(setupbill),2) - - billdate = datetime.strptime(input('Enter Bill date in the format d.m.yyyy '), '%d.%m.%Y') - - now = datetime.now() - - billdate = billdate.replace(hour=now.hour, minute=now.minute, second=now.second) - - insert_bills(billdate, domainbill, serverbill, backupbill, fileserverbill, setupbill) - print_bills() diff --git a/addincome.py b/addincome.py deleted file mode 100644 index c96b803..0000000 --- a/addincome.py +++ /dev/null @@ -1,132 +0,0 @@ -from datetime import datetime, timezone, timedelta -import time -import os -import sys -import os.path -import psycopg2 -import dateutil -from dateutil.parser import parse -from decimal import * -getcontext().prec = 2 - -############################################################################### -# get income rows -############################################################################### - -def print_incomes(): - - conn = None - - try: - - conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") - - cur = conn.cursor() - - cur.execute("SELECT datetime, donations, owner FROM incomes") - - rows = cur.fetchall() - - for row in rows: - - date = row[0].date().strftime('%d.%m.%Y') - print(date,' donation: '+ str(row[1]), 'server owner: ' + str(row[2])) - - cur.close() - - except (Exception, psycopg2.DatabaseError) as error: - - print(error) - - finally: - - if conn is not None: - - conn.close() - -################################################################################### -# add incomes to database -################################################################################### - -def insert_incomes(incomedate, donationincome, ownerincome): - - sql = "INSERT INTO incomes(datetime, donations, owner) VALUES(%s,%s,%s)" - - conn = None - - try: - - conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") - - cur = conn.cursor() - - cur.execute(sql, (incomedate, donationincome, ownerincome)) - print("\n") - print("Updating incomes...") - - conn.commit() - - cur.close() - - except (Exception, psycopg2.DatabaseError) as error: - - print(error) - - finally: - - if conn is not None: - - conn.close() - -############################################################################### -# INITIALISATION -############################################################################### - -# Returns the parameter from the specified file -def get_parameter( parameter, file_path ): - # Check if db_config.txt file exists - if not os.path.isfile(file_path): - if file_path == "config/db_config.txt": - print("File %s not found, exiting. Run db-setup.py."%file_path) - sys.exit(0) - - # Find parameter in file - with open( file_path ) as f: - for line in f: - if line.startswith( parameter ): - return line.replace(parameter + ":", "").strip() - - # Cannot find parameter, exit - print(file_path + " Missing parameter %s "%parameter) - print("Run setup.py") - sys.exit(0) - -# Load configuration from config file -config_filepath = "config/db_config.txt" -budget_db = get_parameter("budget_db", config_filepath) # E.g., budget -budget_db_user = get_parameter("budget_db_user", config_filepath) # E.g., mastodon - -############################################################################### - -while True: - - donationincome = input("Donation income? (q to quit) ") - if donationincome == '': - donationincome = '0.00' - elif donationincome == 'q': - sys.exit("Bye") - donationincome = round(float(donationincome),2) - - ownerincome = input("Server owner income? ") - if ownerincome == '': - ownerincome = '0.00' - ownerincome = round(float(ownerincome),2) - - incomedate = datetime.strptime(input('Income date in the format dd.mm.yyyy? '), '%d.%m.%Y') - - now = datetime.now() - - incomedate = incomedate.replace(hour=now.hour, minute=now.minute, second=now.second) - - insert_incomes(incomedate, donationincome, ownerincome) - print_incomes() diff --git a/budget.py b/budget.py index 02b3d04..a2975c5 100644 --- a/budget.py +++ b/budget.py @@ -1,16 +1,41 @@ -import sys import os -import os.path -import re -from datetime import datetime, timedelta -from mastodon import Mastodon +from datetime import datetime, timezone, timedelta, date +import time +import pytz +import sys import psycopg2 +from mastodon import Mastodon +from mastodon.Mastodon import MastodonMalformedEventError, MastodonNetworkError, MastodonReadTimeout, MastodonAPIError, MastodonIllegalArgumentError +from prettytable import PrettyTable +import pdb -def get_bills(): +tz = pytz.timezone('Europe/Madrid') - current_year = now.year +menu_options = { + 1: 'Add Bill', + 2: 'List Bills', + 3: 'Add Donation', + 4: 'List Donations', + 5: 'Exit', +} +list_show_options = { + 1: 'Month', + 2: 'Year', + 3: 'Total', + 4: 'Back', +} - bills = 0 +def print_menu(): + + for key in menu_options.keys(): + print (key, '-', menu_options[key] ) + +def list_show_menu(): + + for key in list_show_options.keys(): + print (key, '-', list_show_options[key] ) + +def check_db_conn(): try: @@ -18,204 +43,497 @@ def get_bills(): conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") - cur = conn.cursor() - - cur.execute("select sum (domain) + sum(server) + sum(backup) + sum(fileserver) +sum(setup) from bills where date_part('year', datetime) = (%s)", (current_year,)) - - row = cur.fetchone() - - if row[0] != None: - - bills = row[0] - - cur.close() - - return bills - except (Exception, psycopg2.DatabaseError) as error: - sys.exit(error) + sys.stdout.write(f'\n{str(error)}\n') - finally: + sys.exit("Exiting. Run 'python db-setup'") - if conn is not None: +class Bill: - conn.close() + name = "Bill" -def get_donations(): + def __init__(self): - current_year = now.year + self.domain = float(input('Enter Domain Bill: ') or '0.00') + self.server = float(input('Server Bill: ') or '0.00') + self.backup = float(input('Backup Bill: ') or '0.00') + self.fileserver = float(input("File server Bill: ") or '0.00') + self.setup = float(input(f'Setup Bill: ') or '0.00') + + while True: - donations = 0 + try: - try: + self.date = datetime.strptime(input('Enter Bill date in the format d.m.yyyy: '), '%d.%m.%Y') - conn = None + now = datetime.now(tz) - conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") + self.date = self.date.replace(hour=now.hour, minute=now.minute, second=now.second) - cur = conn.cursor() + break - cur.execute("select sum (donations) + sum(owner) from incomes where date_part('year', datetime) = (%s)", (current_year,)) + except: - row = cur.fetchone() + self.date = format(datetime.now(tz).strftime('%d.%m.%Y %H:%M:%S')) - if row[0] != None: + break - donations = row[0] + def save(self): - else: + sql = "INSERT INTO bills(datetime, domain, server, backup, fileserver, setup) VALUES(%s,%s,%s,%s,%s,%s)" - donations = 0 + try: - cur.close() + conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") - return donations + cur = conn.cursor() - except (Exception, psycopg2.DatabaseError) as error: + cur.execute(sql, (self.date, self.domain, self.server, self.backup, self.fileserver, self.setup)) + + print("\nUpdating bills...") - sys.exit(error) + conn.commit() - finally: + cur.close() - if conn is not None: + except (Exception, psycopg2.DatabaseError) as error: - conn.close() + print(error) -def get_fixed_fees(): + finally: - fees_desc_lst = [] + if conn is not None: - fees_types_lst = [] + conn.close() - fees_lst = [] +class Donation: + name = "Donation" - try: + def __init__(self): - conn = None + self.amount = float(input('Donation: ') or '0.00') - conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") + while True: - cur = conn.cursor() + try: - cur.execute("select fee_description, payment_type, fee from fees") + self.date = datetime.strptime(input('Enter Donation date in the format d.m.yyyy: '), '%d.%m.%Y') - rows = cur.fetchall() + now = datetime.now(tz) - for row in rows: + self.date = self.date.replace(hour=now.hour, minute=now.minute, second=now.second) - fees_desc_lst.append(row[0]) + break - fees_types_lst.append(row[1]) + except: - fees_lst.append(row[2]) + self.date = format(datetime.now(tz).strftime('%d.%m.%Y %H:%M:%S')) - cur.close() + break - return (fees_desc_lst, fees_types_lst, fees_lst) + def save(self): - except (Exception, psycopg2.DatabaseError) as error: + sql = "INSERT INTO incomes(datetime, donations) VALUES(%s,%s)" - sys.exit(error) + try: - finally: + conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") - if conn is not None: + cur = conn.cursor() - conn.close() + cur.execute(sql, (self.date, self.amount)) + print("\nUpdating donations...") -def mastodon(): + conn.commit() - # Load secrets from secrets file - secrets_filepath = "secrets/secrets.txt" - uc_client_id = get_parameter("uc_client_id", secrets_filepath) - uc_client_secret = get_parameter("uc_client_secret", secrets_filepath) - uc_access_token = get_parameter("uc_access_token", secrets_filepath) + cur.close() + + except (Exception, psycopg2.DatabaseError) as error: + + print(error) + + finally: + + if conn is not None: + + conn.close() + +class BillShow: + + name = "BillShow" + + def __init__(self): + + while(True): + + print('\n') + + list_show_menu() + + show_option = '' + + try: + + show_option = int(input('\nEnter your choice: ')) + + except: + + print('\nWrong input. Please enter a number between 1 and 4.\n') + + if show_option == 1: + + self.option = 1 + self.criteria = input('Month (current year)? ') + break + + elif show_option == 2: + + self.option = 2 + self.criteria = input('Year ? ') + break + + elif show_option == 3: + + self.option = 3 + self.criteria = None + break + + elif show_option == 4: + + self.option = 4 + self.criteria = None + break + + def show(self, criteria): + + month_date = [] + + month_domain = [] + + month_server = [] + + month_backup = [] + + month_fileserver = [] + + month_setup = [] + + if self.option == 1: + + sql = "select datetime, domain, server, backup, fileserver, setup from bills where date_part('year', datetime) = date_part('year', CURRENT_DATE) and date_part('month', datetime) = (%s) order by 1 asc" + + elif self.option == 2: + + sql = "select datetime, domain, server, backup, fileserver, setup from bills where date_part('year', datetime) = (%s) order by 1 asc" + + elif self.option == 3: + + sql = "select datetime, domain, server, backup, fileserver, setup from bills order by 1 asc" + + try: + + conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") + + cur = conn.cursor() + + if self.option == 1 or self.option == 2: + + cur.execute(sql, (self.criteria,)) + + else: + + cur.execute(sql) + + rows = cur.fetchall() + + for row in rows: + + month_date.append(row[0]) + + month_domain.append(row[1]) + + month_server.append(row[2]) + + month_backup.append(row[3]) + + month_fileserver.append(row[4]) + + month_setup.append(row[5]) + + cur.close() + + except (Exception, psycopg2.DatabaseError) as error: + + print(error) + + finally: + + if conn is not None: + + conn.close() + + return (month_date, month_domain, month_server, month_backup, month_fileserver, month_setup) + +class DonationShow: + + name = "DonationShow" + + def __init__(self): + + while(True): + + print('\n') + + list_show_menu() + + show_option = '' + + try: + + show_option = int(input('\nEnter your choice: ')) + + except: + + print('\nWrong input. Please enter a number between 1 and 4.\n') + + if show_option == 1: + + self.option = 1 + self.criteria = input('Month (current year)? ') + break + + elif show_option == 2: + + self.option = 2 + self.criteria = input('Year ? ') + break + + elif show_option == 3: + + self.option = 3 + self.criteria = None + break + + elif show_option == 4: + + self.option = 4 + self.criteria = None + break + + def show(self, criteria): + + month_date = [] + + month_donation = [] + + if self.option == 1: + + sql = "select datetime, donations from incomes where date_part('year', datetime) = date_part('year', CURRENT_DATE) and date_part('month', datetime) = (%s) order by 1 asc" + + elif self.option == 2: + + sql = "select datetime, donations from incomes where date_part('year', datetime) = (%s) order by 1 asc" + + elif self.option == 3: + + sql = "select datetime, donations from incomes order by 1 asc" + + try: + + conn = psycopg2.connect(database = budget_db, user = budget_db_user, password = "", host = "/var/run/postgresql", port = "5432") + + cur = conn.cursor() + + if self.option == 1 or self.option == 2: + + cur.execute(sql, (self.criteria,)) + + else: + + cur.execute(sql) + + rows = cur.fetchall() + + for row in rows: + + month_date.append(row[0]) + + month_donation.append(row[1]) + + cur.close() + + except (Exception, psycopg2.DatabaseError) as error: + + print(error) + + finally: + + if conn is not None: + + conn.close() + + return (month_date, month_donation) + +def get_config(): + + global budget_db + global budget_db_user # Load configuration from config file - config_filepath = "config/config.txt" - mastodon_hostname = get_parameter("mastodon_hostname", config_filepath) - bot_username = get_parameter("bot_username", config_filepath) - - # Initialise Mastodon API - mastodon = Mastodon( - client_id = uc_client_id, - client_secret = uc_client_secret, - access_token = uc_access_token, - api_base_url = 'https://' + mastodon_hostname, - ) - - # Initialise access headers - headers={ 'Authorization': 'Bearer %s'%uc_access_token } - - return (mastodon, mastodon_hostname, bot_username) - -def db_config(): - - # Load db configuration from config file config_filepath = "config/db_config.txt" - budget_db = get_parameter("budget_db", config_filepath) - budget_db_user = get_parameter("budget_db_user", config_filepath) + budget_db = get_db_params("budget_db", config_filepath) + budget_db_user = get_db_params("budget_db_user", config_filepath) - return (budget_db, budget_db_user) + return (config_filepath, budget_db, budget_db_user) -def get_parameter( parameter, file_path ): +def get_db_params( parameter, file_path ): if not os.path.isfile(file_path): print("File %s not found, exiting."%file_path) sys.exit(0) + # Find parameter in file with open( file_path ) as f: for line in f: if line.startswith( parameter ): return line.replace(parameter + ":", "").strip() + # Cannot find parameter, exit print(file_path + " Missing parameter %s "%parameter) sys.exit(0) -############################################################################### # main if __name__ == '__main__': - mastodon, mastodon_hostname, bot_username = mastodon() + get_config() - budget_db, budget_db_user = db_config() + check_db_conn() - now = datetime.now() + while(True): - donations = get_donations() + print('\n') - bills = get_bills() + print_menu() - fees_desc_lst, fees_types_lst, fees_lst = get_fixed_fees() + option = '' - toot_text = 'Finances of ' + mastodon_hostname + '\n\n' + try: - toot_text += 'Year: ' + str(now.year) + '\n' + option = int(input('\nEnter your choice: ')) - toot_text += '\n' + except: - toot_text += 'Server bills: ' + str(bills) + '€' + '\n' + print('\nWrong input. Please enter a number between 1 and 5.\n') - toot_text += 'Donations: ' + str(donations) + '€' + '\n\n' + if option == 1: - if donations != 0 and bills != 0: + bill = Bill() - toot_text += '%: ' + str(round((donations * 100)/bills,2)) + '\n\n' + if bill.domain == bill.server == bill.backup == bill.fileserver == bill.setup == 0.0: - i = 0 + print('\nAny bill added') - while i < len(fees_lst): + else: - toot_text += '\n' + str(fees_desc_lst[i]) + ': ' + str(fees_lst[i]) + ' (' + str(fees_types_lst[i]) + ')' + bill.save() - i += 1 + elif option == 2: - print(toot_text) + bill = BillShow() - mastodon.status_post(toot_text, in_reply_to_id=None) + if bill.option == 1 or bill.option == 2 or bill.option == 3: + month_date, month_domain, month_server, month_backup, month_fileserver, month_setup = bill.show(bill.criteria) + + if bill.option != 4: + + print_table = PrettyTable() + + print_table.field_names = ['Date', 'Domain', 'Server', 'Backup', 'File server', 'Setup'] + + i = 0 + + total_amount = 0 + + while i < len(month_date): + + b_date = format(month_date[i].strftime('%d.%m.%Y %H:%M:%S')) + + b_domain = float(month_domain[i]) + + b_server = float(month_server[i]) + + b_backup = float(month_backup[i]) + + b_fileserver = float(month_fileserver[i]) + + b_setup = float(month_setup[i]) + + total_amount = total_amount + b_domain + b_server + b_backup + b_fileserver + b_setup + + print_table.add_row([b_date, b_domain, b_server, b_backup, b_fileserver, b_setup]) + + i += 1 + + print_table.add_row(['', '', '', '', 'Total', round(total_amount, 2)]) + + print(print_table) + + elif option == 3: + + donation = Donation() + + if donation.amount == 0.0: + + print('\nAny donation added') + + else: + + donation.save() + + elif option == 4: + + donation = DonationShow() + + if donation.option == 1 or donation.option == 2 or donation.option == 3: + + date_lst, donation_lst = donation.show(donation.criteria) + + if donation.option != 4: + + print_table = PrettyTable() + + print_table.field_names = ['Date','Donation'] + + print_table.align['Donation'] = "r" + + i = 0 + + total_amount = 0 + + while i < len(donation_lst): + + d_date = format(date_lst[i].strftime('%d.%m.%Y %H:%M:%S')) + + d_amount = float(donation_lst[i]) + + total_amount = total_amount + d_amount + + print_table.add_row([d_date, d_amount]) + + i += 1 + + print_table.add_row(['Total', round(total_amount, 2)]) + + print(print_table) + + elif option == 5: + + print('Bye!') + + exit()