from django.template import Context, loader, RequestContext from django.db.models import Q from datetime import datetime from datetime import date from datetime import time from my_beancounter.models import * from django.http import HttpResponse from django.shortcuts import render_to_response from django.contrib.auth.decorators import login_required # File: strptime.py import re import string MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] SPEC = { # map formatting code to a regular expression fragment "%a": "(?P[a-z]+)", "%A": "(?P[a-z]+)", "%b": "(?P[a-z]+)", "%B": "(?P[a-z]+)", "%C": "(?P\d\d?)", "%d": "(?P\d\d?)", "%D": "(?P\d\d?)/(?P\d\d?)/(?P\d\d)", "%e": "(?P\d\d?)", "%h": "(?P[a-z]+)", "%H": "(?P\d\d?)", "%I": "(?P\d\d?)", "%j": "(?P\d\d?\d?)", "%m": "(?P\d\d?)", "%M": "(?P\d\d?)", "%p": "(?Pam|pm)", "%R": "(?P\d\d?):(?P\d\d?)", "%S": "(?P\d\d?)", "%T": "(?P\d\d?):(?P\d\d?):(?P\d\d?)", "%U": "(?P\d\d)", "%w": "(?P\d)", "%W": "(?P\d\d)", "%y": "(?P\d\d)", "%Y": "(?P\d\d\d\d)", "%%": "%" } class TimeParser: def __init__(self, format): # convert strptime format string to regular expression format = string.join(re.split("(?:\s|%t|%n)+", format)) pattern = [] try: for spec in re.findall("%\w|%%|.", format): if spec[0] == "%": spec = SPEC[spec] pattern.append(spec) except KeyError: raise ValueError, "unknown specificer: %s" % spec self.pattern = re.compile("(?i)" + string.join(pattern, "")) def match(self, daytime): # match time string match = self.pattern.match(daytime) if not match: raise ValueError, "format mismatch" get = match.groupdict().get tm = [0] * 9 # extract date elements y = get("year") if y: y = int(y) if y < 68: y = 2000 + y elif y < 100: y = 1900 + y tm[0] = y m = get("month") if m: if m in MONTHS: m = MONTHS.index(m) + 1 tm[1] = int(m) d = get("day") if d: tm[2] = int(d) # extract time elements h = get("hour") if h: tm[3] = int(h) else: h = get("hour12") if h: h = int(h) if string.lower(get("ampm12", "")) == "pm": h = h + 12 tm[3] = h m = get("minute") if m: tm[4] = int(m) s = get("second") if s: tm[5] = int(s) # ignore weekday/yearday for now return tuple(tm) def strptime(string, format="%a %b %d %H:%M:%S %Y"): return TimeParser(format).match(string) if __name__ == "__main__": # try it out import time print strptime("2000-12-20 01:02:03", "%Y-%m-%d %H:%M:%S") print strptime(time.ctime(time.time())) def htmlDates(theMonth,theYear): """Build HTML Select dropdowns for month and year""" m = range(1,13) month = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] monthOptions = '' monthList = zip(m,month) for [num,mon] in monthList: if num == int(theMonth): monthOptions += '\n' % (num,mon) else: monthOptions += '\n' % (num,mon) this_year = datetime.now().year years = range(this_year - 10,this_year + 1) yearOptions = '' for y in years: if y == int(theYear): yearOptions += '' % (y,y) else: yearOptions += '' % (y,y) return (monthOptions,yearOptions) def buildDates(theMonth,theYear): """Return datetime objects for the start and end of the month. If month is 0 return start and end of year""" if theMonth == 0: reportStart = datetime(*strptime(str(theYear) + '1' + '1','%Y%m%d')[0:5]) reportEnd = datetime(*strptime(str(theYear) + '12' + '31','%Y%m%d')[0:5]) else: reportStart = datetime(*strptime(str(theYear) + str(theMonth) + '1','%Y%m%d')[0:5]) if theMonth == 12: theYear = theYear + 1 else: theMonth = theMonth + 1 reportEnd = datetime(*strptime(str(theYear) + str(theMonth) + '1','%Y%m%d')[0:5]) return (reportStart,reportEnd) def sortby(nlist, n): nlist[:] = [(x[n], x) for x in nlist] nlist.sort(reverse=True) nlist[:] = [val for (key, val) in nlist] def detailCategory(theMonth,theYear,catType): """Get list of each category total for a given category type.""" allCats = Category.objects.filter(type__exact=catType) total = 0.00 catTotals = [] catLabels = "" (report_start,report_end) = buildDates(theMonth,theYear) for c in allCats: currentTotal = Entry.objects.filter(date__gte = report_start) & Entry.objects.filter(date__lt = report_end) & Entry.objects.filter(category__id=c.id) catSum = 0.00 for e in currentTotal: catSum = catSum + float(e.amount) catLabels += '{label: "' + c.name + '", v: ' + str(c.id) + '}, ' catTotals += [catSum] catList = zip(allCats,catTotals) sortby(catList,1) total = total + catSum catLabels = catLabels.rstrip(', ') return (catLabels,catList,total) @login_required def overview(request): try: theMonth = int(request.GET['month']) theYear = int(request.GET['year']) except (KeyError): theMonth = datetime.now().month theYear = datetime.now().year (monthOptions,yearOptions) = htmlDates(theMonth,theYear) (income_labels,income_list,income) = detailCategory(theMonth,theYear,"INC") (cogs_labels,cogs_list,cogs) = detailCategory(theMonth,theYear,"COGS") (expense_labels,expense_list,expense) = detailCategory(theMonth,theYear,"EXP") cogs_plus_expense = cogs + expense net = "%.2f" % (income-cogs_plus_expense) title = "Overview" return render_to_response('overview.html', locals(),context_instance=RequestContext(request)) @login_required def history(request): expense_cats = Category.objects.filter(type__exact='EXP') expense_list = {} months = [] total = amount = 0.00 this_id = -1 start = True expenses = Entry.objects.filter(Q(category__type = 'EXP') | Q(category__type = 'COGS')).order_by('date') for e in expenses: month = str(e.date.month) + '/' + str(e.date.year) id = int(str(e.date.month) + str(e.date.year)) if id != this_id: if start == True: start = False else: expense_list[this_id] = [this_month,amount,0] months.append(this_id) this_id = id this_month = month amount = 0 amount += float(e.amount) total += float(e.amount) expense_list[this_id] = [this_month,amount,0] months.append(this_id) income_list = [] total = 0.00 amount = 0.00 this_id = -1 start = True incomes = Entry.objects.filter(category__type = 'INC').order_by('date') for e in incomes: month = str(e.date.month) + '/' + str(e.date.year) id = int(str(e.date.month) + str(e.date.year)) if id != this_id: if start == True: start = False else: expense_list[this_id][2] = amount if this_id in months: i = 0 else: months.append(id) this_id = id this_month = month amount = 0 amount += float(e.amount) total += float(e.amount) expense_list[this_id][2] = amount if this_id in months: i = 0 else: months.append(id) months.sort(reverse=True) history_labels = '' for m in months: history_labels += '{label: "' + expense_list[m][0] + '", v: ' + str(m) + '}, ' history_labels.rstrip(', ') title = 'History' return render_to_response('history.html',locals(),context_instance=RequestContext(request)) @login_required def incomeVsCost(request): try: theMonth = int(request.GET['month']) theYear = int(request.GET['year']) except (KeyError): theMonth = datetime.now().month theYear = datetime.now().year (monthOptions,yearOptions) = htmlDates(theMonth,theYear) (report_start,report_end) = buildDates(theMonth,theYear) cogsCats = Category.objects.filter(type__exact='COGS') total = 0.00 linkNumber = 0 Totals = [] cTotals = [] iTotals = [] iCats = [] cogsLabels = "" for c in cogsCats: cogsEntries = Entry.objects.filter(date__gte = report_start) & Entry.objects.filter(date__lt = report_end) & Entry.objects.filter(category__id=c.id) cSum = 0.00 #total for COGS category for e in cogsEntries: cSum = cSum + float(e.amount) cTotals += [cSum] iSum = 0.00 incomeEntries = Entry.objects.filter(date__gte = report_start) & Entry.objects.filter(date__lt = report_end) & Entry.objects.filter(category__id=c.income.id) #total for associated Income category iCats += [c.income] for i in incomeEntries: iSum = iSum + float(i.amount) iTotals += [iSum] Totals += [iSum-cSum] linkNumber = linkNumber +1 lN = range(7) cogsList = zip(lN,iCats,iTotals,cTotals,Totals) sortby(cogsList,4) h = 0 for i in cogsList: cogsLabels += '{label: "' + i[1].name + '", v: ' + str(i[0]) + '}, ' cogsLabels = cogsLabels.rstrip(', ') return render_to_response('incomevscost.html',locals(),context_instance=RequestContext(request)) @login_required def moneyInMoneyOut(request): try: theMonth = int(request.GET['month']) theYear = int(request.GET['year']) except (KeyError): theMonth = datetime.now().month theYear = datetime.now().year (monthOptions,yearOptions) = htmlDates(theMonth,theYear) (report_start,report_end) = buildDates(theMonth,theYear) peeps = Person.objects.filter() ipeepsList = [] incomeList = [] epeepsList = [] expenseList = [] iLabels = "" eLabels = "" for p in peeps: iSum = 0.00 incomeEntries = Entry.objects.filter(date__gte = report_start) & Entry.objects.filter(date__lt = report_end) & Entry.objects.filter(category__type = 'INC') & Entry.objects.filter(name = p) for i in incomeEntries: iSum = iSum + float(i.amount) if iSum > 0: iLabels += '{label: "' + p.name + '", v: ' + str(p.id) + '}, ' ipeepsList += [p] incomeList += [iSum] eSum = 0.00 expenseEntries = Entry.objects.filter(date__gte = report_start) & Entry.objects.filter(date__lt = report_end) & Entry.objects.filter(Q(category__type = 'EXP') | Q(category__type = 'COGS')) & Entry.objects.filter(name = p) for e in expenseEntries: eSum = eSum + float(e.amount) if eSum > 0: eLabels += '{label: "' + p.name + '", v: ' + str(p.id) + '}, ' epeepsList += [p] expenseList += [eSum] incomeMash = zip(ipeepsList,incomeList) sortby(incomeMash,1) expenseMash = zip(epeepsList,expenseList) sortby(expenseMash,1) return render_to_response('moneyin-moneyout.html',locals(),context_instance=RequestContext(request)) @login_required def balance(request): """Track account balances over time.""" # #Not fully implemented yet. #Also needs different graphing library as Plotkit can't do negative numbers. # accounts = BankAccount.objects.filter(track_balance__exact=True) tally_labels = [] tally = [] this_id = -1 this_account = 0 for a in accounts: this_tally = [] this_tally_labels = "" entries = Entry.objects.filter(bank_account__exact=a).order_by('date') transfers = AccountTransfer.objects.filter(bank_account__exact=a).order_by('date') total = a.initial_balance n = 0 start = True for e in entries: if start: this_date = e.date start = False if this_date != e.date: this_tally.append([n,this_date,total]) this_date = e.date this_tally_labels += '{label: "' + str(e.date) + '", v: ' + str(n) + '}, ' n = n+1 if e.category.type == 'COGS' or e.category.type == 'EXP': amount = -e.amount else: amount = e.amount total = total + amount this_tally.append([n,this_date,total]) tally += [this_tally] this_date = e.date this_tally_labels += '{label: "' + str(e.date) + '", v: ' + str(n) + '}' tally_labels += [this_tally_labels] title = 'Account Balances' return render_to_response('account-balances.html',locals(),context_instance=RequestContext(request))