def _get_route_combinations(start: tuple, destination: tuple,
                            departure: str) -> List[dict]:
    """ retrieves all possible routes for a specific start and destination address """
    public_transport_stops = public_transport_connection_finder.get_public_transport_stops(
        start)

    routes = []
    for public_transport_stop_uic_ref, public_transport_stop_position in public_transport_stops.items(
    ):
        logger.debug(
            f'retrieve route with start at public transport stop: {public_transport_stop_uic_ref}'
        )

        public_transport_departure = _calc_public_transport_departure(
            departure, start, public_transport_stop_position)

        try:
            public_transport_connection = \
                public_transport_connection_finder.get_public_transport_connection(public_transport_stop_uic_ref,
                                                                                   destination,
                                                                                   public_transport_departure)
        except ValidationError:
            """ 
            Happens if the configured lookup radius is too high and the destination is used as a start public 
            transport stop. So both the start and destination value will be the same.
            We'are able to skip the connection and try the next one.
            """
            continue
        except RuntimeError:
            """
            Happens if no connection was found for the given parameters.
            We'are able to skip the connection and try the next one.
            """
            continue

        if not public_transport_connection['path']:
            continue  # skip empty paths, this happens if the path only consists of walking legs

        public_transport_connection_start = tuple(
            public_transport_connection['path'][0]['start_position'])
        start_walking_route = walking_route_finder.get_walking_route(
            start, public_transport_connection_start)

        public_transport_connection_destination = tuple(
            public_transport_connection['path'][-1]['exit_position'])
        end_walking_route = walking_route_finder.get_walking_route(
            public_transport_connection_destination, destination)

        routes.append(
            _generate_route_combination(start_walking_route,
                                        public_transport_connection,
                                        end_walking_route))
    return routes
def _calc_public_transport_departure(departure: str, start: tuple,
                                     destination: tuple) -> str:
    """
    Adds the duration that it takes to get from start to destination to the provided departure time.
    The destination should be a public transport stop to make sure that the pedestrian has enough time to catch
    the public transport.
    """
    walking_route = walking_route_finder.get_walking_route(start, destination)

    initial_departure = datetime.strptime(departure, DEPARTURE_FORMAT)
    public_transport_departure = initial_departure + timedelta(
        seconds=walking_route['duration'])
    return '{:%H:%M}'.format(public_transport_departure)
def _optimize_route_combination(route_combination: dict, start: tuple,
                                destination: tuple) -> dict:
    """ retrieves accurate coordinates for the public transport stops and thus new walking routes will be generated """
    logger.debug("optimize route")

    public_transport_connection = route_combination[
        'public_transport_connection']
    optimized_public_transport_connection = \
        public_transport_connection_finder.optimize_public_transport_connection(public_transport_connection)

    public_transport_connection_start = tuple(
        optimized_public_transport_connection['path'][0]['start_position'])
    start_walking_route = walking_route_finder.get_walking_route(
        start, public_transport_connection_start)

    public_transport_connection_destination = tuple(
        optimized_public_transport_connection['path'][-1]['exit_position'])
    end_walking_route = walking_route_finder.get_walking_route(
        public_transport_connection_destination, destination)

    return _generate_route_combination(start_walking_route,
                                       public_transport_connection,
                                       end_walking_route)
def find_route(start: str, destination: str, departure: str,
               precise_public_transport_stops: bool) -> dict:
    logger.info(f'route from {start} to {destination}')

    start = _parse_location(start)
    destination = _parse_location(destination)
    departure = _parse_departure(departure)

    overall_walking_route = walking_route_finder.get_walking_route(
        start, destination)

    if overall_walking_route['duration'] <= MAX_WALKING_DURATION:
        logger.info(
            "Walking is faster than using public transport, return walking only route"
        )
        return _convert_walking_route_to_overall_response(
            overall_walking_route)

    route_combinations = _get_route_combinations(start, destination, departure)
    if not route_combinations:
        logger.info(
            "No public transport route was returned because the path consists only of walking legs"
        )
        return _convert_walking_route_to_overall_response(
            overall_walking_route)

    best_route_combination = _get_best_route_combination(route_combinations)

    if _is_walking_faster_than_route_combination(overall_walking_route,
                                                 best_route_combination,
                                                 departure):
        return _convert_walking_route_to_overall_response(
            overall_walking_route)

    if precise_public_transport_stops:
        best_route_combination = _optimize_route_combination(
            best_route_combination, start, destination)

    return best_route_combination