Fri, 20 Apr 2018 13:50:02 +0300
show variants in week schedules
busroute.py | file | annotate | diff | comparison | revisions | |
service.py | file | annotate | diff | comparison | revisions |
--- a/busroute.py Thu Apr 19 14:44:54 2018 +0300 +++ b/busroute.py Fri Apr 20 13:50:02 2018 +0300 @@ -16,6 +16,18 @@ else: return region_name +def greatly_simplify_name(region_name): + region = regions.get(region_name) + if region: + if 'greater_-replacement' in region: + return simplify_name(region['greater_replacement']) + elif 'replacement' in region: + return simplify_name(region['replacement']) + else: + return simplify_name(region_name) + else: + return region_name + def reduce_schedule(route, trip_length, whole = False, format = 'medium'): length = ((trip_length / 600) * 3 + len(route) * 2) / 5 have_already = set()
--- a/service.py Thu Apr 19 14:44:54 2018 +0300 +++ b/service.py Fri Apr 20 13:50:02 2018 +0300 @@ -268,7 +268,29 @@ adjusted_time = time - timedelta(hours = 4, minutes = 30) return adjusted_time.weekday() in [4, 5] and is_night_time(time) -def describe(bus_stop): +encircled = '\u24b6\u24b7\u24b8\u24b9\u24ba\u24bb\u24bc\u24bd\u24be\u24bf' \ + '\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca\u24cb' \ + '\u24cc\u24cd\u24ce\u24cf' + +def encircle(char): + from string import ascii_uppercase + try: + return encircled[ascii_uppercase.index(char.upper())] + except IndexError: + return char + +def variant_abbreviations(variants): + '''Makes a mapping of route destination variants to letter abbreviations''' + suggestion = [variant[-1][0].upper() for variant in variants] + if len(set(suggestion)) != len(suggestion): + from string import ascii_uppercase + suggestion = ascii_uppercase + return dict(zip(variants, suggestion)) + +def schedule_entry_hash(schedule_entry): + return schedule_entry['trip'].name, schedule_entry['time'] + +def describe(bus_stop, week_schedule): schedule = [] from collections import defaultdict, Counter from busroute import simplify_name @@ -288,22 +310,44 @@ from collections import defaultdict night_routes = defaultdict(lambda: True) num_leaves = 0 - all_routes = Counter() - for schedule_entry in week_schedule(bus_stop, arrivals = True): #bus_stop.schedule(max_amount = 500, arrivals = True): + trip_mapping = {} + for schedule_entry in week_schedule: + from busroute import greatly_simplify_name sign_tuple = tuple(sign_elements(schedule_entry, format = 'short')) + sign_tuple = tuple(greatly_simplify_name(k) for k in sign_tuple) route = schedule_entry['trip'].route.reference night_routes[route] &= is_weekend_night(schedule_entry['time']) destinations_per_route[route][sign_tuple] += 1 - all_routes[route] += 1 + trip_mapping[schedule_entry_hash(schedule_entry)] = (route, sign_tuple) num_leaves += 1 night_routes = {key for key, value in night_routes.items() if value} routes_per_destination = defaultdict(set) - for route in destinations_per_route: - winner, count = destinations_per_route[route].most_common()[0] - if all_routes[route] >= 10 or all_routes[route] / num_leaves >= 0.01: - winner = filter_names(winner) - #destinations_per_route[route] = winner and ' - '.join(winner) or '' - routes_per_destination[winner].add(route) + all_variants = [] + variant_names = {} + # Collect all variants + for route, tally in destinations_per_route.items(): + variants = variant_abbreviations(tally.keys()) + winner, count = tally.most_common()[0] + for destination, count in tally.items(): + route_name = route + if destination != winner: + route_name += encircle(variants[destination]) + if route in night_routes: + night_routes.add(route_name) + variant_names[route, destination] = route_name + all_variants.append({ + 'name': route_name, + 'destination': filter_names(destination), + 'count': count + }) + all_variants.sort(key = lambda k: k['count']) + route_variant_count = len(all_variants) + # Only consider variants so that they cover at least 99% of bus leaves + coverage = 0 + while all_variants: #coverage / num_leaves < 0.99: + variant = all_variants.pop() + routes_per_destination[variant['destination']].add(variant['name']) + coverage += variant['count'] for key in routes_per_destination: routes_per_destination[key] = sorted(routes_per_destination[key], key = route_key) def route_len(route): @@ -316,7 +360,11 @@ return length or len(route) from math import inf def route_key(route): - return (route in night_routes, route_len(route), str(route)) + return ( + route in night_routes, + route_len(route), + str(route) + ) def routes_key(routes): return min(route_key(route) for route in routes) result = [] @@ -331,9 +379,10 @@ return { 'night-routes': night_routes, 'all-night-routes': lambda entry, description: all(route in description['night-routes'] for route in entry[0]), - 'simple': len(all_routes) <= 1, + 'simple': route_variant_count <= 1, 'description': result, 'wtf': destinations_per_route, + 'variant-map': {k:variant_names[v] for k, v in trip_mapping.items()}, } @app.route('/stop_description/<reference>') @@ -344,7 +393,7 @@ bus_stop = bus_stops[reference] except KeyError: abort(404) - return '<pre>' + pformat(describe(bus_stop)) + '</pre>' + return '<pre>' + pformat(describe(bus_stop, week_schedule(bus_stop, arrivals = True))) + '</pre>' @app.route('/api/describe_destination/<trip_reference>/<stop_reference>/<int:index>') def describe_destination(trip_reference, stop_reference, index): @@ -576,7 +625,9 @@ except KeyError: abort(404) week_model = {} - for schedule_entry in week_schedule(bus_stop, arrivals = True): + bus_stop_schedule = list(week_schedule(bus_stop, arrivals = True)) + description = describe(bus_stop, bus_stop_schedule) + for schedule_entry in bus_stop_schedule: route_ref = schedule_entry['trip'].route.reference if route_filter(route_ref) and dest_filter(schedule_entry['trip']): time = schedule_entry['time'] @@ -588,7 +639,7 @@ day_model[time.hour] = [] hour_model = day_model[time.hour] hour_model.append({ - 'route': route_ref, + 'route': description['variant-map'][schedule_entry_hash(schedule_entry)], 'route-splice': split_route_ref(route_ref), 'trip': schedule_entry['stop'].trip.name, 'night': is_night_time(schedule_entry['time']), @@ -635,7 +686,7 @@ name = tr(bus_stop.name, 'bus-stops'), tr = tr, week = week_model, - description = describe(bus_stop), + description = description, typename = bus_stop.typename, )