def map_data(): """ Given a specific cuisine and a latitude, longitude, and diagonal bound in meters, return a dictionary mapping the ID of the restaurant to a pair of (longitude, latitude). Arguments: cuisine - A string representing the given restaurant type, e.g. 'pizza'. latitude - A float representing the latitude of the center of the map. longitude - A float representing the longitude of the center of the map. diag - The distance, in meters, from the northwest corner of the map to the southeast corner of the map. """ cuisine = request.args.get('cuisine') # Required latitude = request.args.get('lat') # Required longitude = request.args.get('lng') # Required diag = request.args.get('diag') # Required response = {} r = get_redis_client() ################################## TODO ################################### # https://redis.io/commands/georadius # GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key] response.update(r.georadius('geo:'+cuisine, longitude, latitude, float(diag)/2, unit='m', withcoord=True)) ############################### END TODO ################################### return json.dumps(response)
def cuisine_ratings(): """ Return JSON mapping each rating value (1-5) to the number of restaurants that have been rated with this rating. """ cuisine = request.args.get('cuisine') # Required response = {} r = get_redis_client() pipe = r.pipeline(transaction=False) # Batch together multiple requests ################################## TODO #################################### # https://redis.io/commands/zcount # ZCOUNT key min max # Set up the pipeline for i in range(1, 6): pipe.zcount('ratings:' + cuisine, i, i) results = pipe.execute() # Store the results into the response dictionary i = 1 for item in results: response[i] = item i += 1 ############################### END TODO ################################### return json.dumps(response, sort_keys=True)
def rating(): """ Get or set the restaurant's rating. If a rating is provided in input, set the rating for each cuisine sorted set and return it. Otherwise, look up the rating and return it as a string. """ id = request.args.get('id') # Required rating = request.args.get('rating') # Optional r = get_redis_client() # Fetch the cuisine for the restaurant data = get_restaurant_data(r, id, 'tag:cuisine') if 'tag:cuisine' not in data: return "0" cuisine = data['tag:cuisine'] # Set the rating if provided if rating: set_rating(r, cuisine, id, rating) # Always look up rating at the end to make sure it's set rating = get_rating(r, cuisine, id) if rating: return str(rating) else: return "0"
def restaurant_list(): """ Return JSON mapping restaurant identifiers to their public-facing names. To filter the set of restaurants, a cuisine type is provided, as well as the map's diagonal distance. Arguments: cuisine - A string representing the given restaurant type, e.g. 'pizza'. latitude - A float representing the latitude of the center of the map. longitude - A float representing the longitude of the center of the map. diag - The distance, in meters, from the northwest corner of the map to the southeast corner of the map. """ cuisine = request.args.get('cuisine') # Required longitude = request.args.get('lng') # Required latitude = request.args.get('lat') # Required diag = request.args.get('diag') # Required response = {} r = get_redis_client() # Use this LUA template and fill in the function calls to return id, name # pairs for entries that are geographically close. lua_script_template = Template(''' local results = {} for i, id in ipairs($redis_function_call1) do local name = $redis_function_call2 table.insert(results, id) table.insert(results, name) end return results''') ################################## TODO #################################### # https://redis.io/commands/eval # EVAL script numkeys key [key ...] arg [arg ...] # # The restaurant's name is stored in the property 'tag:name' lua_script = lua_script_template.substitute( redis_function_call1="redis.call('GEORADIUS', KEYS[1], ARGV[1], ARGV[2], ARGV[3], 'm')", redis_function_call2="redis.call('HGET', id, 'tag:name')" ) # EVAL call to Redis goes here and returns a list members = r.eval(lua_script, 1, 'geo:'+cuisine, longitude, latitude, float(diag)/2) ############################### END TODO ################################### it = iter(members) for item in it: name = next(it) # Some restaurants may not have a name - handle this gracefully if name is None: name = "Unknown "+cuisine response[item] = name return json.dumps(response, sort_keys=True)
def restaurant_detail(): id = request.args.get('id') # Required field = request.args.get('field') # Optional r = get_redis_client() response = get_restaurant_data(r, id, field) return json.dumps(response, sort_keys=True)
def import_data(filename): """Parse the restaurant information from the given OSM XML data file and store in Redis""" print(filename) global progress global stop r = get_redis_client() r.flushall() total_size = os.path.getsize(filename) print(total_size) with open(filename, 'r') as f: context = ET.iterparse(f, events=('start', 'end')) context = iter(context) event, root = next(context) cuisine_item = False # Whether the current item has a tag with key 'cuisine' node_item = False # Whether the current item has a 'node' tag for event, item in context: if stop: break # Process all nodes and their children progress = float(f.tell()) / total_size if event == 'start': if item.tag == 'node': node_item = True # Currently parsing a node continue elif not node_item: root.clear() # GC continue if item.tag == 'tag' and item.attrib['k'] == 'cuisine': cuisine_item = True if item.tag == 'node': if cuisine_item: cuisine_item = False process_node(r, item) root.clear() node_item = False if not stop: progress = 1 stop = False
def restaurant_frequencies(): """ Return JSON mapping cuisine to the number of restaurants serving that cuisine. The dictionary should contain the top cuisines in order by frequency. """ response = OrderedDict() # Dictionary that maintains order added r = get_redis_client() ################################## TODO #################################### # https://redis.io/commands/zcount # ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] response.update(r.zrevrangebyscore('cuisine_rankings', '+inf', '-inf', withscores=True)) ############################### END TODO ################################### return json.dumps(response)
def clear_ratings(): """ Clear all ratings, leaving other data intact. """ r = get_redis_client() ################################## TODO #################################### # https://redis.io/commands/scan # SCAN cursor [MATCH pattern] [COUNT count] # https://redis.io/commands/del # DEL key [key ...] for key in r.scan_iter('ratings:*'): r.delete(key) ############################### END TODO ################################### return json.dumps({ "result": "ok", })