def get_coronovirus_update(mycity_request):
    """
    Get the latest information about the coronavirus from boston.gov

    :param mycity_request: MyCityRequestDataModel with the user request for information
    :return: MyCityResponseDataModel containing information from boston.gov
    """
    mycity_response = MyCityResponseDataModel()
    mycity_response.card_title = INTENT_CARD_TITLE
    mycity_response.reprompt_text = None
    mycity_response.session_attributes = mycity_request.session_attributes
    mycity_response.should_end_session = True
    try:
        mycity_response.output_speech = _construct_output_speech(
            _get_coronavirus_detail_text())
        mycity_response.output_speech_type = 'SSML'
    except ParseError:
        mycity_response.output_speech = NO_UPDATE_ERROR

    return mycity_response
def get_snow_emergency_parking_intent(mycity_request):
    """
    Populate MyCityResponseDataModel with snow emergency parking response information.

    :param mycity_request: MyCityRequestModel object
    :param mycity_response: MyCityResponseModel object
    :return: MyCityResponseModel object
    """
    print('[method: get_snow_emergency_parking_intent]',
          'MyCityRequestDataModel received:', str(mycity_request))

    mycity_response = MyCityResponseDataModel()
    if intent_constants.CURRENT_ADDRESS_KEY in mycity_request.session_attributes:

        origin_address = _build_origin_address(mycity_request)

        print("Finding snow emergency parking for {}".format(origin_address))

        parking_address, driving_distance, driving_time = \
            _get_snow_emergency_parking_location(origin_address)

        if not parking_address:
            mycity_response.output_speech = "Uh oh. Something went wrong!"
        else:
            mycity_response.output_speech = \
                "The closest snow emergency parking location is at " \
                "{}. It is {} away and should take you {} to drive " \
                "there".format(parking_address, driving_distance, driving_time)

        mycity_response.should_end_session = False
    else:
        print("Error: Called snow_parking_intent with no address")

    # 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 = mycity_request.intent_name

    return mycity_response
Beispiel #3
0
def get_help_response(mycity_request):
    """
    Provides an overview of the skill. This is triggered by AMAZON.HelpIntent.

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseDataModel object that will initiate
        a help process on the user's device
    """
    logger.debug('')
    mycity_response = MyCityResponseDataModel()
    mycity_response.session_attributes = mycity_request.session_attributes
    mycity_response.card_title = "Help"
    mycity_response.output_speech = (
        "You are using Boston Info, a skill that provides general information "
        "about Boston. You can currently ask about your trash and recycling "
        "pickup schedule, the location of the nearest snow emergency parking,"
        "and current alerts from Boston.gov. If you have feedback for the "
        "skill, say, 'I have a suggestion.'")
    mycity_response.reprompt_text = None
    mycity_response.should_end_session = False
    return mycity_response
def get_farmers_markets_today(mycity_request):
    """
    Get all available farmers markets today

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

    # List all available farmers markets today
    markets = gis_utils.get_features_from_feature_server(BASE_URL, QUERY)

    try:
        # Loop through the list of available farmers markets at a certain day
        markets_today = []
        for m in markets:
            if m not in markets_today and \
                    m['attributes']['Day_of_Week'] == DAY:
                markets_today.append(m)

        response = 'Available farmers markets today are:\n'
        for m in markets_today:
            response += m['attributes']['Name'] + ' located at ' + \
                        m['attributes']['Address'] + ' from ' + \
                        m['attributes']['Hours'] + '. '
        mycity_response.output_speech = 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 = "Farmers Markets"
    mycity_response.should_end_session = True

    return mycity_response
def get_address_from_session(mycity_request):
    """
    Looks for a current address in the session attributes and constructs a
    response based on whether one exists or not. If one exists, it is
    preserved in the session.

    :param mycity_request: MyCityRequestDataModel
    :param mycity_response: MyCityResponseDataModel
    :return : MyCityResponseModel object
    """
    print(
        '[module: user_address_intent]',
        '[method: get_address_from_session]',
        'MyCityRequestDataModel received:',
        str(mycity_request)
    )

    mycity_response = MyCityResponseDataModel()
    # print("GETTING ADDRESS FROM SESSION")
    mycity_response.session_attributes = mycity_request.session_attributes
    mycity_response.card_title = mycity_request.intent_name
    mycity_response.reprompt_text = None
    mycity_response.should_end_session = False

    if intent_constants.CURRENT_ADDRESS_KEY in mycity_request.session_attributes:
        current_address = mycity_request.session_attributes[
            intent_constants.CURRENT_ADDRESS_KEY]
        mycity_response.output_speech = "Your address is " + current_address + "."
    else:
        mycity_response.output_speech = "I'm not sure what your address is. " \
                                        "You can tell me your address by saying, " \
                                        "\"my address is\" followed by your address."

    # Setting reprompt_text to None signifies that we do not want to reprompt
    # the user. They will be returned to the top level of the skill and must
    # provide input that corresponds to an intent to continue.

    return mycity_response
Beispiel #6
0
def get_welcome_response(mycity_request):
    """
    Welcomes the user and sets initial session attributes. Is triggered on
    initial launch and on AMAZON.HelpIntent.

    If we wanted to initialize the session to have some attributes we could
    add those here.

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseDataModel object that will initiate
        a welcome process on the user's device
    """
    mycity_response = MyCityResponseDataModel()
    mycity_response.session_attributes = mycity_request.session_attributes
    mycity_response.card_title = "Welcome"
    mycity_response.output_speech = LAUNCH_SPEECH

    # If the user either does not reply to the welcome message or says
    # something that is not understood, they will be prompted again with
    # this text.
    mycity_response.reprompt_text = LAUNCH_REPROMPT_SPEECH
    mycity_response.should_end_session = False
    return mycity_response
Beispiel #7
0
    def get_welcome_response(self, mycity_request):
        """
        If we wanted to initialize the session to have some attributes we could
        add those here.
        """
        print(
            self.LOG_CLASS,
            '[method: get_welcome_response]'
        )
        mycity_response = MyCityResponseDataModel()
        mycity_response.session_attributes = mycity_request.session_attributes
        mycity_response.card_title = "Welcome"
        mycity_response.output_speech = \
            "Welcome to the Boston Public Services skill. How can I help you? "

        # If the user either does not reply to the welcome message or says
        # something that is not understood, they will be prompted again with
        # this text.
        mycity_response.reprompt_text = \
            "For example, you can tell me your address by saying, " \
            "\"my address is\" followed by your address."
        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()
    if intent_constants.CURRENT_ADDRESS_KEY in mycity_request.session_attributes:
        try:
            finder = FinderCSV(mycity_request, PARKING_INFO_URL, ADDRESS_KEY,
                               constants.OUTPUT_SPEECH_FORMAT,
                               format_record_fields)
        except InvalidAddressError:
            mycity_response.output_speech = constants.ERROR_INVALID_ADDRESS
        else:
            print("Finding snow emergency parking for {}".format(
                finder.origin_address))
            finder.start()
            mycity_response.output_speech = finder.get_output_speech()

    else:
        print("Error: Called snow_parking_intent with no address")
        mycity_response.output_speech = constants.ERROR_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
def find_closest_library(mycity_request):
    """
    Finds the closest library in Brookline to a given address

    :param mycity_request: MyCityRequestDataModel object
    :return MyCityResponseDataModel object
    """
    logger.debug('Finding closest library')

    response = MyCityResponseDataModel()
    set_address_in_session(mycity_request)
    current_address = \
            mycity_request.session_attributes.get(intent_constants.CURRENT_ADDRESS_KEY)
    if current_address is None:
        response.dialog_directive = "Delegate"
    else:
        response.output_speech = _get_output_speech_for_address(
            current_address)

    response.reprompt_text = None
    response.session_attributes = mycity_request.session_attributes
    response.card_title = CARD_TITLE_LIBRARY

    return response
Beispiel #10
0
def get_alerts_intent(mycity_request):
    """
    Generate response object with information about citywide alerts

    :param mycity_request: MyCityRequestDataModel object
    :return: MyCityResponseDataModel object
    """
    print(
        '[method: get_alerts_intent]',
        'MyCityRequestDataModel received:\n',
        str(mycity_request)
    )

    mycity_response = MyCityResponseDataModel()
    alerts = get_alerts()
    print("[dictionary with alerts scraped from boston.gov]:\n" + str(alerts))
    alerts = prune_normal_responses(alerts)
    print("[dictionary after pruning]:\n" + str(alerts))
    mycity_response.session_attributes = mycity_request.session_attributes
    mycity_response.card_title = mycity_request.intent_name
    mycity_response.reprompt_text = None
    mycity_response.output_speech = alerts_to_speech_output(alerts)
    mycity_response.should_end_session = True   # leave this as True for right now
    return mycity_response
def get_alerts_intent(mycity_request):
    """
    Generate response object with information about citywide alerts

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

    mycity_response = MyCityResponseDataModel()
    alerts = get_alerts()
    logger.debug("[dictionary with alerts scraped from boston.gov]:\n" +
                 str(alerts))

    alerts = prune_normal_responses(alerts)
    logger.debug("[dictionary after pruning]:\n" + str(alerts))

    mycity_response.session_attributes = mycity_request.session_attributes
    mycity_response.card_title = ALERTS_INTENT_CARD_TITLE
    mycity_response.reprompt_text = None
    mycity_response.output_speech = alerts_to_speech_output(alerts)
    mycity_response.should_end_session = True  # leave this as True for right now
    return mycity_response
Beispiel #12
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_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 #14
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_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
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 #17
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)
            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

    else:
        logger.error("Error: Called trash_day_intent with no address")
        mycity_response.output_speech = speech_constants.ADDRESS_NOT_UNDERSTOOD
        mycity_response.should_end_session = True

    # 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 #18
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
Beispiel #19
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