service.py

changeset 87
9139a94e540c
parent 85
62e753b7d3ff
child 88
3b86597c5a88
--- 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>')

mercurial