def _direct_path( self, instance, mode, pt_object_origin, pt_object_destination, fallback_extremity, request, direct_path_type, request_id, ): # if the crowfly distance between origin and destination is too large, there is no need to call asgard crowfly_distance = crowfly_distance_between( get_pt_object_coord(pt_object_origin), get_pt_object_coord(pt_object_destination)) # if the crowfly distance between origin and destination is # bigger than max_{mode}_direct_path_distance don't compute direct_path if crowfly_distance > int( request['max_{mode}_direct_path_distance'.format(mode=mode)]): return response_pb2.Response() if (crowfly_distance / float(request['{mode}_speed'.format(mode=mode)]) > request['max_{mode}_direct_path_duration'.format(mode=mode)]): return response_pb2.Response() language = self.get_language_parameter(request) req = self._create_direct_path_request( mode, pt_object_origin, pt_object_destination, fallback_extremity, request, direct_path_type, language, ) response = self._call_asgard(req) # car_no_park is interpreted as car for Asgard, we need to overwrite the streetnetwork mode here if mode == "car_no_park": try: response.journeys[0].sections[ 0].street_network.mode = response_pb2.CarNoPark except AttributeError: pass except Exception as e: raise e if response and mode in (FallbackModes.bike.name, FallbackModes.bss.name): response = self._add_cycle_lane_length(response) return response
def _create_crowfly(pt_journey, crowfly_origin, crowfly_destination, begin, end, mode): section = response_pb2.Section() section.type = response_pb2.CROW_FLY section.origin.CopyFrom(crowfly_origin) section.destination.CopyFrom(crowfly_destination) section.duration = end - begin pt_journey.durations.total += section.duration pt_journey.duration += section.duration section.begin_date_time = begin section.end_date_time = end if section.duration > 0: section.street_network.mode = FallbackModes[mode].value # mode is always walking for a teleportation crow_fly else: section.street_network.mode = response_pb2.Walking # Calculate section length from_coord = get_pt_object_coord(section.origin) to_coord = get_pt_object_coord(section.destination) section.length = int(crowfly_distance_between(from_coord, to_coord)) section.id = six.text_type(generate_id()) return section
def build_ridesharing_journeys(from_pt_obj, to_pt_obj, period_extremity, instance): from_coord = get_pt_object_coord(from_pt_obj) to_coord = get_pt_object_coord(to_pt_obj) from_str = "{},{}".format(from_coord.lat, from_coord.lon) to_str = "{},{}".format(to_coord.lat, to_coord.lon) try: rsjs, fps = instance.get_ridesharing_journeys_with_feed_publishers( from_str, to_str, period_extremity) except Exception as e: logging.exception( 'Error while retrieving ridesharing ads and feed_publishers from %s to %s: {}', from_str, to_str) new_relic.record_custom_event('ridesharing_internal_failure', {'message': str(e)}) rsjs = [] fps = [] pb_rsjs = [] pb_tickets = [] pb_feed_publishers = [_make_pb_fp(fp) for fp in fps if fp is not None] for rsj in rsjs: pb_rsj = response_pb2.Journey() pb_rsj_pickup = instance.georef.place("{};{}".format( rsj.pickup_place.lon, rsj.pickup_place.lat)) pb_rsj_dropoff = instance.georef.place("{};{}".format( rsj.dropoff_place.lon, rsj.dropoff_place.lat)) pickup_coord = get_pt_object_coord(pb_rsj_pickup) dropoff_coord = get_pt_object_coord(pb_rsj_dropoff) pb_rsj.requested_date_time = period_extremity.datetime pb_rsj.departure_date_time = rsj.pickup_date_time pb_rsj.arrival_date_time = rsj.dropoff_date_time pb_rsj.tags.append('ridesharing') # start teleport section start_teleport_section = pb_rsj.sections.add() start_teleport_section.id = "section_{}".format( six.text_type(generate_id())) start_teleport_section.type = response_pb2.CROW_FLY start_teleport_section.street_network.mode = response_pb2.Walking start_teleport_section.origin.CopyFrom(from_pt_obj) start_teleport_section.destination.CopyFrom(pb_rsj_pickup) start_teleport_section.length = int( crowfly_distance_between(from_coord, pickup_coord)) start_teleport_section.duration = 0 start_teleport_section.shape.extend([from_coord, pickup_coord]) start_teleport_section.begin_date_time = rsj.pickup_date_time start_teleport_section.end_date_time = rsj.pickup_date_time # report value to journey pb_rsj.distances.walking += start_teleport_section.length # real ridesharing section rs_section = pb_rsj.sections.add() rs_section.id = "section_{}".format(six.text_type(generate_id())) rs_section.type = response_pb2.RIDESHARING rs_section.origin.CopyFrom(pb_rsj_pickup) rs_section.destination.CopyFrom(pb_rsj_dropoff) rs_section.additional_informations.append( response_pb2.HAS_DATETIME_ESTIMATED) rs_section.ridesharing_information.operator = rsj.metadata.system_id rs_section.ridesharing_information.network = rsj.metadata.network if rsj.available_seats is not None: rs_section.ridesharing_information.seats.available = rsj.available_seats if rsj.total_seats is not None: rs_section.ridesharing_information.seats.total = rsj.total_seats if rsj.driver.alias: rs_section.ridesharing_information.driver.alias = rsj.driver.alias if rsj.driver.image: rs_section.ridesharing_information.driver.image = rsj.driver.image if rsj.driver.gender is not None: if rsj.driver.gender == Gender.MALE: rs_section.ridesharing_information.driver.gender = response_pb2.MALE elif rsj.driver.gender == Gender.FEMALE: rs_section.ridesharing_information.driver.gender = response_pb2.FEMALE if rsj.driver.rate is not None and rsj.driver.rate_count: rs_section.ridesharing_information.driver.rating.value = rsj.driver.rate if rsj.driver.rate_count: rs_section.ridesharing_information.driver.rating.count = rsj.driver.rate_count if rsj.metadata.rating_scale_min is not None and rsj.metadata.rating_scale_max is not None: rs_section.ridesharing_information.driver.rating.scale_min = rsj.metadata.rating_scale_min rs_section.ridesharing_information.driver.rating.scale_max = rsj.metadata.rating_scale_max if rsj.ridesharing_ad: l = rs_section.ridesharing_information.links.add() l.key = "ridesharing_ad" l.href = rsj.ridesharing_ad # TODO CO2 = length * coeffCar / (totalSeats + 1) rs_section.length = rsj.distance rs_section.shape.extend(rsj.shape) rs_section.duration = rsj.dropoff_date_time - rsj.pickup_date_time rs_section.begin_date_time = rsj.pickup_date_time rs_section.end_date_time = rsj.dropoff_date_time # report values to journey pb_rsj.distances.ridesharing += rs_section.length pb_rsj.duration += rs_section.duration pb_rsj.durations.total += rs_section.duration pb_rsj.durations.ridesharing += rs_section.duration # end teleport section end_teleport_section = pb_rsj.sections.add() end_teleport_section.id = "section_{}".format( six.text_type(generate_id())) end_teleport_section.type = response_pb2.CROW_FLY end_teleport_section.street_network.mode = response_pb2.Walking end_teleport_section.origin.CopyFrom(pb_rsj_dropoff) end_teleport_section.destination.CopyFrom(to_pt_obj) end_teleport_section.length = int( crowfly_distance_between(dropoff_coord, to_coord)) end_teleport_section.duration = 0 end_teleport_section.shape.extend([dropoff_coord, to_coord]) end_teleport_section.begin_date_time = rsj.dropoff_date_time end_teleport_section.end_date_time = rsj.dropoff_date_time # report value to journey pb_rsj.distances.walking += end_teleport_section.length # create ticket associated ticket = response_pb2.Ticket() ticket.id = "ticket_{}".format(six.text_type(generate_id())) ticket.name = "ridesharing_price_{}".format(ticket.id) ticket.found = True ticket.comment = "Ridesharing price for section {}".format( rs_section.id) ticket.section_id.extend([rs_section.id]) # also add fare to journey ticket.cost.value = rsj.price pb_rsj.fare.total.value = ticket.cost.value ticket.cost.currency = rsj.currency pb_rsj.fare.total.currency = rsj.currency pb_rsj.fare.found = True pb_rsj.fare.ticket_id.extend([ticket.id]) pb_tickets.append(ticket) pb_rsjs.append(pb_rsj) return pb_rsjs, pb_tickets, pb_feed_publishers
def _direct_path( self, instance, mode, pt_object_origin, pt_object_destination, fallback_extremity, request, direct_path_type, request_id, ): """ :param direct_path_type: we need to "invert" a direct path when it's a ending fallback by car if and only if it's returned by kraken. In other case, it's ignored """ should_invert_journey = mode == 'car' and direct_path_type == StreetNetworkPathType.ENDING_FALLBACK if should_invert_journey: pt_object_origin, pt_object_destination = pt_object_destination, pt_object_origin direct_path_request = {} for attr in [ 'walking_speed', 'max_walking_duration_to_pt', 'bike_speed', 'max_bike_duration_to_pt', 'bss_speed', 'max_bss_duration_to_pt', 'car_speed', 'max_car_duration_to_pt', 'car_no_park_speed', 'max_car_no_park_duration_to_pt', 'taxi_speed', 'max_taxi_duration_to_pt', 'ridesharing_speed', 'max_ridesharing_duration_to_pt', ]: direct_path_request[attr] = request[attr] if direct_path_type == StreetNetworkPathType.DIRECT: # in distributed scenario, we allow the street network calculator to compute a very long direct path # in case of Kraken, the stop condition of direct path in Kraken is defined as # max_{mode}_duration_to_pt * 2. # When it comes to a direct path request, we override this parameter with # max_{mode}_direct_path_duration / 2.0 from jormungandr.fallback_modes import FallbackModes as fm direct_path_request['max_{mode}_duration_to_pt'.format( mode=mode)] = int( request['max_{mode}_direct_path_duration'.format( mode=mode)] / 2) # if the crowfly distance between origin and destination is too large, there is no need to call kraken crowfly_distance = crowfly_distance_between( utils.get_pt_object_coord(pt_object_origin), utils.get_pt_object_coord(pt_object_destination)) if (crowfly_distance / float( direct_path_request['{mode}_speed'.format(mode=mode)]) > request['max_{mode}_direct_path_duration'.format( mode=mode)]): return response_pb2.Response() req = self._create_direct_path_request(mode, pt_object_origin, pt_object_destination, fallback_extremity, direct_path_request) response = instance.send_and_receive(req, request_id=request_id) if should_invert_journey: return self._reverse_journeys(response) return response
def _direct_path( self, instance, mode, pt_object_origin, pt_object_destination, fallback_extremity, request, direct_path_type, request_id, ): """ :param direct_path_type: we need to "invert" a direct path when it's a ending fallback by car if and only if it's returned by kraken. In other case, it's ignored """ should_invert_journey = mode == 'car' and direct_path_type == StreetNetworkPathType.ENDING_FALLBACK if should_invert_journey: pt_object_origin, pt_object_destination = pt_object_destination, pt_object_origin direct_path_request = {} for attr in [ 'walking_speed', 'max_walking_duration_to_pt', 'bike_speed', 'max_bike_duration_to_pt', 'bss_speed', 'max_bss_duration_to_pt', 'car_speed', 'max_car_duration_to_pt', 'car_no_park_speed', 'max_car_no_park_duration_to_pt', 'taxi_speed', 'max_taxi_duration_to_pt', 'ridesharing_speed', 'max_ridesharing_duration_to_pt', '_enable_instructions', ]: direct_path_request[attr] = request[attr] if direct_path_type == StreetNetworkPathType.DIRECT: # in distributed scenario, we allow the street network calculator to compute a very long direct path # in case of Kraken, the stop condition of direct path in Kraken is defined as # max_{mode}_duration_to_pt * 2. # When it comes to a direct path request, we override this parameter with # max_{mode}_direct_path_duration / 2.0 from jormungandr.fallback_modes import FallbackModes as fm direct_path_request['max_{mode}_duration_to_pt'.format( mode=mode)] = int( request['max_{mode}_direct_path_duration'.format( mode=mode)] / 2) # if the crowfly distance between origin and destination is too large, there is no need to call kraken crowfly_distance = crowfly_distance_between( utils.get_pt_object_coord(pt_object_origin), utils.get_pt_object_coord(pt_object_destination)) if (crowfly_distance / float( direct_path_request['{mode}_speed'.format(mode=mode)]) > request['max_{mode}_direct_path_duration'.format( mode=mode)]): return response_pb2.Response() req = self._create_direct_path_request(mode, pt_object_origin, pt_object_destination, fallback_extremity, direct_path_request) response = instance.send_and_receive(req, request_id=request_id) if should_invert_journey: response = self._reverse_journeys(response) def has_bss_rent_before_put_back_section(journey): # Here is a little trick with python's generator # the next 3 lines check not only the existences of BSS_RENT and BSS_PUT_BACK, but also check the fact that # BSS_RENT must be located Before BSS_PUT_BACK sections = (s for s in journey.sections) bss_rent = next( (True for s in sections if s.type == response_pb2.BSS_RENT), False) bss_put_back = next( (True for s in sections if s.type == response_pb2.BSS_PUT_BACK), False) return bss_rent and bss_put_back if response.journeys: # Note that: the journey returned by Kraken is a direct path. A direct path of walking/bike/car/car_no_park/ # contains only one section. But for bss, there may be one or three sections. # For bss,we only need attribute the mode to the first section. The most significant mode will be chosen # later if has_bss_rent_before_put_back_section(response.journeys[0]): return response if mode == FallbackModes.bss.name and ( not has_bss_rent_before_put_back_section( response.journeys[0])): response.journeys[0].sections[ 0].street_network.mode = FallbackModes.walking.value else: response.journeys[0].sections[ 0].street_network.mode = FallbackModes[mode].value return response
def _compute_journeys(self, future_manager, request, instance, krakens_call, context, request_type): """ For all krakens_call, call the kraken and aggregate the responses Note: the responses will only attach a crowfly section as a fallback. Street network will be done when calling finalise_journeys return the list of all responses """ logger = logging.getLogger(__name__) logger.debug('request datetime: %s', request['datetime']) request_id = request["request_id"] logger.debug("request_id : {}".format(request_id)) requested_dep_modes_with_pt = { mode for mode, _, direct_path_type in krakens_call if direct_path_type != "only" } requested_arr_modes_with_pt = { mode for _, mode, direct_path_type in krakens_call if direct_path_type != "only" } # These are the modes in first_section_modes[] and direct_path_modes[] # We need to compute direct_paths for them either because we requested it with direct_path_modes[] # Or because we need them to optimize the pt_journey computation requested_direct_path_modes = { mode for mode, _, direct_path_type in krakens_call if direct_path_type == "only" } requested_direct_path_modes.update(requested_dep_modes_with_pt) if context.partial_response_is_empty: logger.debug('requesting places by uri orig: %s dest %s', request['origin'], request['destination']) context.requested_orig = PlaceByUri( future_manager=future_manager, instance=instance, uri=request['origin'], request_id="{}_place_origin".format(request_id), ) context.requested_dest = PlaceByUri( future_manager=future_manager, instance=instance, uri=request['destination'], request_id="{}_place_dest".format(request_id), ) context.requested_orig_obj = get_entry_point_or_raise( context.requested_orig, request['origin']) context.requested_dest_obj = get_entry_point_or_raise( context.requested_dest, request['destination']) context.streetnetwork_path_pool = StreetNetworkPathPool( future_manager=future_manager, instance=instance) period_extremity = PeriodExtremity(request['datetime'], request['clockwise']) for mode in requested_direct_path_modes: context.streetnetwork_path_pool.add_async_request( requested_orig_obj=context.requested_orig_obj, requested_dest_obj=context.requested_dest_obj, mode=mode, period_extremity=period_extremity, request=request, streetnetwork_path_type=StreetNetworkPathType.DIRECT, request_id="{}_direct_path_mode_{}".format( request_id, mode), ) # if max_duration(time to pass in pt) is zero, there is no need to continue, # we return all direct path without pt if request['max_duration'] == 0: res = [ context.streetnetwork_path_pool.wait_and_get( requested_orig_obj=context.requested_orig_obj, requested_dest_obj=context.requested_dest_obj, mode=mode, request=request, period_extremity=period_extremity, streetnetwork_path_type=StreetNetworkPathType.DIRECT, ) for mode in requested_direct_path_modes ] # add SN feed publishers context.streetnetwork_path_pool.add_feed_publishers( request, requested_direct_path_modes, res) return res # We'd like to get the duration of a direct path to do some optimizations in ProximitiesByCrowflyPool and # FallbackDurationsPool. # Note :direct_paths_by_mode is a dict of mode vs future of a direct paths, this line is not blocking context.direct_paths_by_mode = context.streetnetwork_path_pool.get_all_direct_paths( ) crowfly_distance = crowfly_distance_between( get_pt_object_coord(context.requested_orig_obj), get_pt_object_coord(context.requested_dest_obj)) context.orig_proximities_by_crowfly = ProximitiesByCrowflyPool( future_manager=future_manager, instance=instance, requested_place_obj=context.requested_orig_obj, modes=requested_dep_modes_with_pt, request=request, direct_paths_by_mode=context.direct_paths_by_mode, max_nb_crowfly_by_mode=request['max_nb_crowfly_by_mode'], request_id="{}_crowfly_orig".format(request_id), o_d_crowfly_distance=crowfly_distance, ) context.dest_proximities_by_crowfly = ProximitiesByCrowflyPool( future_manager=future_manager, instance=instance, requested_place_obj=context.requested_dest_obj, modes=requested_arr_modes_with_pt, request=request, direct_paths_by_mode=context.direct_paths_by_mode, max_nb_crowfly_by_mode=request['max_nb_crowfly_by_mode'], request_id="{}_crowfly_dest".format(request_id), o_d_crowfly_distance=crowfly_distance, ) context.orig_places_free_access = PlacesFreeAccess( future_manager=future_manager, instance=instance, requested_place_obj=context.requested_orig_obj, request_id="{}_places_free_access_orig".format(request_id), ) context.dest_places_free_access = PlacesFreeAccess( future_manager=future_manager, instance=instance, requested_place_obj=context.requested_dest_obj, request_id="{}_places_free_access_dest".format(request_id), ) context.orig_fallback_durations_pool = FallbackDurationsPool( future_manager=future_manager, instance=instance, requested_place_obj=context.requested_orig_obj, modes=requested_dep_modes_with_pt, proximities_by_crowfly_pool=context. orig_proximities_by_crowfly, places_free_access=context.orig_places_free_access, direct_paths_by_mode=context.direct_paths_by_mode, request=request, direct_path_type=StreetNetworkPathType.BEGINNING_FALLBACK, request_id="{}_fallback_orig".format(request_id), ) context.dest_fallback_durations_pool = FallbackDurationsPool( future_manager=future_manager, instance=instance, requested_place_obj=context.requested_dest_obj, modes=requested_arr_modes_with_pt, proximities_by_crowfly_pool=context. dest_proximities_by_crowfly, places_free_access=context.dest_places_free_access, direct_paths_by_mode=context.direct_paths_by_mode, request=request, direct_path_type=StreetNetworkPathType.ENDING_FALLBACK, request_id="{}_fallback_dest".format(request_id), ) pt_journey_pool = PtJourneyPool( future_manager=future_manager, instance=instance, requested_orig_obj=context.requested_orig_obj, requested_dest_obj=context.requested_dest_obj, streetnetwork_path_pool=context.streetnetwork_path_pool, krakens_call=krakens_call, orig_fallback_durations_pool=context.orig_fallback_durations_pool, dest_fallback_durations_pool=context.dest_fallback_durations_pool, request=request, request_type=request_type, request_id="{}_ptjourney".format(request_id), ) pt_journey_elements = wait_and_build_crowflies( requested_orig_obj=context.requested_orig_obj, requested_dest_obj=context.requested_dest_obj, pt_journey_pool=pt_journey_pool, has_valid_direct_paths=context.streetnetwork_path_pool. has_valid_direct_paths(), orig_places_free_access=context.orig_places_free_access, dest_places_free_acces=context.dest_places_free_access, orig_fallback_durations_pool=context.orig_fallback_durations_pool, dest_fallback_durations_pool=context.dest_fallback_durations_pool, ) context.journeys_to_modes.update( self._map_journeys_to_modes(pt_journey_elements)) # At the stage, all types of journeys have been computed thus we build the final result here res = [] if context.partial_response_is_empty: for mode in requested_direct_path_modes: dp = context.direct_paths_by_mode.get(mode).wait_and_get() if getattr(dp, "journeys", None): res.append(dp) # pt_journeys may contain None and res must be a list of protobuf journey res.extend( (j.pt_journeys for j in pt_journey_elements if j.pt_journeys)) check_final_results_or_raise(res, context.orig_fallback_durations_pool, context.dest_fallback_durations_pool) for r in res: fill_uris(r) context.partial_response_is_empty = False # add SN feed publishers context.streetnetwork_path_pool.add_feed_publishers( request, requested_direct_path_modes, res) return res