266 def is_weekend_night(time): |
266 def is_weekend_night(time): |
267 from datetime import timedelta |
267 from datetime import timedelta |
268 adjusted_time = time - timedelta(hours = 4, minutes = 30) |
268 adjusted_time = time - timedelta(hours = 4, minutes = 30) |
269 return adjusted_time.weekday() in [4, 5] and is_night_time(time) |
269 return adjusted_time.weekday() in [4, 5] and is_night_time(time) |
270 |
270 |
271 encircled = '\u24b6\u24b7\u24b8\u24b9\u24ba\u24bb\u24bc\u24bd\u24be\u24bf' \ |
271 #encircled = '\u24b6\u24b7\u24b8\u24b9\u24ba\u24bb\u24bc\u24bd\u24be\u24bf' \ |
272 '\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca\u24cb' \ |
272 # '\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca\u24cb' \ |
273 '\u24cc\u24cd\u24ce\u24cf' |
273 # '\u24cc\u24cd\u24ce\u24cf' |
|
274 #encircled = '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵' |
|
275 encircled = 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ' |
274 |
276 |
275 def encircle(char): |
277 def encircle(char): |
276 from string import ascii_uppercase |
278 from string import ascii_uppercase |
277 try: |
279 try: |
278 return encircled[ascii_uppercase.index(char.upper())] |
280 return encircled[ascii_uppercase.index(char.upper())] |
293 def describe(bus_stop, week_schedule): |
295 def describe(bus_stop, week_schedule): |
294 schedule = [] |
296 schedule = [] |
295 from collections import defaultdict, Counter |
297 from collections import defaultdict, Counter |
296 from busroute import simplify_name |
298 from busroute import simplify_name |
297 destinations_per_route = defaultdict(Counter) |
299 destinations_per_route = defaultdict(Counter) |
|
300 counts_per_variant = {} |
298 def route_key(route_ref): |
301 def route_key(route_ref): |
|
302 from math import log |
299 try: |
303 try: |
300 return parse_route_ref(route_ref) |
304 return () + parse_route_ref(route_ref) |
301 except ValueError: |
305 except ValueError: |
302 return () |
306 return () |
303 def filter_names(names): |
307 def filter_names(names): |
304 if len(names) == 1 and names[0] == (bus_stop.region and simplify_name(bus_stop.region)): |
308 if len(names) == 1 and names[0] == (bus_stop.region and simplify_name(bus_stop.region)): |
305 return type(names)() |
309 return type(names)() |
338 all_variants.append({ |
342 all_variants.append({ |
339 'name': route_name, |
343 'name': route_name, |
340 'destination': filter_names(destination), |
344 'destination': filter_names(destination), |
341 'count': count |
345 'count': count |
342 }) |
346 }) |
|
347 counts_per_variant[route_name] = count |
343 all_variants.sort(key = lambda k: k['count']) |
348 all_variants.sort(key = lambda k: k['count']) |
344 route_variant_count = len(all_variants) |
349 route_variant_count = len(all_variants) |
345 # Only consider variants so that they cover at least 99% of bus leaves |
350 # Only consider variants so that they cover at least 99% of bus leaves |
346 coverage = 0 |
351 coverage = 0 |
347 while all_variants: #coverage / num_leaves < 0.99: |
352 #while coverage / num_leaves < 0.99: |
348 variant = all_variants.pop() |
353 # variant = all_variants.pop() |
|
354 for variant in all_variants: |
349 routes_per_destination[variant['destination']].add(variant['name']) |
355 routes_per_destination[variant['destination']].add(variant['name']) |
350 coverage += variant['count'] |
356 coverage += variant['count'] |
351 for key in routes_per_destination: |
357 for key in routes_per_destination: |
352 routes_per_destination[key] = sorted(routes_per_destination[key], key = route_key) |
358 routes_per_destination[key] = sorted(routes_per_destination[key], key = route_key) |
353 def route_len(route): |
359 def route_len(route): |
358 else: |
364 else: |
359 break |
365 break |
360 return length or len(route) |
366 return length or len(route) |
361 from math import inf |
367 from math import inf |
362 def route_key(route): |
368 def route_key(route): |
|
369 from math import log |
363 return ( |
370 return ( |
364 route in night_routes, |
371 route in night_routes, |
|
372 counts_per_variant.get(route, 0) < 20, |
365 route_len(route), |
373 route_len(route), |
366 str(route) |
374 str(route) |
367 ) |
375 ) |
368 def routes_key(routes): |
376 def routes_key(routes): |
369 return min(route_key(route) for route in routes) |
377 return min(route_key(route) for route in routes) |
370 result = [] |
378 result = [] |
|
379 rare_variants = {variant['name'] for variant in all_variants if variant['count'] < 20} |
|
380 rare_variant_groups = set() |
371 for regions, routes in sorted( |
381 for regions, routes in sorted( |
372 routes_per_destination.items(), |
382 routes_per_destination.items(), |
373 key = lambda pair: routes_key(pair[1]) |
383 key = lambda pair: routes_key(pair[1]) |
374 ): |
384 ): |
|
385 routes_tuple = tuple(condense_route_list(sorted(routes, key = route_key))) |
375 result.append(( |
386 result.append(( |
376 list(condense_route_list(sorted(routes, key = route_key))), |
387 routes_tuple, |
377 ' - '.join(tr(region, 'region_short_name') for region in regions) |
388 ' - '.join(tr(region, 'region_short_name') for region in regions) |
378 )) |
389 )) |
|
390 if all(variant in rare_variants for variant in routes): |
|
391 rare_variant_groups.add(routes_tuple) |
379 return { |
392 return { |
380 'night-routes': night_routes, |
393 'night-routes': night_routes, |
381 'all-night-routes': lambda entry, description: all(route in description['night-routes'] for route in entry[0]), |
394 'all-night-routes': lambda entry, description: all(route in description['night-routes'] for route in entry[0]), |
382 'simple': route_variant_count <= 1, |
395 'simple': route_variant_count <= 1, |
383 'description': result, |
396 'description': result, |
384 'wtf': destinations_per_route, |
397 'wtf': destinations_per_route, |
385 'variant-map': {k:variant_names[v] for k, v in trip_mapping.items()}, |
398 'variant-map': {k:variant_names[v] for k, v in trip_mapping.items()}, |
|
399 'rare-variants': rare_variants, |
|
400 'rare-variant-groups': rare_variant_groups, |
386 } |
401 } |
387 |
402 |
388 @app.route('/stop_description/<reference>') |
403 @app.route('/stop_description/<reference>') |
389 def bus_stop_description(reference): |
404 def bus_stop_description(reference): |
390 from buses import bus_stops |
405 from buses import bus_stops |