aboutsummaryrefslogtreecommitdiff
path: root/autoplanner.py
blob: 4439146b60952456fd8c32da163c475707d738b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/bin/python3
import http.client
import urllib.request
import urllib.parse
import urllib.error
import json
import dateutil.parser
import re
from uuid import uuid4
from icalendar import Calendar, Event
from shared import *
import sys
from datetime import datetime
import pytz

SECOND = 1
MINUTE = 60 * SECOND
HOUR = 60 * MINUTE
DAY = 24 * HOUR

# don't calculate trip for events further than a week in the future
FUTURE_MAX = 7 * DAY
# allow trips to end two minutes late (lazy student mode)
LATE_MAX = 2 * MINUTE

cal = Calendar()
KEY = ""
CFG = {}
tz_name = None

def read_file(filename):
  f = open(filename, "r")
  r = str(f.read())
  f.close()
  return r

def get_trip(date):
  # data = ""
  # with open("./res.json", "r") as gert:
  #   data = gert.read()
  #   gert.close()
  # return data
  headers = {
    'Ocp-Apim-Subscription-Key': KEY,
    'X-Request-Id': str(uuid4()),
  }
  param_dict = {
    'searchForArrival': True,
    'dateTime': date.isoformat(),
  }
  param_dict.update(CFG)
  params = urllib.parse.urlencode(param_dict)
  conn = http.client.HTTPSConnection('gateway.apiportal.ns.nl')
  conn.request("GET", f"/reisinformatie-api/api/v3/trips?{params}", "", headers)
  response = conn.getresponse()
  data = response.read()
  conn.close()
  debug_output("autoplanner", data)
  return data

def leg2desc(leg):
  desc = ""
  duration = leg['plannedDurationInMinutes']
  desc += f"Totale ritduur is {duration} {'minuut' if duration == 1 else 'minuten'}\n\n"

  desc += "Tussenstops:\n"
  for stop in leg['stops']:
    desc += f" - {stop['name']}\n"
  desc += "\n"
  return desc

def trip2ical(trip, real_date):
  global tz_name
  trips = trip['trips']
  trips = [t for t in trips if t['status'] != 'CANCELLED']
  trips = [t for t in trips if dateutil.parser.parse(t['legs'][-1]['destination']['plannedDateTime']).timestamp() < (real_date.timestamp() + LATE_MAX)]
  actual_trip = next(reversed(trips))
  if actual_trip == None: return # no trip possible

  for leg in actual_trip['legs']:
    if leg['travelType'] == 'WALK': continue

    ev = Event()
    ev.add('summary', f"{leg['name']} -> {leg['direction']}")
    ev.add('description', leg2desc(leg))

    ev_start = dateutil.parser.parse(leg['origin']['plannedDateTime'])
    ev_end = dateutil.parser.parse(leg['destination']['plannedDateTime'])

    # this is a stupid hack
    ev_start.tzinfo.tzname = lambda _: tz_name
    ev_end.tzinfo.tzname = lambda _: tz_name

    ev.add('dtstart', ev_start)
    ev.add('dtend', ev_end)
    cal.add_component(ev)

def main():
  global tz_name
  input_cal = Calendar.from_ical(sys.stdin.read())
  tz_name = input_cal.get('x-wr-timezone')
  tz = pytz.timezone(tz_name)
  events = list(input_cal.walk('vevent'))

  cal.add('prodid', 'trein')
  cal.add('version', '2.0')

  global KEY, CFG
  KEY = get_public_key()
  CFG = json.loads(read_file("./autoplanner.json"))

  now = datetime.now().timestamp()

  # this is garbage code, but gets a list containing the date/time for each
  # first event of every day after today
  times = [event.get('dtstart').dt for event in events] # get datetimes of all event *start* times
  times = [tz.localize(dt.replace(tzinfo=None)) for dt in times] # offset times by timezone from input calendar
  times = [x.timestamp() for x in times] # convert to epoch timestamps
  times = [(x // DAY, x % DAY) for x in times] # split into days and seconds
  times = [x for x in times if x[1] == min([y[1] for y in times if y[0] == x[0]])] # only leave smallest seconds in day
  times = [x for x in times if now < (x[0] * DAY + x[1]) < (now + FUTURE_MAX)] # only leave after today and until FUTURE_MAX
  times = [datetime.fromtimestamp(x[0] * DAY + x[1], tz=tz) for x in times] # convert back to datetime

  for time in times:
    trip = get_trip(time)
    trip2ical(json.loads(trip), time)

  print(str(cal.to_ical(), 'utf-8'))

if __name__ == "__main__":
  main()