service.py

Sun, 17 Sep 2017 20:50:07 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Sun, 17 Sep 2017 20:50:07 +0300
changeset 63
20865458d936
parent 61
0c9b09f694f3
child 64
6dbda3b1015f
permissions
-rwxr-xr-x

Pöö 2

#!/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 configparser import ConfigParser
import locale

from misc import *
from busroute import reduce_schedule
import buses

app = Flask(__name__)
suffix_regions = {'naantalin pikatie', 'helsingin valtatie'}

# Varmista ettei järjestelmän kieliasetukset sotke muotoiluja
def reset_locale():
	locale.setlocale(locale.LC_ALL, locale.getdefaultlocale())

def activate_locale(language = None):
	language = language or language_for_page()
	class result:
		def __enter__(self):
			locale.setlocale(locale.LC_ALL, tr('locale', 'other', language = language))
		def __exit__(self, *args):
			reset_locale()
	return result()

reset_locale()

# Lataa käännökset
class Translator:
	def __init__(self):
		self.languages = {}
	def load_language(self, file_path):
		language_name = path.splitext(path.basename(file_path))[0]
		ini = ConfigParser()
		ini.read(path.join(file_path))
		self.languages[language_name] = ini
	def __call__(self, name, *sections, language = None):
		language = language or language_for_page()
		for section in sections:
			try:
				return self.languages[language][section][name]
			except KeyError:
				pass
		else:
			return name[0:1].upper() + name[1:]

tr = Translator()
for file in listdir('tr'):
	tr.load_language(path.join('tr', file))

def language_for_page():
	from flask import request
	if request.args.get('kääntämätön') is not None:
		return None
	else:
		for language_name in tr.languages:
			if request.args.get(language_name) is not None:
				return language_name
		else:
			return request.accept_languages.best_match(tr.languages)

def sign(schedule_entry):
	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)
	if sign:
		sign_representation = ' - '.join(tr(place, 'paikat') 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)
		#if len(sign_representation) > 25:
		#	k = ceil(len(sign) / 2)
		#	sign_representation = ' - '.join(sign[:k]) + '\n' + ' - '.join(sign[k:])
		return sign_representation
	else:
		return schedule_entry['trip'].schedule[-1].stop.name

def imminent(schedule_entry):
	return (schedule_entry['time'] - now()) <= timedelta(minutes = 3)

@app.route('/stop/<reference>')
def bus_stop_schedule(reference):
	from buses import bus_stops
	schedule = []
	try:
		bus_stop = bus_stops[reference]
	except KeyError:
		abort(404)
	for schedule_entry in bus_stop.schedule(max_amount = 100, arrivals = True):
		schedule.append({
			'time': time_representation(schedule_entry['time']),
			'route': schedule_entry['trip'].route.reference,
			'sign': sign(schedule_entry),
			'trip': schedule_entry['stop'].trip.name,
			'night': is_night_time(schedule_entry['time']),
			'imminent': imminent(schedule_entry),
		})
	return render_template(
		'stop.html',
		schedule = schedule,
		name = bus_stop.code + ' ' + tr(bus_stop.name, 'bus-stops'),
		link_to_map = bus_stop.location.link_to_map,
		region = bus_stop.region,
		location = bus_stop.location,
		cluster = bus_stop.cluster.url_name if len(bus_stop.cluster.stops) > 1 else None,
		tr = tr,
	)

def time_representation(time, relative = True):
	time_difference = time - now()
	if relative and timedelta(minutes = -1) < time_difference < timedelta(minutes = 1):
		return tr('right-now', 'misc-text')
	elif relative and time_difference > timedelta(0) and time_difference < timedelta(minutes = 10):
		return '%dm' % round(time_difference.seconds / 60)
	elif time.date() == today():
		return '%d:%02d' % (time.hour, time.minute)
	elif time_difference < timedelta(7):
		with activate_locale():
			return time.strftime('%-a %H:%M').replace(' ', '\xa0')
	else:
		with activate_locale():
			return time.strftime('%-d.%-m. %H:%M').replace(' ', '\xa0')

@app.route('/stop_cluster/<cluster_name>')
def cluster_schedule(cluster_name):
	from buses import bus_stops, clusters_by_name
	schedule = []
	try:
		cluster = clusters_by_name[cluster_name]
	except KeyError:
		abort(404)
	for schedule_entry in cluster.schedule(max_amount = 100):
		schedule.append({
			'time': time_representation(schedule_entry['time']),
			'route': schedule_entry['trip'].route.reference,
			'sign': sign(schedule_entry),
			'trip': schedule_entry['stop'].trip.name,
			'night': is_night_time(schedule_entry['time']),
			'stop_id': schedule_entry['stop'].stop.reference,
			'stop_code': schedule_entry['stop'].stop.code, 
			'stop_name': tr(schedule_entry['stop'].stop.name, 'bus-stops'),
			'imminent': imminent(schedule_entry),
		})
	stops_in_cluster = sorted(
		({
			'id': stop.reference,
			'code': stop.code,
			'name': tr(stop.name, 'bus-stops'),
		} for stop in cluster.stops),
		key = lambda stop: (len(stop['id']), stop['id'])
	)
	return render_template(
		'cluster.html',
		schedule = schedule,
		name = tr(cluster.name, 'paikat', 'pysäkkiryhmät', 'bus-stops'),
		link_to_map = cluster.center.link_to_map,
		location = cluster.center,
		stops_in_cluster = stops_in_cluster,
		amount_of_stops_in_cluster = len(stops_in_cluster),
		tr = tr,
	)

@app.route('/trip/<trip_reference>')
def trip(trip_reference):
	from flask import request
	from buses import all_trips
	from busroute import simplify_name
	try:
		trip = all_trips[trip_reference]
	except KeyError:
		abort(404)
	schedule = []
	region = ''
	for halt in trip.schedule:
		stop_time = datetime.combine(today(), time()) + halt.arrival_time
		formatted_time = time_representation(stop_time)
		if halt.stop.region != region and not (region and not halt.stop.region):
			if len(schedule) and not schedule[-1]['name']:
				schedule[-1]['name'] = tr(halt.stop.region or '', 'paikat')
			else:
				schedule.append({
					'name': tr(halt.stop.region or '', 'paikat'),
					'time': formatted_time,
					'stops': [],
					'index': len(schedule),
				})
			region = halt.stop.region
		schedule[-1]['stops'].append({
			'time': formatted_time,
			'id': halt.stop.reference,
			'code': halt.stop.code,
			'name': tr(halt.stop.name, 'bus_stops'),
		})
	sign = trip.concise_schedule()
	try:
		sign = [simplify_name(sign[0]), simplify_name(sign[-1])]
	except IndexError:
		sign = [trip.schedule[0].stop.name, trip.schedule[-1].stop.name]
	return render_template('trip.html',
		schedule = schedule,
		trip_reference = trip_reference,
		route = trip.route.reference,
		description = ' - '.join(tr(place, 'paikat') for place in sign),
		night = is_night_time(datetime.combine(today(), time()) + trip.schedule[-1].arrival_time),
		tr = tr,
		length = trip.length / 1000,
	)

@app.route('/route/<name>')
def route_page(name):
	from buses import routes
	route = routes[name.upper()]
	schedule = []
	for trip in route.trips:
		if trip.is_served_at(today()) and datetime.combine(today(), time()) + trip.schedule[-1].arrival_time < now():
			schedule.append({
				'name': trip.reference,
				'from': trip.from_place,
				'to': trip.to_place,
				'time': time_representation(datetime.combine(today(), time()) + trip.schedule[0].departure_time),
			})
	return render_template('route.html',
		name = route.reference + ' ' + route.description,
		tr = tr,
		schedule = schedule,
	)

@app.route('/')
def index():
	return redirect('stop_cluster/kauppatori')

@app.route('/pysäkki/<reference>')
def redirect_pysäkki(reference):
	return redirect('stop/' + str(reference))

@app.route('/pysäkkiryhmä/<reference>')
def redirect_pysäkkiryhmä(reference):
	return redirect('stop_cluster/' + str(reference))

@app.route('/ajovuoro/<reference>')
def redirect_ajovuoro(reference):
	return redirect('trip/' + str(reference))

@app.route('/static/<path:path>')
def static_file(path):
	return send_from_directory(path.join('static', path))

from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('gtfs_zip_path')
parser.add_argument('profile_path')

if __name__ == '__main__':
	parser.add_argument('-p', '--port', type = int, default = 5000)
	parser.add_argument('-d', '--debug', action = 'store_true')

args = parser.parse_args()
profile = ConfigParser()
profile.read(args.profile_path)
buses.load_buses(args.gtfs_zip_path, profile = profile)

if __name__ == '__main__':
	app.run(debug = args.debug, port = args.port)

mercurial