def metrics_page(): metrics_start = time.time() route_id = request.args.get('route_id') if route_id is None: route_id = '12' start_stop_id = request.args.get('start_stop_id') if start_stop_id is None: start_stop_id = '3476' end_stop_id = request.args.get('end_stop_id') direction_id = request.args.get('direction_id') start_date_str = request.args.get('start_date') end_date_str = request.args.get('end_date') date_str = request.args.get('date') if date_str is not None: start_date_str = end_date_str = date_str else: if start_date_str is None: start_date_str = '2019-02-01' if end_date_str is None: end_date_str = start_date_str start_time_str = request.args.get( 'start_time') # e.g. "14:00" (24h time of day) end_time_str = request.args.get( 'end_time') # e.g. "18:00" (24h time of day) params = { 'start_stop_id': start_stop_id, 'end_stop_id': end_stop_id, 'route_id': route_id, 'direction_id': direction_id, 'start_date': start_date_str, 'end_date': end_date_str, 'start_time': start_time_str, 'end_time': end_time_str, } try: dates = util.get_dates_in_range(start_date_str, end_date_str) except Exception as ex: return Response(json.dumps({ 'params': params, 'error': str(ex), }, indent=2), status=400, mimetype='application/json') tz = pytz.timezone('US/Pacific') route_config = nextbus.get_route_config('sf-muni', route_id) start_stop_info = route_config.get_stop_info(start_stop_id) end_stop_info = route_config.get_stop_info( end_stop_id) if end_stop_id else None # 404 if the given stop isn't on the route # TODO: what should be done for the case where the start stop id is valid but the end stop id isn't? if start_stop_info is None: return Response(json.dumps( { 'params': params, 'error': f"Stop {start_stop_id} is not on route {route_id}", }, indent=2), status=404, mimetype='application/json') if direction_id is not None: dir_info = route_config.get_direction_info(direction_id) if dir_info is not None: dir_infos = [dir_info] else: dir_infos = [] else: # TODO: validation for end_stop_id directions if given (see trips.py) dirs = route_config.get_directions_for_stop(start_stop_id) dir_infos = [ route_config.get_direction_info(direction) for direction in dirs ] if end_stop_id: end_stop_dirs = route_config.get_directions_for_stop(end_stop_id) both_stops_same_dir = direction_id in end_stop_dirs directions = [{ 'id': dir_info.id, 'title': dir_info.title } for dir_info in dir_infos] headway_min_arr = [] waits = [] if end_stop_id: completed_trips = [] for d in dates: try: history = arrival_history.get_by_date('sf-muni', route_id, d) df = history.get_data_frame(start_stop_id, tz=tz, direction_id=direction_id, start_time_str=start_time_str, end_time_str=end_time_str) # get all headways for the selected stop (arrival time minus previous arrival time), computed separately for each day df['headway_min'] = metrics.compute_headway_minutes(df) # temporarily skip calculation of wait times until data is shown in front end waits.append( wait_times.get_waits(df, start_stop_info, d, tz, route_id, start_time_str, end_time_str)) if end_stop_id and both_stops_same_dir: trips = trip_times.get_trip_times(df, history, tz, start_stop_id, end_stop_id) completed_trips.append( trips.trip_min[trips.trip_min.notnull()]) headway_min = df.headway_min[df.headway_min.notnull( )] # remove NaN row (first bus of the day) headway_min_arr.append(df.headway_min) except FileNotFoundError as ex: return Response(json.dumps( { 'params': params, 'error': f"Arrival history not found for route {route_id} on {d.isoformat()}", }, indent=2), status=404, mimetype='application/json') except IndexError as ex: return Response(json.dumps( { 'params': params, 'error': f"No arrivals found for stop {start_stop_id} on route {route_id} in direction {direction_id} on {d.isoformat()}", }, indent=2), status=404, mimetype='application/json') headway_min = pd.concat(headway_min_arr) waits = pd.concat(waits) if end_stop_id and both_stops_same_dir: completed_trips = pd.concat(completed_trips) if headway_min.empty: return Response(json.dumps( { 'params': params, 'error': f"No arrivals for stop {start_stop_id} on route {route_id}", }, indent=2), status=404, mimetype='application/json') data = { 'params': params, 'route_title': route_config.title, 'start_stop_title': start_stop_info.title if start_stop_info else None, 'end_stop_title': end_stop_info.title if end_stop_info else None, 'directions': directions, 'headway_min': metrics.get_headways_stats(headway_min), 'wait_times': metrics.get_wait_times_stats(waits, tz), 'trip_times': metrics.get_trip_times_stats(completed_trips, start_stop_id, end_stop_id) if end_stop_id and both_stops_same_dir else None, } metrics_end = time.time() data['processing_time'] = (metrics_end - metrics_start) return Response(json.dumps(data, indent=2), mimetype='application/json')
dest='s3', action='store_true', help='store in s3') parser.set_defaults(s3=False) args = parser.parse_args() agencies = [config.get_agency(args.agency) ] if args.agency is not None else config.agencies if args.route is not None and args.agency is None: raise Exception("Must specify --agency with --route") date_str = args.date if args.date: dates = util.get_dates_in_range(args.date, args.date) elif args.start_date is not None and args.end_date is not None: dates = util.get_dates_in_range(args.start_date, args.end_date) else: raise Exception('missing date, start-date, or end-date') for agency in agencies: if args.agency is not None and args.route is not None: route_ids = args.route else: route_ids = [route.id for route in agency.get_route_list()] for d in dates: compute_arrivals(d, agency, route_ids, args.s3)
def metrics_page(): metrics_start = time.time() route_id = request.args.get('routeId') if route_id is None: route_id = '12' start_stop_id = request.args.get('startStopId') if start_stop_id is None: start_stop_id = '3476' end_stop_id = request.args.get('endStopId') direction_id = request.args.get('directionId') start_date_str = request.args.get('startDate') end_date_str = request.args.get('endDate') date_str = request.args.get('date') if date_str is not None: start_date_str = end_date_str = date_str else: if start_date_str is None: start_date_str = '2019-04-08' if end_date_str is None: end_date_str = start_date_str start_time_str = request.args.get( 'startTime') # e.g. "14:00" (24h time of day) end_time_str = request.args.get( 'endTime') # e.g. "18:00" or "03:00+1" (24h time of day) params = { 'startStopId': start_stop_id, 'endStopId': end_stop_id, 'routeId': route_id, 'directionId': direction_id, 'startDate': start_date_str, 'endDate': end_date_str, 'startTime': start_time_str, 'endTime': end_time_str, } data = {'params': params} try: route_config = nextbus.get_route_config('sf-muni', route_id) start_stop_info = route_config.get_stop_info(start_stop_id) if start_stop_info is None: raise errors.ValidationError( f"Stop {start_stop_id} is not on route {route_id}") data['startStopTitle'] = start_stop_info.title if end_stop_id: end_stop_info = route_config.get_stop_info(end_stop_id) if end_stop_info is None: raise errors.ValidationError( f"Stop {end_stop_id} is not on route {route_id}") data['endStopTitle'] = end_stop_info.title rng = metrics.Range( util.get_dates_in_range(start_date_str, end_date_str), start_time_str, end_time_str, pytz.timezone('US/Pacific')) route_metrics = metrics.RouteMetrics('sf-muni', route_id) keys = [ 'count', 'avg', 'min', 'median', 'max', 'percentiles', 'histogram' ] data['waitTimes'] = route_metrics.get_wait_time_stats( direction_id, start_stop_id, rng, keys) data['tripTimes'] = route_metrics.get_trip_time_stats( direction_id, start_stop_id, end_stop_id, rng, keys) data['headwayMin'] = route_metrics.get_headway_min_stats( direction_id, start_stop_id, rng, keys) except errors.ArrivalHistoryNotFoundError as ex: return make_error_response(params, str(ex), 404) except errors.ValidationError as ex: return make_error_response(params, str(ex), 400) metrics_end = time.time() data['processingTime'] = (metrics_end - metrics_start) res = Response(json.dumps(data, indent=2), mimetype='application/json') if not DEBUG: res.headers['Cache-Control'] = 'max-age=60' return res
version = args.version if version is None: version = arrival_history.DefaultVersion route_id = args.route date_str = args.date start_time_str = args.start_time end_time_str = args.end_time tz = pytz.timezone('US/Pacific') route_ids = [route_id] stop_rows = [] dates = util.get_dates_in_range(args.date, args.date) print(f"Date: {', '.join([str(date) for date in dates])}") print(f"Time of Day: [{start_time_str}, {end_time_str})") def render_distance(dist): return '----' if np.isnan(dist) else ('%3dm' % dist) for route_id in route_ids: route_config = nextbus.get_route_config(agency_id, route_id) df = pd.concat([ arrival_history.get_by_date(agency_id, route_id, d, version) \ .get_data_frame( start_time = util.get_timestamp_or_none(d, start_time_str, tz), end_time = util.get_timestamp_or_none(d, end_time_str, tz)
def metrics_by_interval(): route_id = request.args.get('routeId') if route_id is None: route_id = '12' start_stop_id = request.args.get('startStopId') if start_stop_id is None: start_stop_id = '3476' end_stop_id = request.args.get('endStopId') direction_id = request.args.get('directionId') start_date_str = request.args.get('startDate') end_date_str = request.args.get('endDate') date_str = request.args.get('date') if date_str is not None: start_date_str = end_date_str = date_str else: if start_date_str is None: start_date_str = '2019-04-08' if end_date_str is None: end_date_str = start_date_str start_time_str = request.args.get( 'startTime') # e.g. "14:00" (24h time of day) end_time_str = request.args.get( 'endTime') # e.g. "18:00" (24h time of day) params = { 'startStopId': start_stop_id, 'endStopId': end_stop_id, 'routeId': route_id, 'directionId': direction_id, 'startDate': start_date_str, 'endDate': end_date_str, 'date': date_str, 'startTime': start_time_str, 'endTime': end_time_str, } data = {'params': params} if start_time_str is not None and end_time_str is not None: # round start_time down and end_time up to allow for even intervals start_time = datetime.strptime(start_time_str, '%H:%M').replace(microsecond=0, second=0, minute=0) end_time = datetime.strptime(end_time_str, '%H:%M').replace( microsecond=0, second=0, minute=0) - timedelta(hours=1) time_str_intervals = [] while start_time.hour != end_time.hour + 1: time_str_intervals.append( (start_time.strftime('%H:%M'), (start_time + timedelta(seconds=3600)).strftime('%H:%M'))) start_time += timedelta(seconds=3600) else: if start_time_str or end_time_str: return make_error_response(params, f'Need both a start and end time', 404) time_str_intervals = constants.DEFAULT_TIME_STR_INTERVALS try: route_metrics = metrics.RouteMetrics('sf-muni', route_id) dates = util.get_dates_in_range(start_date_str, end_date_str) keys = ['count', 'avg', 'min', 'median', 'max', 'percentiles'] tz = pytz.timezone('US/Pacific') def get_interval_stats(start_time_str, end_time_str): rng = metrics.Range(dates, start_time_str, end_time_str, tz) return { 'startTime': start_time_str, 'endTime': end_time_str, 'waitTimes': route_metrics.get_wait_time_stats(direction_id, start_stop_id, rng, keys), 'tripTimes': route_metrics.get_trip_time_stats(direction_id, start_stop_id, end_stop_id, rng, keys), } data['intervals'] = [ get_interval_stats(start_time_str, end_time_str) for start_time_str, end_time_str in time_str_intervals ] except errors.ArrivalHistoryNotFoundError as ex: return make_error_response(params, str(ex), 404) except errors.ValidationError as ex: return make_error_response(params, str(ex), 400) return Response(json.dumps(data, indent=2), mimetype='application/json')