-
Notifications
You must be signed in to change notification settings - Fork 0
/
application.py
186 lines (170 loc) · 8.69 KB
/
application.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import urllib,urllib2, json, operator
from rtree import index
from flask import Flask, Response, render_template, jsonify
from statsd import statsd
try:
from flask.ext.cors import cross_origin
except:
from flask_cors import cross_origin
#set various global values
CART_API_URL = "http://data.sfgov.org/resource/rqzj-sfat.json?status=APPROVED" #don't advertize carts with expired permits!
NEARBY_LAT_DELTA = 0.005 #latitude difference used to locate nearby carts
NEARBY_LONG_DELTA = 0.005 #longitude difference used to locate nearby carts
CARTS = [] #list of maps containing cart info
TAG_INFO_FILE = "config/tag_info.json"
TAGS_BY_ITEM = {} #List of food items to be used on the front-end
TAGS_BY_TRUCK = {}
IDX = None
#API_KEY is specific to one AWS EC2 instance. Key will need to be changed to run on other hosts
API_KEY='AIzaSyDitLtRc9yi2JrNiXgC_tUnzJFZd8uVO4s'
#note to self - long is probably not the best x coordinate to use, as distance between degrees varies depending on latitude
def load_category_tags(file_location):
#set TAGS_BY_ITEM
json_file = open(file_location)
return json.load(json_file)
def get_distance_address(start_point, end_point):
distance_url = "http://maps.googleapis.com/maps/api/distancematrix/json?origins="+start_point+"&destinations="+end_point+"&mode=walking&language=en-EN&sensor=false&units=imperial&key="+API_KEY
result= json.load(urllib.urlopen(distance_url))
distance = result['rows'][0]['elements'][0]['distance']['text']
address = result['destination_addresses'][0]
return distance, address
def get_distances_and_addresses(start_point, destinations):
distance_url = "https://maps.googleapis.com/maps/api/distancematrix/json?origins="+start_point+"&destinations="
i=0
multiplier=0
distances = []
addresses = []
while i < len(destinations):
temp_distance_url = distance_url+destinations[i]
i+=1
for n in range(24):
if i < len(destinations):
temp_distance_url = temp_distance_url +'|'+ destinations[i]
i += 1
temp_distance_url = temp_distance_url + "&mode=walking&language=en-EN&sensor=false&units=imperial&key="+API_KEY
try:
result = json.load(urllib.urlopen(temp_distance_url))
for address in result['destination_addresses']:
addresses.append(address)
for element in result['rows'][0]['elements']:
distances.append(element['distance']['text'])
except Exception as ex:
pass
return distances, addresses
def load_cart_info(url):
#Create request object to request the aforementioned list
req = urllib2.Request(url)
req.add_header('Accept', 'application/json')
req.add_header('Content-type', 'application/x-www-form-CART_API_URLencoded')
#Make the request and save the results
res = urllib2.urlopen(req)
out = res.read()
#convert JSON to python dict
allCarts = json.loads(out)
#remove unnecessary fields and keep only carts with valid latitude/longitude (for showing on map)
carts = []
tag_index = {'Anything': []}
IDX = index.Index()
for cart in allCarts:
if 'latitude' in cart:
temp_cart = {}
temp_applicant_name = cart['applicant']
if 'DBA' in temp_applicant_name:
temp_applicant_name = temp_applicant_name.split('DBA')[-1][1:].strip()
elif 'dba' in temp_applicant_name:
temp_applicant_name = temp_applicant_name.split('dba')[-1][1:].strip()
temp_cart['applicant'] = temp_applicant_name
temp_cart['facilitytype'] = cart['facilitytype']
temp_cart['fooditems'] = cart['fooditems']
temp_cart['latitude'] = cart['latitude']
temp_cart['longitude'] = cart['longitude']
carts.append(temp_cart)
fooditems = temp_cart['fooditems'].lower()
tag_index['Anything'].append(len(carts)-1)
for tag in TAGS_BY_ITEM.keys():
matched = False
for item in TAGS_BY_ITEM[tag]:
if item in fooditems and not matched and 'except' not in fooditems:
matched = True
if tag in tag_index:
tag_index[tag].append(len(carts)-1)
else:
tag_index[tag] = [len(carts)-1]
x = float(temp_cart['longitude'])
y = float(temp_cart['latitude'])
#in order to index based on a point, x1 must = x2 and y1 must = y2 (box with no length/width)
IDX.insert(len(carts)-1,(x,y,x,y))
return (carts, tag_index, IDX)
def find_nearby_carts(longitude, latitude,index):
start_point = latitude, longitude
nearby_box = (longitude - NEARBY_LONG_DELTA, latitude - NEARBY_LAT_DELTA, longitude + NEARBY_LONG_DELTA, latitude + NEARBY_LAT_DELTA)
matching_indices = list(IDX.intersection(nearby_box))
return matching_indices
TAGS_BY_ITEM = load_category_tags(TAG_INFO_FILE)
print 'Loading cart info'
CARTS, TAGS_BY_TRUCK, IDX = load_cart_info(CART_API_URL)
print '%d carts loaded' % len(CARTS)
app = Flask(__name__)
@app.route('/')
@statsd.timed('cart-api.request_time', tags=['support','page:options'])
def showIndex():
options = {'/location/<lat_long>': 'Pass comma separated latitude,longitude value to get info for nearby carts.Values returned are in the format:{"data": [list of carts containing address, applicant, distance, facilitytype, fooditems, latitude, and longitude]}', '/location/<lat_long>/<category>': 'Returns all carts near comma separated latitude longitude matching a particular category. List of available categories can be found using the /categories option. Results formatted the same as /location/<lat_long>', '/categories': 'Returns categories in format "data"=[list of categories]'}
statsd.increment('cart-api.requests', tags=['support','page:options'])
return jsonify(options)
@app.route('/categories')
@statsd.timed('cart-api.request_time', tags=['support','page:categories'])
@cross_origin()
def showCategories():
categories = TAGS_BY_TRUCK.keys()
categories.sort()
statsd.increment('cart-api.requests', tags=['support','page:categories'])
return jsonify(data=categories)
@app.route('/truck/<int:index>')
@statsd.timed('cart-api.request_time', tags=['support','page:truck_info'])
def show_truck_info(index):
statsd.increment('cart-api.requests', tags=['support','page:truck_info'])
return jsonify(data=CARTS[index])
@app.route('/distance/<lat_long>')
@statsd.timed('cart-api.request_time', tags=['support','page:distance_test'])
def showDistance(lat_long):
start_point = lat_long
end_point = '37.7841316511211,-122.39591339799'
distance_url = "http://maps.googleapis.com/maps/api/distancematrix/json?origins="+start_point+"&destinations="+end_point+"&mode=walking&language=en-EN&sensor=false&units=imperial&key="+API_KEY
result= json.load(urllib.urlopen(distance_url))
statsd.increment('cart-api.requests', tags=['support','page:distance_test'])
return jsonify(result)
@app.route('/location/<lat_long>')
@app.route('/location/<lat_long>/<category>')
@statsd.timed('cart-api.request_time', tags=['support','page:nearby_carts'])
@cross_origin()
def send_nearby_carts(lat_long, category='Anything'):
if category not in TAGS_BY_TRUCK:
return jsonify(data='Valid categories: %s' % str(TAGS_BY_TRUCK.keys()))
lat_long = lat_long.split(',')
latitude = float(lat_long[0])
longitude = float(lat_long[1])
unsorted_result=[]
unsorted_result_lat_long=[]
result_feet = []
result_miles = []
for index in find_nearby_carts(longitude, latitude, IDX):
if index in TAGS_BY_TRUCK[category]:
unsorted_result.append(CARTS[index])
unsorted_result_lat_long.append(CARTS[index]['latitude']+','+CARTS[index]['longitude'])
distances, addresses = get_distances_and_addresses(lat_long[0]+','+lat_long[1], unsorted_result_lat_long)
if len(distances) == len(unsorted_result):
for i in range(len(unsorted_result)):
unsorted_result[i]['distance'] = distances[i]
unsorted_result[i]['address'] = addresses[i]
if 'ft' in unsorted_result[i]['distance']:
result_feet.append(unsorted_result[i])
else:
result_miles.append(unsorted_result[i])
result_feet.sort(key=operator.itemgetter('distance'))
result_miles.sort(key=operator.itemgetter('distance'))
result = result_feet + result_miles
else:
result = unsorted_result
statsd.increment('cart_api.requests', tags=['support','page:nearby_carts'])
return jsonify(data=result)
app.run(host='0.0.0.0')