def load_total() -> pd.DataFrame: log.info('Loading TOTAL file') result = pd.read_csv(config.mankkoo_file_path('total'), parse_dates=['Date']) result = result.astype({'Date': 'datetime64[ns]', 'Total': 'float'}) result['Date'] = result['Date'].dt.date return result
def load_stocks() -> pd.DataFrame: log.info('Loading STOCK file') result = pd.read_csv(config.mankkoo_file_path('stock'), parse_dates=['Date']) result = result.astype({'Total Value': 'float', 'Date': 'datetime64[ns]'}) result['Date'] = result['Date'].dt.date return result
def update_output(list_of_contents, list_of_names, list_of_dates, bank_id, account_name, account_type): if list_of_contents is not None: try: account.add_new_operations(models.Bank[bank_id], account_name, contents=list_of_contents, account_tye=models.Account(account_type)) return 'success' except Exception as ex: log.info(f'Error occured: {ex}') return 'failure'
def load_global_config() -> dict: """Load global configuration from /data folder Returns: dict: global specific config from config.yaml file """ log.info(f"Loading global {config_file} file") with open(data_path() + config_file) as c: return yaml.safe_load(c)
def load_user_config() -> dict: """Load user configuration from .mankkoo directory Returns: dict: user specific config from config.yaml file """ log.info(f"Loading user {config_file} file") with open(mankkoo_path() + __slash() + config_file, 'r', encoding='utf-8') as c: return yaml.safe_load(c)
def __latest_balance_for_account(df: pd.DataFrame, account_name: str): result = df.loc[(df['Account'] == account_name)] result = result.dropna(subset=['Balance']) try: return result.iloc[-1]['Balance'] except IndexError: log.info( 'There are no latest balance for %s account. Therefore assuming 0.', account_name) return 0
def total_money_data(data: dict) -> pd.DataFrame: """Get summary data of all assets sorted by categories Args: data (dict): dictionary with all financial data Returns: pd.DataFrame: grouped financial standings """ log.info('Fetching latest total money data') checking_account = __latest_account_balance(data, 'checking') savings_account = __latest_account_balance(data, 'savings') cash = __latest_account_balance(data, 'cash') ppk = __latest_account_balance(data, 'retirement') inv = data['investment'].loc[data['investment']['Active'] == True] inv = inv['Start Amount'].sum() stock_buy = data['stock'].loc[data['stock']['Operation'] == 'Buy'] stock_buy = stock_buy['Total Value'].sum() stock_sell = data['stock'].loc[data['stock']['Operation'] == 'Sell'] stock_sell = stock_sell['Total Value'].sum() stock = stock_buy - stock_sell # TODO check how much stock units I have Broker-Title pair buy-sell total = checking_account + savings_account + cash + ppk + inv + stock return pd.DataFrame([{ 'Type': 'Checking Account', 'Total': checking_account, 'Percentage': checking_account / total }, { 'Type': 'Savings Account', 'Total': savings_account, 'Percentage': savings_account / total }, { 'Type': 'Cash', 'Total': cash, 'Percentage': cash / total }, { 'Type': 'PPK', 'Total': ppk, 'Percentage': ppk / total }, { 'Type': 'Investments', 'Total': inv, 'Percentage': inv / total }, { 'Type': 'Stocks', 'Total': stock, 'Percentage': stock / total }])
def load_all() -> dict: """Load aggregated data of all financial data (accounts, investments, etc.) Returns: dict(pandas.DataFrame): a dictonary with categorized financial data """ log.info("Loading mankkoo's files") return dict(account=load_accounts(), investment=load_investments(), stock=load_stocks(), total=load_total(), total_monthly=load_total_monthly())
def load_total_monthly() -> pd.DataFrame: log.info('Loading TOTAL MONTHLY file') result = pd.read_csv(config.mankkoo_file_path('total_monthly'), parse_dates=['Date']) result = result.astype({ 'Date': 'datetime64[ns]', 'Income': 'float', 'Spending': 'float', 'Profit': 'float' }) result['Date'] = result['Date'].dt.date return result
def load_accounts() -> pd.DataFrame: log.info('Loading ACCOUNT file') result = pd.read_csv(config.mankkoo_file_path('account'), parse_dates=['Date'], index_col=0) result = result.astype({ 'Account': 'string', 'Balance': 'float', 'Operation': 'float', 'Date': 'datetime64[ns]' }) result['Date'] = result['Date'].dt.date return result
def load_investments() -> pd.DataFrame: log.info('Loading INVESTMENT file') result = pd.read_csv(config.mankkoo_file_path('investment'), parse_dates=['Start Date', 'End Date']) result = result.astype({ 'Active': 'int', 'Start Amount': 'float', 'End amount': 'float', 'Start Date': 'datetime64[ns]', 'End Date': 'datetime64[ns]' }) result.Active = result.Active.astype('bool') result['Start Date'] = result['Start Date'].dt.date result['End Date'] = result['End Date'].dt.date return result
def add_new_operations(bank: models.Bank, account_name: str, file_name=None, contents=None, account_tye=models.Account.CHECKING) -> pd.DataFrame: """Append bank accounts history with new operations. This method return a pandas DataFrame with calculated balance. Args: bank (importer.Bank): enum of a bank company file_name (str): name of a file from which data will be loaded Raises: KeyError: raised when unsupported bank enum is provided Returns: pandas.DataFrame: DataFrame that holds transactions history with newly added operations """ log.info('Adding new operations for %s account in %s bank', account_name, bank) df_new = importer.load_bank_data(file_name, contents, bank, account_name) df_new['Type'] = account_tye.value df = db.load_accounts() __make_account_backup(df) df = pd.concat([df, df_new]).reset_index(drop=True) df = df.sort_values(by=['Date', 'Bank', 'Account']) df = df.reset_index(drop=True) df = calculate_balance(df, account_name) df.to_csv(config.mankkoo_file_path('account'), index=True, index_label='Row') total.update_total_money(df, df_new['Date'].min()) total.update_monthly_profit(from_date=df_new['Date'].min(), force=True) log.info('%d new operations for %s account were added.', df_new['Bank'].size, account_name) return df
def update_total_money( accounts: pd.DataFrame, from_date: datetime.date, till_date=datetime.date.today()) -> pd.DataFrame: """Calculate and add rows of totals for each day from pd.Series Args: accounts (pd.DataFrame): updated accounts file updated_dates (pd.Series): Series of updated dates for which calculation needs to be done Returns: pd.DataFrame: new, updated total assets standing """ log.info('Updating and calculating total money history from %s to %s', str(from_date), str(till_date)) total = db.load_total() total = __drop_from_total_days(total, from_date) total_new_lines = __calc_totals(accounts, from_date, till_date) total = pd.concat([total, total_new_lines]).reset_index(drop=True) total.to_csv(config.mankkoo_file_path('total'), index=False) log.info('Total money data was updated successfully') return total
def calculate_balance(df: pd.DataFrame, account_name: str) -> pd.DataFrame: """Calculates balance for new operations Args: df (pandas.DataFrame): DataFrame with a column 'Balance' which has some rows with value NaN Returns: pandas.DataFrame: DataFrame with calucated 'Balance' after each operation """ log.info('Calculating balance for %s account.', account_name) # TODO move to importer.py df = df.astype({'Balance': 'float', 'Operation': 'float'}) non_balanced_rows = df['Balance'].index[df['Balance'].apply(pd.isna)] latest_balance = __latest_balance_for_account(df, account_name) log.info('Calculating balance for %s account from %s', account_name, df.iloc[non_balanced_rows[0]]['Date']) for i in non_balanced_rows.values.tolist(): latest_balance = latest_balance + df.loc[i, 'Operation'] df.loc[i, 'Balance'] = round(latest_balance, 2) return df
def update_monthly_profit(from_date: datetime.date, till_date=datetime.datetime.now(), force=False) -> pd.DataFrame: log.info('Updating monthly profit info starting from %s', from_date.strftime("%m-%Y")) from_month = datetime.date(from_date.year, from_date.month, 1) total_monthly = db.load_total_monthly() should_update = __should_monthly_total_be_updated(total_monthly, from_month, force) if not should_update: log.info("Monthly profit won't be updated.") return total = db.load_total() months_list = pd.date_range(from_date - relativedelta(months=1), till_date, freq='MS') result_list = [] for month in months_list: profit = last_month_income(total, month) row_dict = { 'Date': month, 'Income': round(0, 2), 'Spending': round(0, 2), 'Profit': round(profit, 2) } result_list.append(row_dict) df = pd.DataFrame(result_list) total_monthly = total_monthly.drop( total_monthly[total_monthly['Date'] >= from_month].index) total_monthly = pd.concat([total_monthly, df], ignore_index=True) total_monthly.to_csv(config.mankkoo_file_path('total_monthly'), index=False) log.info('Total monthly profit data was updated successfully') return total_monthly
def stock_page(): log.info('Loading stocks page') return html.Div([ html.H3('You are on stocks page') ])
elif pathname == '/stocks': page = ps.stock_page() elif pathname == '/retirement': page = pr.retirement_page() else: page = main.main_page() return html.Div(children=[navbar.navbar(app, pathname), page]) @app.callback(Output('upload-status', 'value'), Input('upload-data', 'contents'), State('upload-data', 'filename'), State('upload-data', 'last_modified'), State('bank-id', 'value'), State('account-name', 'value'), State('account-type', 'value') ) def update_output(list_of_contents, list_of_names, list_of_dates, bank_id, account_name, account_type): if list_of_contents is not None: try: account.add_new_operations(models.Bank[bank_id], account_name, contents=list_of_contents, account_tye=models.Account(account_type)) return 'success' except Exception as ex: log.info(f'Error occured: {ex}') return 'failure' if __name__ == '__main__': log.info("Starting mankkoo's server") app.run_server(debug=True)
def init_data_folder(): """Initilize .mankkoo directory in home folder, config file and all storage files """ m_path = mankkoo_path() log.info("Checking if mankkoo's files are existing...") if not os.path.exists(m_path): log.info('Creating mankkoo directory...') os.makedirs(m_path) for file in mankkoo_files: file_path = mankkoo_file_path(file) if not os.path.exists(file_path): log.info("Creating mankkoo's " + file + " file...") df = pd.DataFrame(columns=__select_columns(file)) df.to_csv(file_path, index=False) log.info(f"{file} was created in .mankkoo directory") if not os.path.exists(m_path + __slash() + config_file): log.info(f"Creating user {config_file} file...") config_d = dict(accounts=dict( ui=dict(default_importer='', hide_accounts=[]))) with open(m_path + __slash() + config_file, 'w') as outfile: yaml.dump(config_d, outfile, allow_unicode=True, default_flow_style=False) log.info(f"User {config_file} file was created in .mankkoo directory")
def retirement_page(): log.info('Loading retirement page') return html.Div([html.H3('You are on retirement page')])
def inv_page(): log.info("Loading investments page") return html.Div([html.H3('You are on investments page')])
def account_page(): log.info("Loading accounts page") global_config = config.load_global_config() user_config = config.load_user_config() bank_ids = ui.decode_bank_ids(global_config['accounts']['importers']) accounts_data = db.load_accounts() accounts_data = accounts_data.iloc[::-1] accounts_names = list(accounts_data.groupby(['Bank', 'Account']).groups) account_tabs = [] for account_name in accounts_names: if account_name[0] + ' - ' + account_name[1] in user_config[ 'accounts']['ui']['hide_accounts']: continue single_account = accounts_data[ (accounts_data['Bank'] == account_name[0]) & (accounts_data['Account'] == account_name[1])] single_account = single_account[[ 'Date', 'Title', 'Details', 'Operation', 'Balance', 'Currency', 'Comment' ]] account_tab = __account_tab(single_account, account_name) account_tabs.append(account_tab) return html.Div( className='height-100 container main-body', children=[ html.Div(className='row', children=[ html.Div( className='col-4', children=[ html.Label(htmlFor='bank-id', children=['Bank']), dcc.Dropdown(id='bank-id', options=bank_ids, value=user_config['accounts'] ['ui']['default_importer']) ]), html.Div(className='col-2', children=[ html.Label(htmlFor='account-type', children=['Account type']), dcc.Dropdown(id='account-type', options=[{ 'label': 'Checking', 'value': 'checking' }, { 'label': 'Savings', 'value': 'savings' }], value='checking') ]), html.Div(className='col-4', children=[ html.Label(htmlFor='account-name', children=['Account name']), dcc.Input(id='account-name', placeholder='Accunt name', type='text', style={'width': '100%'}) ]) ]), html.Div(className='row', children=[ dcc.Upload(id='upload-data', children=html.Div([ 'Drag and Drop or ', html.A('Select Files'), " to upload account's files" ]), style={ 'width': '100%', 'height': '60px', 'lineHeight': '60px', 'borderWidth': '1px', 'borderStyle': 'dashed', 'borderRadius': '5px', 'textAlign': 'center', 'margin': '10px' }, multiple=False), html.Div(id='import-modal-wrapper', style={'display': 'none'}) ]), html.Div(className='row', children=[ dcc.Tabs(id="tabs-styled-with-inline", parent_className='accounts-tabs-container', children=account_tabs) ]), html.Div(id='upload-status-container', hidden=True, children=[ dcc.Input(id='upload-status', type='hidden', value="no-info") ]) ])
def main_page(): log.info("Loading main page") return html.Div( className='height-100 container main-body', children=[ html.Div( className='row', children=[ html.Div( className='col-3', children=[ html.Div( className='card card-indicator', children=[ html.Div( className= 'card-body card-body-indicator', children=[ html.Span( 'Savings', className='card-body-title'), html.Span('{:,.2f} PLN'.format( total_money['Total'].sum()). replace(',', ' ')) ]) ]) ]), html.Div( className='col-3', children=[ html.Div( className='card card-indicator', children=[ html.Div( className= 'card-body card-body-indicator', children=[ html.Span( 'Debt', className='card-body-title'), html.Span( f'No Data', style={'color': '#A40E4C'}), ]) ]) ]), html.Div( className='col-3', children=[ html.Div( className='card card-indicator', children=[ html.Div( className= 'card-body card-body-indicator', children=[ html.Span( 'Last Month Profit', className='card-body-title'), html.Span( f'{last_month_income_sign} {last_month_income:,.2f} PLN', style={ 'color': last_month_income_color }), html.Span( last_month_str, style={'font-size': '0.4em'}) ]) ]) ]), html.Div( className='col-3', children=[ html.Div( className='card card-indicator', children=[ html.Div( className= 'card-body card-body-indicator', children=[ html.Span( 'Investments', className='card-body-title'), html.Span( f'No Data', style={'color': '#A40E4C'}), ]) ]) ]), ]), html.Div( className='row', children=[ html.Div( className='col-4', children=[ html.Div( className='card card-indicator', style={ 'height': '366px', 'width': '400px' }, children=[ html.Div( className='card-body card-body-plotly', children=[ html.Span( 'Savings Distribution', className='card-body-title', style={ 'margin-bottom': '40px' }), total_money_table(total_money) ]) ]) ]), html.Div( className="col-5", children=[ html.Div( className='card card-indicator', children=[ html.Div( className='card-body card-body-plotly', children=[ html.Span( 'Savings Distribution', className='card-body-title', style={ 'margin-bottom': '20px' }), html.Div(dcc.Graph( figure=total_money_pie( total_money)), style={'width': '400px'}) ]) ]) ]) ]), html.Div( className='row', style={'padding-bottom': '50px'}, children=[ html.Div( className='col-12', children=[ html.Div( className='card card-indicator', children=[ html.Div( className='card-body card-body-plotly', children=[ html.Span( 'Savings History', className='card-body-title'), html.Div(dcc.Graph( figure=total_money_chart( data['total'])), style={'width': '1200px'}) ]) ]) ]), ]), html.Div( className='row', style={'padding-bottom': '50px'}, children=[ html.Div( className='col-12', children=[ html.Div( className='card card-indicator', children=[ html.Div( className='card-body card-body-plotly', children=[ html.Span( 'Monthly Profit History', className='card-body-title'), html.Div(dcc.Graph( figure=total_monthly_bar( data['total_monthly'])), style={'width': '1200px'}) ]) ]) ]), ]) ])