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