diff -r 302f840d6a65 -r 9139a94e540c service.py --- a/service.py Mon Mar 12 18:10:58 2018 +0200 +++ b/service.py Thu Mar 29 23:55:36 2018 +0300 @@ -1,15 +1,16 @@ #!/usr/bin/env python3 from flask import Flask, render_template, abort, send_from_directory, redirect from datetime import datetime, date, time, timedelta -from os import path, listdir +from os import path, listdir, environ from configparser import ConfigParser import locale +app = Flask(__name__) + from misc import * from busroute import reduce_schedule import buses -app = Flask(__name__) suffix_regions = {'naantalin pikatie', 'helsingin valtatie', 'kansanpuisto'} # Varmista ettei järjestelmän kieliasetukset sotke muotoiluja @@ -64,16 +65,20 @@ else: return request.accept_languages.best_match(tr.languages) -def sign_elements(schedule_entry, long = False): +def sign_elements(schedule_entry, format = 'medium'): from math import ceil + from busroute import greatly_simplify_name trip_length = schedule_entry['trip'].length - schedule_entry['stop'].traveled_distance + regions = schedule_entry['trip'].concise_schedule(schedule_entry['stop']) + if format == 'short': + regions = [greatly_simplify_name(region) for region in regions] return reduce_schedule( - schedule_entry['trip'].concise_schedule(schedule_entry['stop']), + regions, trip_length = trip_length, - long = long) + format = format) -def sign(schedule_entry, long = False): - sign = sign_elements(schedule_entry, long = long) +def sign(schedule_entry, format = 'medium'): + sign = sign_elements(schedule_entry, format = format) if sign: sign_representation = ' - '.join(tr(place, 'places') for place in sign if place not in suffix_regions) sign_representation += ''.join(' ' + tr(place, 'suffix-places') for place in sign if place in suffix_regions) @@ -81,10 +86,10 @@ else: return schedule_entry['trip'].schedule[-1].stop.name -def long_form_sign(schedule_entry, long = True): +def long_form_sign(schedule_entry, format = 'long'): from math import ceil trip_length = schedule_entry['trip'].length - schedule_entry['stop'].traveled_distance - sign = reduce_schedule(schedule_entry['trip'].concise_schedule(schedule_entry['stop']), trip_length = trip_length, long = long) + sign = reduce_schedule(schedule_entry['trip'].concise_schedule(schedule_entry['stop']), trip_length = trip_length, format = format) if sign: return { 'destination': tr(sign[-1], 'places'), @@ -124,7 +129,7 @@ remaining_length = trip.length if starting_halt: remaining_length -= starting_halt.traveled_distance - places = reduce_schedule(trip.concise_schedule(starting_stop = starting_halt), trip_length = remaining_length, long = False) + places = reduce_schedule(trip.concise_schedule(starting_stop = starting_halt), trip_length = remaining_length, format = 'medium') new_places = set(places) - set(entries) if not new_places or places == old_places: break @@ -194,16 +199,38 @@ except AttributeError: raise ValueError(route_ref) -@app.route('/stop_description/') -def bus_stop_description(reference): - from buses import bus_stops +def condense_route_list(route_list): + def prepare_range_pool(range_pool): + if len(range_pool) < 3: + yield from map(str, range_pool) + else: + yield str(min(range_pool)) + '-' + str(max(range_pool)) + range_pool = [] + for route in route_list: + try: + route_int = int(route) + except ValueError: + yield from prepare_range_pool(range_pool) + range_pool = [] + yield route + else: + if not range_pool or route_int - 1 in range_pool: + range_pool.append(route_int) + else: + yield from prepare_range_pool(range_pool) + range_pool = [route_int] + if range_pool: + yield from prepare_range_pool(range_pool) + +def is_weekend_night(time): + from datetime import timedelta + adjusted_time = time - timedelta(hours = 4, minutes = 30) + return adjusted_time.weekday() in [4, 5] and is_night_time(time) + +def describe(bus_stop): schedule = [] - try: - bus_stop = bus_stops[reference] - except KeyError: - abort(404) from collections import defaultdict, Counter - from busroute import simplify_name + from busroute import greatly_simplify_name destinations_per_route = defaultdict(Counter) def route_key(route_ref): try: @@ -211,7 +238,7 @@ except ValueError: return () def filter_names(names): - if len(names) == 1 and names[0] == (bus_stop.region and simplify_name(bus_stop.region)): + if len(names) == 1 and names[0] == (bus_stop.region and greatly_simplify_name(bus_stop.region)): return type(names)() else: return names @@ -220,31 +247,63 @@ 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): - sign_tuple = tuple(sign_elements(schedule_entry)) - night_routes[schedule_entry['trip'].route.reference] &= is_night_time(schedule_entry['time']) - #for entry in sign_tuple: - # if entry not in names: - # names.append(entry) - #sign_tuple = tuple(names.index(place) for place in sign_tuple) - destinations_per_route[schedule_entry['trip'].route.reference][sign_tuple] += 1 + sign_tuple = tuple(sign_elements(schedule_entry, format = 'short')) + 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 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 count >= 10 or count / num_leaves >= 0.01: + 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) for key in routes_per_destination: routes_per_destination[key] = sorted(routes_per_destination[key], key = route_key) + def route_len(route): + length = 0 + for char in route: + if char.isdigit(): + length += 1 + else: + break + return length or len(route) + from math import inf + def route_key(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 = [] + for regions, routes in sorted( + routes_per_destination.items(), + key = lambda pair: routes_key(pair[1]) + ): + result.append(( + list(condense_route_list(sorted(routes, key = route_key))), + ' - '.join(tr(region, 'regions') for region in regions) + )) + 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, + 'description': result, + 'wtf': destinations_per_route, + } + +@app.route('/stop_description/') +def bus_stop_description(reference): + from buses import bus_stops from pprint import pformat - return '
' + \
-		'names: ' + str(names) + '\n '+ \
-		'night_routes: ' + str(night_routes) + '\n '+ \
-		'destinations_per_route: ' + pformat(dict(destinations_per_route)) + '\n' + \
-		'routes_per_destination: ' + pformat(dict(routes_per_destination)) + '
' + try: + bus_stop = bus_stops[reference] + except KeyError: + abort(404) + return '
' + pformat(describe(bus_stop)) + '
' @app.route('/api/describe_destination///') def describe_destination(trip_reference, stop_reference, index): @@ -294,7 +353,7 @@ 'time_data': schedule_entry['time'], 'time': time_representation(schedule_entry['time']), 'route': schedule_entry['trip'].route.reference, - 'sign': long_form_sign(schedule_entry, long = False), + 'sign': long_form_sign(schedule_entry, format = 'medium'), 'trip': schedule_entry['stop'].trip.name, 'night': is_night_time(schedule_entry['time']), 'imminent': imminent(schedule_entry), @@ -534,6 +593,7 @@ name = tr(bus_stop.name, 'bus-stops'), tr = tr, week = week_model, + description = describe(bus_stop), ) @app.route('/trip/')