- Ajovuoroa ei enää esitetä kahdessa välilehdessä vaan puukuvaimessa

Wed, 28 Jun 2017 12:20:05 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Wed, 28 Jun 2017 12:20:05 +0300
changeset 31
60045b362d71
parent 30
a5bfd99bc2a3
child 32
ca1a0ea81cf6

- Ajovuoroa ei enää esitetä kahdessa välilehdessä vaan puukuvaimessa
- gtfs.zip ei enää tarvitse avata

Makefile file | annotate | diff | comparison | revisions
buses.py file | annotate | diff | comparison | revisions
busroute.py file | annotate | diff | comparison | revisions
compute-regions.py file | annotate | diff | comparison | revisions
region-representatives.json file | annotate | diff | comparison | revisions
regions.gmp file | annotate | diff | comparison | revisions
service.py file | annotate | diff | comparison | revisions
static/style.css file | annotate | diff | comparison | revisions
templates/cluster.html file | annotate | diff | comparison | revisions
templates/trip.html file | annotate | diff | comparison | revisions
tr/en.ini file | annotate | diff | comparison | revisions
tr/fi.ini file | annotate | diff | comparison | revisions
tr/sv.ini file | annotate | diff | comparison | revisions
--- a/Makefile	Sat Jun 24 19:38:05 2017 +0300
+++ b/Makefile	Wed Jun 28 12:20:05 2017 +0300
@@ -1,14 +1,10 @@
-all: gtfs regions-per-stop.json
+all: gtfs.zip regions-per-stop.json
 
-regions-per-stop.json: region-representatives.json regions.gmp gtfs
-	./compute-regions.py > regions-per-stop.json
-
-gtfs: gtfs.zip
-	unzip gtfs.zip -d gtfs
+regions-per-stop.json: region-representatives.json regions.gmp gtfs.zip
+	./compute-regions.py > regions-per-stop.json || rm -f regions-per-stop.json
 
 gtfs.zip:
 	wget http://data.foli.fi/gtfs/gtfs.zip
 
 clean:
 	rm -f regions-per-stop.json gtfs.zip
-	rm -rf gtfs
--- a/buses.py	Sat Jun 24 19:38:05 2017 +0300
+++ b/buses.py	Wed Jun 28 12:20:05 2017 +0300
@@ -67,7 +67,7 @@
 		self.involved_trips = set()
 	def __repr__(self):
 		return 'bus_stops[%r]' % self.reference
-	def schedule(self, max_amount = 50):
+	def schedule(self, *, max_amount = 50, arrivals = False):
 		'''
 			Hakee tämän pysäkin seuraavat `määrä` lähtöä. Päätepysäkille saapuvia busseja ei
 			lasketa. Palauttaa pysähdykset listana jossa alkiot ovat muotoa (aika, halt),
@@ -85,7 +85,7 @@
 		while len(result) < max_amount:
 			try:
 				# hae nykyisen päivän aikataulut ja lisää ne,
-				result += self.schedule_for_day(date)
+				result += self.schedule_for_day(date, arrivals = arrivals)
 			except ValueError:
 				# paitsi jos mentiin kalenterin ulkopuolelle, jolloin lopetetaan,
 				break
@@ -93,7 +93,7 @@
 			date += timedelta(1)
 		# Typistä lopputulos haluttuun tulosmäärään.
 		return result[:max_amount]
-	def schedule_for_day(self, date):
+	def schedule_for_day(self, date, *, arrivals = False):
 		'''
 			Hakee pysäkin aikataulut tiettynä päivänä.
 		'''
@@ -109,7 +109,7 @@
 				# ja jos tämä trip pysähtyy tällä pysäkillä, ei kuitenkaan saapuen
 				# päätepysäkille,
 				stop = trip.contains_stop(self)
-				if stop and not stop.is_arrival: # stop is not trip.schedule[-1]:
+				if stop and (arrivals or not stop.is_arrival) and stop is not trip.schedule[-1]:
 					# ja jos tämä halt on tulevaisuudessa,
 					stop_time = datetime.combine(date, time()) + stop.arrival_time
 					if stop_time >= now():
@@ -294,10 +294,10 @@
 				self.stops |= other.stops
 				other.stops = set()
 				other._center = None
-			def schedule(self, max_amount = 50):
+			def schedule(self, *, max_amount = 50):
 				result = []
 				for stop in self.stops:
-					result += stop.schedule(max_amount)
+					result += stop.schedule(max_amount = max_amount)
 				result.sort(key = lambda schedule_entry: schedule_entry['time'])
 				return result[:max_amount]
 
--- a/busroute.py	Sat Jun 24 19:38:05 2017 +0300
+++ b/busroute.py	Wed Jun 28 12:20:05 2017 +0300
@@ -89,7 +89,8 @@
 		'Uittamo': 50,
 		'Piikkiö': 100,
 		'Paimio': 100,
-		'Pargas': 50,
+		'Pargas': 100,
+		'Nagu': 100,
 		'Yli-Maaria': 70,
 		'Saramäki': 50,
 		'Tarvasjoki': 100,
--- a/compute-regions.py	Sat Jun 24 19:38:05 2017 +0300
+++ b/compute-regions.py	Wed Jun 28 12:20:05 2017 +0300
@@ -2,6 +2,7 @@
 import sys, json
 from misc import *
 from geometry import *
+from zipfile import ZipFile
 
 with open('regions.gmp') as file:
 	data = file.read().split('@')
@@ -11,11 +12,12 @@
 
 bus_stops = {}
 
-with open('gtfs/stops.txt') as file:
-	for row in read_csv(file):
-		location = Sijainti(float(row['stop_lat']), float(row['stop_lon']))
-		reference = row['stop_id']
-		bus_stops[reference] = location
+with ZipFile('gtfs.zip') as archive:
+	with archive.open('stops.txt') as file:
+		for row in read_csv(map(bytes.decode, file)):
+			location = Sijainti(float(row['stop_lat']), float(row['stop_lon']))
+			reference = row['stop_id']
+			bus_stops[reference] = location
 
 district_shapes = []
 for polygon in data[1].splitlines():
--- a/region-representatives.json	Sat Jun 24 19:38:05 2017 +0300
+++ b/region-representatives.json	Wed Jun 28 12:20:05 2017 +0300
@@ -8,6 +8,11 @@
 	"Raadelma": "6153",
 	"Sipilänmäki": "6227",
 	"Piispanlähde": "6015",
+	"Kesämäki": "6060",
+	"Teerimäki": "6076",
+	"Lemu": "6279",
+	"Torppala": "6280",
+	"Kuusisto": "6216",
 	
 	"Paimio": "6619",
 	"Paimion sairaala": "6664",
@@ -93,6 +98,7 @@
 	"Katariina": "516",
 	"Kauppatori": "T1",
 	"Kerttuli": "867",
+	"Kirkkotien terveyskeskus": "218",
 	"Kultaranta": "3027",
 	"Koivula": "788",
 	"Kohmo": "1651",
@@ -104,7 +110,7 @@
 	"Kupittaanpuisto": "1972",
 	"Kurala": "1660",
 	"Kurjenmäki": "1688",
-	"Kuuvuori": "218",
+	"Kuuvuori": "64",
 	"Kähäri": "1022",
 	"Kärsämäki": "192",
 	"Käkölä": "3236",
@@ -167,6 +173,8 @@
 	"Rymättylä": "3151",
 	"Räntämäki": "1441",
 	"Röölä": "3166",
+	"Satakunnantie": "20",
+	"Teräsrautela": "808",
 	"Saaro": "556",
 	"Salonkylä": "3301",
 	"Saramäki": "250",
@@ -218,7 +226,8 @@
 	"Kårkulla": "8105",
 	"Kyrkäng": "8109",
 	"Kirjala": "8102",
-
+	"Nagu": "8950",
+	"Pärnäs": "8951",
 
 	"Poikko": "3184"
 }
--- a/regions.gmp	Sat Jun 24 19:38:05 2017 +0300
+++ b/regions.gmp	Wed Jun 28 12:20:05 2017 +0300
@@ -1,12 +1,11 @@
-roadmap^60.435782981983806, 22.304026704350125^15@polygon^#FF0000,5,1,#ff8800,0.4^^60.3977,22.24465~60.40772,22.24046~60.40792,22.26057~60.40076,22.27409~60.3952,22.25642
+roadmap^60.37820391301176, 22.37610302499832^13@undefined^#FF0000,5,1,#ff8800,0.4^^60.3977,22.24465~60.40772,22.24046~60.40792,22.26057~60.40076,22.27409~60.3952,22.25642
 polygon^#FF0000,5,1,#ff8800,0.4^^60.3977,22.22706~60.38719,22.23676~60.38341,22.22466~60.38518,22.21205~60.39359,22.20706~60.39816,22.21341
 polygon^#FF0000,5,1,#ff8800,0.4^^60.39868,22.21599~60.39804,22.22852~60.38876,22.23916~60.38952,22.24843~60.39329,22.25135~60.40217,22.23474~60.4019,22.21689
 polygon^#FF0000,5,1,#ff8800,0.4^^60.38871,22.16432~60.39236,22.18483~60.39972,22.188~60.40171,22.1823~60.39919,22.15413~60.39151,22.15633
 polygon^#FF0000,5,1,#ff8800,0.4^^60.42123,22.17264~60.41472,22.15422~60.40622,22.14277~60.40003,22.15599~60.40577,22.17143~60.41357,22.18174
-undefined^#FF0000,5,1,#ff8800,0.4^^60.43275,22.20869~60.4239,22.21632~60.41889,22.19831~60.42495,22.19216~60.43033,22.19648
+polygon^#FF0000,5,1,#ff8800,0.4^^60.43275,22.20869~60.4239,22.21632~60.41889,22.19831~60.42495,22.19216~60.43033,22.19648
 undefined^#FF0000,5,1,#ff8800,0.4^^60.42148,22.17333~60.42584,22.18964~60.41902,22.1971~60.41534,22.18056
 undefined^#FF0000,5,1,#ff8800,0.4^^60.49702,22.21496~60.49936,22.22028~60.50048,22.23744~60.49676,22.2392~60.49373,22.23435~60.49306,22.23554~60.49178,22.23402~60.48872,22.22903~60.48729,22.2221~60.49085,22.21505
-undefined^#FF0000,5,1,#ff8800,0.4^^60.45775,22.29049~60.4599,22.29787~60.45951,22.29945~60.45688,22.3026~60.45537,22.29751~60.45501,22.29585~60.45465,22.29285~60.45619,22.29141
 undefined^#FF0000,5,1,#ff8800,0.4^^60.45331,22.2865~60.45033,22.29068~60.45113,22.29322~60.45198,22.29413~60.45225,22.29741~60.45399,22.29853~60.45529,22.2975~60.45495,22.29585~60.45446,22.2915~60.45386,22.28832~60.45369,22.28748
 undefined^#FF0000,5,1,#ff8800,0.4^^60.4445,22.25538~60.44359,22.25256~60.44121,22.24824~60.43617,22.25517~60.43897,22.26358
 undefined^#FF0000,5,1,#ff8800,0.4^^60.4537,22.25043~60.45203,22.25283~60.45345,22.25685~60.45493,22.25432
@@ -49,9 +48,6 @@
 undefined^#FF0000,5,1,#ff8800,0.4^^60.44109,22.27813~60.44283,22.28328~60.43965,22.28611~60.44219,22.29813~60.43711,22.3013~60.43474,22.28554
 undefined^#FF0000,5,1,#ff8800,0.4^^60.44452,22.25624~60.44774,22.26929~60.44727,22.2704~60.4496,22.2777~60.44736,22.2807~60.44634,22.27856~60.44338,22.28208~60.44145,22.27749~60.43898,22.26476
 undefined^#FF0000,5,1,#ff8800,0.4^^60.47118,22.16063~60.47947,22.18281~60.47535,22.18783~60.4732,22.189~60.47133,22.1923~60.47014,22.19453~60.46911,22.19307~60.46615,22.19058~60.46354,22.18959~60.462,22.18921~60.46103,22.18338~60.46691,22.17196
-undefined^#FF0000,5,1,#ff8800,0.4^^60.48169,22.16286~60.47717,22.17423~60.4715,22.16041~60.47522,22.1529
-undefined^#FF0000,5,1,#ff8800,0.4^^60.48953,22.15058~60.49328,22.17831~60.48793,22.18101~60.48119,22.17755~60.48323,22.16537~60.48313,22.15071
-undefined^#FF0000,5,1,#ff8800,0.4^^60.48302,22.16453~60.48082,22.17753~60.48234,22.17873~60.48201,22.18088~60.47987,22.18217~60.47708,22.17531~60.48196,22.1631
 undefined^#FF0000,5,1,#ff8800,0.4^^60.48421,22.13076~60.48235,22.13745~60.47389,22.1541~60.46755,22.13231~60.47493,22.12153~60.48071,22.11884
 undefined^#FF0000,5,1,#ff8800,0.4^^60.47353,22.1541~60.47091,22.15934~60.46926,22.15591~60.46098,22.15119~60.46196,22.13721~60.46798,22.145
 undefined^#FF0000,5,1,#ff8800,0.4^^60.473,22.15251~60.46819,22.14438~60.46243,22.13627~60.46293,22.12142~60.46689,22.13144
@@ -59,7 +55,6 @@
 undefined^#FF0000,5,1,#ff8800,0.4^^60.45853,22.19019~60.45508,22.19054~60.45053,22.19882~60.44925,22.21523~60.45314,22.22142~60.45383,22.22435
 undefined^#FF0000,5,1,#ff8800,0.4^^60.46113,22.19127~60.46132,22.19824~60.46001,22.2073~60.45919,22.21152~60.45652,22.2088~60.45931,22.19073
 undefined^#FF0000,5,1,#ff8800,0.4^^60.46139,22.19131~60.46428,22.19195~60.46767,22.194~60.47022,22.19663~60.46369,22.20573~60.45959,22.21049~60.46167,22.19811
-undefined^#FF0000,5,1,#ff8800,0.4^^60.47602,22.25242~60.47484,22.26457~60.47306,22.26336~60.46917,22.26521~60.46496,22.26894~60.46259,22.26148~60.46492,22.24917~60.47087,22.25384~60.47163,22.25338
 undefined^#FF0000,5,1,#ff8800,0.4^^60.48884,22.18543~60.48132,22.18556~60.47867,22.1974~60.47689,22.20328~60.48104,22.21122~60.48704,22.20809~60.48823,22.19436
 undefined^#FF0000,5,1,#ff8800,0.4^^60.48122,22.18547~60.47853,22.19719~60.47645,22.20365~60.47299,22.19482
 undefined^#FF0000,5,1,#ff8800,0.4^^60.49404,22.26856~60.49248,22.28712~60.48973,22.30216~60.48271,22.29808~60.47627,22.28925~60.47537,22.27866~60.47564,22.26596~60.48309,22.27138~60.48695,22.27301
@@ -190,15 +185,12 @@
 undefined^#FF0000,5,1,#ff8800,0.4^^60.5154,22.37237~60.5143,22.39314~60.50793,22.40026~60.50015,22.38808~60.49888,22.37503~60.50081,22.36358~60.50646,22.359~60.51193,22.3607
 undefined^#FF0000,5,1,#ff8800,0.4^^60.55994,22.41537~60.555,22.44945~60.57174,22.46909~60.57958,22.43482~60.57043,22.42093
 undefined^#FF0000,5,1,#ff8800,0.4^^60.65712,22.51081~60.62803,22.52145~60.62278,22.60523~60.64753,22.63429~60.6662,22.61381~60.66486,22.54679
-undefined^#FF0000,5,1,#ff8800,0.4^^60.41478,22.35632~60.40355,22.35623~60.40425,22.39331~60.41563,22.39314
 undefined^#FF0000,5,1,#ff8800,0.4^^60.37586,23.08399~60.40826,23.09137~60.40088,23.20724~60.35661,23.16847
-undefined^#FF0000,5,1,#ff8800,0.4^^60.17772,21.45699~60.20832,21.56301~60.21663,21.71242~60.19172,21.88408~60.06724,21.92322~60.0621,21.4532~60.12773,21.4083
 undefined^#FF0000,5,1,#ff8800,0.4^^60.5184,22.29066~60.50809,22.2868~60.50501,22.26305~60.50686,22.23835~60.51316,22.23453~60.51705,22.24268
 undefined^#FF0000,5,1,#ff8800,0.4^^60.58368,22.71183~60.5907,22.72732~60.58924,22.75895~60.57904,22.75406~60.57702,22.72431
 undefined^#FF0000,5,1,#ff8800,0.4^^60.66418,23.1142~60.64273,23.11944~60.645,23.1651~60.66338,23.1621
 undefined^#FF0000,5,1,#ff8800,0.4^^60.47127,22.38121~60.46374,22.39039~60.46065,22.42348~60.46995,22.43357~60.48456,22.4226~60.48176,22.40141
 undefined^#FF0000,5,1,#ff8800,0.4^^60.59725,22.8871~60.59487,22.92104~60.58161,22.9095~60.57626,22.87997~60.58278,22.86698~60.59122,22.87079
-undefined^#FF0000,5,1,#ff8800,0.4^^60.47522,22.2368~60.4772,22.24399~60.4774,22.25062~60.4708,22.25337~60.46795,22.25135~60.4713,22.23669~60.47455,22.23838
 undefined^#FF0000,5,1,#ff8800,0.4^^60.48497,22.23079~60.47757,22.23225~60.47539,22.23637~60.47755,22.24371~60.47765,22.25101~60.4807,22.2507~60.48555,22.24749
 undefined^#FF0000,5,1,#ff8800,0.4^^60.4803,22.22448~60.47454,22.23796~60.46833,22.23435~60.47094,22.22033
 undefined^#FF0000,5,1,#ff8800,0.4^^60.48533,22.08042~60.48055,22.08599~60.48721,22.11904~60.48744,22.11588~60.48825,22.11327~60.4878,22.10522~60.48675,22.09685~60.48575,22.09462
@@ -230,4 +222,22 @@
 undefined^#FF0000,5,1,#ff8800,0.4^^60.45324,22.27609~60.45293,22.27966~60.45275,22.28014~60.45463,22.28203~60.45742,22.28287~60.45835,22.28304~60.45865,22.27854
 undefined^#FF0000,5,1,#ff8800,0.4^^60.44228,22.29847~60.44443,22.30868~60.43893,22.31718~60.43753,22.31481~60.43776,22.31424~60.43729,22.31155~60.43557,22.31207~60.43555,22.31654~60.43732,22.31509~60.4388,22.31754~60.43444,22.32246~60.43402,22.30368
 undefined^#FF0000,5,1,#ff8800,0.4^^60.43727,22.31164~60.4377,22.3142~60.43561,22.31633~60.43565,22.31219
+undefined^#FF0000,5,1,#ff8800,0.4^^60.45992,22.29797~60.4596,22.29947~60.45688,22.30291~60.45533,22.29768~60.45492,22.29541~60.45611,22.2943~60.45769,22.29258~60.45845,22.29274
+undefined^#FF0000,5,1,#ff8800,0.4^^60.45565,22.29187~60.45603,22.29421~60.45776,22.29241
+undefined^#FF0000,5,1,#ff8800,0.4^^60.47624,22.25182~60.47511,22.26259~60.46551,22.26783~60.4635,22.26281~60.46648,22.25036~60.47077,22.25405
+undefined^#FF0000,5,1,#ff8800,0.4^^60.46934,22.23543~60.47026,22.23609~60.46751,22.24691~60.46584,22.2509~60.46325,22.26299~60.46041,22.26624~60.46001,22.26549~60.4625,22.26286~60.46526,22.24964~60.46719,22.24547
+undefined^#FF0000,5,1,#ff8800,0.4^^60.48243,22.17861~60.47958,22.18204~60.47522,22.17045~60.47775,22.1646~60.4818,22.16964~60.48068,22.17768
+undefined^#FF0000,5,1,#ff8800,0.4^^60.47765,22.1641~60.4734,22.15647~60.47125,22.1602~60.47505,22.17021
+undefined^#FF0000,5,1,#ff8800,0.4^^60.48956,22.15075~60.49237,22.17243~60.4882,22.18397~60.48126,22.18487~60.48096,22.18352~60.48503,22.16991~60.47459,22.15683~60.47838,22.14752~60.48213,22.15128
+undefined^#FF0000,5,1,#ff8800,0.4^^60.47661,22.24395~60.47092,22.2442~60.46919,22.25191~60.47105,22.25339~60.47688,22.25072
+undefined^#FF0000,5,1,#ff8800,0.4^^60.47539,22.23701~60.4769,22.24347~60.47091,22.24367~60.46943,22.24973~60.46741,22.24789~60.47052,22.23623~60.47465,22.23851
+undefined^#FF0000,5,1,#ff8800,0.4^^60.41406,22.35246~60.41293,22.38932~60.40009,22.39785~60.39995,22.35018
+undefined^#FF0000,5,1,#ff8800,0.4^^60.42279,22.38198~60.41431,22.38639~60.41284,22.39108~60.41059,22.3964~60.41284,22.42172~60.41915,22.42558~60.42347,22.41571
+undefined^#FF0000,5,1,#ff8800,0.4^^60.4365,22.3866~60.43286,22.38798~60.43467,22.40756~60.43889,22.40205~60.43912,22.3941~60.43846,22.38893
+undefined^#FF0000,5,1,#ff8800,0.4^^60.20622,21.88622~60.18907,21.88725~60.18579,21.91975~60.1987,21.92636
+undefined^#FF0000,5,1,#ff8800,0.4^^60.20912,21.70418~60.07443,21.68495~60.10112,21.41399~60.17724,21.43634~60.20639,21.54419
+undefined^#FF0000,5,1,#ff8800,0.4^^60.17414,21.70238~60.17166,21.77147~60.14718,21.76714~60.14894,21.70101
+undefined^#FF0000,5,1,#ff8800,0.4^^60.39145,22.28499~60.39729,22.32338~60.3877,22.33006~60.38116,22.28354
+undefined^#FF0000,5,1,#ff8800,0.4^^60.39168,22.28482~60.39768,22.32306~60.4032,22.30824~60.3984,22.28282
+undefined^#FF0000,5,1,#ff8800,0.4^^60.38299,22.30276~60.36143,22.31478~60.37985,22.45382~60.41216,22.49107
 @@@@@@
--- a/service.py	Sat Jun 24 19:38:05 2017 +0300
+++ b/service.py	Wed Jun 28 12:20:05 2017 +0300
@@ -35,11 +35,14 @@
 		ini = ConfigParser()
 		ini.read(path.join(file_path))
 		self.languages[language_name] = ini
-	def __call__(self, name, section, language = None):
+	def __call__(self, name, *sections, language = None):
 		language = language or language_for_page()
-		try:
-			return self.languages[language][section][name]
-		except KeyError:
+		for section in sections:
+			try:
+				return self.languages[language][section][name]
+			except KeyError:
+				pass
+		else:
 			return name
 
 tr = Translator()
@@ -79,7 +82,7 @@
 		bus_stop = bus_stops[reference]
 	except KeyError:
 		abort(404)
-	for schedule_entry in bus_stop.schedule(100):
+	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,
@@ -108,10 +111,10 @@
 		return '%d:%02d' % (time.hour, time.minute)
 	elif time_difference < timedelta(7):
 		with activate_locale():
-			return time.strftime('%-a %H:%M')
+			return time.strftime('%-a %H:%M').replace(' ', '\xa0')
 	else:
 		with activate_locale():
-			return time.strftime('%-d.%-m. %H:%M')
+			return time.strftime('%-d.%-m. %H:%M').replace(' ', '\xa0')
 
 @app.route('/pysäkkiryhmä/<cluster_name>')
 def cluster_schedule(cluster_name):
@@ -121,7 +124,7 @@
 		cluster = clusters_by_name[cluster_name]
 	except KeyError:
 		abort(404)
-	for schedule_entry in cluster.schedule(100):
+	for schedule_entry in cluster.schedule(max_amount = 100):
 		schedule.append({
 			'time': time_representation(schedule_entry['time']),
 			'route': schedule_entry['trip'].route.reference,
@@ -130,20 +133,20 @@
 			'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'),
+			'stop_name': tr(schedule_entry['stop'].stop.name, 'pysäkit'),
 		})
 	stops_in_cluster = sorted(
 		({
 			'id': stop.reference,
 			'code': stop.code,
-			'name': tr(stop.name, 'bus_stops'),
+			'name': tr(stop.name, 'pysäkit'),
 		} for stop in cluster.stops),
 		key = lambda stop: (len(stop['id']), stop['id'])
 	)
 	return render_template(
 		'cluster.html',
 		schedule = schedule,
-		name = cluster.name,
+		name = tr(cluster.name, 'paikat', 'pysäkkiryhmät', 'pysäkit'),
 		link_to_map = cluster.center.link_to_map,
 		location = cluster.center,
 		stops_in_cluster = stops_in_cluster,
@@ -155,41 +158,40 @@
 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 = []
-	concise_schedule = []
-	used_areas = set()
+	region = ''
 	for halt in trip.schedule:
 		stop_time = datetime.combine(today(), time()) + halt.arrival_time
 		formatted_time = time_representation(stop_time)
-		schedule.append({
+		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,
-			'region': tr(halt.stop.region or '', 'paikat'),
 			'name': tr(halt.stop.name, 'bus_stops'),
 		})
-		region = halt.stop.region
-		if region:
-			if region not in used_areas:
-				concise_schedule.append({
-					'time': formatted_time,
-					'region': region or '',
-				})
-				used_areas.add(region)
-	sign = reduce_schedule([k['region'] for k in concise_schedule], whole = True, trip_length = trip.length)
+	sign = trip.concise_schedule()
 	try:
-		sign = [sign[0], sign[-1]]
+		sign = [simplify_name(sign[0]), simplify_name(sign[-1])]
 	except IndexError:
 		sign = [trip.schedule[0].stop.name, trip.schedule[-1].stop.name]
-	for entry in concise_schedule:
-		entry['region'] = tr(entry['region'], 'paikat')
 	return render_template('trip.html',
 		schedule = schedule,
-		concise_schedule = concise_schedule,
 		trip_reference = trip_reference,
 		route = trip.route.reference,
 		description = ' - '.join(tr(place, 'paikat') for place in sign),
--- a/static/style.css	Sat Jun 24 19:38:05 2017 +0300
+++ b/static/style.css	Wed Jun 28 12:20:05 2017 +0300
@@ -1,12 +1,9 @@
 *
 {
 	font-family: FreeSans, helvetica, Arial, sans-serif;
-	font-weight: light;
 }
 body
 {
-	background: white;
-	color: black;
 	margin: 0;
 	padding: 0;
 	font-size: 24pt;
@@ -31,10 +28,11 @@
 	background: #dc9e00;
 	/*background: linear-gradient(to bottom, #dc9e00 0%,#eaaf19 49%,#dc9e00 92%,#906700 100%);*/
 	/* background: linear-gradient(to bottom, #dcbb00 0%,#eacb19 49%,#dcbb00 92%,#906700 100%); */
-	background: linear-gradient(to bottom, #ffd90f 0%, #ffd90f 95%, #906700 100%);
+	background: linear-gradient(to bottom, #ffd90f 0%, #ffd90f 90%, #AA7700 100%);
 	/*color: white;*/
 	color: black;
 	vertical-align: middle;
+	box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5);
 }
 h1 img, h1 span
 {
@@ -45,9 +43,21 @@
 {
 	margin: auto;
 	margin-bottom: 30pt;
-	/*min-width: 50%;*/
-	min-width: 85%;
+	min-width: 50%;
+	/*min-width: 85%;*/
 	border: 1px solid gray;
+	border-radius: 30px 30px 0 0;
+	box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5);
+}
+
+.aikataulu th:first-child
+{
+	border-radius: 30px 0 0 0;
+}
+
+.aikataulu th:last-child
+{
+	border-radius: 0 30px 0 0;
 }
 
 .sarake-aika, .sarake-linja
@@ -71,29 +81,29 @@
 .aikataulu tr td
 {
 	border-top: 1px solid gray;
-	background-color: rgba(0, 0, 0, 0.05);
+	background-color: rgba(0, 0, 0, 0.04);
+}
+
+.aikataulu tr:first-child td
+{
+	border-top: none;
 }
 
 .aikataulu tr:nth-child(even)
 {
-	background-color: rgba(0, 0, 0, 0.1);
+	background-color: rgba(0, 0, 0, 0.08);
 }
 
 .aikataulu tr.yö td
 {
-	/*
-	background-color: #d8d8ff;
-	color: #008;
-	border-bottom: 1px solid #00A;
-	*/
-	/*
 	background-color: #004;
 	color: #bef;
 	border-top: 1px solid #008;
-	*/
+	/*
 	background-color: rgba(0, 0, 192, 0.25);
 	color: #22A;
 	border-top: 1px solid #00A;
+	*/
 }
 
 .aikataulu th
@@ -155,3 +165,10 @@
 	box-shadow: 0px 0px 10px black;
 	margin-bottom: 30pt;
 }
+
+.nested-schedule
+{
+	width: 100%;
+	border: 1px solid gray;
+	box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);
+}
--- a/templates/cluster.html	Sat Jun 24 19:38:05 2017 +0300
+++ b/templates/cluster.html	Wed Jun 28 12:20:05 2017 +0300
@@ -39,7 +39,7 @@
 	<p id="pysäkki-info">
 	<a class="pysäkki-sijainti" href="{{link_to_map}}" target="_blank">📌 ({{location}})</a>
 	</p>
-	<p>Pysäkit ryhmässä:</p>
+	<p>{{tr('stops-in-cluster', 'misc-text')}}:</p>
 	<div class="stops-in-cluster">
 	<ul>
 	{% for stop in stops_in_cluster %}
--- a/templates/trip.html	Sat Jun 24 19:38:05 2017 +0300
+++ b/templates/trip.html	Wed Jun 28 12:20:05 2017 +0300
@@ -4,35 +4,44 @@
 	<link rel="stylesheet" type="text/css" href="/static/style.css" />
 	<meta charset='UTF-8' />
 	<style>
-	.sarake-pysäkki, .sarake-pysäkkiviite, .sarake-alue
+	.sarake-alue
 	{
 		text-align: left;
 	}
-	td, th, body
+	.sarake-aika
+	{
+		width: 0;
+	}
+	td.folded table
+	{
+		display: none;
+	}
+	td.unfolded a.region-name
 	{
-		font-size: 24pt;
-	}/*
-	body
+		font-weight: bold;
+	}
+	.nested-schedule td
 	{
-		background: url('/static/tausta-bussi.png') no-repeat center center fixed;
-		background-size: cover;
-	}*/
+		margin-left: 0;
+		margin-right: 0;
+		padding-left: 5px;
+		padding-right: 5px;
+	}
+	.nested-schedule
+	{
+		border-radius: 10px;
+		margin-top: 10px;
+	}
 	</style>
 	<script>
-	function openTab(event, category)
+	function toggle_fold(id)
 	{
-		var tabcontent = document.getElementsByClassName("tab");
-		for (var i = 0; i < tabcontent.length; i++)
-			tabcontent[i].style.display = "none";
-
-		var tablinks = document.getElementsByClassName("tab-button");
-		for (var i = 0; i < tablinks.length; i++)
-			tablinks[i].className = tablinks[i].className.replace(" active", "");
-
-		document.getElementById(category).style.display = "block";
-		event.currentTarget.className += " active";
-		window.scrollTo(0, 0);
-	} 
+		element = document.getElementById(id);
+		if (element.className.indexOf(" folded") !== -1)
+			element.className = element.className.replace(" folded", "") + " unfolded";
+		else
+			element.className = element.className.replace(" unfolded", "") + " folded";
+	}
 	</script>
 	<title>{{route}} {{description}}</title>
 </head>
@@ -42,48 +51,29 @@
 	&#127769;
 	{% endif %}
 	{{route}} {{description}}</h1>
-	<nav>
-	<div class="tab-bar">
-	<span></span>
-	<button class="tab-button active" onclick="openTab(event, 'tab-overview')">Reitti</button>
-	<button class="tab-button" onclick="openTab(event, 'tab-stops')">Pysäkit</button>
-	<span></span>
-	</div>
-	</nav>
 	<p style="text-align: center">Ajomatka: {{'%.1f' % length}}km</p>
-	<div id="tab-overview" class="tab">
-		<table class='aikataulu' cellspacing='0'>
-			<tr>
-				<th class='sarake-aika'>Aika</th>
-				<th class='sarake-alue'>Alue</th>
-			</tr>
-			{% for entry in concise_schedule %}
-			<tr>
-				<td class='sarake-aika'>{{entry['time']}}</td>
-				<td class='sarake-alue'>{{entry['region']}}</td>
-			</tr>
-			{% endfor %}
-		</table>
-	</div>
-	<div id="tab-stops" class="tab" style="display: none">
-		<table class='aikataulu' cellspacing='0'>
-			<tr>
-				<th class='sarake-aika'>Aika</th>
-				<th class='sarake-pysäkkiviite'>Pysäkki</th>
-				<th class='sarake-pysäkki'>Nimi</th>
-			</tr>
-			{% for halt in schedule %}
-			<tr>
-				<td class='sarake-aika'>{{halt['time']}}</td>
-				<td class='sarake-pysäkkiviite'>
-					<a href="/pysäkki/{{halt['id']}}"><img src='/static/pysäkki.png' height='24' /> {{halt['code']}}</a>
-				</td>
-				<td class='sarake-pysäkki'>
-					<a href="/pysäkki/{{halt['id']}}">{{halt['name']}}</a>
-				</td>
-			</tr>
-			{% endfor %}
-		</table>
-	</div>
+	<table class='aikataulu' cellspacing='0'>
+		<tr>
+			<th class='sarake-aika'>Aika</th>
+			<th class='sarake-alue'>Alue</th>
+		</tr>
+		{% for entry in schedule %}
+		<tr>
+			<td class='sarake-aika' style='vertical-align: top'>{{entry['time']}}</td>
+			<td class='sarake-alue folded' id="region-schedule-{{entry['index']}}">
+				<a class="region-name" href="javascript:toggle_fold('region-schedule-{{entry['index']}}')">{{entry['name']}}</a>
+				<table class="nested-schedule" cellspacing="0">
+				{% for halt in entry['stops'] %}
+				<tr>
+					<td>{{halt['time']}}</td>
+					<td><a href="/pysäkki/{{halt['id']}}"><img src='/static/pysäkki.png' height='24' />&nbsp;{{halt['code']}}</a></td>
+					<td><a href="/pysäkki/{{halt['id']}}">{{halt['name']}}</a></td>
+				</tr>
+				{% endfor %}
+				</table>
+			</td>
+		</tr>
+		{% endfor %}
+	</table>
 </body>
 </html>
--- a/tr/en.ini	Sat Jun 24 19:38:05 2017 +0300
+++ b/tr/en.ini	Wed Jun 28 12:20:05 2017 +0300
@@ -15,7 +15,14 @@
 [misc-text]
 right-now = now
 nearby-area-schedule = Nearby area schedule
-stops-in-cluster = Bus stops in this cluster
+stops-in-cluster = Bus stops in this group
 
 [other]
 locale = en_US.utf8
+
+[headings]
+time = Time
+route = Route
+bus-stop = Stop
+destination = Destination
+
--- a/tr/fi.ini	Sat Jun 24 19:38:05 2017 +0300
+++ b/tr/fi.ini	Wed Jun 28 12:20:05 2017 +0300
@@ -1,6 +1,8 @@
 [paikat]
-Korpo = Korppoo
-Pargas = Parainen
+korpo = Korppoo
+pargas = Parainen
+nagu = Nauvo
+pärnäs = Pärnäinen
 lentoasema = Lentoasema ✈
 satama = Satama ⚓
 kauppakeskus mylly = Mylly
--- a/tr/sv.ini	Sat Jun 24 19:38:05 2017 +0300
+++ b/tr/sv.ini	Wed Jun 28 12:20:05 2017 +0300
@@ -1697,6 +1697,7 @@
 ruskon keskusta = Rusko centrum
 rymättylä = Rimito
 saramäki = Starrbacka
+satakunnantie = Satakuntavägen
 satama = Hamnen ⚓
 skanssi = Skansen
 toijainen = Toijais

mercurial