From f1163e090c6d7e3ac307383022954e30517028b4 Mon Sep 17 00:00:00 2001 From: Josh Verma <45299491+joshverma@users.noreply.github.com> Date: Tue, 19 Dec 2023 17:11:38 -0500 Subject: [PATCH] TRAN-7600: Add translator for MNMT (#60) * TRAN-7600: add translator for mnmt * TRAN-7600: update registry * TRAN-7600: cast stop_id * TRAN-7600: set arrival times * TRAN-7600: stop name * TRAN-7600: remove seconds from time parser * TRAN-7600: flip condition * TRAN-7600: timezone * TRAN-7600: create trip_update with timezone * TRAN-7600: use actual field * TRAN-7600: add arrival time * TRAN-7600: append terminal to route short name * TRAN-7600: add tests * TRAN-7600: newline --- .../registry/registry.py | 3 +- .../translators/__init__.py | 1 + gtfs_realtime_translators/translators/mnmt.py | 81 ++++++++++++++ test/fixtures/mnmt.json | 45 ++++++++ test/test_mnmt.py | 101 ++++++++++++++++++ 5 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 gtfs_realtime_translators/translators/mnmt.py create mode 100644 test/fixtures/mnmt.json create mode 100644 test/test_mnmt.py diff --git a/gtfs_realtime_translators/registry/registry.py b/gtfs_realtime_translators/registry/registry.py index 6603327..25c7f3b 100644 --- a/gtfs_realtime_translators/registry/registry.py +++ b/gtfs_realtime_translators/registry/registry.py @@ -4,7 +4,7 @@ SeptaRegionalRailTranslator, MtaSubwayGtfsRealtimeTranslator, NjtRailGtfsRealtimeTranslator, \ CtaSubwayGtfsRealtimeTranslator, CtaBusGtfsRealtimeTranslator, PathGtfsRealtimeTranslator, \ PathNewGtfsRealtimeTranslator, SwiftlyGtfsRealtimeTranslator, WcdotGtfsRealTimeTranslator, \ - NjtBusGtfsRealtimeTranslator, MbtaGtfsRealtimeTranslator + NjtBusGtfsRealtimeTranslator, MbtaGtfsRealtimeTranslator, MnmtGtfsRealtimeTranslator class TranslatorKeyWarning(Warning): @@ -25,6 +25,7 @@ class TranslatorRegistry: 'swiftly': SwiftlyGtfsRealtimeTranslator, 'wcdot-bus': WcdotGtfsRealTimeTranslator, 'mbta': MbtaGtfsRealtimeTranslator, + 'mnmt': MnmtGtfsRealtimeTranslator } @classmethod diff --git a/gtfs_realtime_translators/translators/__init__.py b/gtfs_realtime_translators/translators/__init__.py index 05faf49..f5ac549 100644 --- a/gtfs_realtime_translators/translators/__init__.py +++ b/gtfs_realtime_translators/translators/__init__.py @@ -10,3 +10,4 @@ from .swiftly import SwiftlyGtfsRealtimeTranslator from .wcdot_bus import WcdotGtfsRealTimeTranslator from .mbta import MbtaGtfsRealtimeTranslator +from .mnmt import MnmtGtfsRealtimeTranslator diff --git a/gtfs_realtime_translators/translators/mnmt.py b/gtfs_realtime_translators/translators/mnmt.py new file mode 100644 index 0000000..3f0078a --- /dev/null +++ b/gtfs_realtime_translators/translators/mnmt.py @@ -0,0 +1,81 @@ +import json + +import pendulum + +from gtfs_realtime_translators.factories import TripUpdate, FeedMessage + + +class MnmtGtfsRealtimeTranslator: + TIMEZONE = 'America/Chicago' + + def __call__(self, data): + json_data = json.loads(data) + + stops_list = json_data.get('stops') + departures_list = json_data.get('departures') + + entities = [] + if stops_list and departures_list: + entities = self.__make_trip_updates(stops_list, departures_list) + + return FeedMessage.create(entities=entities) + + @classmethod + def __make_trip_updates(cls, stops_list, departures_list): + trip_updates = [] + stop_name = stops_list[0].get("description") + + for index, departure in enumerate(departures_list): + entity_id = str(index + 1) + + trip_id = departure.get('trip_id') + + stop_id = departure.get('stop_id') + if stop_id: + stop_id = str(stop_id) + + headsign = departure.get('description') + route_id = departure.get('route_id') + direction_id = departure.get('direction_id') + + departure_time, scheduled_departure_time = None, None + arrival_time, scheduled_arrival_time = None, None + if cls.__is_realtime_departure(departure): + departure_time = departure.get('departure_time') + arrival_time = departure_time + else: + scheduled_departure_time = departure.get('departure_time') + scheduled_arrival_time = scheduled_departure_time + + route_short_name = cls.__get_route_short_name(departure) + + trip_update = TripUpdate.create(entity_id=entity_id, + departure_time=departure_time, + arrival_time=arrival_time, + scheduled_departure_time=scheduled_departure_time, + scheduled_arrival_time=scheduled_arrival_time, + trip_id=trip_id, + route_id=route_id, + route_short_name=route_short_name, + stop_id=stop_id, + stop_name=stop_name, + headsign=headsign, + direction_id=direction_id, + agency_timezone=cls.TIMEZONE + ) + + trip_updates.append(trip_update) + + return trip_updates + + @classmethod + def __is_realtime_departure(cls, departure): + return departure.get('actual') is True + + @classmethod + def __get_route_short_name(cls, departure): + terminal = departure.get('terminal') + route_short_name = departure.get('route_short_name') + if terminal: + return f'{route_short_name}{terminal}' + return route_short_name diff --git a/test/fixtures/mnmt.json b/test/fixtures/mnmt.json new file mode 100644 index 0000000..85a6c9e --- /dev/null +++ b/test/fixtures/mnmt.json @@ -0,0 +1,45 @@ +{ + "stops": [ + { + "stop_id": 11191, + "latitude": 45.013401, + "longitude": -93.287947, + "description": "Lyndale Ave N & Lowry Ave N" + } + ], + "alerts": [ + { + "stop_closed": false, + "alert_text": "The following stop is closed for Routes 3, 7 and 22 until further notice: Washington Ave S & Park Ave - Stop #19306 (westbound)" + } + ], + "departures": [ + { + "actual": true, + "trip_id": "24557038-DEC23-MVS-BUS-Weekday-03", + "stop_id": 11191, + "departure_text": "14 Min", + "departure_time": 1702923655, + "description": "Brklyn Ctr Tc / N Lyndale / Via Penn Av", + "route_id": "22", + "route_short_name": "22", + "direction_id": 0, + "direction_text": "NB", + "terminal": "A", + "schedule_relationship": "Scheduled" + }, + { + "actual": false, + "trip_id": "24557032-DEC23-MVS-BUS-Weekday-03", + "stop_id": 11191, + "departure_text": "12:42", + "departure_time": 1702924920, + "description": "Brklyn Ctr Tc / N Lyndale / Via Humboldt", + "route_id": "22", + "route_short_name": "22", + "direction_id": 0, + "direction_text": "NB", + "schedule_relationship": "Scheduled" + } + ] +} diff --git a/test/test_mnmt.py b/test/test_mnmt.py new file mode 100644 index 0000000..222a4a0 --- /dev/null +++ b/test/test_mnmt.py @@ -0,0 +1,101 @@ +import pytest + +from gtfs_realtime_translators.translators import MnmtGtfsRealtimeTranslator +from gtfs_realtime_translators.bindings import intersection_pb2 as intersection_gtfs_realtime +from gtfs_realtime_translators.factories import FeedMessage + + +@pytest.fixture +def mnmt(): + with open('test/fixtures/mnmt.json') as f: + raw = f.read() + return raw + + +def test_mnmt_realtime_departure(mnmt): + translator = MnmtGtfsRealtimeTranslator() + message = translator(mnmt) + + entity = message.entity[0] + trip_update = entity.trip_update + stop_time_update = trip_update.stop_time_update[0] + + assert message.header.gtfs_realtime_version == FeedMessage.VERSION + assert entity.id == '1' + + assert stop_time_update.departure.time == 1702923655 + assert stop_time_update.arrival.time == 1702923655 + + assert not stop_time_update.Extensions[ + intersection_gtfs_realtime.intersection_stop_time_update].\ + scheduled_arrival.time + assert not stop_time_update.Extensions[ + intersection_gtfs_realtime.intersection_stop_time_update].\ + scheduled_departure.time + + +def test_mnmt_scheduled_departure(mnmt): + translator = MnmtGtfsRealtimeTranslator() + message = translator(mnmt) + + entity = message.entity[1] + trip_update = entity.trip_update + stop_time_update = trip_update.stop_time_update[0] + + assert message.header.gtfs_realtime_version == FeedMessage.VERSION + assert entity.id == '2' + + assert not stop_time_update.departure.time + assert not stop_time_update.arrival.time + + assert stop_time_update.Extensions[ + intersection_gtfs_realtime.intersection_stop_time_update].\ + scheduled_arrival.time == 1702924920 + assert stop_time_update.Extensions[ + intersection_gtfs_realtime.intersection_stop_time_update].\ + scheduled_departure.time == 1702924920 + + +def test_mnmt_route_short_name_with_terminal(mnmt): + translator = MnmtGtfsRealtimeTranslator() + message = translator(mnmt) + + entity = message.entity[0] + trip_update = entity.trip_update + + assert message.header.gtfs_realtime_version == FeedMessage.VERSION + assert entity.id == '1' + + assert trip_update.Extensions[ + intersection_gtfs_realtime.intersection_trip_update].\ + route_short_name == '22A' + + +def test_mnmt_route_short_name_without_terminal(mnmt): + translator = MnmtGtfsRealtimeTranslator() + message = translator(mnmt) + + entity = message.entity[1] + trip_update = entity.trip_update + + assert message.header.gtfs_realtime_version == FeedMessage.VERSION + assert entity.id == '2' + + assert trip_update.Extensions[ + intersection_gtfs_realtime.intersection_trip_update].\ + route_short_name == '22' + + +def test_mnmt_headsign(mnmt): + translator = MnmtGtfsRealtimeTranslator() + message = translator(mnmt) + + entity = message.entity[0] + trip_update = entity.trip_update + + assert message.header.gtfs_realtime_version == FeedMessage.VERSION + assert entity.id == '1' + + assert trip_update.Extensions[ + intersection_gtfs_realtime.intersection_trip_update].headsign \ + == 'Brklyn Ctr Tc / N Lyndale / Via Penn Av'