File: //home/arjun/projects/buyercall_new/buyercall/buyercall/blueprints/form_leads/form_tasks.py
import logging as log
import traceback
import datetime
import os.path
import redis
from buyercall.lib.util_crypto import AESCipher
from flask import current_app, render_template, json
from buyercall.app import create_celery_app
from sqlalchemy import and_
from buyercall.extensions import db
from flask_weasyprint import HTML, render_pdf
from buyercall.blueprints.agents import Agent
from buyercall.blueprints.contacts.models import Contact, Campaigns, ContactVehicle
from buyercall.blueprints.form_leads.models import (FormLead, ExternalForm,
FormLeadField, ExternalFormField)
from buyercall.blueprints.filters import format_phone_number
from buyercall.lib.util_ses_email import send_ses_email
from buyercall.blueprints.sms.views import send_text_message
celery = create_celery_app(current_app)
def decrypt_value(text):
crypto_key = current_app.config['CRYPTO_SECRET_KEY']
cipher = AESCipher(crypto_key)
if text is not None:
try:
return cipher.decrypt(text)
except TypeError:
return text
except ValueError:
return text
else:
return text
@celery.task
def add_form_contact(form_lead_id, first_name, last_name, email_address, phone_number,
address_1, address_2, city, state, zip, country, rep, interest_vehicle_year, interest_vehicle_make,
interest_vehicle_model, interest_vehicle_vin, interest_vehicle_trim, interest_vehicle_stock_no,
interest_vehicle_price, interest_vehicle_mileage, interest_vehicle_condition,
interest_vehicle_listing_url, tradein_vehicle_year, tradein_vehicle_make, tradein_vehicle_model,
tradein_vehicle_vin, tradein_vehicle_price, tradein_vehicle_mileage, tradein_vehicle_condition,
partnership_account_id, updated_on, dob, eq_lead=False, campaign=None,
**kwargs):
latest_lead = FormLead.query.filter(FormLead.id == form_lead_id)\
.order_by(FormLead.created_on.desc()).first()
phone = format_phone_number(phone_number)
# Add campaign id if available
campaign_id = None
if campaign:
campaign_id = Campaigns.add_campaign(campaign, partnership_account_id)
contact = Contact.query\
.filter(Contact.phonenumber_1 == phone)\
.filter(Contact.partnership_account_id == partnership_account_id)\
.first()
if contact:
log.info('The contact number is {}'.format(contact.phonenumber_1))
contact.updated_on = updated_on
contact.partnership_account_id = partnership_account_id
if first_name:
contact.firstname = first_name
if last_name:
contact.lastname = last_name
if email_address:
contact.email = email_address
if address_1:
contact.address_1 = address_1
if address_2:
contact.address_2 = address_2
if city:
contact.city = city
if state:
contact.state = state
if zip:
contact.zip = zip
if country:
contact.country = country
old_agent = contact.agent_id
if not contact.agent_assigned:
contact.agent_assigned = rep
agent_id = Agent.reverse_agent_id_lookup(contact.partnership_account_id, rep)
if agent_id:
contact.agent_id = agent_id
if dob:
contact.birthday = dob
if eq_lead:
contact.is_eq_lead = eq_lead
if campaign_id:
contact.campaign_id = campaign_id
db.session.commit()
if contact.agent_id != old_agent:
from buyercall.blueprints.mobile.utils import send_agent_push_notification
send_agent_push_notification(contact)
else:
try:
contact = Contact(
firstname=first_name,
lastname=last_name,
phonenumber_1=phone,
email=email_address,
address_1=address_1,
address_2=address_2,
birthday=dob,
city=city,
state=state,
zip=zip,
agent_assigned=rep,
partnership_account_id=partnership_account_id,
is_eq_lead=eq_lead,
campaign_id=campaign_id
)
except Exception as e:
log.error(e)
agent_id = Agent.reverse_agent_id_lookup(contact.partnership_account_id, rep)
if agent_id:
contact.agent_id = agent_id
db.session.add(contact)
db.session.flush()
db.session.commit()
log.info('The contact has been added')
from buyercall.blueprints.mobile.utils import send_agent_push_notification
send_agent_push_notification(contact)
new_contact = Contact \
.query \
.filter(Contact.phonenumber_1 == phone) \
.filter(Contact.partnership_account_id == partnership_account_id) \
.first()
try:
if new_contact:
latest_lead.contact_id = new_contact.id
db.session.commit()
contact_vehicle = ContactVehicle.query\
.filter(ContactVehicle.contact_id == new_contact.id).first()
if contact_vehicle:
if interest_vehicle_year:
contact_vehicle.interest_year = interest_vehicle_year
if interest_vehicle_make:
contact_vehicle.interest_make = interest_vehicle_make
if interest_vehicle_model:
contact_vehicle.interest_model = interest_vehicle_model
if interest_vehicle_vin:
contact_vehicle.interest_vin = interest_vehicle_vin
if interest_vehicle_trim:
contact_vehicle.interest_trim = interest_vehicle_trim
if interest_vehicle_stock_no:
contact_vehicle.interest_stock = interest_vehicle_stock_no
if interest_vehicle_price:
if type(interest_vehicle_price) == str:
contact_vehicle.interest_price = interest_vehicle_price.replace(' ', '').replace(',', '')
else:
contact_vehicle.interest_price = interest_vehicle_price
if interest_vehicle_mileage:
contact_vehicle.interest_mileage = interest_vehicle_mileage
if interest_vehicle_condition:
contact_vehicle.interest_condition = interest_vehicle_condition
if interest_vehicle_listing_url:
contact_vehicle.interest_listing_url = interest_vehicle_listing_url
if tradein_vehicle_year:
contact_vehicle.current_year = tradein_vehicle_year
if tradein_vehicle_make:
contact_vehicle.current_make = tradein_vehicle_make
if tradein_vehicle_model:
contact_vehicle.current_model = tradein_vehicle_model
if tradein_vehicle_vin:
contact_vehicle.current_vin = tradein_vehicle_vin
if tradein_vehicle_price:
if type(tradein_vehicle_price) == str:
contact_vehicle.current_value = tradein_vehicle_price.replace(' ', '').replace(',', '')
else:
contact_vehicle.current_value = tradein_vehicle_price
if tradein_vehicle_mileage:
contact_vehicle.current_mileage = tradein_vehicle_mileage
if tradein_vehicle_condition:
contact_vehicle.current_condition = tradein_vehicle_condition
db.session.commit()
else:
if interest_vehicle_year or interest_vehicle_make or interest_vehicle_model or interest_vehicle_vin or \
interest_vehicle_trim or interest_vehicle_stock_no or interest_vehicle_price or \
interest_vehicle_mileage or tradein_vehicle_year or tradein_vehicle_make or \
tradein_vehicle_model or tradein_vehicle_vin or tradein_vehicle_mileage or \
tradein_vehicle_price:
in_v_price = None
if interest_vehicle_price:
in_v_price = interest_vehicle_price.replace(' ', '').replace(',', '')
ti_veh_vin = ''
if tradein_vehicle_vin:
ti_veh_vin = tradein_vehicle_vin
ti_veh_make = ''
if tradein_vehicle_make:
ti_veh_make = tradein_vehicle_make
ti_veh_model = ''
if tradein_vehicle_model:
ti_veh_model = tradein_vehicle_model
ti_veh_year = None
if tradein_vehicle_year:
ti_veh_year = tradein_vehicle_year
ti_veh_price = None
if tradein_vehicle_price:
ti_veh_price = tradein_vehicle_price.replace(' ', '').replace(',', '')
ti_veh_mileage = None
if tradein_vehicle_mileage:
ti_veh_mileage = tradein_vehicle_mileage
ti_veh_condition = ''
if tradein_vehicle_condition:
ti_veh_condition = tradein_vehicle_condition
in_v_vin = ''
if interest_vehicle_vin:
in_v_vin = interest_vehicle_vin
in_v_mileage = None
if interest_vehicle_mileage:
in_v_mileage = interest_vehicle_mileage
in_v_year = None
if interest_vehicle_year:
in_v_year = interest_vehicle_year
in_v_make = ''
if interest_vehicle_make:
in_v_make = interest_vehicle_make
in_v_model = ''
if interest_vehicle_model:
in_v_model = interest_vehicle_model
in_v_condition = ''
if interest_vehicle_condition:
in_v_condition = interest_vehicle_condition
in_v_trim = ''
if interest_vehicle_trim:
in_v_trim = interest_vehicle_trim
in_v_stock = ''
if interest_vehicle_stock_no:
in_v_stock = interest_vehicle_stock_no
in_v_url = ''
if interest_vehicle_listing_url:
in_v_url = interest_vehicle_listing_url
new_contact_vehicle = ContactVehicle(
contact_id=new_contact.id,
current_vin=ti_veh_vin,
current_make=ti_veh_make,
current_model=ti_veh_model,
current_year=ti_veh_year,
current_value=ti_veh_price,
current_mileage=ti_veh_mileage,
current_condition=ti_veh_condition,
interest_vin=in_v_vin,
interest_make=in_v_make,
interest_model=in_v_model,
interest_year=in_v_year,
interest_trim=in_v_trim,
interest_stock=in_v_stock,
interest_price=in_v_price,
interest_mileage=in_v_mileage,
interest_condition=in_v_condition,
interest_listing_url=in_v_url
)
db.session.add(new_contact_vehicle)
db.session.flush()
db.session.commit()
else:
log.info('no lead contact exist')
except Exception as e:
log.info('No contact exist: ' + str(e))
@celery.task
def generate_pdf(lead_id, account, form, fields, date, ssn, dob, co_ssn, co_dob, fin_info, reference_info,
vehicle_info, contact_vehicle_info, vehicle_seller_info, credit_score, browser_used='',
contact_id=None, contact_vehicle=None, additional_reference_info=None, **kwargs):
# Make a PDF straight from HTML in a string.
html = render_template('lead_form_pdf.html', lead_id=lead_id, account=account, form=form, fields=fields,
date=date, ssn=ssn, dob=dob, co_ssn=co_ssn, co_dob=co_dob, fin_info=fin_info,
reference_info=reference_info, vehicle_info=vehicle_info,
contact_vehicle_info=contact_vehicle_info,
vehicle_seller_info=vehicle_seller_info, credit_score=credit_score,
contact_vehicle=contact_vehicle, additional_reference_info=additional_reference_info)
if browser_used and contact_id and browser_used == 'msie':
current_date = datetime.datetime.now().strftime('%m-%d-%Y')
current_filename = 'form_lead_{}_{}.pdf'.format(contact_id, current_date)
return render_pdf(HTML(string=html), download_filename=current_filename, automatic_download=True)
else:
return render_pdf(HTML(string=html))
@celery.task
def generate_partnership_account_form_lead_pdfs(partnership_account_id, partnership_account_name, form_list_part, **kwargs):
# Get all form leads for partnership account
form_leads = FormLead.query\
.filter(FormLead.partnership_account_id == partnership_account_id)\
.filter(FormLead.id.in_(form_list_part))\
.all()
dir_name = 'upload/' + partnership_account_name.replace(" ", "").replace("-", "").replace("_", "").lower()
if not os.path.exists(dir_name):
try:
os.makedirs(dir_name)
except Exception:
log.error("Directory {} already exists. Proceeding with document generation.".format(dir_name))
for form_lead in form_leads:
try:
format_first_name = "Unknown"
format_last_name = "Unknown"
format_form_id = str(form_lead.form_id)
# Return contact linked to lead
from ..contacts.models import Contact
contact = Contact\
.query\
.filter(Contact.id == form_lead.contact_id,
Contact.partnership_account_id == form_lead.partnership_account_id)\
.first()
# Return form used to capture lead
form = ExternalForm.query\
.filter(ExternalForm.id == form_lead.form_id)\
.first()
# Return the predefined form fields
form_fields = ExternalFormField.query.filter(
form_lead.form_id == ExternalFormField.form_id
).subquery()
# Return the form field values for the specific lead
fields = FormLeadField.query.filter(
FormLeadField.lead_id == form_lead.id
).outerjoin(
(form_fields, FormLeadField.field_id == form_fields.c.field_id)
).order_by('position').with_entities(
form_fields.c.display_name,
FormLeadField.field_id,
FormLeadField.field_value
).all()
ss_number = ''
dob_date = ''
co_ssn = ''
co_dob = ''
test_fields = []
for field in fields:
decrypted_field = FormLeadField(field.field_id, decrypt_value(field.field_value))
if contact:
if contact.firstname and (decrypted_field.field_id == 'firstname' or decrypted_field.field_id == 'firstnamefield'):
decrypted_field.field_value = contact.firstname
elif contact.lastname and (decrypted_field.field_id == 'lastname' or decrypted_field.field_id == 'lastnamefield'):
decrypted_field.field_value = contact.lastname
elif contact.email and (decrypted_field.field_id == 'email' or decrypted_field.field_id == 'emailfield'):
decrypted_field.field_value = contact.email
elif contact.city and (decrypted_field.field_id == 'city' or decrypted_field.field_id == 'cityfield' or field.field_id == 'citytfield'):
decrypted_field.field_value = contact.city
elif contact.state and (decrypted_field.field_id == 'state' or decrypted_field.field_id == 'statefield'):
decrypted_field.field_value = contact.state
elif contact.zip and (decrypted_field.field_id == 'zip' or decrypted_field.field_id == 'zipfield'):
decrypted_field.field_value = contact.zip
elif contact.address_1 and (decrypted_field.field_id == 'address_1' or decrypted_field.field_id == 'streetfield'):
decrypted_field.field_value = contact.address_1
elif contact.address_2 and (decrypted_field.field_id == 'address_2'):
decrypted_field.field_value = contact.address_2
field_id = decrypted_field.field_id
field_value = decrypted_field.field_value
test_field = {}
test_field['field_id'] = field_id
test_field['field_value'] = field_value
test_fields.append(test_field)
if field.field_id == 'firstname' or field.field_id == 'firstnamefield':
format_first_name = decrypted_field.field_value.replace(" ", "")
elif field.field_id == 'lastname' or field.field_id == 'lastnamefield':
format_last_name = decrypted_field.field_value.replace(" ", "")
if field.field_id == 'cumemberfield' or field.field_id == 'ssnfield' or field.field_id == 'ssn':
ss_number = decrypted_field.field_value
if len(ss_number) > 16:
ss_number = 'unavailable'
elif field.field_id == 'datepicker' or field.field_id == 'dobfield' or field.field_id == 'birthday':
dob_date = decrypted_field.field_value
if len(dob_date) > 16:
dob_date = 'unavailable'
elif field.field_id == 'cosignerssnfield' or field.field_id == 'co_applicant_ssn':
co_ssn = decrypted_field.field_value
if len(co_ssn) > 16:
co_ssn = 'unavailable'
elif field.field_id == 'cosignerdateofbirthfield' or field.field_id == 'co_applicant_birthday':
co_dob = decrypted_field.field_value
if len(co_dob) > 16:
co_dob = 'unavailable'
finance_fields = FormLeadField.query \
.filter(and_(FormLeadField.lead_id == form_lead.id,
FormLeadField.field_id.in_(['cosignerbankruptcyfield', 'co_applicant_bankruptcy_declaration',
'cosignerrepossessionfield', 'co_applicant_repossession_declaration',
'cosigneramountowedfield', 'co_applicant_load_amount_outstanding',
'cosignerbanknamefield', 'co_applicant_bank_name',
'cosignercheckingbalancefield', 'co_applicant_checking_balance'
'cosignersavingbalancefield', 'co_applicant_saving_balance',
'cosignerchildcarepaymentfield', 'co_applicant_child_care_payment',
'cosignerutilitiespaymentfield', 'co_applicant_utilities_payment',
'cosignerdownpaymentfield', 'co_applicant_down_payment_amount',
'cosignermaintenancepaymentfield', 'co_applicant_maintenance_payment'])))\
.first()
if finance_fields:
fin_info = True
else:
fin_info = False
# Return current day's date
today = datetime.date.today()
today_str = today.strftime('%b, %d %Y')
# Retrieve the credit score for the form lead
# Get the credit score for the leads in the form leads table
from buyercall.blueprints.contacts.models import CreditReports
credit_report = CreditReports.query \
.filter(and_(CreditReports.contact_id == form_lead.contact_id,
CreditReports.partnership_account_id == partnership_account_id,
CreditReports.is_successful.is_(True)))\
.order_by(CreditReports.created_on.desc())\
.first()
if credit_report:
credit_score = decrypt_value(credit_report.credit_score)
else:
credit_score = 'Unknown'
generate_server_pdf.delay(form_lead.id, format_form_id, format_first_name, format_last_name, partnership_account_name,
dir_name, form.display_name, test_fields, today_str, ss_number, dob_date, co_ssn,
co_dob, fin_info, credit_score)
except Exception:
error_filename = '{}_{}_{}_{}.txt'.format(format_form_id, form_lead.id, format_first_name, format_last_name)
f = open(dir_name.join(error_filename), "w+")
f.write(traceback.format_exc())
f.close()
@celery.task
def generate_server_pdf(lead_id, form_id, first_name, last_name, account, dir_name, form, fields, date, ssn, dob,
co_ssn, co_dob, fin_info, credit_score, **kwargs):
# Make a PDF straight from HTML in a string.
html = render_template('lead_form_pdf.html', lead_id=lead_id, account=account, form=form, fields=fields, date=date,
ssn=ssn, dob=dob, co_ssn=co_ssn, co_dob=co_dob, fin_info=fin_info, credit_score=credit_score)
pdf = HTML(string=html).write_pdf()
try:
filename = os.path.join(dir_name, '{}_{}_{}_{}.pdf'.format(form_id, lead_id, first_name, last_name))
if not os.path.exists(filename):
f = open(os.path.join(dir_name, '{}_{}_{}_{}.pdf'.format(form_id, lead_id, first_name, last_name)), 'wb')
f.write(pdf)
else:
log.error("File {} already exists.".format(filename))
except Exception:
log.error(traceback.format_exc())
@celery.task(soft_time_limit=60)
def form_lead_email_notifications(lead_id, **kwargs):
from buyercall.blueprints.form_leads.views import send_notifications
lead = FormLead.query.filter(FormLead.id == lead_id).first()
if lead:
send_notifications(lead)
else:
log.info('There was no form lead found for lead id: {}'.format(lead_id))
@celery.task(soft_time_limit=60)
def form_lead_email_notifications_with_options(lead_id, error_occurred=False, external_provider_name=None,
is_conditional=False, conditional_subject_line=None, conditional_emails=None,
conditional_adf_emails=None,
**kwargs):
from buyercall.blueprints.form_leads.views import send_notifications
lead = FormLead.query.filter(FormLead.id == lead_id).first()
if lead:
send_notifications(lead, error_occurred, external_provider_name, is_conditional, conditional_subject_line,
conditional_emails, conditional_adf_emails)
else:
log.info('There was no form lead found for lead id: {}'.format(lead_id))
# This celery task gets used for sending the auto responds email to the lead after form submission
@celery.task(soft_time_limit=60)
def send_email_auto_responds(
lead_email, lead_name, form_name, company_name, email_from, email_subject, email_body, **kwargs
):
try:
variables = ['#NAME#', '#FORM#', '#COMPANY#']
if any(x in email_subject for x in variables):
new_email_subject = email_subject.replace('#NAME#', lead_name).replace('#FORM#', form_name)\
.replace('#COMPANY#', company_name)
else:
new_email_subject = email_subject
if any(x in email_body for x in variables):
new_email_body = email_body.replace('#NAME#', lead_name).replace('#FORM#', form_name)\
.replace('#COMPANY#', company_name)
else:
new_email_body = email_body
send_ses_email(recipients=[lead_email],
p_id=1,
subject=new_email_subject,
text=new_email_body,
sender=email_from,
account_name=company_name
)
except Exception:
log.error(traceback.format_exc())
# This celery task gets used for sending the auto responds email to the lead after form submission
@celery.task(soft_time_limit=60)
def send_sms_auto_responds(lead_phone_number, lead_name, form_name, company_name, inbound_id, message_body, **kwargs):
try:
variables = ['#NAME#', '#FORM#', '#COMPANY#']
# Check if any dynamic variables are used in the text message body and replace them
if any(x in message_body for x in variables):
dynamic_message_body = message_body.replace('#NAME#', lead_name).replace('#FORM#', form_name)\
.replace('#COMPANY#', company_name)
new_message_body = dynamic_message_body + ' Reply STOP to unsubscribe.'
else:
new_message_body = message_body + ' Reply STOP to unsubscribe.'
# There is no media for sms auto responder messages yet. So we set media as an empty string
media = ''
log.info('The leads number is: {}'.format(lead_phone_number))
log.info('The companys inbound id is: {}'.format(inbound_id))
log.info('The text message body is: {}'.format(new_message_body))
log.info('The text message media is: {}'.format(media))
send_text_message(inbound_id, lead_phone_number, new_message_body, media)
except Exception:
log.error(traceback.format_exc())
@celery.task(soft_time_limit=60)
def form_external_api_request(lead_id, partnership_account_id, service_provider_id, **kwargs):
from buyercall.blueprints.form_leads.views import determine_external_api_request
determine_external_api_request(lead_id, partnership_account_id, service_provider_id)
@celery.task(soft_time_limit=60)
def form_prequalify_credit_check(bureau, credit_type, contact_id, partnership_account_id, firstname, lastname, address,
city, state, zip_code, service_provider, birthday, **kwargs):
from ..contacts.contact_tasks import pull_prequalify_credit
pull_prequalify_credit(bureau, credit_type, contact_id, partnership_account_id, firstname, lastname, address,
city, state, zip_code, service_provider, birthday)
@celery.task
def check_conditional_notification(lead_id, form_id, conditions, **kwargs):
try:
redis_db = redis.StrictRedis(host=celery.conf['REDIS_CONFIG_URL'], port=celery.conf['REDIS_CONFIG_PORT'])
processed_key = 'lead-' + str(lead_id) + '-form-processed'
processed_state = str(redis_db.get(processed_key).decode("utf-8"))
log.error('Entering check_conditional_notification celery task for lead ' + str(lead_id))
if processed_state and processed_state == 'incomplete':
log.error('Lead conditional notification incomplete. Processing conditional notification.')
from buyercall.blueprints.form_leads.views import \
process_conditional_notification
process_conditional_notification(lead_id, form_id, conditions)
else:
log.error('Lead conditional notification complete. Exiting celery task.')
except Exception:
log.error(traceback.format_exc())