db-pkpass

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

commit
f874077d0344a8b1390ecf089f5b07ef2621de0e
parent
efe0b9bab4cdce866b9bb1b6e09800887c36a20e
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2025-05-30 17:58
check bahncard

Diffstat

M db_pkpass.py 84 ++++++++++++++++++++++++++++++++++++-------------------------

1 files changed, 50 insertions, 34 deletions


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

@@ -97,9 +97,7 @@ def extract_legs(pdf):
   97    97             text = text.rstrip('\n').replace(',\n', ', ')
   98    98             if text.startswith('Halt\nDatum\nZeit\nGleis'):
   99    99                 state = 1
  100    -1             elif state == 0:
  101    -1                 pass
  102    -1             elif text.startswith('Ihre Reiseverbindung und Reservierung'):
   -1   100             elif state == 0 or text.startswith('Ihre Reiseverbindung '):
  103   101                 pass
  104   102             elif text.startswith('Wichtige Nutzungshinweise') or not text.strip():
  105   103                 break
@@ -155,12 +153,18 @@ def extract_legs(pdf):
  155   153     return legs
  156   154 
  157   155 
  158    -1 def extract_order_id(pdf):
  159    -1     key = 'Auftragsnummer: '
   -1   156 def extract_id(pdf):
  160   157     for page in pdf:
  161   158         for text in page.get_text().split('\n'):
  162    -1             if text.startswith(key):
  163    -1                 return text[len(key):]
   -1   159             for label in ['Auftragsnummer', 'BahnCard-Nr.']:
   -1   160                 key = f'{label}: '
   -1   161                 if text.startswith(key):
   -1   162                     return label, text[len(key):]
   -1   163     raise ValueError('No order ID found')
   -1   164 
   -1   165 
   -1   166 def extract_title(pdf):
   -1   167     return pdf[0].get_text('blocks')[1][4].strip()
  164   168 
  165   169 
  166   170 def extract_validity(pdf):
@@ -170,14 +174,19 @@ def extract_validity(pdf):
  170   174         for text in page.get_text().split('\n'):
  171   175             if text.startswith(key1):
  172   176                 s_start, s_end = text[len(key1):].split(' bis ')
  173    -1                 start = strptime(s_start, '%d.%m.%Y %H:%M Uhr')
  174    -1                 end = strptime(s_end, '%d.%m.%Y %H:%M Uhr')
   -1   177                 try:
   -1   178                     start = strptime(s_start, '%d.%m.%Y %H:%M Uhr')
   -1   179                     end = strptime(s_end, '%d.%m.%Y %H:%M Uhr')
   -1   180                 except ValueError:
   -1   181                     start = strptime(s_start, '%d.%m.%Y')
   -1   182                     end = strptime(s_end, '%d.%m.%Y')
  175   183                 return start, end
  176   184             elif text.startswith(key2):
  177   185                 s_start = text[len(key2):]
  178   186                 start = strptime(s_start, '%d.%m.%Y')
  179   187                 end = start + datetime.timedelta(days=1)
  180   188                 return start, end
   -1   189     raise ValueError('No validity information found')
  181   190 
  182   191 
  183   192 def format_stop(stop, train=None):
@@ -199,21 +208,17 @@ def format_legs(legs):
  199   208 
  200   209 
  201   210 def extract_content(pdf):
  202    -1     order_id = extract_order_id(pdf)
   -1   211     title = extract_title(pdf)
   -1   212     id_label, id_value = extract_id(pdf)
  203   213     validity = extract_validity(pdf)
  204   214 
  205    -1     legs = extract_legs(pdf, validity[0])
  206    -1     start = legs[0]['start']['station']
  207    -1     destination = legs[-1]['destination']['station']
  208    -1     date = legs[0]['start']['datetime']
  209    -1 
  210    -1     return {
   -1   215     data = {
  211   216         'formatVersion': 1,
  212   217         'organizationName': 'Deutsche Bahn AG',
  213   218         'passTypeIdentifier': 'ticket.ce9e.org',
  214   219         'teamIdentifier': 'XXXXXXXXXX',
  215    -1         'serialNumber': order_id,
  216    -1         'description': f'{start} → {destination} ({date.date().isoformat()})',
   -1   220         'serialNumber': id_value,
   -1   221         'description': title,
  217   222         'expirationDate': validity[1].isoformat(),
  218   223         'relevantDates': [
  219   224             {
@@ -231,28 +236,39 @@ def extract_content(pdf):
  231   236         ],
  232   237         'boardingPass': {
  233   238             'transitType': 'PKTransitTypeTrain',
  234    -1             'secondaryFields': [
  235    -1                 {
  236    -1                     'key': 'date',
  237    -1                     'label': 'Datum',
  238    -1                     'dateStyle': 'PKDateStyleFull',
  239    -1                     'timeStyle': 'PKDateStyleNone',
  240    -1                     'value': date.isoformat(),
  241    -1                 },
  242    -1                 {
  243    -1                     'key': 'legs',
  244    -1                     'label': 'Reiseplan',
  245    -1                     'value': format_legs(legs),
  246    -1                 },
   -1   239             'auxiliaryFields': [
  247   240                 {
  248    -1                     'key': 'order-id',
  249    -1                     'label': 'Auftragsnummer',
  250    -1                     'value': order_id,
   -1   241                     'key': 'id',
   -1   242                     'label': id_label,
   -1   243                     'value': id_value,
  251   244                 },
  252   245             ],
  253   246         },
  254   247     }
  255   248 
   -1   249     legs = extract_legs(pdf)
   -1   250     if legs:
   -1   251         start = legs[0]['start']['station']
   -1   252         destination = legs[-1]['destination']['station']
   -1   253         date = legs[0]['start']['datetime']
   -1   254         data['description'] = f'{start} → {destination} ({date.date().isoformat()})'
   -1   255         data['boardingPass']['secondaryFields'] = [
   -1   256             {
   -1   257                 'key': 'date',
   -1   258                 'label': 'Datum',
   -1   259                 'dateStyle': 'PKDateStyleFull',
   -1   260                 'timeStyle': 'PKDateStyleNone',
   -1   261                 'value': date.isoformat(),
   -1   262             },
   -1   263             {
   -1   264                 'key': 'legs',
   -1   265                 'label': 'Reiseplan',
   -1   266                 'value': format_legs(legs),
   -1   267             },
   -1   268         ]
   -1   269 
   -1   270     return data
   -1   271 
  256   272 
  257   273 if __name__ == '__main__':
  258   274     parser = argparse.ArgumentParser()