Created
June 12, 2018 08:31
-
-
Save lensraster/470cded52fe24a25cde52e4c8b4af53b to your computer and use it in GitHub Desktop.
Parting's bisinesses/views.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 Viewing' | |
context['service_name'] = 'Service' if service else 'No 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