def get_expenses_for_rows(df, stor_exp_data_path, stor_data_path, budg_path, bankconfig): """ Gets the expense data for stores, prompting the user when multiple expenses exist for a store params: df - pandas dataframe stor_exp_data_path - filepath to expensesDB """ print( "\nIterating your transactions. If you want to quit halfway, type ctrl c to save!\n" ) # initialize the objects for tracking changes exp_stor_db = data_help.read_jsonFile(stor_exp_data_path) stor_db = data_help.read_jsonFile(stor_data_path) budg_db = data_help.read_jsonFile(budg_path) try: for idx, row in df.iterrows(): # iterate through only the data which has no expenses declared. if pd.isnull(row[env.EXPENSE]): # get relevant expenses for that month set by the user. month_end_date = util.get_month_from_timestamp(row[env.DATE], start=False) if type(row[env.BANK_STORENAME]) is str: match = bankconfig.regex_str.search( row[env.BANK_STORENAME]) if match: processed_text = util.process_text(match.group(0)) print( f"Was able to filter - {row[env.BANK_STORENAME]} -> {processed_text}" ) storename = processed_text else: print(f"Unable to filter - {row[env.BANK_STORENAME]}") storename = row[env.BANK_STORENAME] else: # default case use empty str print("No storename exists for this transaction.") storename = "" print( "Curr Transaction: %-10s | %-10s | %-10s | %-10s " % (row[env.DATE], row[env.AMOUNT], storename, row[env.TYPE])) selected_exp, exp_stor_db, stor_db, storename = search_store_relationships( storename, exp_stor_db, budg_db[month_end_date], stor_exp_data_path, stor_db, stor_data_path) df.at[idx, env.FILT_STORENAME] = storename df.at[idx, env.EXPENSE] = selected_exp except KeyboardInterrupt: print( "\n\nQuitting to main menu. Your data inputs will be saved, and you can resume where you left off by restarting and selecting 'v' for view data!\n" ) return df
def get_expenses(db_exp_data_fpaths: list, db_inc_data_fpaths: list, stor_pair_path: str, stor_exp_data_path: str, budg_path: str, exp_path: str, dont_print_cols=None, bankconfig=None): """ main method for the importing of expense data """ bank_json = data_help.read_jsonFile(bankconfig.settings_path) exp_df = data_help.load_csvs( db_exp_data_fpaths, dtype=bankconfig.exp_dtypes, parse_dates=env.pdates_colname ) # only using on csv db for now. newest will be last? idk verify later. exp_df = data_help.drop_for_substring( exp_df, env.BANK_STORENAME, bankconfig.ignorable_transactions, "\nRemoving the below expense transactions as they are either an internal bank acct transfer, cash advance or credit payment." ) dates = data_help.extract_months(exp_df[env.DATE], start=False) # check for any missing budgets either this month or any month in the data expManager.get_budgets(budg_path, exp_path, dates) exp_df = expManager.get_expenses_for_rows(exp_df, stor_exp_data_path, stor_pair_path, budg_path, bankconfig) print("\nFinished gathering your expense data: \n") util.print_fulldf(exp_df, dont_print_cols) data_help.write_data(exp_df, db_exp_data_fpaths[0])
def edit_settings(settings_path): """ Main interface for editing settings for the app """ done = False prompt = "Please select a setting to edit: " while not done: settings = data_help.read_jsonFile(settings_path) setting_sels = util.select_indices_of_list(prompt, list(settings.keys()), return_matches=True, abortchar='q', print_lst=True) if setting_sels is None: return None for setting in setting_sels: if setting == env.BANK_SELECTION_KEY: value = util.select_indices_of_list( "Please select from the list: ", settings[env.BANK_CHOICES_KEY], return_matches=True, abortchar='q') else: data_type = type(settings[setting]) value = util.get_input_given_type( f"Enter your '{data_type}' for {setting}={settings[setting]}. ", data_type, abortchar='q', setting=settings[setting]) if value is not None: # none type returned upon quit settings[setting] = value done = True data_help.write_to_jsonFile(settings_path, settings)
def set_dict_keys_to_lowercase(dct_path): """ Function I wrote just to run on the stores exp database to keep all stores lowercase. """ dct = data_help.read_jsonFile(dct_path) for key in dct.keys(): dct = data_help.modify_dict_key(dct, key, key.lower()) data_help.write_to_jsonFile(dct_path, dct)
def get_budgets(budg_path, exp_path, dates=None): """ Prompts user for budgeting options given a new month if no budget is present for that month """ exp_budg = data_help.read_jsonFile(budg_path) exp_data = data_help.read_jsonFile(exp_path) if dates == None: dates = [util.get_current_month()] for date in dates: exp_budg_keys = exp_budg.keys() if date not in exp_budg_keys: # check for current month to find exp categories print( f"I have detected some data with for the month {date} that has no budget set." ) print( "Please set the budget for this month.. or delete the data and run the program again." ) if len(exp_budg) != 0: user_in = util.get_user_input_for_chars( "Would you like to the whole thing (w) or create new (n)? ", ['w', 'n']) if user_in == 'w': key = util.select_dict_key_using_integer( exp_budg, "Please select a budget to copy: ", print_children=True, print_vals=False, print_child_vals=True) exp_budg[date] = exp_budg[key] elif user_in == 'n': exp_budg[date] = declare_new_budget(date, exp_data) else: exp_budg[date] = declare_new_budget(date, exp_data) print(f"Your budget is now saved for {date}.") else: print(f"Your monthly budget for {date} is: ") util.print_simple_dict(exp_budg[date], print_vals=True) data_help.write_to_jsonFile(budg_path, exp_budg) return
def store_editor(db_exp_data_fpaths, db_exprec_data_fpath, stor_pair_path, exp_stor_data_path, budg_path, exp_path, bankconfig=None): """ Edits a store's name across all databases. params: db_exp_data_fpaths - filepaths to expense csv's stor_pair_path - filepath to store name database exp_stor_data_path - filepath to store-expense data base budg_path - filepath to budget database """ done = False while not done: df = data_help.load_csvs(db_exp_data_fpaths, dtype=bankconfig.exp_dtypes, parse_dates=env.pdates_colname) df_rec = data_help.load_csv(db_exprec_data_fpath, dtype=bankconfig.exp_dtypes, parse_dates=env.pdates_colname) stor_data = data_help.read_jsonFile(stor_pair_path) exp_stor_data = data_help.read_jsonFile(exp_stor_data_path) exp_data = data_help.read_jsonFile(exp_path) budg_db = data_help.read_jsonFile(budg_path) prompt = "Would you like to: \n(a) - change storenames\n(b) - edit bank to database store relationships\n(q) - quit\ntype here: " user_in = util.get_user_input_for_chars(prompt, ['a', 'b', 'q']) if user_in == 'a': change_storename(db_exp_data_fpaths, df, db_exprec_data_fpath, df_rec, exp_stor_data, stor_data, stor_pair_path, exp_stor_data_path) elif user_in == 'b': change_storepair(db_exp_data_fpaths, df, db_exprec_data_fpath, df_rec, exp_stor_data, stor_data, stor_pair_path, exp_stor_data_path, exp_data, budg_db) elif user_in == 'q': done = True return
def choose_bank(json_path: str): """ Prompt user for banks """ settings_json = data_help.read_jsonFile(json_path) if env.BANK_SELECTION_KEY not in settings_json.keys(): prompt = f"Please choose your bank(s) from the list of banks: " choices = util.select_indices_of_list(prompt, env.BANK_OPTIONS, return_matches=True) settings_json[env.BANK_SELECTION_KEY] = choices data_help.write_to_jsonFile(json_path, settings_json)
def initialize_settings(settings_path): settings = data_help.read_jsonFile(settings_path) for key in env.SETTINGS_TEMPL.keys(): # add keys in globvar if key not in settings.keys(): settings[key] = env.SETTINGS_TEMPL[key] keys_to_rem = [] for key in settings.keys(): # remove keys not in globvar if key not in env.SETTINGS_TEMPL.keys(): keys_to_rem.append(key) for key in keys_to_rem: settings.pop(key) data_help.write_to_jsonFile(settings_path, settings)
def budget_editor(budg_path): """ Allows user to play around with the budget database """ done = False prompt = "Would you like to \n(a) - change budget amounts\n(q) - quit?\nType here: " while not done: budg_data = data_help.read_jsonFile(budg_path) print(env.OUTPUT_SEP_STR) user_in = util.get_user_input_for_chars(prompt, ['a', 'q']) if user_in == 'a': change_budget_amounts(budg_data, budg_path) elif user_in == 'q': done = True
def notes_editor(db_exp_data_fpaths, db_inc_data_fpaths, notes_path, bankconfig=None): """ Main menu for editing notes """ done = False while not done: exp_df = data_help.load_csvs(db_exp_data_fpaths, dtype=bankconfig.exp_dtypes, parse_dates=env.pdates_colname) inc_df = data_help.load_csvs(db_inc_data_fpaths, dtype=bankconfig.inc_dtypes, parse_dates=env.pdates_colname) notes_dict = data_help.read_jsonFile(notes_path) exp_months = data_help.extract_months(exp_df[env.DATE], start=False) inc_months = data_help.extract_months(inc_df[env.DATE], start=False) months_in_data = util.add_set_to_set(exp_months, inc_months, sort=True) if notes_dict == {}: prompt = "You have not entered any notes yet. Which month(s) would you like to add notes for? " edit_prompt_base = "Please enter your note below for month " else: prompt = "Please select a month to edit : " edit_prompt_base = "Edit your note below for month " sel_months = util.select_indices_of_list( prompt, list_to_compare_to=months_in_data, return_matches=True, abortchar='q') if sel_months is not None: notes = edit_notes(edit_prompt_base, notes_dict, sel_months, notes_path) else: done = True
def setup_expense_names(exp_path: str): """ Gets a list of expense names from the user and saves them. """ exp_list = data_help.read_jsonFile(exp_path) if len(exp_list) == 0: exp_list[env.EXPENSE_DATA_KEY] = util.format_input_to_list( "Please input your expense categories, I will add a Misc category since it is reserved. " ) idxs_matched = util.check_lst_for_values( exp_list[env.EXPENSE_DATA_KEY], env.MISC_POS_VALUES) for idx in sorted(idxs_matched, reverse=True): print( f"Found {exp_list[env.EXPENSE_DATA_KEY][idx]} in your expenses. Removing since '{env.EXPENSE_MISC_STR}' is reserved as miscellaneous category." ) del exp_list[env.EXPENSE_DATA_KEY][idx] exp_list[env.EXPENSE_DATA_KEY].append(env.EXPENSE_MISC_STR) data_help.write_to_jsonFile(exp_path, exp_list) if env.EXPENSES_SUBTRACTED_KEY not in exp_list.keys(): exp_list[env.EXPENSES_SUBTRACTED_KEY] = [] data_help.write_to_jsonFile(exp_path, exp_list)
def view_money_data(db_exp_data_fpaths, db_inc_data_fpaths, stor_pair_path, stor_exp_data_path, budg_path, notes_path, exp_path, dont_print_cols=None, bankconfig=None, settings_path=None): """ main method for the viewing of data params: exp_path - path to expenses.json dont_print_cols - ignore columns for output to CLI """ df_exp = data_help.load_csvs(db_exp_data_fpaths, dtype=bankconfig.exp_dtypes, parse_dates=env.pdates_colname, index=env.DATE) df_inc = data_help.load_csvs( db_inc_data_fpaths, dtype=bankconfig.inc_dtypes, parse_dates=env.pdates_colname, index=env.DATE) # TODO SHOW NET INCOME ON PLOTS df_exp = data_help.combine_and_drop(df_exp, env.AMOUNT, env.ADJUSTMENT, 'subtract') df_inc = data_help.combine_and_drop(df_inc, env.AMOUNT, env.ADJUSTMENT, 'subtract') exp_dict = data_help.read_jsonFile(exp_path) settings = data_help.read_jsonFile(settings_path) if df_inc.empty: # set index to datetime if empty. df_inc.set_index(pd.to_datetime(df_inc.index), inplace=True) budg_db = data_help.read_jsonFile(budg_path) # load the budget data df_budg = pd.DataFrame(budg_db) df_budg = df_budg.T # transpose to make index date str df_budg.set_index(pd.to_datetime(df_budg.index), inplace=True) # set index to date time notes_dict = data_help.read_jsonFile(notes_path) years = data_help.extract_years(df_exp.index.to_series()) years_to_show = util.select_indices_of_list( "Which of the above year(s) would you like to take a peak at, 'a' for all: ", years, return_matches=True, abortchar='q', ret_all_char='a') if years_to_show is None: # select_indices_of_list returns None if user aborts return None dfs = data_help.drop_dt_indices_not_in_selection(years_to_show, years, [df_exp, df_budg, df_inc]) df_exp = dfs[0] df_budg = dfs[1] df_inc = dfs[2] if df_exp is None: # quit condition return None freq, freq_desc = get_plotting_frequency() if freq == None: return None if freq == env.YEAR_FREQ: plot_for_date(years, dont_print_cols, exp_dict, df_inc, df_budg, df_exp, settings, notes_dict, freq=freq, freq_desc=freq_desc) elif freq == env.MONTH_FREQ: df_exp, df_budg, df_inc, months = choose_months_in_dfs( df_exp, df_budg, df_inc, years_to_show) if df_exp is None: return None plot_for_date(years, dont_print_cols, exp_dict, df_inc, df_budg, df_exp, settings, notes_dict, freq=env.MONTH_FREQ, freq_desc=freq_desc, months=months) elif freq == 'b': df_exp_mnth, df_budg_mnth, df_inc_mnth, months = choose_months_in_dfs( df_exp, df_budg, df_inc, years_to_show) if df_exp_mnth is None: return None plot_for_date(years, dont_print_cols, exp_dict, df_inc_mnth, df_budg_mnth, df_exp_mnth, settings, notes_dict, freq=env.MONTH_FREQ, freq_desc='month', override_show=True, months=months) plot_for_date(years, dont_print_cols, exp_dict, df_inc, df_budg, df_exp, settings, notes_dict, freq=env.YEAR_FREQ, freq_desc='year') else: pass
json_paths = [ budg_path, stor_exp_data_path, stor_pair_path, exp_path, notes_path, settings_path ] initialize_dirs(list_of_dirs) initialize_dbs(json_paths) # check for expense list and setup if none are there. expManager.setup_expense_names(exp_path) # check for bank choice and setup if no choice is there. expManager.choose_bank(settings_path) # initialize settings initialize_settings(settings_path) settings = data_help.read_jsonFile(settings_path) bankconfig = util.get_bank_conf(settings, settings_path) initialize_csvs([exp_recbin_path, inc_recbin_path], [bankconfig.exp_colnames, bankconfig.inc_colnames]) print("--- --- --- --- --- --- --- --- --- --- --- --- ---") print("--- --- --- -- SHOW ME YOUR MONEY -- --- --- --- --") print(f"--- --- --- --- --- V. {env.VERSION} --- --- --- --- --- ---") print("WELCOME TO SHOW ME YOUR MONEYYYYY COME ON DOWN!") quit = False index = 0 bankconfigreloaded = bankconfig while not quit: print(" ----|$$| MAIN MENU |$$|---- ") user_in = util.get_user_input_for_chars(
def expenses_editor(db_exp_data_fpaths, exp_recbin_path, stor_pair_path, exp_stor_data_path, budg_path, exp_path, bankconfig=None): """ Edits an expense's name across all databases """ done = False while not done: exp_data = data_help.read_jsonFile(exp_path) df_rec = data_help.load_csv(exp_recbin_path, dtype=bankconfig.exp_dtypes, parse_dates=env.pdates_colname) df = data_help.load_csvs(db_exp_data_fpaths, dtype=bankconfig.exp_dtypes, parse_dates=env.pdates_colname) stor_data = data_help.read_jsonFile(stor_pair_path) exp_stor_data = data_help.read_jsonFile(exp_stor_data_path) budg_data = data_help.read_jsonFile(budg_path) prompt = "\n".join( ("Would you like to:", "(a) - add an expense", "(b) - edit an expenses name", "(c) - pair expenses to stores", "(d) - delete an expense **CAUTION**", "(e) - edit an expense within your database", "(f) - unpair an expense from stores", "(g) - add expense to be subtracted in plot title", "(h) - remove expense to be subtracted in plot title", "(q) - quit editor", "type here: ")) user_in = util.get_user_input_for_chars( prompt, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'q', 's']) if user_in == 'a': add_expense(exp_data, exp_stor_data, exp_path, exp_stor_data_path) elif user_in == 'b': # TODO edit_expense_name(db_exp_data_fpaths[0], df, exp_recbin_path, df_rec, exp_data, budg_data, exp_stor_data, exp_path, budg_path, exp_stor_data_path) elif user_in == 'c': add_expenses_to_store(exp_stor_data, exp_stor_data_path, exp_data[env.EXPENSE_DATA_KEY]) elif user_in == 'd': remove_expense_from_dbs(db_exp_data_fpaths[0], exp_recbin_path, exp_stor_data, exp_data, budg_data, df, df_rec, exp_stor_data_path, budg_path, exp_path) elif user_in == 'e': edit_cell_in_dfcol(db_exp_data_fpaths[0], df, col_name=env.EXPENSE, opt_col=env.FILT_STORENAME, opt_dict=exp_stor_data) elif user_in == 'f': remove_exp_from_store(db_exp_data_fpaths[0], df, exp_recbin_path, df_rec, exp_stor_data, exp_stor_data_path) elif user_in == 'g': prompt = "Which expense(s) would you like to be subtracted in the title to your plots? " util.edit_list_in_dict(prompt, exp_data[env.EXPENSE_DATA_KEY], exp_data, env.EXPENSES_SUBTRACTED_KEY, exp_path, add=True) elif user_in == 'h': prompt = "Which expense(s) would you like to remove? " util.edit_list_in_dict(prompt, exp_data[env.EXPENSES_SUBTRACTED_KEY], exp_data, env.EXPENSES_SUBTRACTED_KEY, exp_path, add=False) elif user_in == 'q': done = True elif user_in == 's': print("Ah so youre an alchemist then.") sync_expenses(exp_data, exp_stor_data, exp_path, exp_stor_data_path)