Skip to content

Commit

Permalink
complete nearby location and filters done
Browse files Browse the repository at this point in the history
  • Loading branch information
amaravindhan committed Nov 9, 2020
1 parent 50f6b40 commit 2ada543
Show file tree
Hide file tree
Showing 17 changed files with 203 additions and 64 deletions.
Binary file not shown.
Binary file modified property/__pycache__/forms.cpython-36.pyc
Binary file not shown.
Binary file modified property/__pycache__/models.cpython-36.pyc
Binary file not shown.
Binary file removed property/__pycache__/tests.cpython-36.pyc
Binary file not shown.
Binary file modified property/__pycache__/utils.cpython-36.pyc
Binary file not shown.
Binary file modified property/__pycache__/views.cpython-36.pyc
Binary file not shown.
4 changes: 4 additions & 0 deletions property/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@

class PropertyConfig(AppConfig):
name = 'property'

def ready(self):
from .taskSchedulers import startScheduler
startScheduler()
37 changes: 35 additions & 2 deletions property/forms.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
from django import forms
from django.db.models import Max, Min
from datetime import date
from django.contrib.gis.db.models.functions import Distance
from django.contrib.gis.geos import Point

from .models import Property, PropertyImage, PropertyVideo, Amenities

longitude = -73.12082590786636
latitude = 40.91638132127517
userlocation = Point(longitude, latitude, srid=4326)

class PropertyForm(forms.ModelForm):

Expand Down Expand Up @@ -203,6 +208,8 @@ class PropertyFilterSortForm(forms.Form):
bath = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple(), choices=commonChoices, required=False)
minPri = forms.IntegerField(required=False)
maxPri = forms.IntegerField(required=False)
disPro = forms.FloatField(required=False)
disAmen = forms.FloatField(required=False)
amenities = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(attrs={
'style': 'display:none; margin-top:10px;'
}),
Expand All @@ -212,8 +219,13 @@ class PropertyFilterSortForm(forms.Form):

def __init__(self, request=None, *args, **kwargs):
super(PropertyFilterSortForm, self).__init__(*args, **kwargs)
maxp = Property.objects.aggregate(Max('rentPerPerson'))
minp = Property.objects.aggregate(Min('rentPerPerson'))
obj = Property.objects.all().annotate(distance=Distance(userlocation, 'location'))
maxp = obj.aggregate(Max('rentPerPerson'))
minp = obj.aggregate(Min('rentPerPerson'))
maxDisP = obj.aggregate(Max('distance'))
minDisP = obj.aggregate(Min('distance'))
maxDisA = obj.aggregate(Max('averageDistance'))
minDisA = obj.aggregate(Min('averageDistance'))
self.fields['minPri'] = forms.IntegerField(
widget=forms.NumberInput(attrs={
'placeholder': 'Min', 'type': 'range', 'step': '1',
Expand All @@ -232,6 +244,27 @@ def __init__(self, request=None, *args, **kwargs):
min_value = minp.get('rentPerPerson__min', 0),
required=False,
)
self.fields['disPro'] = forms.FloatField(
widget=forms.NumberInput(attrs={
'placeholder': 'Property Distance',
'type': 'range', 'step': '0.1',
'value': round(minDisP.get('distance__min', 0).mi, 1)
}),
min_value=round(minDisP.get('distance__min', 0).mi, 1),
max_value=round(maxDisP.get('distance__max', 0).mi, 1),
required=False
)
self.fields['disAmen'] = forms.FloatField(
widget=forms.NumberInput(attrs={
'placeholder': 'Property Amenity',
'type': 'range', 'step': '0.1',
'value': minDisA.get('averageDistance__min', 0)
}),
min_value=minDisA.get('averageDistance__min', 0),
max_value=maxDisA.get('averageDistance__max', 0),
required=False
)

def clean(self):
minprice = self.cleaned_data.get("minPri")
maxprice = self.cleaned_data.get("maxPri")
Expand Down
8 changes: 1 addition & 7 deletions property/locationApi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@

logging.basicConfig(level=logging.DEBUG, filename='completeApp.log', format='%(asctime)s - %(name)s - %(process)d - %(levelname)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S')

# logging.warning('This will get logged to a file')
# logging.debug('This will get logged to a file')
# logging.info('This will get logged to a file')
# logging.error('This will get logged to a file')
# logging.critical('This will get logged to a file')

def get_lat_long_from_address(address):
""" function to get lat and long based on property address"""
status = True
Expand Down Expand Up @@ -113,4 +107,4 @@ def get_near_by_text_places(instance=None, place=None, count=1):
'address is - {} and error is - {}'.format(instance.title, instance.address, e))
success = False

return success, nearByName, locationDict, placeId
return success, nearByName, locationDict, placeId
78 changes: 63 additions & 15 deletions property/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
from django.db.models.signals import pre_save, post_delete, post_save
from django.contrib.gis.geos import fromstr
from django.contrib.gis.db.models.functions import Distance
from django.utils import timezone

import datetime, os

from users.models import UserLandLord, UserStudent
from .utils import unique_slug_generator, unique_file_path_generator
from .locationApi import *
from .utils import unique_slug_generator, unique_file_path_generator, random_string_generator
from .locationApi import get_lat_long_from_address, get_near_by_text_places, get_near_by_types, logging
from .taskSchedulers import scheduler


# Create your models here.
Expand Down Expand Up @@ -63,7 +65,7 @@ class Property(geoModel.Model):
address = models.CharField(max_length=250, help_text="Address of your property")
location = geoModel.PointField(null=True, blank=True)
locationType = models.CharField(null=True, blank=True, max_length=200)
averageDistance = models.IntegerField(default=0, help_text='Average distance between the nearby amenities (in miles).')
averageDistance = models.FloatField(default=0, help_text='Average distance between the nearby amenities (in miles).')
placeId = models.CharField(null=True, blank=True, max_length=300)
sqft = models.FloatField(
verbose_name="Square Feet",
Expand Down Expand Up @@ -234,6 +236,17 @@ def __str__(self):
return self.nearByType


class PropertyJobStore(models.Model):

propObject = models.OneToOneField(Property, on_delete=models.CASCADE)
jobid = models.CharField(max_length=50)
address = models.CharField(max_length=300)
updatedDate = models.DateTimeField(auto_now=True)

def __str__(self):
return address


class PostQuestion(models.Model):

propKey = models.ForeignKey(Property, on_delete=models.CASCADE, verbose_name="Property")
Expand Down Expand Up @@ -274,20 +287,17 @@ def auto_add_unique_slug_field(sender, instance, **kwargs):
def get_or_create_near_by(instance, nearByType):
return PropertyNearby.objects.get_or_create(propObject=instance, nearByType=nearByType)

@receiver(post_save, sender=Property)
def auto_add_nearby_necessary_fields(sender, instance, **kwargs):
"""
Automatically add neary by necessary in the property.
"""
nearByTypes = ['restaurant', 'shopping_mall', 'bar', ]
nearByText = ['Costco in', 'Target in', 'Walmart in', ]
def fetch_near_by_places(instance):
# {type_to_search: display name in frontend}
nearByTypes = {'restaurant': 'Restaurant', 'shopping_mall': 'Shopping Mall', 'bar': 'Bar', }
nearByText = {'Costco in': 'Costco', 'Target in': 'Target', 'Walmart in': 'Walmart', }
for types in nearByTypes:
success, nearByName, locationDict, placeId = get_near_by_types(instance=instance, types=types)
if success:
longitude = locationDict.get('lng', 0)
latitude = locationDict.get('lat', 0)
location = fromstr(f'POINT({longitude} {latitude})', srid=4326)
obj = get_or_create_near_by(instance, types)
obj = get_or_create_near_by(instance, nearByTypes[types])
obj[0].nearByName = nearByName
obj[0].location = location
obj[0].placeId = placeId
Expand All @@ -298,7 +308,7 @@ def auto_add_nearby_necessary_fields(sender, instance, **kwargs):
longitude = locationDict.get('lng', 0)
latitude = locationDict.get('lat', 0)
location = fromstr(f'POINT({longitude} {latitude})', srid=4326)
obj = get_or_create_near_by(instance, place)
obj = get_or_create_near_by(instance, nearByText[place])
obj[0].nearByName = nearByName
obj[0].location = location
obj[0].placeId = placeId
Expand All @@ -310,9 +320,47 @@ def auto_add_nearby_necessary_fields(sender, instance, **kwargs):
nearby.distanceToProp = nearby.distance.mi
nearby.save()
totalmiles = totalmiles + nearby.distance.mi
if not instance.averageDistance or instance.averageDistance != round(totalmiles/count):
instance.averageDistance = round(totalmiles/count)
instance.save()
# if not instance.averageDistance or instance.averageDistance != round(totalmiles/count):
instance.averageDistance = round(totalmiles/count, 1)
instance.save()
logging.info('Nearby location fetch finished for property "{}"'.format(instance.title))

def startFetchNearByJob(instance):
jobId = random_string_generator(size=5)
runDate = instance.updatedDate + datetime.timedelta(seconds=30)
s = scheduler.add_job(fetch_near_by_places, 'date', [instance], run_date = runDate, id = jobId, misfire_grace_time=300, coalesce=True)
logging.info('Nearby location fetch added for property "{}" and job id {}'.format(instance.title, jobId))
return jobId

@receiver(post_save, sender=Property)
def auto_add_nearby_necessary_fields(sender, instance, **kwargs):
"""
Automatically add near by necessary in the property.
"""
address = '{}, {} {}'.format(instance.address, instance.city, instance.zipcode)
if kwargs.get('created'):
newJob = startFetchNearByJob(instance)
PropertyJobStore.objects.create(propObject=instance, jobid=newJob,
address=address)
else:
if PropertyJobStore.objects.filter(propObject=instance).exists():
oldJob = PropertyJobStore.objects.get(propObject=instance)
if oldJob.address == address:
oldJob_updated_time = oldJob.updatedDate + datetime.timedelta(days=30)
if oldJob_updated_time <= timezone.now():
newJob = startFetchNearByJob(instance)
oldJob.address = address
oldJob.jobid = newJob
oldJob.save()
else:
newJob = startFetchNearByJob(instance)
oldJob.address = address
oldJob.jobid = newJob
oldJob.save()
else:
newJob = startFetchNearByJob(instance)
PropertyJobStore.objects.create(propObject=instance, jobid=newJob,
address=address)

@receiver(pre_save, sender=PropertyImage)
def auto_delete_property_image_on_modified(sender, instance, **kwargs):
Expand Down
19 changes: 19 additions & 0 deletions property/taskSchedulers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.schedulers.base import STATE_STOPPED
from django.conf import settings

scheduler = BackgroundScheduler()

def startScheduler():
"""Function to start background Scheduler"""
# print(scheduler.state)
if scheduler.state == STATE_STOPPED:
url = 'postgres://{}:{}@{}:{}/{}'.format(
settings.DATABASES['default']['USER'],
settings.DATABASES['default']['PASSWORD'],
settings.DATABASES['default']['HOST'],
settings.DATABASES['default']['PORT'],
settings.DATABASES['default']['NAME']
)
scheduler.add_jobstore('sqlalchemy', url=url, tablename="taskstore")
scheduler.start()
23 changes: 12 additions & 11 deletions property/templates/property/properties.html
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ <h6>Price Range</h6>
{{filterSortForm.maxPri}}
{{filterSortForm.maxPri.errors}}
</div>

{% comment %}
<div data-role="rangeslider">
<small>Min</small><br>
Expand Down Expand Up @@ -198,7 +197,7 @@ <h6>Amenities</h6>
{% endfor %}
</p>

<h6>Lifestyle</h6>
{% comment %} <h6>Lifestyle</h6>
<p class="filter-lifestyle-tags pull-left" style="list-style-type:none; width:100%;">
<span onclick="interestfilterclick(this, 1)" id="interestspan1" class="btn btn-default btn-xs" style="background-color:transparent; border:2px solid #383836; border-radius:50px;"> <i class="fa fa-check" style="display:none;"></i> interest 1 </span>
<input type="checkbox" class="form-control" name="interest[]" value="1" id="interestinput1" style="display:none; margin-top:10px;"/>
Expand All @@ -211,11 +210,11 @@ <h6>Lifestyle</h6>
{% for interest in interests %}
<input type="checkbox" class="form-control" name="interest[]" value="{{interest.pk}}" id="interestinput{{interest.pk}}" style="display:non; margin-top:10px;"/>
{% endfor %}
</p>
</p> {% endcomment %}


<h6>Distance <small>(Max in km)</small></h6>
<input type="number" name="filter-distance-max" class="form-control" style="height:30px;">
{% comment %} <h6>Distance <small>(Max in km)</small></h6>
<input type="number" name="filter-distance-max" class="form-control" style="height:30px;"> {% endcomment %}

<br>
{% comment %} <span class="btn form-contro" style="cursor:pointer; background-color:rgba(0, 0, 0, 0.8); width:100%; color:#fff;"> Apply Filters </span> {% endcomment %}
Expand Down Expand Up @@ -253,7 +252,8 @@ <h6>Distance <small>(Max in km)</small></h6>
<span style="font-size:12px; position:absolute; font-family:Century Gothic;">&nbsp;Distance from university</span>
<div class="range-wrap">
<div class="range-value" id="rangeV3"></div>
<input id="range3" type="range" min="1" max="5" value="5" step="1">
{{filterSortForm.disPro}}
{{filterSortForm.disPro.errors}}
</div>
</div>
</div>
Expand All @@ -264,7 +264,8 @@ <h6>Distance <small>(Max in km)</small></h6>
<span style="font-size:12px; position:absolute; font-family:Century Gothic;">&nbsp;Distance to &nbsp; <abbr title="Restaurants"><i class="fa fa-cutlery" style="font-size:15px; color:#005918;"></i></abbr> &nbsp; <abbr title="Shopping"><i class="fa fa-shopping-bag" style="font-size:15px; color:#26006e;"></i></abbr> &nbsp; <abbr title="Beverages"><i class="fa fa-glass" style="font-size:15px; color:#750800;"></i></abbr></span>
<div class="range-wrap">
<div class="range-value" id="rangeV4"></div>
<input id="range4" type="range" min="1" max="10" value="10" step="1">
{{filterSortForm.disAmen}}
{{filterSortForm.disAmen.errors}}
</div>
</div>
</div>
Expand Down Expand Up @@ -292,14 +293,14 @@ <h6>Distance <small>(Max in km)</small></h6>
<div class="col-sm-6 col-md-3 pxp-content-side-search-form-col">
<div class="form-group">
<label for="pxp-p-filter-distance" class="d-none d-sm-inline-block">Distance</label>
<input type="text" class="form-control" placeholder="Max" id="pxp-p-filter-distance" name="pxp-p-filter-distance">
<input type="text" class="form-control" placeholder="Max" id="pxp-p-filter-distance">
</div>
</div>

<div class="col-sm-6 col-md-3 pxp-content-side-search-form-col">
<div class="form-group">
<label for="pxp-p-filter-size-max" class="d-none d-sm-inline-block">Lifestyle</label>
<select class="form-control" id="pxp-p-filter-lifestyle" name="pxp-p-filter-lifestyle" placeholder="Max">
<select class="form-control" id="pxp-p-filter-lifestyle" placeholder="Max">
<option> ~ Select ~ </option>
</select>
</div>
Expand Down Expand Up @@ -663,7 +664,7 @@ <h2 class="pxp-content-side-h2">{{total_count}} Results</h2>
<script>

var
range3 = document.getElementById('range3'),
range3 = document.getElementById('{{filterSortForm.disPro.id_for_label}}'),
rangeV3 = document.getElementById('rangeV3'),
setValue = ()=>{
var
Expand All @@ -681,7 +682,7 @@ <h2 class="pxp-content-side-h2">{{total_count}} Results</h2>
<script>

var
range4 = document.getElementById('range4'),
range4 = document.getElementById('{{filterSortForm.disAmen.id_for_label}}'),
rangeV4 = document.getElementById('rangeV4'),
setValue2 = ()=>{
var
Expand Down
Loading

0 comments on commit 2ada543

Please sign in to comment.