--- a/schedule_viewer.py +++ b/schedule_viewer.py @@ -22,6 +22,8 @@ import BaseHTTPServer, sys, urlparse +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +from SocketServer import ForkingMixIn import bisect from gtfsscheduleviewer.marey_graph import MareyGraph import gtfsscheduleviewer @@ -57,34 +59,8 @@ return list(iterable) return simplejson.JSONEncoder.default(self, obj) -# Code taken from -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/425210/index_txt -# An alternate approach is shown at -# http://mail.python.org/pipermail/python-list/2003-July/212751.html -# but it requires multiple threads. A sqlite object can only be used from one -# thread. -class StoppableHTTPServer(BaseHTTPServer.HTTPServer): - def server_bind(self): - BaseHTTPServer.HTTPServer.server_bind(self) - self.socket.settimeout(1) - self._run = True - - def get_request(self): - while self._run: - try: - sock, addr = self.socket.accept() - sock.settimeout(None) - return (sock, addr) - except socket.timeout: - pass - - def stop(self): - self._run = False - - def serve(self): - while self._run: - self.handle_request() - +class ForkedHTTPServer(ForkingMixIn, HTTPServer): + """Handle requests in a separate forked process.""" def StopToTuple(stop): """Return tuple as expected by javascript function addStopMarkerFromList""" @@ -249,14 +225,14 @@ def handle_json_wrapper_GET(self, handler, parsed_params, handler_name): """Call handler and output the return value in JSON.""" schedule = self.server.schedule - # round times to nearest 100 seconds + # round times to nearest 1000 seconds - up to 17 minutes out of date if "time" in parsed_params: - parsed_params['time'] = int(round(float(parsed_params['time']),-2)) + parsed_params['time'] = int(round(float(parsed_params['time']),-3)) paramkey = tuple(sorted(parsed_params.items())) if handler_name in self.cache and paramkey in self.cache[handler_name] : print ("Cache hit for ",handler_name," params ",parsed_params) else: - print ("Cache miss for ",handler_name," params ",parsed_params) + print ("Cache miss for ",handler_name," params ",parsed_params) result = handler(parsed_params) if not handler_name in self.cache: self.cache[handler_name] = {} @@ -282,6 +258,23 @@ result.sort(key = lambda x: x[1:3]) return result + def handle_json_GET_routesearch(self, params): + """Return a list of routes with matching short name.""" + schedule = self.server.schedule + routeshortname = params.get('routeshortname', None) + result = [] + for r in schedule.GetRouteList(): + if r.route_short_name == routeshortname: + servicep = None + for t in schedule.GetTripList(): + if t.route_id == r.route_id: + servicep = t.service_period + break + result.append( (r.route_id, r.route_short_name, r.route_long_name, servicep.service_id) ) + result.sort(key = lambda x: x[1:3]) + return result + + def handle_json_GET_routerow(self, params): schedule = self.server.schedule route = schedule.GetRoute(params.get('route', None)) @@ -299,8 +292,19 @@ except: print "Error for GetStartTime of trip #" + t.trip_id + sys.exc_info()[0] else: - result.append ( (starttime, t.trip_id) ) - return sorted(result, key=lambda trip: trip[0]) + cursor = t._schedule._connection.cursor() + cursor.execute( + 'SELECT arrival_secs,departure_secs FROM stop_times WHERE ' + 'trip_id=? ORDER BY stop_sequence DESC LIMIT 1', (t.trip_id,)) + (arrival_secs, departure_secs) = cursor.fetchone() + if arrival_secs != None: + endtime = arrival_secs + elif departure_secs != None: + endtime = departure_secs + else: + endtime =0 + result.append ( (starttime, t.trip_id, endtime) ) + return sorted(result, key=lambda trip: trip[2]) def handle_json_GET_triprows(self, params): """Return a list of rows from the feed file that are related to this @@ -385,6 +389,7 @@ stops.append(s) if scale == 5: + print stops return [StopToTuple(s) for s in stops] else: dist_stop_list = [] @@ -396,6 +401,7 @@ elif dist < dist_stop_list[-1][0]: bisect.insort(dist_stop_list, (dist, s)) dist_stop_list.pop() # Remove stop with greatest distance + print dist_stop_list return [StopToTuple(s) for dist, s in dist_stop_list] def handle_json_GET_boundboxstops(self, params): @@ -466,6 +472,7 @@ if s.stop_id.lower() == query: return StopToTuple(s) return [] + def handle_json_GET_stoproutes(self, params): """Given a stop_id return all routes to visit the stop.""" schedule = self.server.schedule @@ -475,27 +482,32 @@ result = {} for trip in trips: route = schedule.GetRoute(trip.route_id) - if not trip.route_short_name+route.route_long_name in result: - result[trip.route_short_name+route.route_long_name] = (route.route_id, route.route_short_name, route.route_long_name, trip.trip_id) + if service_period == None or trip.service_id == service_period: + if not route.route_short_name+route.route_long_name+trip.service_id in result: + result[route.route_short_name+route.route_long_name+trip.service_id] = (route.route_id, route.route_short_name, route.route_long_name, trip.trip_id, trip.service_id) return result def handle_json_GET_stopalltrips(self, params): - """Given a stop_id return all trips to visit the stop.""" + """Given a stop_id return all trips to visit the stop (without times).""" schedule = self.server.schedule stop = schedule.GetStop(params.get('stop', None)) service_period = params.get('service_period', None) - time_trips = stop.GetStopTimeTrips(schedule) + trips = stop.GetTrips(schedule) + result = [] + for trip in trips: + if service_period == None or trip.service_id == service_period: + result.append((trip.trip_id, trip.service_id)) + return result + + def handle_json_GET_stopalltriptimes(self, params): + """Given a stop_id return all trips to visit the stop (with times). + DEPRECIATED?""" + schedule = self.server.schedule + stop = schedule.GetStop(params.get('stop', None)) + service_period = params.get('service_period', None) + time_trips = stop.GetStopTrips(schedule) result = [] for time, (trip, index), tp in time_trips: - headsign = None - # Find the most recent headsign from the StopTime objects - for stoptime in trip.GetStopTimes()[index::-1]: - if stoptime.stop_headsign: - headsign = stoptime.stop_headsign - break - # If stop_headsign isn't found, look for a trip_headsign - if not headsign: - headsign = trip.trip_headsign route = schedule.GetRoute(trip.route_id) trip_name = '' if route.route_short_name: @@ -507,6 +519,8 @@ if service_period == None or trip.service_id == service_period: result.append((time, (trip.trip_id, trip_name, trip.service_id), tp)) return result + + def handle_json_GET_stoptrips(self, params): """Given a stop_id and time in seconds since midnight return the next @@ -517,7 +531,6 @@ limit = int(params.get('limit', 15)) service_period = params.get('service_period', None) time_range = int(params.get('time_range', 24*60*60)) - filtered_time_trips = [] for trip, index in stop._GetTripIndex(schedule): @@ -526,20 +539,10 @@ time, stoptime, tp = trip.GetTimeInterpolatedStops()[index] if time > requested_time and time < (requested_time + time_range): bisect.insort(filtered_time_trips, (time, (trip, index), tp)) - result = [] for time, (trip, index), tp in filtered_time_trips: if len(result) > limit: break - headsign = None - # Find the most recent headsign from the StopTime objects - for stoptime in trip.GetStopTimes()[index::-1]: - if stoptime.stop_headsign: - headsign = stoptime.stop_headsign - break - # If stop_headsign isn't found, look for a trip_headsign - if not headsign: - headsign = trip.trip_headsign route = schedule.GetRoute(trip.route_id) trip_name = '' if route.route_short_name: @@ -548,9 +551,6 @@ if len(trip_name): trip_name += " - " trip_name += route.route_long_name - # comment out directions because we already have them in the long name - #if headsign: - # trip_name += " (Direction: %s)" % headsign if service_period == None or trip.service_id == service_period: result.append((time, (trip.trip_id, trip_name, trip.service_id), tp)) return result @@ -681,14 +681,13 @@ t0 = datetime.datetime.now() schedule.Load(options.feed_filename) print ("Loaded in", (datetime.datetime.now() - t0).seconds , "seconds") - server = StoppableHTTPServer(server_address=('', options.port), + server = ForkedHTTPServer(server_address=('', options.port), RequestHandlerClass=RequestHandlerClass) server.key = options.key server.schedule = schedule server.file_dir = options.file_dir server.host = options.host - server.feed_path = options.feed_filename - + server.feed_path = options.feed_filename print ("To view, point your browser at http://localhost:%d/" %