Exemple #1
0
def get_crime_incidents_intent(mycity_request):
    """
    Populate MyCityResponseDataModel with crime incidents response information.

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseDataModel object
    """
    logger.debug('[method: get_crime_incidents_intent]')

    coordinates = {}
    current_address = None
    if intent_constants.CURRENT_ADDRESS_KEY not in \
            mycity_request.session_attributes:
        coordinates = get_address_coordinates_from_geolocation(mycity_request)

        if not coordinates:
            if mycity_request.device_has_geolocation:
                return request_geolocation_permission_response()

            # Try getting registered device address
            mycity_request, location_permissions \
                = get_address_from_user_device(mycity_request)
            if not location_permissions:
                return request_device_address_permission_response()

    # Convert address to coordinates if we only have user address
    if intent_constants.CURRENT_ADDRESS_KEY \
            in mycity_request.session_attributes:
        current_address = mycity_request.session_attributes[
            intent_constants.CURRENT_ADDRESS_KEY]
        coordinates = gis_utils.geocode_address(current_address)

    # If we don't have coordinates by now, and we have all required
    #  permissions, ask the user for an address
    if not coordinates:
        return request_user_address_response(mycity_request)

    mycity_response = MyCityResponseDataModel()

    # If our address/coordinates are not in Boston, send a response letting
    # the user know the intent only works in Boston.
    if not is_location_in_city(current_address, coordinates):
        mycity_response.output_speech = NOT_IN_BOSTON_SPEECH
    else:
        response = get_crime_incident_response(coordinates)
        mycity_response.output_speech = \
            _build_text_from_response(response)

    # Setting reprompt_text to None signifies that we do not want to reprompt
    # the user. If the user does not respond or says something that is not
    # understood, the session will end.
    mycity_response.reprompt_text = None
    mycity_response.session_attributes = mycity_request.session_attributes
    mycity_response.card_title = CARD_TITLE_CRIME
    mycity_response.should_end_session = True

    return mycity_response
def _get_coordinates_for_address(address):
    """
    Populates the GPS coordinates for the provided address

    :param address: address to query
    :return: a tuple of the form (lat, long)

    """
    coordinates = geocode_address(address)
    logger.debug("Got coordinates: {}".format(coordinates))
    _lat = "{:.2f}".format(float(coordinates[1]))
    _long = "{:.2f}".format(float(coordinates[0]))
    return (_lat, _long)
def get_address_coordinates_from_session(mycity_request) -> dict:
    """
    Gets coordinates of the provided address from the session attributes.
    Returns None if no address is available.
    :param mycity_request: MyCityRequestDataModel for the current request
    :return dict: Dictionary containing coordinates of the address
    """
    user_address = None
    if intent_constants.CURRENT_ADDRESS_KEY in mycity_request.session_attributes:
        current_address = mycity_request.session_attributes[
            intent_constants.CURRENT_ADDRESS_KEY]
        parsed_address = StreetAddressParser().parse(current_address)
        address = " ".join([
            parsed_address["house"], parsed_address["street_name"],
            parsed_address["street_type"]
        ])
        user_address = gis_utils.geocode_address(address)

    return user_address
Exemple #4
0
def get_nearby_food_trucks(mycity_request):
    """
    Gets food truck info near an address

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseObject
    """
    mycity_response = MyCityResponseDataModel()

    # Get current address location
    if CURRENT_ADDRESS_KEY in mycity_request.session_attributes:
        current_address = \
            mycity_request.session_attributes[CURRENT_ADDRESS_KEY]

        # Parsing street address using street-address package
        address_parser = StreetAddressParser()
        a = address_parser.parse(current_address)
        address = str(a["house"]) + " " + str(a["street_name"]) + " " \
                  + str(a["street_type"])

        # Parsing zip code
        zip_code = str(a["other"]).zfill(5) if a["other"] else None
        zip_code_key = intent_constants.ZIP_CODE_KEY
        if zip_code is None and zip_code_key in \
                mycity_request.session_attributes:
            zip_code = mycity_request.session_attributes[zip_code_key]

        # Get user's GIS Geocode Address and list of available trucks
        usr_addr = gis_utils.geocode_address(address)
        truck_unique_locations = get_truck_locations()
        nearby_food_trucks = []

        try:
            # Loop through food truck list and search for nearby food trucks
            # limit to 5 to speed up response
            counter = 0
            for t in truck_unique_locations:
                dist = gis_utils.calculate_distance(usr_addr, t)
                if dist <= MILE:
                    nearby_food_trucks.append(t)
                    counter += 1
                    if counter == FOOD_TRUCK_LIMIT:
                        break

            count = len(nearby_food_trucks)
            if count == 0:
                mycity_response.output_speech = "I didn't find any food trucks!"

            if count == 1:
                response = f"I found {count} food truck within a mile " \
                    "from your address! "
                response += add_response_text(nearby_food_trucks)
                mycity_response.output_speech = response

            if 1 < count <= 3:
                response = f"I found {count} food trucks within a mile " \
                    "from your address! "
                response += add_response_text(nearby_food_trucks)
                mycity_response.output_speech = response

            elif count > 3:
                response = f"There are at least {count} food trucks within " \
                           f"a mile from your address! Here are the first " \
                           + str(count) + ". "
                response += add_response_text(nearby_food_trucks)
                mycity_response.output_speech = response

        except InvalidAddressError:
            address_string = address
            if zip_code:
                address_string = address_string + " with zip code {}"\
                    .format(zip_code)
            mycity_response.output_speech = \
                speech_constants.ADDRESS_NOT_FOUND.format(address_string)
            mycity_response.dialog_directive = "ElicitSlotFoodTruck"
            mycity_response.reprompt_text = None
            mycity_response.session_attributes = \
                mycity_request.session_attributes
            mycity_response.card_title = "Food Trucks"
            mycity_request = clear_address_from_mycity_object(mycity_request)
            mycity_response = clear_address_from_mycity_object(mycity_response)
            return mycity_response

        except BadAPIResponse:
            mycity_response.output_speech = \
                "Hmm something went wrong. Maybe try again?"

        except MultipleAddressError:
            mycity_response.output_speech = \
                speech_constants.MULTIPLE_ADDRESS_ERROR.format(address)
            mycity_response.dialog_directive = "ElicitSlotZipCode"

    else:
        logger.error("Error: Called food_truck_intent with no address")
        mycity_response.output_speech = "I didn't understand that address, " \
                                        "please try again"

    # Setting reprompt_text to None signifies that we do not want to reprompt
    # the user. If the user does not respond or says something that is not
    # understood, the session will end.
    mycity_response.reprompt_text = None
    mycity_response.session_attributes = mycity_request.session_attributes
    mycity_response.card_title = "Food Trucks"

    return mycity_response
def get_nearby_food_trucks(mycity_request):
    """
    Gets food truck info near an address

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseObject
    """
    mycity_response = MyCityResponseDataModel()

    coordinates = None
    user_address = None
    if intent_constants.CURRENT_ADDRESS_KEY not in \
            mycity_request.session_attributes:
        # If not provided, try to get the user address through
        # geolocation and device address

        coordinates = address_utils.\
            get_address_coordinates_from_geolocation(mycity_request)

        if not coordinates:
            if mycity_request.device_has_geolocation:
                return location_services_utils.\
                    request_geolocation_permission_response()

            # Try getting registered device address
            mycity_request, location_permissions = location_services_utils.\
                get_address_from_user_device(mycity_request)
            if not location_permissions:
                return location_services_utils.\
                    request_device_address_permission_response()

    if not coordinates:
        if intent_constants.CURRENT_ADDRESS_KEY \
            not in mycity_request.session_attributes:
            # We don't have coordinates or an address by now,
            # and we have all required permissions, ask the user
            return request_user_address_response(mycity_request)

        user_address = mycity_request.session_attributes[
            intent_constants.CURRENT_ADDRESS_KEY]
        coordinates = gis_utils.geocode_address(user_address)

    if not is_location_in_city(user_address, coordinates):
        mycity_response.output_speech = speech_const.NOT_IN_BOSTON_SPEECH
        mycity_response.should_end_session = True
        mycity_response.card_title = CARD_TITLE
        return mycity_response

    # Get list of available trucks
    truck_unique_locations = get_truck_locations(coordinates)

    # Create custom response based on number of trucks returned
    try:
        if len(truck_unique_locations) == 0:
            mycity_response.output_speech = "I didn't find any food " \
                                            "trucks near you!"

        if len(truck_unique_locations) == 1:
            response = f"I found {len(truck_unique_locations)} food " \
                        f"truck within a mile from your address! "
            response += add_response_text(truck_unique_locations)
            mycity_response.output_speech = response

        if 1 < len(truck_unique_locations) <= 3:
            response = f"I found {len(truck_unique_locations)} food " \
                        f"trucks within a mile from your address! "
            response += add_response_text(truck_unique_locations)
            mycity_response.output_speech = response

        if len(truck_unique_locations) > 3:
            response = f"There are at least {len(truck_unique_locations)}" \
                        f" food trucks within a mile from your " \
                        f"address! Here are the first five. "
            response += add_response_text(truck_unique_locations)
            mycity_response.output_speech = response

    except InvalidAddressError:
        mycity_response.output_speech = \
            ft_speech_constants.ADDRESS_NOT_FOUND.format("that address")
        mycity_response.dialog_directive = "ElicitSlotFoodTruck"
        mycity_response.reprompt_text = None
        mycity_response.session_attributes = \
            mycity_request.session_attributes
        mycity_response.card_title = CARD_TITLE
        mycity_request = clear_address_from_mycity_object(mycity_request)
        mycity_response = clear_address_from_mycity_object(mycity_response)
        mycity_response.should_end_session = True
        return mycity_response

    except BadAPIResponse:
        mycity_response.output_speech = \
            "Hmm something went wrong. Maybe try again?"

    # Setting reprompt_text to None signifies that we do not want to reprompt
    # the user. If the user does not respond or says something that is not
    # understood, the session will end.
    mycity_response.reprompt_text = None
    mycity_response.session_attributes = mycity_request.session_attributes
    mycity_response.card_title = CARD_TITLE
    mycity_response.should_end_session = True

    return mycity_response
def get_voting_location(mycity_request: MyCityRequestDataModel) -> \
        MyCityResponseDataModel:
    """
    Generates response object for a polling location inquiry which includes
    a user's location to vote.

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseDataModel object
    """

    logger.debug('MyCityRequestDataModel received:' +
                 mycity_request.get_logger_string())
    mycity_response = MyCityResponseDataModel()
    mycity_response.card_title = CARD_TITLE

    # check for address for locating voting location
    if intent_constants.CURRENT_ADDRESS_KEY not in \
        mycity_request.session_attributes:
        mycity_request, location_permissions = \
            get_address_from_user_device(mycity_request)
        if not location_permissions:
            return request_device_address_permission_response()
        elif intent_constants.CURRENT_ADDRESS_KEY not in \
                mycity_request.session_attributes:
            return request_user_address_response(mycity_request)

    current_address = \
        mycity_request.session_attributes[intent_constants.CURRENT_ADDRESS_KEY]

    # If we have more specific info then just the street
    # address, make sure we are in Boston
    if not is_address_in_city(current_address):
        mycity_response.output_speech = NOT_IN_BOSTON_SPEECH
        mycity_response.should_end_session = True
        mycity_response.card_title = CARD_TITLE
        return mycity_response

    # grab relevant information from session address
    parsed_address, _ = usaddress.tag(current_address)

    if not is_address_valid(parsed_address):
        mycity_response.output_speech = ADDRESS_NOT_UNDERSTOOD
        mycity_response.dialog_directive = "ElicitSlotVotingIntent"
        mycity_response.reprompt_text = None
        mycity_response.session_attributes = mycity_request.session_attributes
        mycity_response.card_title = CARD_TITLE
        mycity_response.should_end_session = True
        return clear_address_from_mycity_object(mycity_response)

    zipcode = None
    if "Zipcode" in mycity_request.intent_variables and \
        "value" in mycity_request.intent_variables["Zipcode"]:
        zipcode = \
                mycity_request.intent_variables["Zipcode"]["value"].zfill(5)

    mycity_response.reprompt_text = None
    mycity_response.should_end_session = True

    try:
        top_candidate = gis_utils.geocode_address(current_address, zipcode)
        ward_precinct = vote_utils.get_ward_precinct_info(top_candidate)
        poll_location = vote_utils.get_polling_location(ward_precinct)
        output_speech = LOCATION_SPEECH. \
            format(poll_location[LOCATION_NAME], poll_location[LOCATION_ADDRESS])
        mycity_response.output_speech = output_speech
    except ParseError:
        mycity_response.output_speech = NO_WARD_OR_PRECINCT
    except BadAPIResponse:
        mycity_response.output_speech = BAD_API_RESPONSE
    except MultipleAddressError as error:
        address_list = ', '.join(error.addresses)
        mycity_response.output_speech = MULTIPLE_ADDRESS_ERROR
        mycity_response.dialog_directive = "ElicitSlotZipCode"
        mycity_response.should_end_session = False

    return mycity_response
Exemple #7
0
def get_nearby_food_trucks(mycity_request):
    """
    Gets food truck info near an address

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseObject
    """
    mycity_response = MyCityResponseDataModel()

    # Get current address location
    if CURRENT_ADDRESS_KEY in mycity_request.session_attributes:
        current_address = \
            mycity_request.session_attributes[CURRENT_ADDRESS_KEY]

        # Parsing street address using street-address package
        address_parser = StreetAddressParser()
        a = address_parser.parse(current_address)
        address = str(a["house"]) + " " + str(a["street_name"]) + " " \
                  + str(a["street_type"])

        # Parsing zip code
        zip_code = str(a["other"]).zfill(5) if a["other"] else None
        zip_code_key = intent_constants.ZIP_CODE_KEY
        if zip_code is None and zip_code_key in \
                mycity_request.session_attributes:
            zip_code = mycity_request.session_attributes[zip_code_key]

        # Get user's GIS Geocode Address and list of available trucks
        usr_addr = gis_utils.geocode_address(address)
        truck_unique_locations = get_truck_locations(usr_addr)

        # Create custom response based on number of trucks returned
        try:
            if len(truck_unique_locations) == 0:
                mycity_response.output_speech = "I didn't find any food trucks!"

            if len(truck_unique_locations) == 1:
                response = f"I found {len(truck_unique_locations)} food " \
                           f"truck within a mile from your address! "
                response += add_response_text(truck_unique_locations)
                mycity_response.output_speech = response

            if 1 < len(truck_unique_locations) <= 3:
                response = f"I found {len(truck_unique_locations)} food " \
                           f"trucks within a mile from your address! "
                response += add_response_text(truck_unique_locations)
                mycity_response.output_speech = response

            if len(truck_unique_locations) > 3:
                response = f"There are at least {len(truck_unique_locations)}" \
                           f" food trucks within a mile from your " \
                           f"address! Here are the first five. "
                response += add_response_text(truck_unique_locations)
                mycity_response.output_speech = response

        except InvalidAddressError:
            address_string = address
            if zip_code:
                address_string = address_string + " with zip code {}"\
                    .format(zip_code)
            mycity_response.output_speech = \
                speech_constants.ADDRESS_NOT_FOUND.format(address_string)
            mycity_response.dialog_directive = "ElicitSlotFoodTruck"
            mycity_response.reprompt_text = None
            mycity_response.session_attributes = \
                mycity_request.session_attributes
            mycity_response.card_title = CARD_TITLE
            mycity_request = clear_address_from_mycity_object(mycity_request)
            mycity_response = clear_address_from_mycity_object(mycity_response)
            return mycity_response

        except BadAPIResponse:
            mycity_response.output_speech = \
                "Hmm something went wrong. Maybe try again?"

        except MultipleAddressError:
            mycity_response.output_speech = \
                speech_constants.MULTIPLE_ADDRESS_ERROR.format(address)
            mycity_response.dialog_directive = "ElicitSlotZipCode"

    else:
        logger.error("Error: Called food_truck_intent with no address")
        mycity_response.output_speech = "I didn't understand that address, " \
                                        "please try again"

    # Setting reprompt_text to None signifies that we do not want to reprompt
    # the user. If the user does not respond or says something that is not
    # understood, the session will end.
    mycity_response.reprompt_text = None
    mycity_response.session_attributes = mycity_request.session_attributes
    mycity_response.card_title = CARD_TITLE

    return mycity_response