def create_trip_update(id, trip_id, circulation_date, stops, status='update'): trip_update = TripUpdate( VehicleJourney( { 'trip': { 'id': trip_id }, 'stop_times': [{ 'arrival_time': datetime.time(8, 10), 'stop_point': { 'stop_area': { 'timezone': 'UTC' } } }] }, circulation_date), status) trip_update.id = id for stop in stops: st = StopTimeUpdate({'id': stop['id']}, stop['departure'], stop['arrival']) st.arrival_status = stop['arrival_status'] st.departure_status = stop['departure_status'] trip_update.stop_time_updates.append(st) db.session.add(trip_update) return trip_update
def test_multiple_delays_in_2_updates(navitia_vj): """ same test as test_multiple_delays, but with nothing in the db and with 2 trip updates """ with app.app_context(): contributor = model.Contributor( id=GTFS_CONTRIBUTOR_ID, navitia_coverage=None, connector_type=ConnectorType.gtfs_rt.value ) builder = kirin.poller_workers.gtfs_rt.KirinModelBuilder(contributor) trip_update = TripUpdate(_create_db_vj(navitia_vj), status="update", contributor_id=contributor.id) real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) trip_update.stop_time_updates = [ StopTimeUpdate({"id": "sa:1"}, departure_delay=timedelta(minutes=5), dep_status="update") ] handle(builder, real_time_update, [trip_update]) trip_update = TripUpdate(_create_db_vj(navitia_vj), status="update", contributor_id=contributor.id) real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) trip_update.stop_time_updates = [ StopTimeUpdate({"id": "sa:1"}, departure_delay=timedelta(minutes=10), dep_status="update"), StopTimeUpdate({"id": "sa:2"}, arrival_delay=timedelta(minutes=2), arr_status="update"), ] res, _ = handle(builder, real_time_update, [trip_update]) _check_multiples_delay(res) # we also check that there is what we want in the db db_trip_updates = res.query.from_self(TripUpdate).all() assert len(db_trip_updates) == 1 assert db_trip_updates[0].status == "update" assert len(RealTimeUpdate.query.all()) == 2 assert len(StopTimeUpdate.query.all()) == 3
def create_trip_update( id, trip_id, circulation_date, stops, status="update", contributor_id=GTFS_CONTRIBUTOR_ID ): trip_update = TripUpdate( VehicleJourney( { "trip": {"id": trip_id}, "stop_times": [ {"utc_arrival_time": datetime.time(8, 10), "stop_point": {"stop_area": {"timezone": "UTC"}}} ], }, datetime.datetime.combine(circulation_date, datetime.time(7, 10)), datetime.datetime.combine(circulation_date, datetime.time(9, 10)), ), contributor_id, status, ) trip_update.id = id for stop in stops: st = StopTimeUpdate({"id": stop["id"]}, stop["departure"], stop["arrival"]) st.arrival_status = stop["arrival_status"] st.departure_status = stop["departure_status"] trip_update.stop_time_updates.append(st) db.session.add(trip_update) return trip_update
def test_cancellation_then_delay_in_2_updates(navitia_vj): """ same as test_cancellation_then_delay, but with a clear db and in 2 updates """ with app.app_context(): contributor = model.Contributor( id=GTFS_CONTRIBUTOR_ID, navitia_coverage=None, connector_type=ConnectorType.gtfs_rt.value ) builder = kirin.poller_workers.gtfs_rt.KirinModelBuilder(contributor) trip_update = TripUpdate( _create_db_vj(navitia_vj), status="delete", effect=TripEffect.NO_SERVICE.name, contributor_id=contributor.id, ) trip_update.stop_time_updates = [] real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) handle(builder, real_time_update, [trip_update]) trip_update = TripUpdate( _create_db_vj(navitia_vj), status="none", effect=TripEffect.UNKNOWN_EFFECT.name, contributor_id=contributor.id, ) real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) trip_update.stop_time_updates = [ StopTimeUpdate({"id": "sa:1"}, arr_status="none", dep_status="none", order=0), StopTimeUpdate({"id": "sa:2"}, arr_status="none", dep_status="none", order=1), StopTimeUpdate({"id": "sa:3"}, arrival_delay=timedelta(minutes=40), arr_status="update", order=2), ] res, _ = handle(builder, real_time_update, [trip_update]) _check_cancellation_then_delay(res)
def test_multiple_delays(setup_database, navitia_vj): """ We receive a delay on the first and second stoptimes of a vj, and there was already some delay on the first st of this vj sa:1 sa:2 sa:3 VJ navitia 8:10 9:05-9:10 10:05 VJ in db 8:15* 9:05-9:10 10:05 update kirin 8:20* *9:07-9:10 10:05 """ with app.app_context(): contributor = model.Contributor( id=GTFS_CONTRIBUTOR_ID, navitia_coverage=None, connector_type=ConnectorType.gtfs_rt.value ) builder = kirin.poller_workers.gtfs_rt.KirinModelBuilder(contributor) trip_update = TripUpdate(_create_db_vj(navitia_vj), status="update", contributor_id=contributor.id) real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) trip_update.stop_time_updates = [ # Note: the delay is based of the navitia's vj StopTimeUpdate({"id": "sa:1"}, departure_delay=timedelta(minutes=10), dep_status="update"), StopTimeUpdate({"id": "sa:2"}, arrival_delay=timedelta(minutes=2), arr_status="update"), ] res, _ = handle(builder, real_time_update, [trip_update]) _check_multiples_delay(res) # we also check that there is what we want in the db db_trip_updates = res.query.from_self(TripUpdate).all() assert len(db_trip_updates) == 2 for tu in db_trip_updates: assert tu.status == "update" assert len(RealTimeUpdate.query.all()) == 3 # 2 already in db, one new update assert len(StopTimeUpdate.query.all()) == 6 # 3 st * 2 vj in the db
def create_trip_update(id, trip_id, circulation_date, stops, status='update'): trip_update = TripUpdate(VehicleJourney({'trip': {'id': trip_id}}, circulation_date), status) trip_update.id = id for stop in stops: st = StopTimeUpdate({'id': stop['id']}, stop['departure'], stop['arrival']) st.arrival_status = stop['arrival_status'] st.departure_status = stop['departure_status'] trip_update.stop_time_updates.append(st) db.session.add(trip_update) return trip_update
def test_find_stop(): with app.app_context(): vj = create_trip_update('70866ce8-0638-4fa1-8556-1ddfa22d09d3', 'vj1', datetime.date(2015, 9, 8)) st1 = StopTimeUpdate({'id': 'sa:1'}, None, None) vj.stop_time_updates.append(st1) st2 = StopTimeUpdate({'id': 'sa:2'}, None, None) vj.stop_time_updates.append(st2) st3 = StopTimeUpdate({'id': 'sa:3'}, None, None) vj.stop_time_updates.append(st3) assert vj.find_stop('sa:1') == st1 assert vj.find_stop('sa:3') == st3 assert vj.find_stop('sa:4') is None
def create_trip_update(id, trip_id, circulation_date, stops, status='update'): trip_update = TripUpdate( VehicleJourney({'trip': { 'id': trip_id }}, circulation_date), status) trip_update.id = id for stop in stops: st = StopTimeUpdate({'id': stop['id']}, stop['departure'], stop['arrival']) st.arrival_status = stop['arrival_status'] st.departure_status = stop['departure_status'] trip_update.stop_time_updates.append(st) db.session.add(trip_update) return trip_update
def _make_stop_time_update(base_arrival, base_departure, last_departure, input_st, stop_point, order): dep, dep_status, dep_delay = _get_update_info_of_stop_event( base_departure, input_st.departure, input_st.departure_status, input_st.departure_delay ) arr, arr_status, arr_delay = _get_update_info_of_stop_event( base_arrival, input_st.arrival, input_st.arrival_status, input_st.arrival_delay ) # in case where arrival/departure time are None if arr is None: arr = dep if dep is not None else last_departure dep = dep if dep is not None else arr # in case where the previous departure time are greater than the current arrival if last_departure and last_departure > arr: arr_delay += last_departure - arr arr = last_departure # in the real world, the departure time must be greater or equal to the arrival time if arr > dep: dep_delay += arr - dep dep = arr return StopTimeUpdate( navitia_stop=stop_point, departure=dep, departure_delay=dep_delay, dep_status=dep_status, arrival=arr, arrival_delay=arr_delay, arr_status=arr_status, message=input_st.message, order=order, )
def test_handle_new_trip_out_of_order(navitia_vj): """ We have one vj with only one stop time updated, but it's not the first so we have to reorder the stop times in the resulting trip_update """ with app.app_context(): trip_update = TripUpdate(VehicleJourney(navitia_vj, datetime.date(2015, 9, 8)), status='update') st = StopTimeUpdate({'id': 'sa:2'}, departure_delay=timedelta(minutes=40), dep_status='update', arrival_delay=timedelta(minutes=44), arr_status='update') real_time_update = RealTimeUpdate(raw_data=None, connector='ire') trip_update.stop_time_updates.append(st) res = handle(real_time_update, [trip_update], 'kisio-digital') assert len(res.trip_updates) == 1 trip_update = res.trip_updates[0] assert len(trip_update.stop_time_updates) == 3 assert trip_update.stop_time_updates[0].stop_id == 'sa:1' assert trip_update.stop_time_updates[0].departure == _dt("8:10") assert trip_update.stop_time_updates[0].arrival == _dt("8:10") assert trip_update.stop_time_updates[1].stop_id == 'sa:2' assert trip_update.stop_time_updates[1].departure == _dt("9:50") assert trip_update.stop_time_updates[1].arrival == _dt("9:49") assert trip_update.stop_time_updates[2].stop_id == 'sa:3' assert trip_update.stop_time_updates[2].departure == _dt("10:05") assert trip_update.stop_time_updates[2].arrival == _dt("10:05")
def test_cancellation_then_delay(navitia_vj): """ we have a cancelled vj in the db, and we receive an update on the 3rd stoptime, at the end we have a delayed vj sa:1 sa:2 sa:3 VJ navitia 8:10 9:05-9:10 10:05 VJ in db - update kirin 8:10 9:05-9:10 10:45* """ with app.app_context(): vju = create_trip_update('70866ce8-0638-4fa1-8556-1ddfa22d09d3', 'vehicle_journey:1', datetime.date(2015, 9, 8), [], status='delete') rtu = RealTimeUpdate(None, 'ire') rtu.id = '10866ce8-0638-4fa1-8556-1ddfa22d09d3' rtu.trip_updates.append(vju) db.session.add(rtu) db.session.commit() with app.app_context(): trip_update = TripUpdate(VehicleJourney(navitia_vj, datetime.date(2015, 9, 8)), status='update') real_time_update = RealTimeUpdate(raw_data=None, connector='ire') trip_update.stop_time_updates = [ StopTimeUpdate({'id': 'sa:3'}, arrival_delay=timedelta(minutes=40), arr_status='update'), ] res = handle(real_time_update, [trip_update], 'kisio-digital') _check_cancellation_then_delay(res)
def test_delays_then_cancellation_in_2_updates(navitia_vj): """ Same test as above, but with nothing in the db, and with 2 updates """ with app.app_context(): contributor = model.Contributor( id=GTFS_CONTRIBUTOR_ID, navitia_coverage=None, connector_type=ConnectorType.gtfs_rt.value ) builder = kirin.poller_workers.gtfs_rt.KirinModelBuilder(contributor) trip_update = TripUpdate(_create_db_vj(navitia_vj), status="update", contributor_id=contributor.id) real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) trip_update.stop_time_updates = [ StopTimeUpdate({"id": "sa:1"}, departure_delay=timedelta(minutes=5), dep_status="update") ] handle(builder, real_time_update, [trip_update]) trip_update = TripUpdate( _create_db_vj(navitia_vj), status="delete", effect=TripEffect.NO_SERVICE.name, contributor_id=contributor.id, ) real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) res, _ = handle(builder, real_time_update, [trip_update]) assert len(res.trip_updates) == 1 trip_update = res.trip_updates[0] assert trip_update.status == "delete" assert len(trip_update.stop_time_updates) == 3 for stu in trip_update.stop_time_updates: assert stu.arrival_status == "delete" assert stu.departure_status == "delete" assert len(trip_update.real_time_updates) == 2
def test_cancellation_then_delay_in_2_updates(navitia_vj): """ same as test_cancellation_then_delay, but with a clear db and in 2 updates """ with app.app_context(): trip_update = TripUpdate(VehicleJourney(navitia_vj, datetime.date(2015, 9, 8)), status='delete') trip_update.stop_time_updates = [] real_time_update = RealTimeUpdate(raw_data=None, connector='ire', contributor='realtime.ire') handle(real_time_update, [trip_update], 'kisio-digital') trip_update = TripUpdate(VehicleJourney(navitia_vj, datetime.date(2015, 9, 8)), status='update') real_time_update = RealTimeUpdate(raw_data=None, connector='ire', contributor='realtime.ire') trip_update.stop_time_updates = [ StopTimeUpdate({'id': 'sa:3'}, arrival_delay=timedelta(minutes=40), arr_status='update', order=2), ] res, _ = handle(real_time_update, [trip_update], 'kisio-digital') _check_cancellation_then_delay(res)
def test_find_stop(): with app.app_context(): vj = create_trip_update("70866ce8-0638-4fa1-8556-1ddfa22d09d3", "vj1", datetime.date(2015, 9, 8)) st1 = StopTimeUpdate({"id": "sa:1"}, None, None, order=0) vj.stop_time_updates.append(st1) st2 = StopTimeUpdate({"id": "sa:2"}, None, None, order=1) vj.stop_time_updates.append(st2) st3 = StopTimeUpdate({"id": "sa:3"}, None, None, order=2) vj.stop_time_updates.append(st3) assert vj.find_stop("sa:1", 0) == st1 assert vj.find_stop("sa:1") == st1 assert vj.find_stop("sa:2", 1) == st2 assert vj.find_stop("sa:3", 2) == st3 assert vj.find_stop("sa:4") is None
def test_cancellation_then_delay_in_2_updates(navitia_vj): """ same as test_cancellation_then_delay, but with a clear db and in 2 updates """ with app.app_context(): trip_update = TripUpdate(_create_db_vj(navitia_vj), status="delete", contributor_id=COTS_CONTRIBUTOR_ID) trip_update.stop_time_updates = [] real_time_update = make_rt_update( raw_data=None, connector_type=ConnectorType.cots.value, contributor_id=COTS_CONTRIBUTOR_ID) handle(real_time_update, [trip_update], contributor_id=COTS_CONTRIBUTOR_ID, is_new_complete=False) trip_update = TripUpdate(_create_db_vj(navitia_vj), status="update", contributor_id=COTS_CONTRIBUTOR_ID) real_time_update = make_rt_update( raw_data=None, connector_type=ConnectorType.cots.value, contributor_id=COTS_CONTRIBUTOR_ID) trip_update.stop_time_updates = [ StopTimeUpdate({"id": "sa:3"}, arrival_delay=timedelta(minutes=40), arr_status="update", order=2) ] res, _ = handle(real_time_update, [trip_update], COTS_CONTRIBUTOR_ID, is_new_complete=False) _check_cancellation_then_delay(res)
def test_past_midnight(): """ integration of a past midnight """ navitia_vj = { "trip": {"id": "vehicle_journey:1"}, "stop_times": [ { "utc_arrival_time": datetime.time(22, 10), "utc_departure_time": datetime.time(22, 15), "stop_point": {"id": "sa:1"}, }, # arrive at sa:2 at 23:10 and leave the day after { "utc_arrival_time": datetime.time(23, 10), "utc_departure_time": datetime.time(2, 15), "stop_point": {"id": "sa:2", "stop_area": {"timezone": "UTC"}}, }, { "utc_arrival_time": datetime.time(3, 20), "utc_departure_time": datetime.time(3, 25), "stop_point": {"id": "sa:3", "stop_area": {"timezone": "UTC"}}, }, ], } with app.app_context(): contributor = model.Contributor( id=GTFS_CONTRIBUTOR_ID, navitia_coverage=None, connector_type=ConnectorType.gtfs_rt.value ) builder = kirin.poller_workers.gtfs_rt.KirinModelBuilder(contributor) vj = VehicleJourney( navitia_vj, datetime.datetime(2015, 9, 8, 21, 15, 0), datetime.datetime(2015, 9, 9, 4, 20, 0) ) trip_update = TripUpdate(vj, status="update", contributor_id=contributor.id) st = StopTimeUpdate({"id": "sa:2"}, departure_delay=timedelta(minutes=31), dep_status="update", order=1) real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) trip_update.stop_time_updates.append(st) res, _ = handle(builder, real_time_update, [trip_update]) assert len(res.trip_updates) == 1 trip_update = res.trip_updates[0] assert trip_update.status == "update" assert len(trip_update.stop_time_updates) == 3 assert trip_update.stop_time_updates[0].stop_id == "sa:1" assert trip_update.stop_time_updates[0].arrival == _dt("22:10") assert trip_update.stop_time_updates[0].departure == _dt("22:15") assert trip_update.stop_time_updates[1].stop_id == "sa:2" assert trip_update.stop_time_updates[1].arrival == _dt("23:10") assert trip_update.stop_time_updates[1].arrival_delay == timedelta(0) assert trip_update.stop_time_updates[1].departure == _dt("2:46", day=9) assert trip_update.stop_time_updates[1].departure_delay == timedelta(minutes=31) assert trip_update.stop_time_updates[2].stop_id == "sa:3" assert trip_update.stop_time_updates[2].arrival == _dt("3:51", day=9) assert trip_update.stop_time_updates[2].arrival_delay == timedelta(minutes=31) assert trip_update.stop_time_updates[2].departure == _dt("3:56", day=9) assert trip_update.stop_time_updates[2].departure_delay == timedelta(minutes=31)
def test_delays_then_cancellation_in_2_updates(navitia_vj): """ Same test as above, but with nothing in the db, and with 2 updates """ with app.app_context(): trip_update = TripUpdate(VehicleJourney(navitia_vj, datetime.date(2015, 9, 8)), status='update') real_time_update = RealTimeUpdate(raw_data=None, connector='ire') trip_update.stop_time_updates = [ StopTimeUpdate({'id': 'sa:1'}, departure_delay=timedelta(minutes=5), dep_status='update'), ] handle(real_time_update, [trip_update], 'kisio-digital') trip_update = TripUpdate(VehicleJourney(navitia_vj, datetime.date(2015, 9, 8)), status='delete') real_time_update = RealTimeUpdate(raw_data=None, connector='ire') res = handle(real_time_update, [trip_update], 'kisio-digital') assert len(res.trip_updates) == 1 trip_update = res.trip_updates[0] assert trip_update.status == 'delete' assert len(trip_update.stop_time_updates) == 0 assert len(trip_update.real_time_updates) == 2
def test_multiple_delays_in_2_updates(navitia_vj): """ same test as test_multiple_delays, but with nothing in the db and with 2 trip updates """ with app.app_context(): trip_update = TripUpdate(_create_db_vj(navitia_vj), status="update", contributor_id=COTS_CONTRIBUTOR_ID) real_time_update = make_rt_update( raw_data=None, connector_type=ConnectorType.cots.value, contributor_id=COTS_CONTRIBUTOR_ID) trip_update.stop_time_updates = [ StopTimeUpdate({"id": "sa:1"}, departure_delay=timedelta(minutes=5), dep_status="update") ] handle(real_time_update, [trip_update], contributor_id=COTS_CONTRIBUTOR_ID, is_new_complete=False) trip_update = TripUpdate(_create_db_vj(navitia_vj), status="update", contributor_id=COTS_CONTRIBUTOR_ID) real_time_update = make_rt_update( raw_data=None, connector_type=ConnectorType.cots.value, contributor_id=COTS_CONTRIBUTOR_ID) trip_update.stop_time_updates = [ StopTimeUpdate({"id": "sa:1"}, departure_delay=timedelta(minutes=10), dep_status="update"), StopTimeUpdate({"id": "sa:2"}, arrival_delay=timedelta(minutes=2), arr_status="update"), ] res, _ = handle(real_time_update, [trip_update], contributor_id=COTS_CONTRIBUTOR_ID, is_new_complete=False) _check_multiples_delay(res) # we also check that there is what we want in the db db_trip_updates = res.query.from_self(TripUpdate).all() assert len(db_trip_updates) == 1 assert db_trip_updates[0].status == "update" assert len(RealTimeUpdate.query.all()) == 2 assert len(StopTimeUpdate.query.all()) == 3
def test_handle_new_vj(): """an easy one: we have one vj with only one stop time updated""" navitia_vj = { "trip": {"id": "vehicle_journey:1"}, "stop_times": [ { "utc_arrival_time": None, "utc_departure_time": datetime.time(8, 10), "stop_point": {"id": "sa:1", "stop_area": {"timezone": "UTC"}}, }, { "utc_arrival_time": datetime.time(9, 10), "utc_departure_time": None, "stop_point": {"id": "sa:2", "stop_area": {"timezone": "UTC"}}, }, ], } with app.app_context(): contributor = model.Contributor( id=GTFS_CONTRIBUTOR_ID, navitia_coverage=None, connector_type=ConnectorType.gtfs_rt.value ) builder = kirin.poller_workers.gtfs_rt.KirinModelBuilder(contributor) trip_update = TripUpdate(_create_db_vj(navitia_vj), contributor_id=contributor.id, status="update") st = StopTimeUpdate({"id": "sa:1"}, departure_delay=timedelta(minutes=5), dep_status="update") real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) trip_update.stop_time_updates.append(st) res, _ = handle(builder, real_time_update, [trip_update]) assert len(res.trip_updates) == 1 trip_update = res.trip_updates[0] assert trip_update.status == "update" assert len(trip_update.stop_time_updates) == 2 assert trip_update.stop_time_updates[0].stop_id == "sa:1" assert trip_update.stop_time_updates[0].departure == _dt("8:15") assert trip_update.stop_time_updates[0].arrival == _dt("8:15") assert trip_update.stop_time_updates[1].stop_id == "sa:2" assert trip_update.stop_time_updates[1].departure == _dt("9:15") assert trip_update.stop_time_updates[1].arrival == _dt("9:15") # testing that RealTimeUpdate is persisted in db db_trip_updates = real_time_update.query.from_self(TripUpdate).all() assert len(db_trip_updates) == 1 assert db_trip_updates[0].status == "update" db_st_updates = real_time_update.query.from_self(StopTimeUpdate).order_by("stop_id").all() assert len(db_st_updates) == 2 assert db_st_updates[0].stop_id == "sa:1" assert db_st_updates[0].departure == _dt("8:15") assert db_st_updates[0].arrival == _dt("8:15") assert db_st_updates[0].trip_update_id == db_trip_updates[0].vj_id assert db_st_updates[1].stop_id == "sa:2" assert db_st_updates[1].departure == _dt("9:15") assert db_st_updates[1].arrival == _dt("9:15") assert db_st_updates[1].trip_update_id == db_trip_updates[0].vj_id
def test_simple_delay(navitia_vj): """Test on delay when there is nothing in the db""" with app.app_context(): contributor = model.Contributor( id=GTFS_CONTRIBUTOR_ID, navitia_coverage=None, connector_type=ConnectorType.gtfs_rt.value ) builder = kirin.poller_workers.gtfs_rt.KirinModelBuilder(contributor) trip_update = TripUpdate(_create_db_vj(navitia_vj), status="update", contributor_id=contributor.id) st = StopTimeUpdate( {"id": "sa:1"}, arrival_delay=timedelta(minutes=5), arr_status="update", departure_delay=timedelta(minutes=10), dep_status="update", order=0, ) real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) trip_update.stop_time_updates.append(st) res, _ = handle(builder, real_time_update, [trip_update]) assert len(res.trip_updates) == 1 trip_update = res.trip_updates[0] assert trip_update.status == "update" assert len(trip_update.stop_time_updates) == 3 assert trip_update.stop_time_updates[0].stop_id == "sa:1" assert trip_update.stop_time_updates[0].departure == _dt("8:20") # 8:10 + 10mn assert trip_update.stop_time_updates[0].departure_delay == timedelta(minutes=10) assert trip_update.stop_time_updates[0].departure_status == "update" assert trip_update.stop_time_updates[1].stop_id == "sa:2" assert trip_update.stop_time_updates[1].arrival == _dt("9:15") assert trip_update.stop_time_updates[1].arrival_delay == timedelta(minutes=10) assert trip_update.stop_time_updates[1].arrival_status == "update" assert trip_update.stop_time_updates[1].departure == _dt("9:20") assert trip_update.stop_time_updates[1].departure_delay == timedelta(minutes=10) assert trip_update.stop_time_updates[1].departure_status == "update" # testing that RealTimeUpdate is persisted in db db_trip_updates = real_time_update.query.from_self(TripUpdate).all() assert len(db_trip_updates) == 1 assert db_trip_updates[0].status == "update" db_st_updates = real_time_update.query.from_self(StopTimeUpdate).order_by("stop_id").all() assert len(db_st_updates) == 3 assert db_st_updates[0].stop_id == "sa:1" assert db_st_updates[0].departure == _dt("8:20") # 8:10 + 10mn assert db_st_updates[0].departure_delay == timedelta(minutes=10) assert db_st_updates[0].departure_status == "update" assert db_st_updates[1].stop_id == "sa:2" assert db_st_updates[1].arrival == _dt("9:15") assert db_st_updates[1].departure == _dt("9:20") assert db_st_updates[1].trip_update_id == db_trip_updates[0].vj_id assert db_st_updates[2].stop_id == "sa:3"
def test_populate_pb_with_one_stop_time(): """ an easy one: we have one vj with only one stop time updated fill protobuf from trip_update Verify protobuf """ navitia_vj = { 'trip': { 'id': 'vehicle_journey:1' }, 'stop_times': [{ 'arrival_time': None, 'departure_time': datetime.time(8, 10), 'stop_point': { 'id': 'sa:1' } }, { 'arrival_time': datetime.time(9, 10), 'departure_time': None, 'stop_point': { 'id': 'sa:2' } }] } with app.app_context(): trip_update = TripUpdate() vj = VehicleJourney(navitia_vj, datetime.date(2015, 9, 8)) trip_update.vj = vj st = StopTimeUpdate({'id': 'sa:1'}, departure=_dt("8:15"), arrival=None) real_time_update = RealTimeUpdate(raw_data=None, connector='ire') real_time_update.trip_updates.append(trip_update) trip_update.stop_time_updates.append(st) db.session.add(real_time_update) db.session.commit() feed_entity = convert_to_gtfsrt(real_time_update.trip_updates) assert feed_entity.header.incrementality == gtfs_realtime_pb2.FeedHeader.DIFFERENTIAL assert feed_entity.header.gtfs_realtime_version == '1' pb_trip_update = feed_entity.entity[0].trip_update assert pb_trip_update.trip.trip_id == 'vehicle_journey:1' assert pb_trip_update.trip.start_date == '20150908' assert pb_trip_update.trip.schedule_relationship == gtfs_realtime_pb2.TripDescriptor.SCHEDULED pb_stop_time = feed_entity.entity[0].trip_update.stop_time_update[0] assert pb_stop_time.arrival.time == 0 assert pb_stop_time.departure.time == to_posix_time(_dt("8:15")) assert pb_stop_time.stop_id == 'sa:1' assert pb_trip_update.HasExtension(kirin_pb2.trip_message) == False assert pb_trip_update.trip.HasExtension(kirin_pb2.contributor) == False
def test_multiple_delays_in_2_updates(navitia_vj): """ same test as test_multiple_delays, but with nothing in the db and with 2 trip updates """ with app.app_context(): trip_update = TripUpdate(VehicleJourney(navitia_vj, datetime.date(2015, 9, 8)), status='update') real_time_update = RealTimeUpdate(raw_data=None, connector='ire', contributor='realtime.ire') trip_update.stop_time_updates = [ StopTimeUpdate({'id': 'sa:1'}, departure_delay=timedelta(minutes=5), dep_status='update'), ] handle(real_time_update, [trip_update], 'kisio-digital') trip_update = TripUpdate(VehicleJourney(navitia_vj, datetime.date(2015, 9, 8)), status='update') real_time_update = RealTimeUpdate(raw_data=None, connector='ire', contributor='realtime.ire') trip_update.stop_time_updates = [ StopTimeUpdate({'id': 'sa:1'}, departure_delay=timedelta(minutes=10), dep_status='update'), StopTimeUpdate({'id': 'sa:2'}, arrival_delay=timedelta(minutes=2), arr_status='update'), ] res, _ = handle(real_time_update, [trip_update], 'kisio-digital') _check_multiples_delay(res) # we also check that there is what we want in the db db_trip_updates = res.query.from_self(TripUpdate).all() assert len(db_trip_updates) == 1 assert db_trip_updates[0].status == 'update' assert len(RealTimeUpdate.query.all()) == 2 assert len(StopTimeUpdate.query.all()) == 3
def test_populate_pb_skipped_for_detour_stop_times_status(): st_added_status = StopTimeUpdate({'id': 'id1'}, dep_status='deleted_for_detour', arr_status='deleted_for_detour') pb_stop_time = gtfs_realtime_pb2.TripUpdate.StopTimeUpdate() fill_stop_times(pb_stop_time, st_added_status) assert pb_stop_time.departure.Extensions[ kirin_pb2.stop_time_event_status] == kirin_pb2.DELETED_FOR_DETOUR assert pb_stop_time.arrival.Extensions[ kirin_pb2.stop_time_event_status] == kirin_pb2.DELETED_FOR_DETOUR
def test_populate_pb_deleted_stop_times_status(): st_added_status = StopTimeUpdate({"id": "id1"}, dep_status="delete", arr_status="delete") pb_stop_time = gtfs_realtime_pb2.TripUpdate.StopTimeUpdate() fill_stop_times(pb_stop_time, st_added_status) assert pb_stop_time.departure.Extensions[ kirin_pb2.stop_time_event_status] == kirin_pb2.DELETED assert pb_stop_time.arrival.Extensions[ kirin_pb2.stop_time_event_status] == kirin_pb2.DELETED
def test_populate_pb_no_status_stop_times_status(): st_no_status = StopTimeUpdate({'id': 'id1'}, dep_status='none', arr_status='none') pb_stop_time = gtfs_realtime_pb2.TripUpdate.StopTimeUpdate() fill_stop_times(pb_stop_time, st_no_status) assert pb_stop_time.departure.Extensions[ kirin_pb2.stop_time_event_status] == kirin_pb2.SCHEDULED assert pb_stop_time.arrival.Extensions[ kirin_pb2.stop_time_event_status] == kirin_pb2.SCHEDULED
def test_populate_pb_added_for_detour_stop_times_status(): st_added_status = StopTimeUpdate({"id": "id1"}, dep_status="added_for_detour", arr_status="added_for_detour") pb_stop_time = gtfs_realtime_pb2.TripUpdate.StopTimeUpdate() fill_stop_times(pb_stop_time, st_added_status) assert pb_stop_time.departure.Extensions[ kirin_pb2.stop_time_event_status] == kirin_pb2.ADDED_FOR_DETOUR assert pb_stop_time.arrival.Extensions[ kirin_pb2.stop_time_event_status] == kirin_pb2.ADDED_FOR_DETOUR
def test_manage_consistency(navitia_vj): """ we receive an update for a vj already in the database sa:1 sa:2 sa:3 VJ navitia 08:10 09:05-09:10 10:05 update kirin - *10:15-09:20* - expected result 08:10-08:10 10:15-10:15 10:15-10:15 > propagating 10 min from sa:2 departure to sa:3, not needing correction for consistency """ with app.app_context(): contributor = model.Contributor( id=GTFS_CONTRIBUTOR_ID, navitia_coverage=None, connector_type=ConnectorType.gtfs_rt.value ) builder = kirin.poller_workers.gtfs_rt.KirinModelBuilder(contributor) trip_update = TripUpdate(_create_db_vj(navitia_vj), status="update", contributor_id=contributor.id) st = StopTimeUpdate( {"id": "sa:2"}, arrival_delay=timedelta(minutes=70), dep_status="update", departure_delay=timedelta(minutes=10), arr_status="update", order=1, ) st.arrival_status = st.departure_status = "update" real_time_update = make_rt_update(raw_data=None, contributor_id=contributor.id) real_time_update.id = "30866ce8-0638-4fa1-8556-1ddfa22d09d3" trip_update.stop_time_updates.append(st) res, _ = handle(builder, real_time_update, [trip_update]) assert len(res.trip_updates) == 1 trip_update = res.trip_updates[0] assert trip_update.status == "update" assert len(trip_update.real_time_updates) == 1 assert len(trip_update.stop_time_updates) == 3 stu_map = {stu.stop_id: stu for stu in trip_update.stop_time_updates} assert "sa:1" in stu_map assert stu_map["sa:1"].arrival == _dt("8:10") assert stu_map["sa:1"].departure == _dt("8:10") assert "sa:2" in stu_map assert stu_map["sa:2"].arrival == _dt("10:15") assert stu_map["sa:2"].departure == _dt("10:15") assert "sa:3" in stu_map assert stu_map["sa:3"].arrival == _dt("10:15") assert stu_map["sa:3"].departure == _dt("10:15")
def test_manage_consistency(navitia_vj): """ we receive an update for a vj already in the database sa:1 sa:2 sa:3 VJ navitia 08:10 09:05-09:10 10:05 update kirin - *10:15-09:20* - expected result 08:10-08:10 10:15-10:15 11:10-11:10 """ with app.app_context(): trip_update = TripUpdate(_create_db_vj(navitia_vj), status="update", contributor_id=COTS_CONTRIBUTOR_ID) st = StopTimeUpdate( {"id": "sa:2"}, arrival_delay=timedelta(minutes=70), dep_status="update", departure_delay=timedelta(minutes=10), arr_status="update", order=1, ) st.arrival_status = st.departure_status = "update" real_time_update = make_rt_update( raw_data=None, connector_type=ConnectorType.cots.value, contributor_id=COTS_CONTRIBUTOR_ID) real_time_update.id = "30866ce8-0638-4fa1-8556-1ddfa22d09d3" trip_update.stop_time_updates.append(st) res, _ = handle(real_time_update, [trip_update], contributor_id=COTS_CONTRIBUTOR_ID, is_new_complete=False) assert len(res.trip_updates) == 1 trip_update = res.trip_updates[0] assert trip_update.status == "update" assert len(trip_update.real_time_updates) == 1 assert len(trip_update.stop_time_updates) == 3 stu_map = {stu.stop_id: stu for stu in trip_update.stop_time_updates} assert "sa:1" in stu_map assert stu_map["sa:1"].arrival == _dt("8:10") assert stu_map["sa:1"].departure == _dt("8:10") assert "sa:2" in stu_map assert stu_map["sa:2"].arrival == _dt("10:15") assert stu_map["sa:2"].departure == _dt("10:15") assert "sa:3" in stu_map assert stu_map["sa:3"].arrival == _dt("11:10") assert stu_map["sa:3"].departure == _dt("11:10")
def test_multiple_delays(setup_database, navitia_vj): """ We receive a delay on the first and second stoptimes of a vj, and there was already some delay on the first st of this vj sa:1 sa:2 sa:3 VJ navitia 8:10 9:05-9:10 10:05 VJ in db 8:15* 9:05-9:10 10:05 update kirin 8:20* *9:07-9:10 10:05 """ with app.app_context(): trip_update = TripUpdate(VehicleJourney(navitia_vj, datetime.date(2015, 9, 8)), status='update') real_time_update = RealTimeUpdate(raw_data=None, connector='ire', contributor='realtime.ire') trip_update.stop_time_updates = [ # Note: the delay is based of the navitia's vj StopTimeUpdate({'id': 'sa:1'}, departure_delay=timedelta(minutes=10), dep_status='update'), StopTimeUpdate({'id': 'sa:2'}, arrival_delay=timedelta(minutes=2), arr_status='update'), ] res, _ = handle(real_time_update, [trip_update], 'kisio-digital') _check_multiples_delay(res) # we also check that there is what we want in the db db_trip_updates = res.query.from_self(TripUpdate).all() assert len(db_trip_updates) == 2 for tu in db_trip_updates: assert tu.status == 'update' assert len( RealTimeUpdate.query.all()) == 3 # 2 already in db, one new update assert len(StopTimeUpdate.query.all()) == 6 # 3 st * 2 vj in the db
def create_trip_update(vj_id, trip_id, circulation_date, contributor_id): vj = VehicleJourney( { "trip": {"id": trip_id}, "stop_times": [ {"utc_arrival_time": datetime.time(8, 0), "stop_point": {"stop_area": {"timezone": "UTC"}}} ], }, datetime.datetime.combine(circulation_date, datetime.time(7, 0)), datetime.datetime.combine(circulation_date, datetime.time(9, 0)), ) vj.id = vj_id trip_update = TripUpdate(vj=vj, contributor_id=contributor_id) # Add 3 StopTimeUpdate st1 = StopTimeUpdate({"id": "sa:1"}, None, None, order=0) trip_update.stop_time_updates.append(st1) st2 = StopTimeUpdate({"id": "sa:2"}, None, None, order=1) trip_update.stop_time_updates.append(st2) st3 = StopTimeUpdate({"id": "sa:3"}, None, None, order=2) trip_update.stop_time_updates.append(st3) db.session.add(vj) db.session.add(trip_update) return trip_update
def test_update_stoptime(): with app.app_context(): st = StopTimeUpdate({'id': 'foo'}, departure_delay=datetime.timedelta(minutes=10), arrival_delay=datetime.timedelta(minutes=10), dep_status='update', arr_status='update') st.update_arrival(time=None, status=None, delay=datetime.timedelta(minutes=0)) assert st.arrival_delay == datetime.timedelta(minutes=0) st.update_departure(time=None, status=None, delay=datetime.timedelta(minutes=0)) assert st.departure_delay == datetime.timedelta(minutes=0)
def merge(navitia_vj, db_trip_update, new_trip_update): """ We need to merge the info from 3 sources: * the navitia base schedule * the trip update already in the bd (potentially not existent) * the incoming trip update The result is either the db_trip_update if it exists, or the new_trip_update (it is updated as a side effect) The mechanism is quite simple: * the result trip status is the new_trip_update's status (ie in the db the trip was cancelled, and a new update is only an update, the trip update is not cancelled anymore, only updated) * for each navitia's stop_time and for departure|arrival: - if there is an update on this stoptime (in new_trip_update): we compute the new datetime based on the new information and the navitia's base schedule - else if there is the stoptime in the db: we keep this db stoptime - else we keep the navitia's base schedule Note that the results is either 'db_trip_update' or 'new_trip_update'. Side effects on this object are thus wanted because of database persistency (update or creation of new objects) ** Important Note **: we DO NOT HANDLE changes in navitia's schedule for the moment it will need to be handled, but it will be done after """ res = db_trip_update if db_trip_update else new_trip_update res_stoptime_updates = [] res.status = new_trip_update.status if new_trip_update.message: res.message = new_trip_update.message res.contributor = new_trip_update.contributor if res.status == 'delete': # for trip cancellation, we delete all stoptimes update res.stop_time_updates = [] return res last_nav_dep = None circulation_date = new_trip_update.vj.circulation_date for navitia_stop in navitia_vj.get('stop_times', []): stop_id = navitia_stop.get('stop_point', {}).get('id') new_st = new_trip_update.find_stop(stop_id) db_st = db_trip_update.find_stop(stop_id) if db_trip_update else None # TODO handle forbidden pickup/dropoff (in those case set departure/arrival at None) nav_departure_time = navitia_stop.get('departure_time') nav_arrival_time = navitia_stop.get('arrival_time') timezone = _get_timezone(navitia_stop) arrival = departure = None if nav_arrival_time: if last_nav_dep and last_nav_dep > nav_arrival_time: # last departure is after arrival, it's a past-midnight circulation_date += timedelta(days=1) arrival = _get_datetime(circulation_date, nav_arrival_time, timezone) if nav_departure_time: if nav_arrival_time and nav_arrival_time > nav_departure_time: # departure is before arrival, it's a past-midnight circulation_date += timedelta(days=1) departure = _get_datetime(circulation_date, nav_departure_time, timezone) if new_st: res_st = db_st or StopTimeUpdate(navitia_stop['stop_point']) # we have an update on the stop time, we consider it if new_st.departure_status == 'update': dep = departure + new_st.departure_delay if departure else None res_st.update_departure(time=dep, status='update', delay=new_st.departure_delay) elif db_st: # we have no update on the departure for this st, we take it from the db (if it exists) res_st.update_departure(time=db_st.departure, status=db_st.departure_status, delay=db_st.departure_delay) else: # we store the base's schedule res_st.update_departure(time=departure, status='none', delay=None) if new_st.arrival_status == 'update': arr = arrival + new_st.arrival_delay if arrival else None res_st.update_arrival(time=arr, status='update', delay=new_st.arrival_delay) elif db_st: res_st.update_arrival(time=db_st.arrival, status=db_st.arrival_status, delay=db_st.arrival_delay) else: # we store the base's schedule res_st.update_arrival(time=arrival, status='none', delay=None) res_st.message = new_st.message # we might need to update the st's order res_st.order = len(res_stoptime_updates) res_stoptime_updates.append(res_st) elif db_st: db_st.order = len(res_stoptime_updates) res_stoptime_updates.append(db_st) else: # nothing in db and in new trip update, we take the base schedule new_st = StopTimeUpdate(navitia_stop['stop_point'], departure=departure, arrival=arrival) new_st.order = len(res_stoptime_updates) res_stoptime_updates.append(new_st) last_nav_dep = nav_departure_time res.stop_time_updates = res_stoptime_updates return res