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
def call_kraken(self, request_type, request, instance, krakens_call): """ For all krakens_call, call the kraken and aggregate the responses return the list of all responses """ logger = logging.getLogger(__name__) logger.debug('datetime: %s', request['datetime']) # odt_stop_points is a set of stop_point.uri with is_zonal = true used to manage tad_zonal odt_stop_points = set() # crowfly_stop_points is a set of stop_point.uri used to create a crowfly section. crowfly_stop_points = set() if not g.requested_origin: g.requested_origin = instance.georef.place(request['origin']) if not g.requested_origin: r = self._make_error_response("The entry point: {} is not valid".format(request['origin']), response_pb2.Error.unknown_object) return [r] if not g.requested_destination: g.requested_destination = instance.georef.place(request['destination']) if not g.requested_destination: r = self._make_error_response("The entry point: {} is not valid".format(request['destination']), response_pb2.Error.unknown_object) return [r] worker = AsyncWorker(instance, krakens_call, request) resp = [] # Now we compute the direct path with all requested departure # mode their time will be used to initialized our PT calls and # to bound the fallback duration of the first section. is_fallback_at_end = False fallback_extremity = PeriodExtremity(request['datetime'], request['clockwise']) futures = worker.get_direct_path_futures(g.fallback_direct_path_pool, g.requested_origin, g.requested_destination, fallback_extremity, is_fallback_at_end, {mode for mode, _ in krakens_call}) for future in gevent.iwait(futures): resp_key, resp_direct_path = future.get() g.fallback_direct_path_pool[resp_key] = resp_direct_path if resp_direct_path.journeys: resp_direct_path.journeys[0].internal_id = str(generate_id()) resp.append(resp_direct_path) if request.get('max_duration', 0): direct_path_duration_by_mode = make_direct_path_duration_by_mode(g.fallback_direct_path_pool) # Get all stop_points around the requested origin within a crowfly range # Calls on origins and destinations are asynchronous orig_futures, dest_futures = worker.get_crowfly_futures(g.requested_origin, g.requested_destination, direct_path_duration_by_mode) gevent.joinall(orig_futures + dest_futures) for future in orig_futures: g.origins_places_crowfly.update(future.get()) for future in dest_futures: g.destinations_places_crowfly.update(future.get()) # Once we get crow fly stop points with origins and destinations, we start # the computation NM: the fallback matrix which contains the arrival duration for crowfly stop_points # from origin/destination # Ex: # stop_point1 stop_point2 stop_point3 # request_origin_1 86400(s) 43200(s) 21600(s) # As a side note this won't work the day when our ETA will be impacted by the datetime of the journey, # at least for the arrival when doing a "departure after" request. orig_futures, dest_futures = worker.get_routing_matrix_futures(g.requested_origin, g.requested_destination, g.origins_places_crowfly, g.destinations_places_crowfly, direct_path_duration_by_mode) gevent.joinall(orig_futures + dest_futures) for future in orig_futures: g.origins_fallback.update(future.get()) for future in dest_futures: g.destinations_fallback.update(future.get()) # In Some special cases, like "odt" or "departure(arrive) from(to) a stop_area", # the first(last) section should be treated differently orig_futures, dest_futures = worker.get_update_crowfly_duration_futures() gevent.joinall(orig_futures + dest_futures) def _updater(_futures, fb, crowfly_stop_points, odt_stop_points): for f in _futures: crowfly_res, odt_res, fb_res = f.get() crowfly_stop_points |= crowfly_res odt_stop_points |= odt_res for mode in (mode for mode in fb_res if mode in fb): fb.merge_reached_values(mode, fb_res[mode]) _updater(orig_futures, g.origins_fallback, crowfly_stop_points, odt_stop_points) _updater(dest_futures, g.destinations_fallback, crowfly_stop_points, odt_stop_points) # We update the fallback duration matrix if the requested origin/destination is also # present in the fallback duration matrix, which means from stop_point_1 to itself, it takes 0 second # Ex: # stop_point1 stop_point2 stop_point3 # stop_point_1 0(s) ... ... for dep_mode, arr_mode in krakens_call: g.origins_fallback.reset_if_exist(dep_mode, g.requested_origin.uri) g.destinations_fallback.reset_if_exist(arr_mode, g.requested_destination.uri) # Here starts the computation for pt journey journey_parameters = create_parameters(request) futures = worker.get_pt_journey_futures(g.requested_origin, g.requested_destination, g.fallback_direct_path_pool, g.origins_fallback, g.destinations_fallback, journey_parameters) response_tuples = [] for future in gevent.iwait(futures): dep_mode, arr_mode, local_resp = future.get() if local_resp is None: continue is_fallback_at_end = False fallback_extremity = PeriodExtremity(request['datetime'], request['clockwise']) dp_key = make_direct_path_key(dep_mode, g.requested_origin.uri, g.requested_destination.uri, is_fallback_at_end, fallback_extremity) direct_path = g.fallback_direct_path_pool.get(dp_key) if local_resp.HasField(b"error") and local_resp.error.id == response_pb2.Error.error_id.Value('no_solution') \ and direct_path.journeys: local_resp.ClearField(b"error") if local_resp.HasField(b"error"): #Here needs to modify error message of no_solution if len(g.origins_fallback[dep_mode]) == 0: self.update_error_message(local_resp, response_pb2.Error.no_origin, "no origin point") elif len(g.destinations_fallback[arr_mode]) == 0: self.update_error_message(local_resp, response_pb2.Error.no_destination, "no destination point") return [local_resp] # for log purpose we put and id in each journeys for j in local_resp.journeys: j.internal_id = str(generate_id()) response_tuples.append((dep_mode, arr_mode, j)) resp.append(local_resp) # Once the pt journey is found, we need to reconstruct the whole journey with fallback regarding the mode # For the sake of performance, we compute at first all fallback direct path asynchronously # then we update the pool of direct paths futures = worker.get_fallback_direct_path_futures(response_tuples, crowfly_stop_points, odt_stop_points) for future in gevent.iwait(futures): resp_key, resp_direct_path = future.get() g.fallback_direct_path_pool[resp_key] = resp_direct_path # Now we construct the whole journey by concatenating the fallback direct path with the pt journey worker.build_journeys(response_tuples, crowfly_stop_points, odt_stop_points) #If resp doesn't contain any response we have to add an error message if len(resp) == 0: if len(g.origins_fallback[dep_mode]) == 0 and len(g.destinations_fallback[arr_mode]) == 0: resp.append(self._make_error_response("no origin point nor destination point", response_pb2.Error.no_origin_nor_destination)) elif len(g.origins_fallback[dep_mode]) == 0: resp.append(self._make_error_response("no origin point", response_pb2.Error.no_origin)) elif len(g.destinations_fallback[arr_mode]) == 0: resp.append(self._make_error_response("no destination point", response_pb2.Error.no_destination)) return resp for r in resp: fill_uris(r) return resp