Thu, 29 Mar 2018 23:55:36 +0300
updates
busroute.py | file | annotate | diff | comparison | revisions | |
profiles/föli.ini | file | annotate | diff | comparison | revisions | |
service.py | file | annotate | diff | comparison | revisions | |
templates/stop_week.html | file | annotate | diff | comparison | revisions |
--- a/busroute.py Mon Mar 12 18:10:58 2018 +0200 +++ b/busroute.py Thu Mar 29 23:55:36 2018 +0300 @@ -7,16 +7,18 @@ region_info.read('regions.ini') def simplify_name(name): - name = profile['replacements'].get(name, name) - return name + return profile['replacements'].get(name, name) -def reduce_schedule(route, trip_length, whole = False, long = False): +def greatly_simplify_name(name): + return profile['more replacements'].get(name, simplify_name(name)) + +def reduce_schedule(route, trip_length, whole = False, format = 'medium'): priorities = profile['priorities'] length = ((trip_length / 600) * 3 + len(route) * 2) / 5 + have_already = set() + i = 0 if not route: return '' - have_already = set() - i = 0 while i < len(route): if route[i] in profile['replacements']: route[i] = profile['replacements'][route[i]] @@ -32,6 +34,8 @@ factor = 1 / max(f(i + 1) for i in range(len(route))) while float(priorities.get(route[-1], 0)) < 0: del route[-1] + if not route: + return '' destination = route[-1] for i, stop in enumerate(route): # muunna indeksi siten että myöhemmät alueet korostuvat @@ -50,27 +54,35 @@ for i, stop in enumerate(route) \ if reitti_arvot[stop] >= 1 ], key = lambda stop: -stop[1]) - if long: + if format == 'long': weights = weights[:4] + elif format == 'short': + weights = weights[:2] + # repeat for the second sign value + try: + if weights[1][0] != destination and weights[1][1] < (500 / length ** 1.15): + del weights[1] + except IndexError: + pass else: # enintään neljä tulee kylttiin weights = weights[:3] - # jos kolmas kylttiarvo ei ole tarpeeksi merkittävä suhteessa reitin pituuteen niin otetaan se pois + # if the third sign value is not significant enough, drop it try: if weights[2][0] != destination and weights[2][1] < (725 / length ** 0.8): del weights[2] except IndexError: pass + # repeat for the second sign value try: if weights[1][0] != destination and weights[1][1] < (500 / length ** 1.15): del weights[1] except IndexError: pass - # lajitellaan painoarvot uudestaan reittijärjestykseen jotta sign tulee oikeinpäin + # reorder to get the sign the right way around weights = sorted(weights, key = lambda weight_data: weight_data[2]) - # muodostetaan sign.. + # form the sign.. sign = [paino[0] for paino in weights] - to_place = sign[-1] old_sign = sign.copy() sign = [] for place in old_sign:
--- a/profiles/föli.ini Mon Mar 12 18:10:58 2018 +0200 +++ b/profiles/föli.ini Thu Mar 29 23:55:36 2018 +0300 @@ -19,7 +19,7 @@ oriniemi = 50 pikisaari = 50 häppilä = 20 -haarla = 40 +haarla = 20 ylioppilaskylä = 50 halinen = 70 kakskerta = 200 @@ -79,14 +79,14 @@ turkuhalli = 100 messukeskus = 100 naantalin pikatie = 25 -helsingin valtatie = 25 +helsingin valtatie = 20 lauste = 25 -pompo = 25 -loukinainen = 25 -tuorla = 25 -satakunnantie = 25 -hepokulta = 25 -nättinummi = 25 +pompo = 20 +loukinainen = 20 +tuorla = 20 +satakunnantie = 20 +hepokulta = 20 +nättinummi = 20 nummenmäki = 10 kurala = 10 itäharju = 25 @@ -95,17 +95,17 @@ iso-heikkilä = 25 patterinhaka = 25 illoinen = 50 -luolavuori = 25 -mäntymäki = 25 -kurjenmäki = 25 -kuusisto = 25 -rautatieasema = 25 -majakkaranta = 25 -itäranta = 25 -martti = 25 -vähä-heikkilä = 25 -särkilahti = 25 -urusvuori = 25 +luolavuori = 20 +mäntymäki = 20 +kurjenmäki = 20 +kuusisto = 20 +rautatieasema = 20 +majakkaranta = 20 +itäranta = 20 +martti = 20 +vähä-heikkilä = 20 +särkilahti = 20 +urusvuori = 20 port arthur = 50 brinkhall = 100 myllykylä = 100 @@ -129,11 +129,22 @@ paimion sairaala = paimio hanhijoki = paimio +[more replacements] +harjattula = kakskerta +brinkhall = kakskerta +myllykylä = kakskerta +hylkilahti = kakskerta +armonlaakso = kakskerta +laalahti = kakskerta +kupittaa as = kupittaa +vaala = lauste + [tr:fi:places] korpo = Korppoo pargas = Parainen nagu = Nauvo pärnäs = Pärnäinen +port arthur = Portsa lentoasema = Lentoasema satama = Satama kauppakeskus mylly = Mylly @@ -141,6 +152,8 @@ naantalin keskusta = Naantali raision keskusta = Raisio yli-maaria = Yli-Maaria +iso-heikkilä = Iso-Heikkilä +koski tl = Koski Tl [tr:fi:suffix-places] naantalin pikatie = pikatietä @@ -158,7 +171,7 @@ linja-autoasema = Bus station rautatieasema = Railway station kaarinan keskusta = Kaarina -pernon telakka = Perno docks +pernon telakka = Perno shipyard kasarmialue = Barracks area kupittaa as = Kupittaa station
--- 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/<reference>') -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/<reference>') +def bus_stop_description(reference): + from buses import bus_stops from pprint import pformat - return '<pre>' + \ - '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)) + '</pre>' + try: + bus_stop = bus_stops[reference] + except KeyError: + abort(404) + return '<pre>' + pformat(describe(bus_stop)) + '</pre>' @app.route('/api/describe_destination/<trip_reference>/<stop_reference>/<int:index>') 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/<trip_reference>')
--- a/templates/stop_week.html Mon Mar 12 18:10:58 2018 +0200 +++ b/templates/stop_week.html Thu Mar 29 23:55:36 2018 +0300 @@ -140,6 +140,33 @@ background-color: #331f29; color: white; } + + .routes-legend .route + { + font-weight: bold + } + + .routes-legend .night-route + { + color: blue + } + + .routes-legend + { + border-spacing: 0px; + border-collapse: separate; + width: 100%; + border: 1px solid rgba(0, 0, 0, 0.2); + } + + .routes-legend tr + { + width: 100%; + } + + .routes-legend td.description + { + } </style> </head> {% macro night_class(hour) %} @@ -147,6 +174,12 @@ night {% endif %} {% endmacro %} + +{% macro all_night_routes(entry, description) %} +{% if description['all-night-routes'](entry, description) %} +night-route +{% endif %} +{% endmacro %} <body> <table class='aikataulu' cellspacing="0"> <thead> @@ -155,6 +188,22 @@ <span><img src="../static/pysäkki.png" height="96" /> {{ref}} {{name}}</span> </th> </tr> + <tr> + <td colspan='100'> + <table class='routes-legend'> + {% for entry in description['description'] %} + <tr> + <td> + {% for route in entry[0] %} + <span class="route {{route in description['night-routes'] and 'night-route' or ''}}">{{ route }}</span> + {% endfor %} + </td> + <td class="{{all_night_routes(entry, description)}} description">{{ entry[1] }}</td> + </tr> + {% endfor %} + </table> + </td> + </tr> </thead> <tbody> {% for day in week %} @@ -166,7 +215,9 @@ <th class='hour-column {{night_class(hour)}}'>{{hour}}</th> {% for entry in hour_schedule %} <td class='minute-time'><span>{{'%02d' % entry['minute']}}</span></td> + {% if not description['simple'] %} <td class='minute-route'><span>{{entry['route']}}</span></td> + {% endif %} {% endfor %} </tr> {% endfor %}