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

View File

@ -65,7 +65,8 @@ class GeoAPI:
def get_coordinates_from_query(self, location_string):
try:
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:
return None
return result["lat"], result["lon"]
@ -89,6 +90,36 @@ class GeoAPI:
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__":
geo = GeoAPI(debug=False)
print(geo.get_coordinates_from_query("12101"))

View File

@ -1,7 +1,7 @@
import logging
from django.utils.translation import gettext_lazy as _
from .geo import GeoAPI
from .geo import GeoAPI, LocationProxy
from ..forms import AdoptionNoticeSearchForm
from ..models import SearchSubscription, AdoptionNotice, AdoptionNoticeNotification, SexChoicesWithAll, Location
@ -21,16 +21,20 @@ def notify_search_subscribers(adoption_notice: AdoptionNotice):
class Search:
def __init__(self):
def __init__(self, request=None, search_subscription=None):
self.sex = None
self.area_search = None
self.max_distance = None
self.location_string = None
self.search_position = None
self.location = None
self.place_not_found = False # Indicates that a location was given but could not be geocoded
self.search_form = None
if request:
self.search_from_request(request)
elif search_subscription:
self.search_from_search_subscription(search_subscription)
def __str__(self):
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
if self.area_search and not self.place_not_found:
# 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 True
@ -82,9 +86,9 @@ class Search:
self.location_string = self.search_form.cleaned_data["location_string"]
self.max_distance = int(self.search_form.cleaned_data["max_distance"])
geo_api = GeoAPI()
self.search_position = geo_api.get_coordinates_from_query(self.location_string)
if self.search_position is None:
try:
self.location = LocationProxy(self.location_string)
except ValueError:
self.place_not_found = True
else:
self.search_form = AdoptionNoticeSearchForm()
@ -94,7 +98,6 @@ class Search:
search = Search()
search.sex = search_subscription.sex
search.location = search_subscription.location
search.search_position = (search_subscription.location.latitude, search_subscription.location.longitude)
search.area_search = True
search.max_distance = search_subscription.max_distance
@ -104,7 +107,8 @@ class Search:
def subscribe(self, user):
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,
location=self.location,
sex=self.sex,
@ -115,7 +119,6 @@ class Search:
Returns true if a user is already subscribed to a search with these parameters
"""
user_subscriptions = SearchSubscription.objects.filter(owner=user)
self._locate()
for subscription in user_subscriptions:
if self == subscription:
return True