view globalneighbors/web.py @ 4:8e130b7bfed9

remove unintended boilerplate
author Jeff Hammel <k0scist@gmail.com>
date Sat, 24 Jun 2017 14:48:55 -0700
parents 1b94f3bf97e5
children 316e1d54ffd4
line wrap: on
line source

#!/usr/bin/env python

"""
web handler for GlobalNeighbors
"""

# imports
import argparse
import json
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


def autocomplete(cities, startswith=None):
    """autocomplete function for city names"""
    ### TODO: sort once, ahead of time

    if startswith:
        retval = []
        for i in cities:
            try:
                if i[name].startswith(startswith):
                    retval.append(i[name])
            except Exception as e:
                import pdb; pdb.set_trace()
        return sorted(retval)
    else:
        return sorted([i[name] for i in cities])


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,
                            startswith=startswith)

    def GET(self, request):
        return Response(content_type=self.content_type,
                        body=json.dumps(self.cities(
                            startswith=request.GET.get('q'))))


class GlobalHandler(Handler):
    """WSGI HTTP Handler"""

    content_type = 'text/plain'

    def __init__(self, datafile):
        self.datafile = datafile

        # parse data
        self.cities = read_city_list(self.datafile,
                                     fields=fields)
        self.locations = locations(self.cities)

        # TODO: declare handlers
        self.handlers = {'/cities':
                         CitiesHandler(self.locations,
                                       self.cities)}


    def GET(self, request):
        if request.path_info in ('', '/'):
            # Landing page
            return Response(content_type=self.content_type,
                            body="""Global Neighbors
                            Serving {} cities""".format(len(self.cities)))
        else:
            handler = self.handlers.get(request.path_info)
            if handler:
                return request.get_response(handler)



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)]")
    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()