예제 #1
0
    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
예제 #2
0
    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