diff options
| author | Daniel Friesel <derf@finalrewind.org> | 2021-07-04 21:00:03 +0200 | 
|---|---|---|
| committer | Daniel Friesel <derf@finalrewind.org> | 2021-07-04 21:00:03 +0200 | 
| commit | 457165573d28ea3ddb803d285756e3506422363e (patch) | |
| tree | 088be1821bb082064ad6aa7c8413677f8fffa8b3 | |
| parent | 8f688c51a07bc79d62ecee416bdfdcbabc14e262 (diff) | |
match HAFAS and EFA data (WiP)
| -rwxr-xr-x | bin/nvm | 129 | 
1 files changed, 124 insertions, 5 deletions
@@ -35,6 +35,87 @@ env = Environment(loader=FileSystemLoader("templates"), autoescape=select_autoes  apis = None +class EFA: +    def __init__(self, url): +        self.url = url + "/XML_DM_REQUEST" +        self.post_data = { +            "command": "", +            "deleteAssignedStops_dm": "1", +            "help": "Hilfe", +            "itdLPxx_id_dm": ":dm", +            "itdLPxx_mapState_dm": "", +            "itdLPxx_mdvMap2_dm": "", +            "itdLPxx_mdvMap_dm": "3406199:401077:NAV3", +            "itdLPxx_transpCompany": "vrr", +            "itdLPxx_view": "", +            "language": "de", +            "mode": "direct", +            "nameInfo_dm": "invalid", +            "nameState_dm": "empty", +            "outputFormat": "JSON", +            "ptOptionsActive": "1", +            "requestID": "0", +            "reset": "neue Anfrage", +            "sessionID": "0", +            "submitButton": "anfordern", +            "typeInfo_dm": "invalid", +            "type_dm": "stop", +            "useProxFootSearch": "0", +            "useRealtime": "1", +        } + +    async def get_departures(self, place, name, ts): +        self.post_data.update( +            { +                "itdDateDay": ts.day, +                "itdDateMonth": ts.month, +                "itdDateYear": ts.year, +                "itdTimeHour": ts.hour, +                "itdTimeMinute": ts.minute, +                "name_dm": name, +            } +        ) +        if place is None: +            self.post_data.pop("placeInfo_dm", None) +            self.post_data.pop("placeState_dm", None) +            self.post_data.pop("place_dm", None) +        else: +            self.post_data.update( +                {"placeInfo_dm": "invalid", "placeState_dm": "empty", "place_dm": place} +            ) +        departures = list() +        async with aiohttp.ClientSession() as session: +            async with session.post(self.url, data=self.post_data) as response: +                # EFA may return JSON with a text/html Content-Type, which response.json() does not like. +                departures = json.loads(await response.text()) + +        return list(map(EFADeparture, departures["departureList"])) + + +class EFADeparture: +    def __init__(self, data): +        self.line = data["servingLine"]["symbol"] +        self.train_no = data["servingLine"].get("trainNum", None) +        self.occupancy = data.get("occupancy", None) +        self.platform = data.get("platform", None)  # platformName? +        self.direction = data["servingLine"].get("direction", None) + +        # Ensure compatibility with DB HAFAS +        if self.line.startswith("U") and not " " in self.line: +            self.line = "U " + self.line[1:] + +        datetime = data["dateTime"] +        year = int(datetime["year"]) +        month = int(datetime["month"]) +        day = int(datetime["day"]) +        hour = int(datetime["hour"]) +        minute = int(datetime["minute"]) +        self.iso8601 = f"{year:04d}-{month:02d}-{day:02d}T{hour:02d}:{minute:02d}" + +    def __repr__(self): +        return f"EFADeparture<line {self.line} to {self.direction}, scheduled departure at {self.iso8601}>" + +  class TransportAPIs:      def __init__(self):          self.apis = list() @@ -94,8 +175,10 @@ class Departure:              self.when = None          try:              self.plannedWhen = dateutil.parser.parse(self.plannedWhen) +            self.iso8601 = self.plannedWhen.strftime("%Y-%m-%dT%H:%M")          except TypeError:              self.plannedWhen = None +            self.iso8601          if self.cancelled:              self.classes += " cancelled" @@ -111,6 +194,9 @@ class Departure:              self.delay = self.delay // 60              self.delay = f"{self.delay:+.0f}" +    def __repr__(self): +        return f"Departure<line {self.line} to {self.direction}, scheduled departure at {self.iso8601}>" +      def set_relative(self, now):          minutes = (self.sort_by - now) // 60          if minutes < 1: @@ -120,6 +206,22 @@ class Departure:          else:              self.relativeWhen = f"{minutes//60:.0f}h {minutes%60:.0f}min" +    def add_efa(self, candidates): +        dest_candidates = list() +        for candidate in candidates: +            if candidate.iso8601 != self.iso8601: +                continue +            if candidate.line != self.line.name: +                continue +            dest_candidates.append(candidate) +        if len(dest_candidates) == 1: +            self._add_efa(dest_candidates[0]) +        # else: TODO check destination + +    def _add_efa(self, efa_departure): +        if efa_departure.platform and not self.platform: +            self.platform = efa_departure.platform +  class Line:      def __init__(self, obj): @@ -142,6 +244,9 @@ class Line:                  self.name = self.name[4:]              self.css_class = "bus" +    def __repr__(self): +        return self.name +  async def show_departure_board(request):      try: @@ -158,15 +263,14 @@ async def show_departure_board(request):      if type(departures) is dict and departures.get("error", False):          return web.HTTPNotFound(body=json.dumps(departures), headers=headers) -    departures = list(map(Departure, departures)) +    now = datetime.now() +    now_ts = now.timestamp() -    if len(departures): -        efa_endpoint = apis.get_efa(departures[0].location) +    departures = list(map(Departure, departures))      station_name_freq = dict() -    now = datetime.now().timestamp()      for departure in departures: -        departure.set_relative(now) +        departure.set_relative(now_ts)          station_name_freq[departure.station_name] = (              station_name_freq.get(departure.station_name, 0) + 1          ) @@ -176,6 +280,21 @@ async def show_departure_board(request):      else:          station_name = "NVM" +    efa_by_iso8601 = dict() + +    if len(departures) and ", " in station_name: +        name, place = station_name.split(", ") +        efa_endpoint = apis.get_efa(departures[0].location) +        efa = EFA(efa_endpoint["endpoint"]) +        efa_departures = await efa.get_departures(place, name, now) +        for departure in efa_departures: +            if departure.iso8601 not in efa_by_iso8601: +                efa_by_iso8601[departure.iso8601] = list() +            efa_by_iso8601[departure.iso8601].append(departure) + +    for departure in departures: +        departure.add_efa(efa_by_iso8601.get(departure.iso8601, list())) +      departure_board = env.get_template("departure_list.html")      return web.Response(          body=departure_board.render(title=station_name, departures=departures),  | 
