예제 #1
0
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')
예제 #2
0
                        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)
예제 #3
0
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
예제 #4
0
    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)
예제 #5
0
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')