feat: use location proxy to make Location search interface more intuitive

This commit is contained in:
moanos [he/him] 2024-12-31 15:40:33 +01:00
parent 8e2c0e857c
commit 399ecf73ad
3 changed files with 63 additions and 24 deletions

View File

@ -11,6 +11,7 @@ from django.contrib.auth.models import AbstractUser
from .tools import misc, geo from .tools import misc, geo
from notfellchen.settings import MEDIA_URL from notfellchen.settings import MEDIA_URL
from .tools.geo import LocationProxy
class Language(models.Model): class Language(models.Model):
@ -45,26 +46,30 @@ class Location(models.Model):
def __str__(self): def __str__(self):
return f"{self.name} ({self.latitude:.5}, {self.longitude:.5})" return f"{self.name} ({self.latitude:.5}, {self.longitude:.5})"
@property
def position(self):
return (self.latitude, self.longitude)
@property @property
def str_hr(self): def str_hr(self):
return f"{self.name.split(',')[0]}" return f"{self.name.split(',')[0]}"
@staticmethod @staticmethod
def get_location_from_string(location_string): def get_location_from_string(location_string):
geo_api = geo.GeoAPI() try:
geojson = geo_api.get_geojson_for_query(location_string) proxy = LocationProxy(location_string)
if geojson is None: except ValueError:
return None return None
result = geojson[0] location = Location.get_location_from_proxy(proxy)
if "name" in result: return location
name = result["name"]
else: @staticmethod
name = result["display_name"] def get_location_from_proxy(proxy):
location = Location.objects.create( location = Location.objects.create(
place_id=result["place_id"], place_id=proxy.place_id,
latitude=result["lat"], latitude=proxy.latitude,
longitude=result["lon"], longitude=proxy.longitude,
name=name, name=proxy.name,
) )
return location return location

View File

@ -65,7 +65,8 @@ class GeoAPI:
def get_coordinates_from_query(self, location_string): def get_coordinates_from_query(self, location_string):
try: try:
result = \ result = \
self.requests.get(self.api_url, {"q": location_string, "format": "jsonv2"}, headers=self.headers).json()[0] self.requests.get(self.api_url, {"q": location_string, "format": "jsonv2"},
headers=self.headers).json()[0]
except IndexError: except IndexError:
return None return None
return result["lat"], result["lon"] return result["lat"], result["lon"]
@ -89,6 +90,36 @@ class GeoAPI:
return result return result
class LocationProxy:
"""
Location proxy is used as a precursor to the location model without the need to create unnecessary database objects
"""
def __init__(self, location_string):
"""
Creates the location proxy from the location string
"""
self.geo_api = GeoAPI()
geojson = self.geo_api.get_geojson_for_query(location_string)
if geojson is None:
raise ValueError
result = geojson[0]
if "name" in result:
self.name = result["name"]
else:
self.name = result["display_name"]
self.place_id = result["place_id"]
self.latitude = result["lat"]
self.longitude = result["lon"]
def __eq__(self, other):
return self.place_id == other.place_id
@property
def position(self):
return (self.latitude, self.longitude)
if __name__ == "__main__": if __name__ == "__main__":
geo = GeoAPI(debug=False) geo = GeoAPI(debug=False)
print(geo.get_coordinates_from_query("12101")) print(geo.get_coordinates_from_query("12101"))

View File

@ -1,7 +1,7 @@
import logging import logging
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .geo import GeoAPI from .geo import GeoAPI, LocationProxy
from ..forms import AdoptionNoticeSearchForm from ..forms import AdoptionNoticeSearchForm
from ..models import SearchSubscription, AdoptionNotice, AdoptionNoticeNotification, SexChoicesWithAll, Location from ..models import SearchSubscription, AdoptionNotice, AdoptionNoticeNotification, SexChoicesWithAll, Location
@ -21,16 +21,20 @@ def notify_search_subscribers(adoption_notice: AdoptionNotice):
class Search: class Search:
def __init__(self): def __init__(self, request=None, search_subscription=None):
self.sex = None self.sex = None
self.area_search = None self.area_search = None
self.max_distance = None self.max_distance = None
self.location_string = None
self.search_position = None
self.location = None self.location = None
self.place_not_found = False # Indicates that a location was given but could not be geocoded self.place_not_found = False # Indicates that a location was given but could not be geocoded
self.search_form = None self.search_form = None
if request:
self.search_from_request(request)
elif search_subscription:
self.search_from_search_subscription(search_subscription)
def __str__(self): def __str__(self):
return f"Search: {self.sex=}, {self.location=}, {self.search_position=}, {self.area_search=}, {self.max_distance=}" return f"Search: {self.sex=}, {self.location=}, {self.search_position=}, {self.area_search=}, {self.max_distance=}"
@ -57,7 +61,7 @@ class Search:
# make sure it's an area search and the place is found to check location # make sure it's an area search and the place is found to check location
if self.area_search and not self.place_not_found: if self.area_search and not self.place_not_found:
# If adoption notice is in not in search distance, return false # If adoption notice is in not in search distance, return false
if not adoption_notice.in_distance(self.search_position, self.max_distance): if not adoption_notice.in_distance(self.location.position, self.max_distance):
return False return False
return True return True
@ -82,9 +86,9 @@ class Search:
self.location_string = self.search_form.cleaned_data["location_string"] self.location_string = self.search_form.cleaned_data["location_string"]
self.max_distance = int(self.search_form.cleaned_data["max_distance"]) self.max_distance = int(self.search_form.cleaned_data["max_distance"])
geo_api = GeoAPI() try:
self.search_position = geo_api.get_coordinates_from_query(self.location_string) self.location = LocationProxy(self.location_string)
if self.search_position is None: except ValueError:
self.place_not_found = True self.place_not_found = True
else: else:
self.search_form = AdoptionNoticeSearchForm() self.search_form = AdoptionNoticeSearchForm()
@ -94,7 +98,6 @@ class Search:
search = Search() search = Search()
search.sex = search_subscription.sex search.sex = search_subscription.sex
search.location = search_subscription.location search.location = search_subscription.location
search.search_position = (search_subscription.location.latitude, search_subscription.location.longitude)
search.area_search = True search.area_search = True
search.max_distance = search_subscription.max_distance search.max_distance = search_subscription.max_distance
@ -104,7 +107,8 @@ class Search:
def subscribe(self, user): def subscribe(self, user):
logging.info(f"{user} subscribed to search") logging.info(f"{user} subscribed to search")
self._locate() if isinstance(self.location, LocationProxy):
self.location = Location.get_location_from_proxy(self.location)
SearchSubscription.objects.create(owner=user, SearchSubscription.objects.create(owner=user,
location=self.location, location=self.location,
sex=self.sex, sex=self.sex,
@ -115,7 +119,6 @@ class Search:
Returns true if a user is already subscribed to a search with these parameters Returns true if a user is already subscribed to a search with these parameters
""" """
user_subscriptions = SearchSubscription.objects.filter(owner=user) user_subscriptions = SearchSubscription.objects.filter(owner=user)
self._locate()
for subscription in user_subscriptions: for subscription in user_subscriptions:
if self == subscription: if self == subscription:
return True return True