Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Something like this... #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
68 changes: 68 additions & 0 deletions financial_future.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
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 )

for month in months_to_project():
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 months_to_project():
start, today, end = figure_out_today_and_start_and_end_dates()
return report_days(today, end)

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 )
136 changes: 136 additions & 0 deletions financial_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
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()

for month in months_to_report_on():
historical_records.append( book.finances_for( month ) )

return historical_records

def get_requested_financial_book():
provided_by_user = sys.argv[1]
return Book( gnucash_filename = provided_by_user )

def months_to_report_on():
start, today, end = figure_out_today_and_start_and_end_dates()
return report_days( start, today )

class Book:
def __init__( self, gnucash_filename ):
self.book = gnucashxml.from_filename( gnucash_filename )

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 )

monthly_finances = {
DATE: month,
ASSETS: assets,
LIABILITIES: liabilities,
CAPITAL: assets + liabilities,
DUES: self.get_amount_of( "Member Dues", month ),
DONATIONS: self.get_amount_of( "Regular donations", month ),
FOOD_DONATIONS: self.get_amount_of( "Food and Drink Donations", month ),
MEMBERS: self.get_number_of( "Member Dues", month ),
DONATING_MEMBERS: self.get_number_of( "Regular donations", month ),
EXPENSES: expenses,
FOOD_EXPENSES: self.get_amount_of( "Groceries", month ),
CAPITAL_TARGET: expenses * -3,
}

display( monthly_finances )

return monthly_finances

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_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_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