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 getPaymentHistory(datastore, loan, asOfDate): pass def getAmortization(datastore, loan, paymentHistory): pass def loadLoanInformation(filename): datastore = getDatastore(filename) loanModel = {} loanModel['loan'] = getLoan(datastore) loanModel['lender'] = getLender(datastore) loanModel['borrower'] = getBorrower(datastore) loanModel['paymentHistory'] = getPaymentHistory(datastore, loan, asOfDate) loanModel['futureAmortization'] = getAmortization(datastore, loan, loanModel.paymentHistory) return loanModel def getLoan(datastore): # read in the loan profile information loan = {} 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): lender = {} # 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): borrower = {} # 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=loanModel.principal, future_payments=loanModel.future_payments, past_payments=loanModel.past_payments, balloon_payment=loanModel.remaining_principal, total_interest_paid_to_date=loanModel.total_interest, statement=loanModel.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 = loanModel.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 = loanModel.principal payment_day_of_month = loanInformation.payment_day_of_month daily_interest_rate = loanInformation.daily_interest_rate total_periods = loanInformation.total_periods 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) # 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()