Mercurial > hg > GlobalNeighbors
view globalneighbors/web.py @ 10:21ed15391e8a
add a placeholder view for a city based on geoid
author | Jeff Hammel <k0scist@gmail.com> |
---|---|
date | Sun, 25 Jun 2017 12:28:36 -0700 |
parents | 254195d0bac2 |
children | d1b99c695511 |
line wrap: on
line source
#!/usr/bin/env python """ web handler for GlobalNeighbors """ # imports import argparse import json import os import sys import time from webob import Request, Response, exc from wsgiref import simple_server from .locations import locations from .locations import city_dict from .read import read_cities from .read import read_city_list from .schema import fields from .schema import name from .template import template_dir from .template import TemplateLoader here = os.path.dirname(os.path.abspath(__file__)) static_dir = os.path.join(here, 'static') class PassthroughFileserver(object): """serve files if they exist""" def __init__(self, app, directory=static_dir): self.app = app self.directory = directory self.fileserver = StaticURLParser(self.directory) def __call__(self, environ, start_response): path = environ['PATH_INFO'].strip('/') if path and os.path.exists(os.path.join(self.directory, path)) and '..' not in path: return self.fileserver(environ, start_response) return self.app(environ, start_response) def autocomplete(cities, startswith=None, limit=None): """autocomplete function for city names""" ### TODO: # - sort once, ahead of time # - return most populous cities if startswith: retval = [] for i in cities: if i[name].startswith(startswith): retval.append(i[name]) return sorted(retval)[:limit] else: return sorted([i[name] for i in cities])[:limit] class Handler(object): """base class for HTTP handler""" def __call__(self, environ, start_response): request = Request(environ) method = getattr(self, request.method, None) response = None if method is not None: response = method(request) if response is None: response = exc.HTTPNotFound() return response(environ, start_response) class CitiesHandler(Handler): """cities ReST API""" content_type = 'application/json' def __init__(self, locations, cities): self.locations = locations self._cities = cities def cities(self, startswith=None): """return list of cities""" return autocomplete(self._cities.values(), startswith=startswith) def GET(self, request): return Response(content_type=self.content_type, body=json.dumps(self.cities( startswith=request.GET.get('term')))) class GlobalHandler(Handler): """WSGI HTTP Handler""" content_type = 'text/html' def __init__(self, datafile, template_dir=template_dir): # parse data self.datafile = datafile self.cities = read_city_list(self.datafile, fields=fields) self.locations = locations(self.cities) # convert cities to a dict for lookup self.cities = {city['geonameid'] : city for city in self.cities} # declare handlers self.handlers = {'/cities': CitiesHandler(self.locations, self.cities)} # template loader self.loader = TemplateLoader(template_dir) self.index = self.loader.load('index.html') self.citypage = self.loader.load('city.html') def GET(self, request): if request.path_info in ('', '/'): # Landing page return Response(content_type=self.content_type, body=self.index.render(n_cities=len(self.cities))) elif request.path_info in self.handlers: return request.get_response(self.handlers[request.path_info]) else: try: geoid = int(request.path.strip('/')) city = self.cities.get(geoid) if not city: return return Response(content_type=self.content_type, body=self.citypage.render(**city)) except ValueError: pass def main(args=sys.argv[1:]): """CLI""" # parse command line parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('cities', help="cities1000 data file") parser.add_argument('-p', '--port', dest='port', type=int, default=8080, help="port to serve on") parser.add_argument('--hostname', dest='hostname', default='localhost', help="host name [DEFAULT: %(default)s]") options = parser.parse_args(args) # instantiate WSGI handler app = GlobalHandler(datafile=options.cities) # serve it (Warning! Single threaded!) server = simple_server.make_server(host='0.0.0.0', port=options.port, app=app) try: print ("Serving on http://{hostname}:{port}/".format(**options.__dict__)) server.serve_forever() except KeyboardInterrupt: pass if __name__ == '__main__': main()