Skip to content

Instantly share code, notes, and snippets.

@lensraster
Created June 12, 2018 08:31
Show Gist options
  • Save lensraster/470cded52fe24a25cde52e4c8b4af53b to your computer and use it in GitHub Desktop.
Save lensraster/470cded52fe24a25cde52e4c8b4af53b to your computer and use it in GitHub Desktop.
Parting's bisinesses/views.py
import json
import copy
import urllib
import urlparse
import geocoder
from apps.businesses.forms import ReviewForm
from apps.businesses.helpers import Pagination
from apps.businesses.models import Business, Address, CustomBusinessServicePackage
from apps.geos.models import State, GeoPoint
from apps.utils.states import states
from django.conf import settings
from django.contrib.gis.measure import D
from django.contrib.gis.db.models.functions import Distance
from django.contrib.gis.geos import Point
from django.core.mail import send_mail
from django.urls import reverse
from django.db.models import Q, Count, Avg
from django.http import Http404, HttpResponse
from django.views.generic import TemplateView
from django.contrib.humanize.templatetags.humanize import intcomma
from collections import OrderedDict
default_prices = copy.copy(settings.DEFAULT_PRICES)
for key in default_prices:
default_prices[key] = intcomma(default_prices[key])
class ResultView(TemplateView):
service_types = OrderedDict({
'traditional': ('avg_cost', 'traditional funeral'),
'memorial': ('memorial', 'cremation memorial'),
'direct_burial': ('direct_burial', 'direct burial'),
'direct_cremation': ('direct_cremation', 'direct cremation'),
'memorial_no_viewing': ('memorial_no_viewing', 'cremation memorial with no viewing'),
'traditional_no_viewing': ('traditional_no_viewing', 'traditional funeral with no viewing'),
'memorial_no_service': ('memorial_no_service', 'cremation memorial with no services'),
'traditional_no_service': ('traditional_no_service', 'traditional funeral with no services'),
})
service_type_maps = {
'traditional': {'burial': True, 'wake': True, 'service': True},
'memorial': {'burial': False, 'wake': True, 'service': True},
'memorial_no_viewing': {'burial': False, 'wake': False, 'service': True},
'direct_burial': {'burial': True, 'wake': False, 'service': False},
'direct_cremation': {'burial': False, 'wake': False, 'service': False},
'traditional_no_viewing': {'burial': True, 'wake': False, 'service': True},
'memorial_no_service': {'burial': False, 'wake': True, 'service': False},
'traditional_no_service': {'burial': True, 'wake': True, 'service': False},
}
def get_service_type(self, burial, wake, service):
for service_item in self.service_type_maps:
item = self.service_type_maps[service_item]
if item['burial'] == burial and item['wake'] == wake and item['service'] == service:
return service_item
return {}
def get_context_data(self, **kwargs):
context = super(ResultView, self).get_context_data(**kwargs)
context['critical_css'] = 'search-mobile.css' if self.request.user_agent.is_mobile else 'search.css'
return context
def build_results_context(self, context, point_data):
def filter_query(base_query, filter_param):
query = Q(**{'{}__gt'.format(filter_param): 0})
if 'direct_cremation' in filter_param:
query |= Q(business__is_body_donation=True)
return base_query.filter(query)
sort = self.request.GET.get('sort') or 'closest'
def to_python(val):
if not val:
return True
return True if val == 'true' else False
burial = to_python(self.request.GET.get('burial'))
wake = to_python(self.request.GET.get('wake'))
service = to_python(self.request.GET.get('service'))
_type = self.get_service_type(burial, wake, service)
funeral_type = _type if _type in self.service_types else 'traditional'
service_type_data = self.service_types[funeral_type]
built_filter_query = '&burial={0}&wake={1}&service={2}'.format(str(burial).lower(), str(wake).lower(), str(service).lower())
context['burial'] = str(burial).lower()
context['wake'] = str(wake).lower()
context['service'] = str(service).lower()
context['loan_service_type'] = 'burial' if funeral_type in ['traditional', 'direct_burial'] else 'cremation'
context['burial_name'] = 'Burial' if burial else 'Cremation'
context['wake_name'] = 'Viewing' if wake else 'No Viewing'
context['service_name'] = 'Service' if service else 'No Service'
page = self.request.GET.get('page')
page = int(page) if page else 1
start_page = (page - 1) * settings.ITEMS_PER_PAGE
end_page = page * settings.ITEMS_PER_PAGE
base_query = self.get_base_query(point_data)
filter_string = self.get_filter_string(service_type_data[0])
if sort == 'highest_reviewed':
order_by = '-num_reviews'
elif sort == 'highest_rated':
order_by = 'avg_review'
elif sort == 'lowest_price':
order_by = filter_string
else:
order_by = 'distance'
query = filter_query(base_query, filter_string)
order_by_args = ['-business__claimed', order_by]
if sort == 'closest':
order_by_args.insert(1, '-state_wide')
query = query.order_by(*order_by_args)
context['total_prices'] = self.get_total_prices(query, point_data)
context['reviews_count'] = self.get_reviews_count(query)
context['total_min_price'] = context['total_prices'][funeral_type + '_min'] if context['total_prices'] else None
count = len(query)
context['results_count'] = count
context['built_filter_query'] = built_filter_query
items = Address.unique(query)[start_page:end_page]
# If there are no services of the requested type, than we show the links to the pages of the services that
# are available for the given search location
if not count:
current_url = self.request.build_absolute_uri()
parts = urlparse.urlparse(current_url)
query_args = dict(urlparse.parse_qsl(parts.query))
available_services = []
for key, _type_data in self.service_types.items():
if not key == funeral_type:
_type_data = self.service_types[key]
_count = len(Address.unique(filter_query(base_query, self.get_filter_string(_type_data[0]))))
if _count:
_args = copy.copy(query_args)
_args['type'] = key
_url = parts._replace(query=urllib.urlencode(_args)).geturl()
available_services.append((_type_data[1], _url))
context['available_services'] = available_services
context['pagination'] = Pagination(page, settings.ITEMS_PER_PAGE, count).to_json()
context['businesses'] = self.get_business_context(items)
context['sort_query'] = self.get_sort_query(sort)
context['type_query'] = self.get_type_query(funeral_type)
context['type'] = funeral_type
context['sort'] = sort
context['service_name'] = service_type_data[1]
context['point_data'] = point_data
context['search_box_text'] = point_data['search_box_text']
_custom_packages = CustomBusinessServicePackage.objects.filter(business__id__in=[b.id for b in context['businesses']],
has_burial=burial, has_viewing=wake, has_service=service)
pricing_data = {}
for b in context['businesses']:
b.focus_price = self.get_focus_price(funeral_type, b.pricelookup)
package = None
for _cp in _custom_packages:
if _cp.business.id == b.id:
package = _cp
break
if not package:
for _p in b.businessservicepackage_set.all():
if _p.has_burial == burial and _p.has_viewing == wake and _p.has_service == service:
package = _p
break
if package:
pricing_data[b.id] = b.serialize_packages([package], b.get_service_pricing(only_itemizable=True))[0]
else:
continue
context['pricing_data'] = json.dumps(pricing_data)
def get_focus_price(self, type, pricelookup):
if type in ['memorial', 'direct_burial', 'direct_cremation', 'memorial_no_viewing', 'traditional_no_viewing',
'memorial_no_service', 'traditional_no_service']:
return getattr(pricelookup, type)
else:
return pricelookup.avg_cost
def get_business_context(self, address_data):
context = []
for a in address_data:
a.business.address = a
a.business.num_reviews = a.num_reviews
a.business.avg_review = a.avg_review
if not a.business.avg_review:
a.business.avg_review = 0
if hasattr(a, "distance"):
a.business.distance = a.distance
distance = a.distance.mi if not a.state_wide else 0
a.business.formatted_distance = str(int(distance) if distance >= 1 else "%.1f" % distance) + ' mi away'
if a.business.formatted_distance == '1 mi away':
a.business.formatted_distance = '1 mi away'
context.append(a.business)
return context
def get_reviews_count(self, address_data):
# Setting initial values
reviews_count = 0
for a in address_data:
reviews_count += a.num_reviews
if reviews_count:
return reviews_count
else:
return None
def get_total_prices(self, address_data, point_data):
# Setting initial values
data = {}
for key in self.service_types:
data[key + '_min'] = data[key + '_sum'] = data[key + '_count'] = 0
for a in address_data:
if (point_data['city'].lower() == a.city.lower()):
for key, _type_data in self.service_types.items():
service_price = getattr(a.business.pricelookup, _type_data[0])
# Calculate the average service price
if service_price > 0:
data[key + '_sum'] += service_price
data[key + '_count'] += 1
# Minimum service price search
if not data[key + '_min'] or service_price < data[key + '_min']:
data[key + '_min'] = service_price
data_empty = True
for key in self.service_types:
if data[key + '_count'] > 0:
data_empty = False
data[key + '_avg'] = int(data[key + '_sum'] / data[key + '_count'])
else:
del data[key + '_min']
del data[key + '_sum']
del data[key + '_count']
if not data_empty:
return data
else:
return None
def get_sort_query(self, sort):
return self.get_query('sort', sort)
def get_zip_query(self, zip):
return self.get_query('zip', zip)
def get_type_query(self, type):
return self.get_query('type', type)
def get_filter_string(self, key):
return 'business__pricelookup__{}'.format(key)
def get_query(self, name, value):
if value:
return "&" + name + "=" + value
else:
return ""
def get_base_query(self, point_data):
return Address.get_base_query(point_data).annotate(distance=Distance('point', point_data['point']), num_reviews=Count('business__review'), avg_review=Avg('business__review__rating'))
class BusinessesSEMPageView(ResultView):
sites = []
service_types_short = OrderedDict({
'tf': 'traditional',
'cm': 'memorial',
'db': 'direct_burial',
'dc': 'direct_cremation'
})
def get_template_names(self):
return ["businesses/sem-pages/funnel-%s/sem-page.html" % self.kwargs['funnel']]
def get_context_data(self, **kwargs):
try:
assert(self.request.GET["sites"])
self.sites = self.request.GET["sites"].split(",")
businesses = Business.objects.filter(pk__in=self.sites)
assert(len(businesses))
zip = businesses[0].address.zip_postal_code
zip_point = GeoPoint.objects.filter(zipcode=zip).first()
assert(zip_point)
except (KeyError, AssertionError):
raise Http404
context = super(BusinessesSEMPageView, self).get_context_data(**kwargs)
base_query = self.get_base_query(zip_point)
context['businesses'] = self.get_business_context(base_query)
_type = self.request.GET.get('service')
funeral_type = _type if _type in self.service_types_short else 'tf'
context['type'] = self.service_types_short[funeral_type]
context['service'] = self.service_types[self.service_types_short[funeral_type]][1]
self.sites = map(int, self.sites)
context['businesses'].sort(key=lambda t: self.sites.index(t.pk))
context['funnel'] = self.kwargs['funnel']
data = {"businesses": [b.to_json(as_dict=True) for b in context['businesses']]}
context['json_data'] = json.dumps(data)
context['query_service'] = _type
context['query_keyword'] = self.request.GET.get('keyword')
context['disable_chat'] = True
return context
def get_base_query(self, point_data):
return Address.objects.select_related('business').select_related('business__pricelookup') \
.prefetch_related('business__businessservice_set') \
.filter(Q(business__pk__in=self.sites), business__is_active=True) \
.annotate(num_reviews=Count('business__review'), avg_review=Avg('business__review__rating'))
class SearchView(ResultView):
def get_context_data(self, **kwargs):
zip = self.request.GET.get('zip')
zip_point = GeoPoint.objects.filter(zipcode=zip).first()
if not zip_point:
raise Http404
point_data = {
'point': zip_point.point,
'city': zip_point.city,
'state_abbrev': zip_point.state_abbrev,
'lat': zip_point.latitude,
'lng': zip_point.longitude,
'search_box_text': '{}, {}'.format(zip_point.city, zip_point.state_abbrev)
}
point_data['db_repr'] = 'POINT({0} {1})'.format(point_data['lng'], point_data['lat'])
context = super(SearchView, self).get_context_data(**kwargs)
context['zip_query'] = self.get_zip_query(zip)
self.build_results_context(context, point_data)
self.template_name = 'mobile/businesses/search.html' if self.request.user_agent.is_mobile else 'businesses/search.html'
return context
class GeoPointView(ResultView):
def get_context_data(self, **kwargs):
point_data = {
'city': kwargs.get('city').replace('-', ' ').title(),
'state_abbrev': kwargs.get('state').upper()
}
if kwargs.get('area'):
query = '{}, {}, {}'.format(kwargs['area'].replace('-', ' ').title(),
point_data['city'], point_data['state_abbrev'])
point = geocoder.google(query).latlng
point_data['point'] = Point(point[0], point[1])
point_data['lat'] = point[0]
point_data['lng'] = point[1]
point_data['search_box_text'] = query
point_data['area'] = kwargs['area'].replace('-', ' ').title()
else:
city_regex = '^{}$'.format('..?'.join(kwargs.get('city').split('-')))
geopoint = GeoPoint.objects.filter(city__iregex=city_regex, state_abbrev=point_data['state_abbrev'])\
.order_by('-geoname_id', 'zipcode').first()
if not geopoint:
raise Http404
point_data['point'] = geopoint.point
point_data['search_box_text'] = '{}, {}'.format(point_data['city'], point_data['state_abbrev'])
point_data['lat'] = geopoint.latitude
point_data['lng'] = geopoint.longitude
context = super(GeoPointView, self).get_context_data(**kwargs)
context['canonical_url'] = self.request.build_absolute_uri().split('?')[0]
point_data['db_repr'] = 'POINT({0} {1})'.format(point_data['lng'], point_data['lat'])
self.build_results_context(context, point_data)
self.template_name = 'mobile/businesses/search.html' if self.request.user_agent.is_mobile else 'businesses/search.html'
return context
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
render_kwargs = {}
if not (len(context['businesses']) + len(context.get('available_services', []))):
render_kwargs['status'] = 410
return self.render_to_response(context, **render_kwargs)
class GeoStateView(TemplateView):
template_name = "businesses/state.html"
def get_context_data(self, **kwargs):
context = super(GeoStateView, self).get_context_data(**kwargs)
search_state = kwargs.get('state').upper()
cities = GeoPoint.objects.filter(state_abbrev__iexact=search_state, show_on_state_page=True) \
.order_by('city', '-is_top').distinct('city')
# Sort cities
top_cities = []
regular_cities = []
for city in cities:
if city.is_top and len(top_cities) < 16:
top_cities.append(city)
else:
regular_cities.append(city)
context['cities'] = {
'top': top_cities,
'regular': regular_cities
}
try:
context['state_data'] = State.objects.get(state_code__iexact=search_state)
except State.DoesNotExist:
context['state_data'] = None
context['state'] = search_state
context['search_box_text'] = context['state_name'] = states.get(search_state, search_state)
return context
class BusinessesView(TemplateView):
def get_context_data(self, **kwargs):
context = super(BusinessesView, self).get_context_data(**kwargs)
business_name = kwargs.get('business')
last_dash = business_name.rfind('-')
zipcode = business_name[last_dash + 1:len(business_name)]
business_name = business_name[:last_dash]
business = Business.objects.filter(seo_url=business_name, addresses__zip_postal_code=zipcode).first()
if not business:
raise Http404
context['business'] = business
context['reviews'] = business.review_set.all().order_by('-created') if business.review_set else []
context['reviews_page_size'] = 4
context['services'] = business.businessservice_set.filter(service__is_extended_service=False).order_by('service__ordinal')
if not business.claimed:
context['top'] = Address.objects.filter(point__distance_lte=(business.address.get_point(), D(mi=10)))\
.filter(~Q(id=business.address.id)) \
.filter(business__claimed__gte=1) \
.annotate(distance=Distance('point', business.address.get_point()), num_reviews=Count('business__review'), avg_review=Avg('business__review__rating')) \
.order_by('-business__claimed', 'distance')[:]
else:
context['top'] = []
context['packages'] = json.dumps(business.get_packages())
context['top'] = [b.business for b in context['top']]
context['form'] = ReviewForm(self.request.POST or None) # instance= None
_addr = business.address
context['list_url'] = reverse('businesses-city-list',
kwargs={'city': _addr.slugify_city, 'state': _addr.state_province_region})
context['search_box_text'] = "{}, {}".format(_addr.city.title(), _addr.slugify_state)
context['critical_css'] = 'facility-mobile.css' if self.request.user_agent.is_mobile else 'facility.css'
context['maps_api_key'] = settings.GOOGLE_API_KEY
def to_python(val):
if not val:
return True
return True if val == 'true' else False
burial = to_python(self.request.GET.get('burial'))
wake = to_python(self.request.GET.get('wake'))
service = to_python(self.request.GET.get('service'))
context['burial'] = str(burial).lower()
context['wake'] = str(wake).lower()
context['service'] = str(service).lower()
context['burial_name'] = 'Burial' if burial else 'Cremation'
context['wake_name'] = 'Viewing' if wake else 'No&nbsp;Viewing'
context['service_name'] = 'Service' if service else 'No&nbsp;Service'
self.template_name = 'mobile/businesses/funeralhome.html' if self.request.user_agent.is_mobile else 'businesses/funeralhome.html'
return context
class CremationGuideView(TemplateView):
template_name = 'businesses/cremation-guide.html'
def post(self, request, *args, **kwargs):
#form = CremationGuideForm(self.request.POST or None)
#if form.is_valid():
lead_msg = """
Name: {0}
Phone: {1}
Email: {2}
Location: {3}
For: {4}
Time left to live: {5}
Deceased: {6}
Medcure Opt-In: {7}
Disqualified by State: {8}
Disqualified by Weight: {9}
Disqualified by HIV/AIDS/Hep B/Hep C: {10}
Disqualified by drug/jail/etc: {11}
Death Timing: {12}
Autopsy: {13}
Body refrigerated in time: {14}
"""
recipient_list = [settings.LEAD_RECIPIENT_EMAIL]
if hasattr(settings, 'ZAPIER_LEAD_EMAIL'):
recipient_list.append(settings.ZAPIER_LEAD_EMAIL)
send_mail('New Cremation Lead', lead_msg.format(request.POST.get('lead_name') or request.POST.get('med_lead_name'),
request.POST.get('lead_phone') or request.POST.get('med_lead_phone'),
request.POST.get('lead_email') or request.POST.get('med_lead_email'),
request.POST.get('zip_code'), request.POST.get('customer'),
request.POST.get('customer_left_to_live') or '', request.POST.get('passed_away') or '',
request.POST.get('participate') or '', request.POST.get('disqualified_state') or '',
request.POST.get('disqualified_weight') or '', request.POST.get('disqualified_disease') or '',
request.POST.get('disqualified_condition') or '', request.POST.get('death_timing') or '',
request.POST.get('autopsy') or '', request.POST.get('refrigerated') or ''),
'[email protected]', recipient_list, fail_silently=False)
return HttpResponse(json.dumps({'success': True}), content_type="application/json")
def get_context_data(self, **kwargs):
context = super(CremationGuideView, self).get_context_data(**kwargs)
context['zip_code'] = self.request.GET.get('location', '')
context['cremation_guide'] = settings.CREMATION_GUIDE
return context
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment