Skip to content

Commit edd5443

Browse files
committed
Added functionality to search in ReST.
Details: - Factored out common code into search.py - Added functionality - Search using query - Search using report id - Flushed out query priorities
1 parent 1491368 commit edd5443

File tree

3 files changed

+118
-34
lines changed

3 files changed

+118
-34
lines changed

Diff for: mainapp/search.py

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'''
2+
module containing search funcions and exceptions for fixmystreet.ca
3+
'''
4+
5+
from mainapp.models import GoogleAddressLookup, Ward
6+
7+
class SearchException(Exception):
8+
pass
9+
10+
class SearchAddressException(SearchException):
11+
pass
12+
13+
class SearchAddressDisambiguateError(SearchAddressException):
14+
pass
15+
16+
class SearchAddressNotSupported(SearchException):
17+
pass
18+
19+
def search_address(address, match_index=-1, disambiguate=[]):
20+
'''
21+
Tries to parse `address` returning a single point, if the address is
22+
ambigous `match_index` will be used to pick out the correct address. if
23+
`match_index` is -1 or not 0 <= `match_index` < len(possible_addresses) the
24+
disambiguate list is populated with possible addresses and a
25+
`SearchAddressDisambiguateError` is raised.
26+
'''
27+
address_lookup = GoogleAddressLookup( address )
28+
29+
if not address_lookup.resolve():
30+
raise SearchAddressException("Sorry, we couldn\'t retreive the coordinates of that location, please use the Back button on your browser and try something more specific or include the city name at the end of your search.")
31+
32+
if not address_lookup.exists():
33+
raise SearchAddressException("Sorry, we couldn\'t find the address you entered. Please try again with another intersection, address or postal code, or add the name of the city to the end of the search.")
34+
35+
if address_lookup.matches_multiple() and match_index == -1:
36+
disambiguate += address_lookup.get_match_options()
37+
raise SearchAddressDisambiguateError('Cannot pinpoint given address')
38+
39+
point_str = "POINT(" + address_lookup.lon(match_index) + " " + address_lookup.lat(match_index) + ")"
40+
return point_str
41+
42+
def search_wards(point_str):
43+
'''
44+
returns a list of wards that contain `point_str`
45+
'''
46+
wards = Ward.objects.filter(geom__contains=point_str)
47+
if (len(wards) == 0):
48+
raise SearchAddressNotSupported("Sorry, we don't yet have that area in our database. Please have your area councillor contact fixmystreet.ca.")
49+
return wards

Diff for: mainapp/views/main.py

+22-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.shortcuts import render_to_response, get_object_or_404
22
from django.http import HttpResponseRedirect
3-
from mainapp.models import Report, ReportUpdate, Ward, FixMyStreetMap, ReportCountQuery, City, FaqEntry, GoogleAddressLookup
3+
from mainapp.models import Report, ReportUpdate, Ward, FixMyStreetMap, ReportCountQuery, City, FaqEntry
4+
from mainapp import search
45
from django.template import Context, RequestContext
56
from django.contrib.gis.measure import D
67
from django.contrib.gis.geos import *
@@ -36,34 +37,32 @@ def search_address(request):
3637
# return HttpResponseRedirect("/search?q=" + address )
3738

3839
address = request.GET["q"]
39-
address_lookup = GoogleAddressLookup( address )
40-
41-
if not address_lookup.resolve():
42-
return index(request, _("Sorry, we couldn\'t retreive the coordinates of that location, please use the Back button on your browser and try something more specific or include the city name at the end of your search."))
43-
44-
if not address_lookup.exists():
45-
return index( request, _("Sorry, we couldn\'t find the address you entered. Please try again with another intersection, address or postal code, or add the name of the city to the end of the search."))
46-
47-
if address_lookup.matches_multiple() and not request.GET.has_key("index"):
48-
addrs = address_lookup.get_match_options()
40+
41+
match_index = -1
42+
if request.GET.has_key("index"):
43+
match_index = int(request.GET["index"] or 0)
44+
45+
addrs = []
46+
try:
47+
point_str = search.search_address(address, match_index, addrs)
48+
except search.SearchAddressDisambiguateError, e:
49+
# addrs = address_lookup.get_match_options()
4950
addr_list = ""
5051
for i in range(0,len(addrs)):
5152
link = "/search?q=" + urlquote(address) + "&index=" + str(i)
5253
addr_list += "<li><a href='%s'>%s</a></li>" % ( link, addrs[i] )
5354
addr_list += "</ul>"
5455
return index(request,disambiguate = addr_list )
55-
56-
# otherwise, we have a specific match
57-
match_index = 0
58-
if request.GET.has_key("index"):
59-
match_index = int(request.GET["index"])
60-
61-
point_str = "POINT(" + address_lookup.lon(match_index) + " " + address_lookup.lat(match_index) + ")"
62-
pnt = fromstr(point_str, srid=4326)
63-
wards = Ward.objects.filter(geom__contains=point_str)
64-
if (len(wards) == 0):
65-
return( index(request, _("Sorry, we don't yet have that area in our database. Please have your area councillor contact fixmystreet.ca.")))
66-
56+
except search.SearchAddressException, e:
57+
return index(request, _(str(e)))
58+
59+
pnt = fromstr(point_str, srid=4326)
60+
61+
try:
62+
wards = search.search_wards(point_str)
63+
except search.SearchAddressNotSupported, e:
64+
return( index(request, _(str(e))))
65+
6766
reports = Report.objects.filter(is_confirmed = True,point__distance_lte=(pnt,D(km=4))).distance(pnt).order_by('distance')
6867
gmap = FixMyStreetMap(pnt,True,reports)
6968

Diff for: mainapp/views/rest.py

+47-11
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55
from django.forms.util import ErrorDict
66

77
from mainapp.models import Report
8+
from mainapp import search
89

910

11+
class InputValidationException(Exception):
12+
pass
13+
1014
class RestCollection(Collection):
1115
''' Subclasses Collection to provide multiple responders '''
1216
def __init__(self, queryset, responders=None, **kwargs):
@@ -33,24 +37,56 @@ def __call__(self, request, format, *args, **kwargs):
3337
for rest resources, such that it would match one of the keys
3438
in self.responders
3539
'''
40+
error_code = 400
41+
errors = ErrorDict({'info': ["An error has occured"]})
3642
if format in self.responders:
3743
self.responder = self.responders[format]
38-
return Collection.__call__(self, request, *args, **kwargs)
39-
errors = ErrorDict(
40-
{'info': ['Requested content type "%s" not available!' %format]})
41-
# Using the last used responder to reutrn a 415
42-
return self.responder.error(request, 415, errors)
44+
try:
45+
return Collection.__call__(self, request, *args, **kwargs)
46+
except InputValidationException, e:
47+
errors = ErrorDict({'info': [str(e)]})
48+
error_code = 412
49+
else:
50+
error_code = 415
51+
errors = ErrorDict(
52+
{'info': ['Requested content type "%s" not available!' %format]})
53+
# Using the last used responder to return error
54+
return self.responder.error(request, error_code, errors)
4355

4456

4557
class ReportRest(RestCollection):
4658

4759
def read(self, request):
48-
lon = request.GET["lon"]
49-
lat = request.GET["lat"]
50-
radius = float(request.GET.get('r', 4))
51-
point_str = "POINT(%s %s)" %(lon, lat)
52-
pnt = fromstr(point_str, srid=4326)
53-
reports = Report.objects.filter(is_confirmed = True,point__distance_lte=(pnt,D(km=radius))).distance(pnt).order_by('distance')
60+
ids = request.GET.getlist("id")
61+
if ids:
62+
try:
63+
ids = [int(id) for id in ids]
64+
except (TypeError, ValueError), e:
65+
raise InputValidationException(str(e))
66+
reports = Report.objects.filter(id__in = ids)
67+
# process ids right now
68+
else:
69+
lon = request.GET.get("lon")
70+
lat = request.GET.get("lat")
71+
address = request.GET.get("q")
72+
if lat and lon:
73+
point_str = "POINT(%s %s)" %(lon, lat)
74+
elif address:
75+
addrs = []
76+
match_index = int(request.GET.get('index', -1))
77+
try:
78+
point_str = search.search_address(address, match_index, addrs)
79+
except search.SearchAddressDisambiguateError, e:
80+
return self.responder.error(request, 412, ErrorDict({
81+
'info': [str(e)],
82+
'possible_addresses': addrs }))
83+
else:
84+
raise InputValidationException('Must supply either a `q`, `lat` `lon`, or a report `id`')
85+
86+
radius = float(request.GET.get('r', 4))
87+
pnt = fromstr(point_str, srid=4326)
88+
reports = Report.objects.filter(is_confirmed = True,point__distance_lte=(pnt,D(km=radius))).distance(pnt).order_by('distance')
89+
5490
return self.responder.list(request, reports)
5591

5692
reports_rest = ReportRest(

0 commit comments

Comments
 (0)