Beispiel #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_trash_day_info(mycity_request):
    """
    Generates response object for a trash day inquiry.

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseDataModel object
    """
    logger.debug('MyCityRequestDataModel received:' +
                 mycity_request.get_logger_string())

    mycity_response = MyCityResponseDataModel()

    # Determine if we have required address information. Request if we do not.
    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]

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

    # 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

    if not address_utils.is_address_valid(parsed_address):
        mycity_response.output_speech = speech_constants.ADDRESS_NOT_UNDERSTOOD
        mycity_response.dialog_directive = "ElicitSlotTrash"
        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)

    # currently assumes that trash day is the same for all units at
    # the same street address
    address = " ".join([
        parsed_address['AddressNumber'], parsed_address['StreetName'],
        parsed_address['StreetNamePostType']
    ])
    neighborhood = parsed_address["PlaceName"] \
        if "PlaceName" in parsed_address \
        and not parsed_address["PlaceName"].isdigit() \
        else None

    if "Neighborhood" in mycity_request.intent_variables and \
        "value" in mycity_request.intent_variables["Neighborhood"]:
        neighborhood = \
            mycity_request.intent_variables["Neighborhood"]["value"]

    try:
        trash_days = get_trash_and_recycling_days(address, neighborhood)
        trash_days_speech = build_speech_from_list_of_days(trash_days)

        mycity_response.output_speech = speech_constants.PICK_UP_DAY.format(
            trash_days_speech)
        mycity_response.should_end_session = True

    except InvalidAddressError:
        address_string = address
        mycity_response.output_speech = speech_constants.ADDRESS_NOT_FOUND.format(
            address_string)
        mycity_response.dialog_directive = "ElicitSlotTrash"
        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)

    except BadAPIResponse:
        mycity_response.output_speech = speech_constants.BAD_API_RESPONSE
        mycity_response.should_end_session = True

    except MultipleAddressError as error:
        addresses = [
            re.sub(r' \d{5}', '', address) for address in error.addresses
        ]
        address_list = ', '.join(addresses)
        mycity_response.output_speech = speech_constants.MULTIPLE_ADDRESS_ERROR.format(
            address_list)
        mycity_response.dialog_directive = "ElicitSlotNeighborhood"
        mycity_response.should_end_session = False

    # 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
Beispiel #3
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()

    # See if the user provided a custom address
    user_address = _get_address_coordinates_from_session(mycity_request)

    # If not, try to get user position from geolocation
    if not user_address:
        user_address = _get_address_coordinates_from_geolocation(
            mycity_request)

    if not user_address:
        # Couldn't get address. Request the to use geolocation if possible, fall back to
        # asking for speech input if device doesn't support it.
        if mycity_request.device_has_geolocation:
            return location_services_utils.request_geolocation_permission_response(
            )
        else:
            return request_user_address_response(mycity_request)

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

    # 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 = \
            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?"

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

    # 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_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
Beispiel #5
0
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
def get_snow_emergency_parking_intent(mycity_request):
    """
    Populate MyCityResponseDataModel with snow emergency parking response information.

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseDataModel object
    """
    logger.debug('MyCityRequestDataModel received:' +
                 mycity_request.get_logger_string())

    mycity_response = MyCityResponseDataModel()

    coordinates = 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 we don't have coordinates or an address by now, and we have all required permissions, ask the user
    if not coordinates and intent_constants.CURRENT_ADDRESS_KEY not in mycity_request.session_attributes:
        return user_address_intent.request_user_address_response(
            mycity_request)

    try:
        finder = FinderCSV(mycity_request,
                           PARKING_INFO_URL,
                           ADDRESS_KEY,
                           constants.OUTPUT_SPEECH_FORMAT,
                           format_record_fields,
                           origin_coordinates=coordinates)
    except InvalidAddressError:
        mycity_response.output_speech = constants.ERROR_INVALID_ADDRESS
    else:
        if finder.is_in_city():
            logger.debug("Address or coords deemed to be in Boston:\n%s\n%s",
                         finder.origin_address, finder.origin_coordinates)
            finder_location_string = finder.origin_address if finder.origin_address \
                else "{}, {}".format(finder.origin_coordinates['x'], finder.origin_coordinates['y'])
            print("Finding snow emergency parking for {}".format(
                finder_location_string))
            finder.start()
            mycity_response.output_speech = finder.get_output_speech()
        else:
            logger.debug(
                "Address or coords deemed to be NOT in Boston: <address: %s> <coords: %s>",
                finder.origin_address, finder.origin_coordinates)
            mycity_response.output_speech = NOT_IN_BOSTON_SPEECH

    # 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 = SNOW_PARKING_CARD_TITLE
    mycity_response.should_end_session = True

    return mycity_response
Beispiel #7
0
def get_trash_day_info(mycity_request):
    """
    Generates response object for a trash day inquiry.

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseDataModel object
    """
    logger.debug('MyCityRequestDataModel received:' +
                 mycity_request.get_logger_string())

    mycity_response = MyCityResponseDataModel()
    if intent_constants.CURRENT_ADDRESS_KEY in mycity_request.session_attributes:
        current_address = \
            mycity_request.session_attributes[intent_constants.CURRENT_ADDRESS_KEY]

        # grab relevant information from session address
        address_parser = StreetAddressParser()
        a = address_parser.parse(current_address)

        if not address_utils.is_address_valid(a):
            mycity_response.output_speech = speech_constants.ADDRESS_NOT_UNDERSTOOD
            mycity_response.dialog_directive = "ElicitSlotTrash"
            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)

        # currently assumes that trash day is the same for all units at
        # the same street address
        address = str(a['house']) + " " + str(a['street_full'])
        zip_code = str(a["other"]).zfill(
            5) if a["other"] and a["other"].isdigit() else None
        neighborhood = a["other"] if a["other"] and not a["other"].isdigit(
        ) 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]

        if "Neighborhood" in mycity_request.intent_variables and \
            "value" in mycity_request.intent_variables["Neighborhood"]:
            neighborhood = mycity_request.intent_variables["Neighborhood"][
                "value"]

        try:
            trash_days = get_trash_and_recycling_days(address, zip_code,
                                                      neighborhood)
            trash_days_speech = build_speech_from_list_of_days(trash_days)

            mycity_response.output_speech = speech_constants.PICK_UP_DAY.format(
                trash_days_speech)
            mycity_response.should_end_session = True

        except InvalidAddressError:
            address_string = address
            if zip_code:
                address_string = address_string + " with zip code {}"\
                    .format(zip_code)
            if neighborhood:
                address_string = address_string + " in {}".format(neighborhood)
            mycity_response.output_speech = speech_constants.ADDRESS_NOT_FOUND.format(
                address_string)
            mycity_response.dialog_directive = "ElicitSlotTrash"
            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)

        except BadAPIResponse:
            mycity_response.output_speech = speech_constants.BAD_API_RESPONSE
            mycity_response.should_end_session = True

        except MultipleAddressError as error:
            addresses = [
                re.sub(r' \d{5}', '', address) for address in error.addresses
            ]
            address_list = ', '.join(addresses)
            mycity_response.output_speech = speech_constants.MULTIPLE_ADDRESS_ERROR.format(
                address_list)
            mycity_response.dialog_directive = "ElicitSlotNeighborhood"
            mycity_response.should_end_session = False

    # Determine if we have required address information. Request if we do not.
    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]

    # grab relevant information from session address
    address_parser = StreetAddressParser()
    a = address_parser.parse(current_address)

    if not address_utils.is_address_valid(a):
        mycity_response.output_speech = speech_constants.ADDRESS_NOT_UNDERSTOOD
        mycity_response.dialog_directive = "ElicitSlotTrash"
        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)

    # currently assumes that trash day is the same for all units at
    # the same street address
    address = str(a['house']) + " " + str(a['street_full'])
    zip_code = str(
        a["other"]).zfill(5) if a["other"] and a["other"].isdigit() else None
    neighborhood = a["other"] if a["other"] and not a["other"].isdigit(
    ) 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]

    if "Neighborhood" in mycity_request.intent_variables and \
        "value" in mycity_request.intent_variables["Neighborhood"]:
        neighborhood = mycity_request.intent_variables["Neighborhood"]["value"]

    try:
        trash_days = get_trash_and_recycling_days(address, zip_code,
                                                  neighborhood)
        trash_days_speech = build_speech_from_list_of_days(trash_days)

        mycity_response.output_speech = speech_constants.PICK_UP_DAY.format(
            trash_days_speech)
        mycity_response.should_end_session = True

    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 = "ElicitSlotTrash"
        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)

    except BadAPIResponse:
        mycity_response.output_speech = speech_constants.BAD_API_RESPONSE
        mycity_response.should_end_session = True

    except MultipleAddressError as error:
        addresses = [
            re.sub(r' \d{5}', '', address) for address in error.addresses
        ]
        address_list = ', '.join(addresses)
        mycity_response.output_speech = speech_constants.MULTIPLE_ADDRESS_ERROR.format(
            address_list)
        mycity_response.dialog_directive = "ElicitSlotNeighborhood"
        mycity_response.should_end_session = False

    # 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