updates

Thu, 29 Mar 2018 23:55:36 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Thu, 29 Mar 2018 23:55:36 +0300
changeset 87
9139a94e540c
parent 86
302f840d6a65
child 88
3b86597c5a88

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 %}

mercurial