import json from decimal import * from datetime import * from jinja2 import Environment, FileSystemLoader from fpdf import FPDF, HTMLMixin import smtplib from email.MIMEMultipart import MIMEMultipart from email.MIMEText import MIMEText from email.MIMEBase import MIMEBase from email import Encoders def loadLoanInformation(filename): datastore = getDataStore(filename) loanModel['loan'] = getLoan(dataStore) loanModel['lender'] = getLender(datastore) loanModel['borrower'] = getBorrower(datastore) loanModel['paymentHistory'] = getPaymentHistory(datastore, loan, asOfDate) loanModel['futureAmortization'] = getAmortization(datastore, loan, paymentHistory) return loanModel def getLoan(datastore): # read in the loan profile information annual_rate = Decimal(datastore['loan.interest rate']) / 100 daily_interest_rate = annual_rate / 360 principal = Decimal(datastore["loan.principal"]).quantize(Decimal("1.00")) periods_per_year = Decimal(datastore["loan.periods per year"]) total_periods = Decimal(datastore["loan.periods"]) payment_day_of_month = int(datastore['loan.payment day of month']) if "monthly_payment" in datastore: monthly_payment = Decimal(datastore["monthly_payment"]).quantize(Decimal("1.00")) else: # calculate expected monthly payment periodic_rate = annual_rate / periods_per_year discount_factor = (((1 + periodic_rate) ** total_periods) - 1) / ( periodic_rate * ((1 + periodic_rate) ** total_periods)) monthly_payment = (principal / discount_factor).quantize(Decimal("1.00")) loan['loan.account_number'] = '123456789' loan['principal'] = principal loan['term'] = total_periods loan['annual_rate'] = annual_rate loan['daily_interest_rate'] = daily_interest_rate loan['rate'] = '' + (annual_rate * 100).__str__() + '%' loan['next_payment_amt'] = 0 loan['next_payment_date'] = '12/12/12' return loan def getLender(datastore): #to be replaced with real loading code lender['name'] = 'Rivanna Graphite Investments, LLC' lender['phone'] = '703.343.0782' lender['address'] = '743 Madison St NW' lender['city'] = 'Washington' lender['state'] = 'DC' lender['zip'] = '20011' return getLender def getBorrower(datastore): #to be replaced with real loading code borrower['name'] = 'Bear Houses, LLC' borrower['address'] = '123 Any Street' borrower['city'] = 'Alltown' borrower['state'] = 'VA' borrower['zip'] = '11111' return borrower def getDataStore(filename=None): try: if filename: with open(filename, 'r') as f: datastore = json.load(f) except Exception as e: print "An error occurred opening your loan file '#s'. " % filename print "The Exception:" print e.__repr__() quit() return datastore def calculateLoanAmortization(loanModel): return loanModel def transformTemplate(template_fileName, loanModel): # template_filename = "statement.txt.jinja" # setup jinja for creating the statement env = Environment(loader=FileSystemLoader('.')) template = env.get_template(template_fileName) report = template.render(original_principal_balance=principal, future_payments=future_payments, \ past_payments=past_payments, balloon_payment=remaining_principal, \ total_interest_paid_to_date=total_interest, statement=statement) return report def generatePDFStatement(): template_filename = "statement.pdf.jinja" pass def generateHTMLStatement(): pass def generateTextStatement(): pass def generateEmail(from_address, to_address, subject, body, attachment): msg = MIMEMultipart() msg['Subject'] = subject msg['From'] = from_address msg['To'] = to_address msg.attach(MIMEText(body)) part = MIMEBase('application', "octet-stream") part.set_payload(attachment) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="statement.pdf"') msg.attach(part) return msg def main(): # this program reads the json file describing a mortgage and calculate the actual and projected amortization # the first payment in the file should be the interest paid at closing # the interest at closing covers interest that would be incurred from the date of closing until the first day # that will be covered by the first loan payment # i.e., the first loan payment is expected a month and a half after closing, the first two weeks interest is due # at closing. The first payment will incur interest from one month before the bill is due. from_address = 'jkent3rd@gmail.com' passwd = "pvyrbcnzrjoizprn" subject = "Mortgage or Loan Statement" to_address = 'jkent3rd@gmail.com' body = 'This email contains a PDF of your mortgage statement.' # read in the file # filename = "./10Kloan.txt" # filename = "./10Kloan.test.txt" # filename = "./dadmortgage.txt" # filename = "./brendamortgage.txt" filename = "./greenfield_mortgage.txt" template_filename = "statement.pdf.jinja" loanInformation = loadLoanInformation(filename) loanModel = calculateLoanAmortization(loanInformation) # read in the statement information statement, lender, borrower, loan = {}, {}, {}, {} lender = loanModel.lender borrower = loanModel.borrower past_payments = loadModel.past_payments future_payments = loanModel.future_payments statement['title'] = "Mortgage Statement - 185 James River Rd" statement['date'] = "Today" statement['lender'] = lender statement['borrower'] = borrower statement['loan'] = loan # loop over the payments and calculate the actual amortization actual_payments = loanModel["payments"] remaining_principal = principal annual_interest = 0 total_interest = 0 interest_paid_through_date = datetime.strptime(loanModel["start_interest_date"], "%Y-%m-%d").date() next_payment_date = datetime.strptime(loanModel["first payment month"], '%Y-%m-%d').date() next_bill_date = date(year=next_payment_date.year, month=next_payment_date.month, day=payment_day_of_month) old_bill_date = next_bill_date payment_number = 1 current_year = next_bill_date.year for payment in actual_payments: payment_date = datetime.strptime((payment[0]), '%Y-%m-%d').date() payment_amount = Decimal(payment[1]).quantize(Decimal("1.00")) days_since_last_payment = (payment_date - interest_paid_through_date).days new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) new_principal = payment_amount - new_interest interest_paid_through_date = payment_date total_interest = total_interest + new_interest annual_interest = annual_interest + new_interest remaining_principal = remaining_principal - new_principal # create the payment record for the template to render payment_record = {} payment_record['payment_number'] = payment_number payment_record['bill_date'] = next_bill_date payment_record['payment_date'] = payment_date payment_record['days_of_interest'] = days_since_last_payment payment_record['payment_amount'] = payment_amount payment_record['principal_payment'] = payment_amount - new_interest payment_record['interest_payment'] = new_interest payment_record['new_balance'] = remaining_principal payment_record['interest_to_date'] = total_interest payment_record['annual_interest_to_date'] = annual_interest past_payments.append(payment_record) payment_number = payment_number + 1 old_bill_date = next_bill_date if old_bill_date.month < 12: next_bill_date = date(year=old_bill_date.year, month=old_bill_date.month + 1, day=payment_day_of_month) else: annual_interest_record['year'] = old_bill_date.year annual_interest_record['interest'] = annual_interest past_interest.append(annual_interest_record) next_bill_date = date(year=old_bill_date.year + 1, month=1, day=payment_day_of_month) payment_record['print_interest_total'] = True payment_record[ 'interest_total_message'] = "Total interest for " + old_bill_date.year.__str__() + " was $" + annual_interest.__str__() annual_interest = 0 if old_bill_date.month < 12: payment_record['print_interest_total'] = True payment_record[ 'interest_total_message'] = "Total interest to date for " + old_bill_date.year.__str__() + " is $" + annual_interest.__str__() # loop over remaining scheduled payments and present estimated amortization while (payment_number <= total_periods) and (remaining_principal > 0): days_since_last_payment = (next_bill_date - interest_paid_through_date).days new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00")) # make sure the last payment isn't too much if new_interest + remaining_principal < monthly_payment: monthly_payment = new_interest + remaining_principal new_principal = monthly_payment - new_interest remaining_principal = remaining_principal - new_principal interest_paid_through_date = next_bill_date # complete the future payment amortization record future_payment_record = {} future_payment_record['payment_number'] = payment_number future_payment_record['payment_date'] = next_bill_date future_payment_record['days_of_interest'] = days_since_last_payment future_payment_record['payment_amount'] = monthly_payment future_payment_record['principal_payment'] = new_interest future_payment_record['interest_payment'] = new_interest future_payment_record['new_balance'] = remaining_principal future_payments.append(future_payment_record) payment_number = payment_number + 1 old_bill_date = next_bill_date if old_bill_date.month < 12: next_bill_date = date(year=old_bill_date.year, month=old_bill_date.month + 1, day=payment_day_of_month) else: next_bill_date = date(year=old_bill_date.year + 1, month=1, day=payment_day_of_month) report = transformTemplate('',loanModel) # create pdf class MyFPDF(FPDF, HTMLMixin): pass pdf = MyFPDF() pdf.set_font(family='Arial', size=12) pdf.add_page() pdf.write_html(report) # pdf.output(name='test.pdf', dest='F') attachment = pdf.output(dest='S') # send email try: server = smtplib.SMTP('smtp.gmail.com', 587) server.ehlo() server.starttls() server.login(from_address, passwd) email = generateEmail(from_address, to_address, subject, body, attachment) server.sendmail(from_address, to_address, email.as_string()) server.close() except: print "Couldn't send email, dumping statement to file." pdf.output(name='test.pdf', dest='F') quit() if __name__ == '__main__': main()