def get_trash_pickup_info(mycity_request): """ Generates a response to a trash day request :param mycity_request: MyCityRequestDataModel containing the user request :return: MyCityResponseDataModel containing the speech to return to the user """ LOGGER.debug('Getting trash day information') 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: # Delegate to the Alexa interaction model for getting the user address LOGGER.debug('Requesting user address') response.dialog_directive = "Delegate" else: response.output_speech = _get_output_speech_for_address( current_address) response.card_title = CARD_TITLE_TRASH_DAY return response
def find_closest_police_station(mycity_request): """ Finds the closest police station in Brookline to a given address :param mycity_request: MyCityRequestDataModel object :return: MyCityResponseDataModel object """ logger.debug('Finding closest police station') 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.card_title = CARD_TITLE_POLICE_STATION response.should_end_session = True return 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_voting_precinct(mycity_request): """ Generates a response to a voting precinct request :param mycity_request: MyCityRequestDataModel containing the user request :return: MyCityResponseDataModel containing the speech to return to the user """ LOGGER.debug('Getting voting precinct information') 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: # Delegate to the Alexa interaction model for getting the user address LOGGER.debug('Requesting user address.') response.dialog_directive = "Delegate" else: response.output_speech = _get_output_speech_for_address( current_address, mycity_request) response.card_title = CARD_TITLE_VOTING response.should_end_session = True return 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_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_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() coordinates = None user_address = None if intent_constants.CURRENT_ADDRESS_KEY not in \ mycity_request.session_attributes: # If not provided, try to get the user address through # geolocation and device address coordinates = address_utils.\ get_address_coordinates_from_geolocation(mycity_request) if not coordinates: if mycity_request.device_has_geolocation: return location_services_utils.\ request_geolocation_permission_response() # Try getting registered device address mycity_request, location_permissions = location_services_utils.\ get_address_from_user_device(mycity_request) if not location_permissions: return location_services_utils.\ request_device_address_permission_response() if not coordinates: if intent_constants.CURRENT_ADDRESS_KEY \ not in mycity_request.session_attributes: # We don't have coordinates or an address by now, # and we have all required permissions, ask the user return request_user_address_response(mycity_request) user_address = mycity_request.session_attributes[ intent_constants.CURRENT_ADDRESS_KEY] coordinates = gis_utils.geocode_address(user_address) if not is_location_in_city(user_address, coordinates): mycity_response.output_speech = speech_const.NOT_IN_BOSTON_SPEECH mycity_response.should_end_session = True mycity_response.card_title = CARD_TITLE return mycity_response # Get list of available trucks truck_unique_locations = get_truck_locations(coordinates) # Create custom response based on number of trucks returned try: if len(truck_unique_locations) == 0: mycity_response.output_speech = "I didn't find any food " \ "trucks near you!" if len(truck_unique_locations) == 1: response = f"I found {len(truck_unique_locations)} food " \ f"truck within a mile from your address! " response += add_response_text(truck_unique_locations) mycity_response.output_speech = response if 1 < len(truck_unique_locations) <= 3: response = f"I found {len(truck_unique_locations)} food " \ f"trucks within a mile from your address! " response += add_response_text(truck_unique_locations) mycity_response.output_speech = response if len(truck_unique_locations) > 3: response = f"There are at least {len(truck_unique_locations)}" \ f" food trucks within a mile from your " \ f"address! Here are the first five. " response += add_response_text(truck_unique_locations) mycity_response.output_speech = response except InvalidAddressError: mycity_response.output_speech = \ ft_speech_constants.ADDRESS_NOT_FOUND.format("that address") mycity_response.dialog_directive = "ElicitSlotFoodTruck" mycity_response.reprompt_text = None mycity_response.session_attributes = \ mycity_request.session_attributes mycity_response.card_title = CARD_TITLE mycity_request = clear_address_from_mycity_object(mycity_request) mycity_response = clear_address_from_mycity_object(mycity_response) mycity_response.should_end_session = True return mycity_response except BadAPIResponse: mycity_response.output_speech = \ "Hmm something went wrong. Maybe try again?" # Setting reprompt_text to None signifies that we do not want to reprompt # the user. If the user does not respond or says something that is not # understood, the session will end. mycity_response.reprompt_text = None mycity_response.session_attributes = mycity_request.session_attributes mycity_response.card_title = CARD_TITLE mycity_response.should_end_session = True return mycity_response
def get_voting_location(mycity_request: MyCityRequestDataModel) -> \ MyCityResponseDataModel: """ Generates response object for a polling location inquiry which includes a user's location to vote. :param mycity_request: MyCityRequestDataModel object :return: MyCityResponseDataModel object """ logger.debug('MyCityRequestDataModel received:' + mycity_request.get_logger_string()) mycity_response = MyCityResponseDataModel() mycity_response.card_title = CARD_TITLE # check for address for locating voting location if intent_constants.CURRENT_ADDRESS_KEY not in \ mycity_request.session_attributes: mycity_request, location_permissions = \ get_address_from_user_device(mycity_request) if not location_permissions: return request_device_address_permission_response() elif intent_constants.CURRENT_ADDRESS_KEY not in \ mycity_request.session_attributes: return request_user_address_response(mycity_request) current_address = \ mycity_request.session_attributes[intent_constants.CURRENT_ADDRESS_KEY] # If we have more specific info then just the street # address, make sure we are in Boston if not is_address_in_city(current_address): mycity_response.output_speech = NOT_IN_BOSTON_SPEECH mycity_response.should_end_session = True mycity_response.card_title = CARD_TITLE return mycity_response # grab relevant information from session address parsed_address, _ = usaddress.tag(current_address) if not is_address_valid(parsed_address): mycity_response.output_speech = ADDRESS_NOT_UNDERSTOOD mycity_response.dialog_directive = "ElicitSlotVotingIntent" mycity_response.reprompt_text = None mycity_response.session_attributes = mycity_request.session_attributes mycity_response.card_title = CARD_TITLE mycity_response.should_end_session = True return clear_address_from_mycity_object(mycity_response) zipcode = None if "Zipcode" in mycity_request.intent_variables and \ "value" in mycity_request.intent_variables["Zipcode"]: zipcode = \ mycity_request.intent_variables["Zipcode"]["value"].zfill(5) mycity_response.reprompt_text = None mycity_response.should_end_session = True try: top_candidate = gis_utils.geocode_address(current_address, zipcode) ward_precinct = vote_utils.get_ward_precinct_info(top_candidate) poll_location = vote_utils.get_polling_location(ward_precinct) output_speech = LOCATION_SPEECH. \ format(poll_location[LOCATION_NAME], poll_location[LOCATION_ADDRESS]) mycity_response.output_speech = output_speech except ParseError: mycity_response.output_speech = NO_WARD_OR_PRECINCT except BadAPIResponse: mycity_response.output_speech = BAD_API_RESPONSE except MultipleAddressError as error: address_list = ', '.join(error.addresses) mycity_response.output_speech = MULTIPLE_ADDRESS_ERROR mycity_response.dialog_directive = "ElicitSlotZipCode" mycity_response.should_end_session = False return mycity_response
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