db-pkpass

Convert Deutsche Bahn PDF tickets to PKPass
git clone https://git.ce9e.org/db-pkpass.git

commit
c590c7c41b37f45dd16d1ab24ee14ee64d523274
parent
dbd1d7b3b2525fd28e171d4b2d744c77e069144e
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2025-05-30 16:48
setup tests

Diffstat

A .gitignore 2 ++
A Makefile 17 +++++++++++++++++
A tests.py 658 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

3 files changed, 677 insertions, 0 deletions


diff --git a/.gitignore b/.gitignore

@@ -0,0 +1,2 @@
   -1     1 muster/*.pdf
   -1     2 muster/*.png

diff --git a/Makefile b/Makefile

@@ -0,0 +1,17 @@
   -1     1 .PHONY: test
   -1     2 test: .venv muster
   -1     3 	.venv/bin/python3 -m unittest
   -1     4 
   -1     5 .venv:
   -1     6 	python3 -m venv .venv
   -1     7 	.venv/bin/python3 -m pip install -r requirements.txt
   -1     8 
   -1     9 muster:
   -1    10 	mkdir -p muster
   -1    11 	wget 'https://assets.static-bahn.de/dam/jcr:95540b93-5c38-4554-8f00-676214f4ba76/Muster%20918-9.zip'
   -1    12 	cd muster && unzip "../Muster 918-9.zip"
   -1    13 	rm "Muster 918-9.zip"
   -1    14 
   -1    15 .PHONY: clean
   -1    16 clean:
   -1    17 	rm -rf venv muster

diff --git a/tests.py b/tests.py

@@ -0,0 +1,658 @@
   -1     1 import unittest
   -1     2 import datetime
   -1     3 from zoneinfo import ZoneInfo
   -1     4 
   -1     5 import pymupdf
   -1     6 
   -1     7 import db_pkpass
   -1     8 
   -1     9 TZ = ZoneInfo(key='Europe/Berlin')
   -1    10 
   -1    11 
   -1    12 class ExtractLegsTests(unittest.TestCase):
   -1    13     maxDiff = None
   -1    14 
   -1    15     def _test_extract_leg(self, path, expected):
   -1    16         with open(path, 'rb') as fh:
   -1    17             pdf = pymupdf.open(stream=fh.read())
   -1    18         actual = db_pkpass.extract_legs(pdf)
   -1    19         self.assertEqual(actual, expected)
   -1    20 
   -1    21     def test_normalpreis(self):
   -1    22         self._test_extract_leg('muster/Muster 918-9 Normalpreis.pdf', [
   -1    23             {
   -1    24                 'start': {
   -1    25                     'station': 'Mainz Hbf',
   -1    26                     'datetime': datetime.datetime(2022, 10, 30, 15, 51, tzinfo=TZ),
   -1    27                     'platform': '3a/b',
   -1    28                 },
   -1    29                 'destination': {
   -1    30                     'station': 'Koblenz Hbf',
   -1    31                     'datetime': datetime.datetime(2022, 10, 30, 16, 54, tzinfo=TZ),
   -1    32                     'platform': '1',
   -1    33                 },
   -1    34                 'train': 'RE 2 (4260)',
   -1    35             },
   -1    36         ])
   -1    37 
   -1    38     def test_supersparpreis(self):
   -1    39         self._test_extract_leg('muster/Muster 918-9 FV_SuperSparpreis.pdf', [
   -1    40             {
   -1    41                 'start': {
   -1    42                     'station': 'Mannheim ARENA/Maimarkt',
   -1    43                     'datetime': datetime.datetime(2022, 4, 22, 11, 45, tzinfo=TZ),
   -1    44                     'platform': '1',
   -1    45                 },
   -1    46                 'destination': {
   -1    47                     'station': 'Mannheim Hbf',
   -1    48                     'datetime': datetime.datetime(2022, 4, 22, 11, 51, tzinfo=TZ),
   -1    49                     'platform': '1',
   -1    50                 },
   -1    51                 'train': 'S 2',
   -1    52             },
   -1    53             {
   -1    54                 'start': {
   -1    55                     'station': 'Mannheim Hbf',
   -1    56                     'datetime': datetime.datetime(2022, 4, 22, 11, 59, tzinfo=TZ),
   -1    57                     'platform': '5',
   -1    58                 },
   -1    59                 'destination': {
   -1    60                     'station': 'Stuttgart Hbf',
   -1    61                     'datetime': datetime.datetime(2022, 4, 22, 12, 38, tzinfo=TZ),
   -1    62                     'platform': '5',
   -1    63                 },
   -1    64                 'train': 'ICE 573',
   -1    65             },
   -1    66             {
   -1    67                 'start': {
   -1    68                     'station': 'Stuttgart Hbf',
   -1    69                     'datetime': datetime.datetime(2022, 4, 22, 12, 50, tzinfo=TZ),
   -1    70                     'platform': '15 B-C',
   -1    71                 },
   -1    72                 'destination': {
   -1    73                     'station': 'Nürtingen',
   -1    74                     'datetime': datetime.datetime(2022, 4, 22, 13, 24, tzinfo=TZ),
   -1    75                     'platform': '2',
   -1    76                 },
   -1    77                 'train': 'RE 12 (19223)',
   -1    78             },
   -1    79             {
   -1    80                 'start': {
   -1    81                     'station': 'Bahnhof, Nürtingen',
   -1    82                     'datetime': datetime.datetime(2022, 4, 22, 13, 35, tzinfo=TZ),
   -1    83                 },
   -1    84                 'destination': {
   -1    85                     'station': 'Hauptbahnhof Listplatz, Reutlingen',
   -1    86                     'datetime': datetime.datetime(2022, 4, 22, 14, 16, tzinfo=TZ),
   -1    87                 },
   -1    88                 'train': 'Bus SEV',
   -1    89             },
   -1    90         ])
   -1    91 
   -1    92     def test_supersparpreis_young(self):
   -1    93         self._test_extract_leg('muster/Muster 918-9 FV_SuperSparpreisYoung.pdf', [
   -1    94             {
   -1    95                 'start': {
   -1    96                     'station': 'Essen Hbf',
   -1    97                     'datetime': datetime.datetime(2022, 10, 30, 16, 21, tzinfo=TZ),
   -1    98                     'platform': '1',
   -1    99                 },
   -1   100                 'destination': {
   -1   101                     'station': 'Duisburg Hbf',
   -1   102                     'datetime': datetime.datetime(2022, 10, 30, 16, 34, tzinfo=TZ),
   -1   103                     'platform': '3',
   -1   104                 },
   -1   105                 'train': 'RE 2 (10222)',
   -1   106             },
   -1   107             {
   -1   108                 'start': {
   -1   109                     'station': 'Duisburg Hbf',
   -1   110                     'datetime': datetime.datetime(2022, 10, 30, 16, 42, tzinfo=TZ),
   -1   111                     'platform': '3',
   -1   112                 },
   -1   113                 'destination': {
   -1   114                     'station': 'Köln Hbf',
   -1   115                     'datetime': datetime.datetime(2022, 10, 30, 17, 29, tzinfo=TZ),
   -1   116                     'platform': '9 D-G',
   -1   117                 },
   -1   118                 'train': 'RE 5 (28525)',
   -1   119             },
   -1   120             {
   -1   121                 'start': {
   -1   122                     'station': 'Köln Hbf',
   -1   123                     'datetime': datetime.datetime(2022, 10, 30, 17, 53, tzinfo=TZ),
   -1   124                     'platform': '7',
   -1   125                 },
   -1   126                 'destination': {
   -1   127                     'station': 'Mainz Hbf',
   -1   128                     'datetime': datetime.datetime(2022, 10, 30, 19, 39, tzinfo=TZ),
   -1   129                     'platform': '4a/b',
   -1   130                 },
   -1   131                 'train': 'ICE 929',
   -1   132             },
   -1   133             {
   -1   134                 'start': {
   -1   135                     'station': 'Mainz Hbf',
   -1   136                     'datetime': datetime.datetime(2022, 10, 30, 19, 49, tzinfo=TZ),
   -1   137                     'platform': '4a',
   -1   138                 },
   -1   139                 'destination': {
   -1   140                     'station': 'Darmstadt Hbf',
   -1   141                     'datetime': datetime.datetime(2022, 10, 30, 20, 21, tzinfo=TZ),
   -1   142                     'platform': '8',
   -1   143                 },
   -1   144                 'train': 'HLB RB75 (28731)',
   -1   145             },
   -1   146             {
   -1   147                 'start': {
   -1   148                     'station': 'Darmstadt Hbf',
   -1   149                     'datetime': datetime.datetime(2022, 10, 30, 20, 37, tzinfo=TZ),
   -1   150                     'platform': '10',
   -1   151                 },
   -1   152                 'destination': {
   -1   153                     'station': 'Stuttgart Hbf',
   -1   154                     'datetime': datetime.datetime(2022, 10, 30, 21, 55, tzinfo=TZ),
   -1   155                     'platform': '15',
   -1   156                 },
   -1   157                 'train': 'IC 1999',
   -1   158             },
   -1   159         ])
   -1   160 
   -1   161     def test_supersparpreis_2erw(self):
   -1   162         self._test_extract_leg('muster/Muster 918-9 FV_SuperSparpreis_2Erw.pdf', [
   -1   163             {
   -1   164                 'start': {
   -1   165                     'station': 'Köln Hbf',
   -1   166                     'datetime': datetime.datetime(2022, 10, 20, 14, 36, tzinfo=TZ),
   -1   167                     'platform': '3',
   -1   168                 },
   -1   169                 'destination': {
   -1   170                     'station': 'Essen Hbf',
   -1   171                     'datetime': datetime.datetime(2022, 10, 20, 15, 33, tzinfo=TZ),
   -1   172                     'platform': '6',
   -1   173                 },
   -1   174                 'train': 'ICE 1059',
   -1   175             },
   -1   176         ])
   -1   177 
   -1   178     def test_supersparpreis_3erw_rueckfahrt(self):
   -1   179         self._test_extract_leg('muster/Muster 918-9 FV_SuperSparpreis_3Erw_InklR\udcfcckfahrt.pdf', [
   -1   180             {
   -1   181                 'start': {
   -1   182                     'station': 'Hamburg Hbf',
   -1   183                     'datetime': datetime.datetime(2022, 11, 4, 3, 20, tzinfo=TZ),
   -1   184                     'platform': '14',
   -1   185                 },
   -1   186                 'destination': {
   -1   187                     'station': 'Fulda',
   -1   188                     'datetime': datetime.datetime(2022, 11, 4, 6, 46, tzinfo=TZ),
   -1   189                     'platform': '4',
   -1   190                 },
   -1   191                 'train': 'ICE 591',
   -1   192             },
   -1   193             {
   -1   194                 'start': {
   -1   195                     'station': 'Fulda',
   -1   196                     'datetime': datetime.datetime(2022, 11, 4, 6, 56, tzinfo=TZ),
   -1   197                     'platform': '4',
   -1   198                 },
   -1   199                 'destination': {
   -1   200                     'station': 'Nürnberg Hbf',
   -1   201                     'datetime': datetime.datetime(2022, 11, 4, 9, 6, tzinfo=TZ),
   -1   202                     'platform': '8',
   -1   203                 },
   -1   204                 'train': 'ICE 781',
   -1   205             },
   -1   206             {
   -1   207                 'start': {
   -1   208                     'station': 'Nürnberg Hbf',
   -1   209                     'datetime': datetime.datetime(2022, 11, 4, 9, 17, tzinfo=TZ),
   -1   210                     'platform': '9',
   -1   211                 },
   -1   212                 'destination': {
   -1   213                     'station': 'München Hbf',
   -1   214                     'datetime': datetime.datetime(2022, 11, 4, 10, 20, tzinfo=TZ),
   -1   215                     'platform': '19',
   -1   216                 },
   -1   217                 'train': 'ICE 1121',
   -1   218             },
   -1   219             {
   -1   220                 'start': {
   -1   221                     'station': 'München Hbf Gl.5-10',
   -1   222                     'datetime': datetime.datetime(2022, 11, 4, 10, 43, tzinfo=TZ),
   -1   223                     'platform': '10',
   -1   224                 },
   -1   225                 'destination': {
   -1   226                     'station': 'Rosenheim',
   -1   227                     'datetime': datetime.datetime(2022, 11, 4, 11, 27, tzinfo=TZ),
   -1   228                     'platform': '5',
   -1   229                 },
   -1   230                 'train': 'BRB RB54 (79065)',
   -1   231             },
   -1   232             {
   -1   233                 'start': {
   -1   234                     'station': 'Rosenheim',
   -1   235                     'datetime': datetime.datetime(2022, 11, 4, 16, 32, tzinfo=TZ),
   -1   236                     'platform': '3',
   -1   237                 },
   -1   238                 'destination': {
   -1   239                     'station': 'München Hbf Gl.5-10',
   -1   240                     'datetime': datetime.datetime(2022, 11, 4, 17, 18, tzinfo=TZ),
   -1   241                     'platform': '10',
   -1   242                 },
   -1   243                 'train': 'BRB RB54 (79082)',
   -1   244             },
   -1   245             {
   -1   246                 'start': {
   -1   247                     'station': 'München Hbf',
   -1   248                     'datetime': datetime.datetime(2022, 11, 4, 17, 35, tzinfo=TZ),
   -1   249                     'platform': '16',
   -1   250                 },
   -1   251                 'destination': {
   -1   252                     'station': 'Nürnberg Hbf',
   -1   253                     'datetime': datetime.datetime(2022, 11, 4, 18, 39, tzinfo=TZ),
   -1   254                     'platform': '9',
   -1   255                 },
   -1   256                 'train': 'ICE 1120',
   -1   257             },
   -1   258             {
   -1   259                 'start': {
   -1   260                     'station': 'Nürnberg Hbf',
   -1   261                     'datetime': datetime.datetime(2022, 11, 4, 18, 53, tzinfo=TZ),
   -1   262                     'platform': '6',
   -1   263                 },
   -1   264                 'destination': {
   -1   265                     'station': 'Hamburg Hbf',
   -1   266                     'datetime': datetime.datetime(2022, 11, 5, 0, 5, tzinfo=TZ),
   -1   267                     'platform': '13',
   -1   268                 },
   -1   269                 'train': 'ICE 782',
   -1   270             },
   -1   271         ])
   -1   272 
   -1   273     def test_supersparpreis_senior_rueckfahrt(self):
   -1   274         self._test_extract_leg('muster/Muster 918-9 FV_SuperSparpreisSenior_InklR\udcfcckfahrt.pdf', [
   -1   275             {
   -1   276                 'start': {
   -1   277                     'station': 'Flensburg',
   -1   278                     'datetime': datetime.datetime(2022, 10, 30, 15, 15, tzinfo=TZ),
   -1   279                     'platform': '2',
   -1   280                 },
   -1   281                 'destination': {
   -1   282                     'station': 'Hamburg Hbf',
   -1   283                     'datetime': datetime.datetime(2022, 10, 30, 17, 16, tzinfo=TZ),
   -1   284                     'platform': '11D-F',
   -1   285                 },
   -1   286                 'train': 'RE 7 (21077)',
   -1   287             },
   -1   288             {
   -1   289                 'start': {
   -1   290                     'station': 'Hamburg Hbf',
   -1   291                     'datetime': datetime.datetime(2022, 10, 30, 17, 35, tzinfo=TZ),
   -1   292                     'platform': '5',
   -1   293                 },
   -1   294                 'destination': {
   -1   295                     'station': 'Berlin Hbf (tief)',
   -1   296                     'datetime': datetime.datetime(2022, 10, 30, 19, 22, tzinfo=TZ),
   -1   297                     'platform': '1',
   -1   298                 },
   -1   299                 'train': 'ICE 803',
   -1   300             },
   -1   301             {
   -1   302                 'start': {
   -1   303                     'station': 'Berlin Hbf (tief)',
   -1   304                     'datetime': datetime.datetime(2022, 10, 30, 20, 5, tzinfo=TZ),
   -1   305                     'platform': '1',
   -1   306                 },
   -1   307                 'destination': {
   -1   308                     'station': 'München Hbf',
   -1   309                     'datetime': datetime.datetime(2022, 10, 31, 0, 2, tzinfo=TZ),
   -1   310                     'platform': '18',
   -1   311                 },
   -1   312                 'train': 'ICE 1109',
   -1   313             },
   -1   314             {
   -1   315                 'start': {
   -1   316                     'station': 'München Hbf (tief)',
   -1   317                     'datetime': datetime.datetime(2022, 10, 31, 0, 17, tzinfo=TZ),
   -1   318                     'platform': '1',
   -1   319                 },
   -1   320                 'destination': {
   -1   321                     'station': 'München Ost',
   -1   322                     'datetime': datetime.datetime(2022, 10, 31, 0, 26, tzinfo=TZ),
   -1   323                     'platform': '4',
   -1   324                 },
   -1   325                 'train': 'S 1 S 1',
   -1   326             },
   -1   327             {
   -1   328                 'start': {
   -1   329                     'station': 'München Ost',
   -1   330                     'datetime': datetime.datetime(2022, 10, 31, 0, 52, tzinfo=TZ),
   -1   331                     'platform': '8',
   -1   332                 },
   -1   333                 'destination': {
   -1   334                     'station': 'Rosenheim',
   -1   335                     'datetime': datetime.datetime(2022, 10, 31, 1, 29, tzinfo=TZ),
   -1   336                     'platform': '4',
   -1   337                 },
   -1   338                 'train': 'BRB RB54 (79569)',
   -1   339             },
   -1   340             {
   -1   341                 'start': {
   -1   342                     'station': 'Rosenheim',
   -1   343                     'datetime': datetime.datetime(2022, 11, 1, 12, 32, tzinfo=TZ),
   -1   344                     'platform': '3',
   -1   345                 },
   -1   346                 'destination': {
   -1   347                     'station': 'München Ost',
   -1   348                     'datetime': datetime.datetime(2022, 11, 1, 13, 6, tzinfo=TZ),
   -1   349                     'platform': '6',
   -1   350                 },
   -1   351                 'train': 'BRB RB54 (79074)',
   -1   352             },
   -1   353             {
   -1   354                 'start': {
   -1   355                     'station': 'München Ost',
   -1   356                     'datetime': datetime.datetime(2022, 11, 1, 13, 15, tzinfo=TZ),
   -1   357                     'platform': '2',
   -1   358                 },
   -1   359                 'destination': {
   -1   360                     'station': 'München Hbf (tief)',
   -1   361                     'datetime': datetime.datetime(2022, 11, 1, 13, 23, tzinfo=TZ),
   -1   362                     'platform': '2',
   -1   363                 },
   -1   364                 'train': 'S 1 S 1',
   -1   365             },
   -1   366             {
   -1   367                 'start': {
   -1   368                     'station': 'München Hbf',
   -1   369                     'datetime': datetime.datetime(2022, 11, 1, 13, 35, tzinfo=TZ),
   -1   370                 },
   -1   371                 'destination': {
   -1   372                     'station': 'Hamburg Hbf',
   -1   373                     'datetime': datetime.datetime(2022, 11, 1, 19, 53, tzinfo=TZ),
   -1   374                     'platform': '12',
   -1   375                 },
   -1   376                 'train': 'ICE 786',
   -1   377             },
   -1   378             {
   -1   379                 'start': {
   -1   380                     'station': 'Hamburg Hbf',
   -1   381                     'datetime': datetime.datetime(2022, 11, 1, 20, 43, tzinfo=TZ),
   -1   382                     'platform': '12C-F',
   -1   383                 },
   -1   384                 'destination': {
   -1   385                     'station': 'Flensburg',
   -1   386                     'datetime': datetime.datetime(2022, 11, 1, 22, 40, tzinfo=TZ),
   -1   387                     'platform': '2',
   -1   388                 },
   -1   389                 'train': 'RE 7 (21082)',
   -1   390             },
   -1   391         ])
   -1   392 
   -1   393     def test_city_ticket(self):
   -1   394         self._test_extract_leg('muster/Muster 918-9 CityTicket.pdf', [
   -1   395             {
   -1   396                 'start': {
   -1   397                     'station': 'Kassel-Harleshausen',
   -1   398                     'datetime': datetime.datetime(2022, 4, 21, 12, 12, tzinfo=TZ),
   -1   399                     'platform': '1',
   -1   400                 },
   -1   401                 'destination': {
   -1   402                     'station': 'Kassel Hbf (tief)',
   -1   403                     'datetime': datetime.datetime(2022, 4, 21, 12, 18, tzinfo=TZ),
   -1   404                 },
   -1   405                 'train': 'RT 1',
   -1   406             },
   -1   407             {
   -1   408                 'start': {
   -1   409                     'station': 'Kassel Hbf',
   -1   410                     'datetime': datetime.datetime(2022, 4, 21, 12, 23, tzinfo=TZ),
   -1   411                     'platform': '7',
   -1   412                 },
   -1   413                 'destination': {
   -1   414                     'datetime': datetime.datetime(2022, 4, 21, 12, 28, tzinfo=TZ),
   -1   415                     'platform': '7',
   -1   416                     'station': 'Kassel-Wilhelmshöhe',
   -1   417                 },
   -1   418                 'train': 'RE 30 (4159)',
   -1   419             },
   -1   420             {
   -1   421                 'start': {
   -1   422                     'datetime': datetime.datetime(2022, 4, 21, 12, 37, tzinfo=TZ),
   -1   423                     'platform': '2',
   -1   424                     'station': 'Kassel-Wilhelmshöhe',
   -1   425                 },
   -1   426                 'destination': {
   -1   427                     'datetime': datetime.datetime(2022, 4, 21, 14, 0, tzinfo=TZ),
   -1   428                     'platform': '5',
   -1   429                     'station': 'Frankfurt(Main)Süd',
   -1   430                 },
   -1   431                 'train': 'ICE 75',
   -1   432             },
   -1   433             {
   -1   434                 'start': {
   -1   435                     'datetime': datetime.datetime(2022, 4, 21, 14, 5, tzinfo=TZ),
   -1   436                     'station': 'Südbahnhof, Frankfurt a.M.',
   -1   437                 },
   -1   438                 'destination': {
   -1   439                     'datetime': datetime.datetime(2022, 4, 21, 14, 7, tzinfo=TZ),
   -1   440                     'station': 'Willy-Brandt-Platz, Frankfurt a.M.',
   -1   441                 },
   -1   442                 'train': 'U 1',
   -1   443             },
   -1   444         ])
   -1   445 
   -1   446     def test_city_ticket_international(self):
   -1   447         self._test_extract_leg('muster/Muster 918-9 CityTicket_International.pdf', [
   -1   448             {
   -1   449                 'start': {
   -1   450                     'station': 'Mainz Hbf',
   -1   451                     'datetime': datetime.datetime(2022, 10, 30, 16, 13, tzinfo=TZ),
   -1   452                     'platform': '6a',
   -1   453                 },
   -1   454                 'destination': {
   -1   455                     'station': 'Karlsruhe Hbf',
   -1   456                     'datetime': datetime.datetime(2022, 10, 30, 17, 53, tzinfo=TZ),
   -1   457                     'platform': '1',
   -1   458                 },
   -1   459                 'train': 'RE 4 (4481)',
   -1   460             },
   -1   461             {
   -1   462                 'start': {
   -1   463                     'station': 'Karlsruhe Hbf',
   -1   464                     'datetime': datetime.datetime(2022, 10, 30, 18, 0, tzinfo=TZ),
   -1   465                     'platform': '2',
   -1   466                 },
   -1   467                 'destination': {
   -1   468                     'station': 'Basel SBB',
   -1   469                     'datetime': datetime.datetime(2022, 10, 30, 19, 47, tzinfo=TZ),
   -1   470                     'platform': '4',
   -1   471                 },
   -1   472                 'train': 'ICE 373',
   -1   473             },
   -1   474             {
   -1   475                 'start': {
   -1   476                     'station': 'Basel SBB',
   -1   477                     'datetime': datetime.datetime(2022, 10, 30, 20, 33, tzinfo=TZ),
   -1   478                     'platform': '7',
   -1   479                 },
   -1   480                 'destination': {
   -1   481                     'station': 'Chur',
   -1   482                     'datetime': datetime.datetime(2022, 10, 30, 22, 52, tzinfo=TZ),
   -1   483                     'platform': '7',
   -1   484                 },
   -1   485                 'train': 'IC 587',
   -1   486             },
   -1   487         ])
   -1   488 
   -1   489     def test_laenderticket_bayern_nacht(self):
   -1   490         self._test_extract_leg('muster/Muster 918-9 L\udce4nderticket Bayern Nacht.pdf', [
   -1   491             {
   -1   492                 'start': {
   -1   493                     'station': 'Augsburg Hbf',
   -1   494                     'datetime': datetime.datetime(2022, 4, 25, 21, 39, tzinfo=TZ),
   -1   495                     'platform': '9',
   -1   496                 },
   -1   497                 'destination': {
   -1   498                     'station': 'München Hbf',
   -1   499                     'datetime': datetime.datetime(2022, 4, 25, 22, 31, tzinfo=TZ),
   -1   500                     'platform': '17',
   -1   501                 },
   -1   502                 'train': 'RE 9 (57167) RE 8 (57067)',
   -1   503             },
   -1   504             {
   -1   505                 'start': {
   -1   506                     'station': 'München Hbf',
   -1   507                     'datetime': datetime.datetime(2022, 4, 25, 23, 24, tzinfo=TZ),
   -1   508                     'platform': '24',
   -1   509                 },
   -1   510                 'destination': {
   -1   511                     'station': 'Plattling',
   -1   512                     'datetime': datetime.datetime(2022, 4, 26, 0, 54, tzinfo=TZ),
   -1   513                     'platform': '3',
   -1   514                 },
   -1   515                 'train': 'RE 3 (4092)',
   -1   516             },
   -1   517             {
   -1   518                 'start': {
   -1   519                     'station': 'Plattling',
   -1   520                     'datetime': datetime.datetime(2022, 4, 26, 1, 1, tzinfo=TZ),
   -1   521                     'platform': '5',
   -1   522                 },
   -1   523                 'destination': {
   -1   524                     'station': 'Deggendorf Hbf',
   -1   525                     'datetime': datetime.datetime(2022, 4, 26, 1, 9, tzinfo=TZ),
   -1   526                     'platform': '2',
   -1   527                 },
   -1   528                 'train': 'WBA RB35 (83957)',
   -1   529             },
   -1   530         ])
   -1   531 
   -1   532     def test_laenderticket_rheinland_pfalz(self):
   -1   533         self._test_extract_leg('muster/Muster 918-9 L\udce4nderticket Rheinland-Pfalz.pdf', [
   -1   534             {
   -1   535                 'start': {
   -1   536                     'station': 'Bingen(Rhein) Hbf',
   -1   537                     'datetime': datetime.datetime(2022, 4, 25, 21, 56, tzinfo=TZ),
   -1   538                     'platform': '203',
   -1   539                 },
   -1   540                 'destination': {
   -1   541                     'station': 'Bad Kreuznach',
   -1   542                     'datetime': datetime.datetime(2022, 4, 25, 22, 16, tzinfo=TZ),
   -1   543                     'platform': '1',
   -1   544                 },
   -1   545                 'train': 'RB 65 (12750)',
   -1   546             },
   -1   547             {
   -1   548                 'start': {
   -1   549                     'station': 'Bad Kreuznach',
   -1   550                     'datetime': datetime.datetime(2022, 4, 25, 22, 24, tzinfo=TZ),
   -1   551                     'platform': '3',
   -1   552                 },
   -1   553                 'destination': {
   -1   554                     'station': 'Saarbrücken Hbf',
   -1   555                     'datetime': datetime.datetime(2022, 4, 26, 0, 10, tzinfo=TZ),
   -1   556                     'platform': '1',
   -1   557                 },
   -1   558                 'train': 'RE 3 (29536)',
   -1   559             },
   -1   560         ])
   -1   561 
   -1   562     def test_laenderticket_saarland(self):
   -1   563         self._test_extract_leg('muster/Muster 918-9 L\udce4nderticket Saarland.pdf', [
   -1   564             {
   -1   565                 'start': {
   -1   566                     'station': 'Saarbrücken Hbf',
   -1   567                     'datetime': datetime.datetime(2022, 4, 25, 21, 51, tzinfo=TZ),
   -1   568                     'platform': '1',
   -1   569                 },
   -1   570                 'destination': {
   -1   571                     'station': 'Gau Algesheim',
   -1   572                     'datetime': datetime.datetime(2022, 4, 25, 23, 57, tzinfo=TZ),
   -1   573                     'platform': '1',
   -1   574                 },
   -1   575                 'train': 'RE 3 (29535) RB 33 (29589)',
   -1   576             },
   -1   577             {
   -1   578                 'start': {
   -1   579                     'station': 'Gau Algesheim',
   -1   580                     'datetime': datetime.datetime(2022, 4, 26, 0, 29, tzinfo=TZ),
   -1   581                     'platform': '4',
   -1   582                 },
   -1   583                 'destination': {
   -1   584                     'station': 'Bingen(Rhein) Hbf',
   -1   585                     'datetime': datetime.datetime(2022, 4, 26, 0, 40, tzinfo=TZ),
   -1   586                     'platform': '201',
   -1   587                 },
   -1   588                 'train': 'RB 26 (25398)',
   -1   589             },
   -1   590         ])
   -1   591 
   -1   592     def test_laenderticket_sachsen_anhalt(self):
   -1   593         self._test_extract_leg('muster/Muster 918-9 L\udce4nderticket Sachsen-Anhalt.pdf', [
   -1   594             {
   -1   595                 'start': {
   -1   596                     'station': 'Magdeburg Hbf',
   -1   597                     'datetime': datetime.datetime(2022, 4, 25, 21, 4, tzinfo=TZ),
   -1   598                     'platform': '7',
   -1   599                 },
   -1   600                 'destination': {
   -1   601                     'station': 'Dessau Hbf',
   -1   602                     'datetime': datetime.datetime(2022, 4, 25, 21, 57, tzinfo=TZ),
   -1   603                     'platform': '2',
   -1   604                 },
   -1   605                 'train': 'RE 13 (16133)',
   -1   606             },
   -1   607         ])
   -1   608 
   -1   609     def test_laenderticket_thueringen(self):
   -1   610         self._test_extract_leg('muster/Muster 918-9 L\udce4nderticket Th\udcfcringen.pdf', [
   -1   611             {
   -1   612                 'start': {
   -1   613                     'station': 'Erfurt Hbf',
   -1   614                     'datetime': datetime.datetime(2022, 10, 20, 14, 37, tzinfo=TZ),
   -1   615                     'platform': '8a',
   -1   616                 },
   -1   617                 'destination': {
   -1   618                     'station': 'Weimar',
   -1   619                     'datetime': datetime.datetime(2022, 10, 20, 14, 50, tzinfo=TZ),
   -1   620                     'platform': '2',
   -1   621                 },
   -1   622                 'train': 'RE 16 (74511)',
   -1   623             },
   -1   624         ])
   -1   625 
   -1   626     def test_quer_durchs_land_ticket(self):
   -1   627         self._test_extract_leg('muster/Muster 918-9 Quer-durchs-Land Ticket.pdf', [
   -1   628             {
   -1   629                 'start': {
   -1   630                     'station': 'Groß Gerau-Dornberg',
   -1   631                     'datetime': datetime.datetime(2022, 10, 6, 14, 36, tzinfo=TZ),
   -1   632                     'platform': '3',
   -1   633                 },
   -1   634                 'destination': {
   -1   635                     'station': 'Mannheim Hbf',
   -1   636                     'datetime': datetime.datetime(2022, 10, 6, 15, 19, tzinfo=TZ),
   -1   637                     'platform': '7a',
   -1   638                 },
   -1   639                 'train': 'RE 70 (4571)',
   -1   640             },
   -1   641         ])
   -1   642 
   -1   643     def test_schleswig_holstein_ticket(self):
   -1   644         self._test_extract_leg('muster/Muster 918-9 Schleswig-Holstein Ticket.pdf', [
   -1   645             {
   -1   646                 'start': {
   -1   647                     'station': 'Schleswig',
   -1   648                     'datetime': datetime.datetime(2022, 10, 20, 15, 7, tzinfo=TZ),
   -1   649                     'platform': '3',
   -1   650                 },
   -1   651                 'destination': {
   -1   652                     'station': 'Kiel Hbf',
   -1   653                     'datetime': datetime.datetime(2022, 10, 20, 15, 57, tzinfo=TZ),
   -1   654                     'platform': '6a',
   -1   655                 },
   -1   656                 'train': 'RE 74 (21225)',
   -1   657             },
   -1   658         ])