Skip to content

Commit

Permalink
Something like this...
Browse files Browse the repository at this point in the history
  • Loading branch information
dbernar1 committed Dec 15, 2014
1 parent e3c27af commit 0dcbdd8
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 322 deletions.
41 changes: 41 additions & 0 deletions financial_common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from datetime import datetime
from dateutil.relativedelta import relativedelta

DATE = 'Date'
ASSETS = 'Assets'
LIABILITIES = 'Liabilities'
CAPITAL = 'Capital'
DUES = 'Dues'
DONATIONS = 'Donations'
FOOD_DONATIONS = 'Food donations'
MEMBERS = 'Members'
DONATING_MEMBERS = 'Donating members'
EXPENSES = 'Expenses'
FOOD_EXPENSES = 'Food expenses'
PROJECTED_CAPITAL = 'Projected capital'
PROJECTED_DUES = 'Projected dues'
PROJECTED_DONATIONS = 'Projected donations'
PROJECTED_MEMBERS = 'Projected members'
PROJECTED_DONATING_MEMBERS = 'Projected donating members'
PROJECTED_FOOD_DONATIONS = 'Projected food donations'
PROJECTED_FOOD_EXPENSES = 'Projected food expenses'
CAPITAL_TARGET = 'Target balance (3 month buffer)'

MONTH_START_DAY = 4

def figure_out_today_and_start_and_end_dates():
today = datetime.now().replace(second=0, microsecond=0)
if today.day < MONTH_START_DAY:
today -= relativedelta(months=+1)
delta = relativedelta(months=+6)
start = today - delta
end = today + delta

return [start, today, end]

def report_days(start_date, end_date):
delta = relativedelta(months=+1)
d = start_date.replace(day=MONTH_START_DAY)
while d < end_date.replace(day=MONTH_START_DAY):
d += delta
yield d
33 changes: 33 additions & 0 deletions financial_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import csv

import financial_common

def write_to_csv_the(financial_records):
fieldnames = [
DATE,
ASSETS,
LIABILITIES,
CAPITAL,
PROJECTED_CAPITAL,
DUES,
PROJECTED_DUES,
DONATIONS,
PROJECTED_DONATIONS,
MEMBERS,
PROJECTED_MEMBERS,
DONATING_MEMBERS,
PROJECTED_DONATING_MEMBERS,
EXPENSES,
FOOD_DONATIONS,
PROJECTED_FOOD_DONATIONS,
FOOD_EXPENSES,
PROJECTED_FOOD_EXPENSES,
CAPITAL_TARGET,
]

with open('foobar.csv', 'wb') as csvfile:
writer = csv.DictWriter(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL, fieldnames=fieldnames)

writer.writeheader()
for finances_for_a_month in financial_records:
writer.writerow(finances_for_a_month)
65 changes: 65 additions & 0 deletions financial_future.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from __future__ import print_function
from datetime import datetime

import financial_common

def get_future_projections_based_on( historical_records ):
future_projections = []

monthly_amount_projections = get_monthly_amount_projections_based_on( historical_records )

start, today, end = figure_out_today_and_start_and_end_dates()
for month in report_days(today, end):
starting_capital = future_projections[-1][PROJECTED_CAPITAL] if len(future_projections) else historical_records[-1][CAPITAL]

future_projections.append( project_finances_for( month, starting_capital, finances_for_current_month, monthly_amount_projections ) )

return future_projections

def project_finances_for( month, starting_capital, finances_for_current_month, monthly_amount_projections ):
print(month)

return {
# same for each projected month
DATE: month,
PROJECTED_DUES: finances_for_current_month[DUES],
PROJECTED_DONATIONS: finances_for_current_month[DONATIONS],
PROJECTED_MEMBERS: finances_for_current_month[MEMBERS],
PROJECTED_DONATING_MEMBERS: finances_for_current_month[DONATING_MEMBERS],
PROJECTED_FOOD_DONATIONS: monthly_amount_projections[ 'food income' ],
PROJECTED_FOOD_EXPENSES: monthly_amount_projections[ 'food expenses' ],

# differs in each month
PROJECTED_CAPITAL: starting_capital + monthly_amount_projections['income'] + monthly_amount_projections['expenses'],

# two different values - before & after rent increase
CAPITAL_TARGET: (monthly_amount_projections['expenses'] - rent_increase_for( month )) * -3,
}

def get_monthly_amount_projections_based_on( historical_records ):
current_month_amounts = historical_records[-1]

monthly_amount_projections['income'] = current_month_amounts[DUES] + current_month_amounts[DONATIONS]
monthly_amount_projections['expenses'] = get_average( EXPENSES, historical_records )
monthly_amount_projections['food income'] = get_average( FOOD_DONATIONS, historical_records )
monthly_amount_projections['food expenses'] = get_average( FOOD_EXPENSES, historical_records )

print( "Projected income: ", monthly_amount_projections['income'] )
print( "Projected expenses: ", monthly_amount_projections['expenses'] )

return monthly_amount_projections

def rent_increase_for( month ):
february_2015 = datetime(2015, 02, 01):

if month >= february_2015
rent_increase = 100
else
rent_increase = 0

return rent_increase

def get_average( datum_key, financial_records ):
total = sum( record[datum_key] for record in financial_records )

return total / len( financial_records )
147 changes: 147 additions & 0 deletions financial_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import calendar
import gnucashxml
import sys
from datetime import datetime
from __future__ import print_function

import financial_common

def get_historical_records():
historical_records = []

book = get_requested_financial_book()

start, today, end = figure_out_today_and_start_and_end_dates()
for month in report_days(start, today):
monthly_finances = book.finances_for(month, book)
display(monthly_finances)
historical_records.append(monthly_finances)

def get_requested_financial_book():
provided_by_user = sys.argv[1]
return Book( filename = gnucashxml.from_filename(provided_by_user) )

class Book:
def __init__( self, gnu_cash_book ):
self.book = gnu_cash_book

def finances_for( self, month, book ):
assets = self.get_assets_on( date = month )
liabilities = self.get_liability_on( date = month )
expenses = self.get_expenses_for( month )

return {
DATE: month,
ASSETS: assets,
LIABILITIES: liabilities,
CAPITAL: assets + liabilities,
DUES: self.get_dues_for( month ),
DONATIONS: self.get_donations_for( month ),
FOOD_DONATIONS: self.get_food_donations_for( month ),
MEMBERS: self.get_num_paying_members_in( month ),
DONATING_MEMBERS: self.get_num_donating_members_in( month ),
EXPENSES: expenses,
FOOD_EXPENSES: self.get_food_expenses_for( month ),
CAPITAL_TARGET: expenses * -3,
}

def get_assets_on( self, date ):
assets = self.book.find_account("Current Assets")

asset_total = 0
for account in assets.children:
if account.name != "Prepaid Rent":
asset_total += sum(split.value for split in account.splits if split.transaction.date.replace(tzinfo=None) <= date)

return asset_total

def get_liability_on( self, date ):
liability = self.get_total( "Active Members", date ) + self.get_total( "Former Members", date )

return liability

def get_total( self, account_name, date ):
account = self.book.find_account( account_name )
total = sum( split.value for split in account.get_all_splits() if split.transaction.date.replace(tzinfo=None) <= date)

return total

def get_expenses_for( self, month_end ):
end_date = month_end
start_date = subtract_month(month_end)

expense_accounts = self.book.find_account("Expenses")
expenses = 0
for account in expense_accounts.children:
if account.name != "Anti-social 10-04" and account.name != "Groceries":
for split in account.splits:
if start_date < split.transaction.date.replace(tzinfo=None) <= end_date:
expenses += split.value

for subaccount in account.children:
for split in subaccount.splits:
if start_date < split.transaction.date.replace(tzinfo=None) <= end_date:
expenses += split.value


return expenses * -1

def get_dues_for( self, month_end ):
return self.get_monthly_amount_of( "Member Dues", month_end )

def get_food_expenses_for_month( self, month_end ):
return self.get_monthly_amount_of( "Groceries", month_end )

def get_food_donations_for_month( self, month_end ):
return self.get_monthly_amount_of( "Food and Drink Donations", month_end )

def get_donations_for( self, month_end ):
return self.get_monthly_amount_of( "Regular donations", month_end )

def get_donating_members( self, month_end ):
return self.get_monthly_number_of( "Regular donations", month_end )

def get_paying_members( self, month_end ):
return self.get_monthly_number_of( "Member Dues", book, month_end )

def get_monthly_number_of( self, account_name, month_end ):
return sum( len( entry.transaction.splits ) - 1 for entry in self.get_entries_for( account_name, month_end ) )

def get_monthly_amount_of( self, account_name, month_end ):
return sum( entry.value for entry in self.get_entries_for( account_name, month_end ) ) * -1

def get_entries_for( self, account_name, month_end ):
end_date = month_end
start_date = subtract_month(month_end)

account = self.book.find_account( account_name )

entries = []

for split in account.get_all_splits():
if start_date < split.transaction.date.replace(tzinfo=None) <= end_date:
entries.add( split )

return entries

def subtract_month(date):
month = date.month - 2
month = month % 12 + 1
year = date.year - 1/12
day = min(date.day, calendar.monthrange(year, month)[1])
return datetime(year, month, day)

def display( monthly_finances ):
print(monthly_finances[ DATE ])
print("Total assets: ", monthly_finances[ ASSETS ])
print("Total liability: ", monthly_finances[ LIABILITIES ])
print("Available capital: ", monthly_finances[ CAPITAL ])
print("Dues collected last month: ", monthly_finances[ DUES ])
print("Dues paying members: ", monthly_finances[ MEMBERS ])
print("Regular donations collected last month: ", monthly_finances[ DONATIONS ])
print("Regularly donating members: ", monthly_finances[ DONATING_MEMBERS ])
print("Food donations: ", monthly_finances[ FOOD_DONATIONS ])
print("Total expected income: ", (monthly_finances[ DUES ] + monthly_finances[ DONATIONS ] + monthly_finances[ FOOD_DONATIONS ]))
print("Expenses: ", monthly_finances[ EXPENSES ])
print("Food expenses: ", monthly_finances[ FOOD_EXPENSES ])
print()
Loading

0 comments on commit 0dcbdd8

Please sign in to comment.