def test_get_voting_location_without_supplied_address( self, mock_get_address): mycity_request = MyCityRequestDataModel() mock_get_address.return_value = (MyCityRequestDataModel(), False) response = get_voting_location(mycity_request) self.assertTrue( "read::alexa:device:all:address" in response.card_permissions)
def test_returns_session_attributes(self): mycity_request = MyCityRequestDataModel() mycity_request.session_attributes["test_key"] = "test_value" mycity_response = coronavirus_update_intent.get_coronovirus_update( mycity_request) self.assertEqual("test_value", mycity_response.session_attributes["test_key"])
def test_does_not_require_boston_address_if_desired_address_provided(self, mock_get_days, mock_get_address): device_address_request = MyCityRequestDataModel() device_address_request.session_attributes[ intent_constants.CURRENT_ADDRESS_KEY] \ = "10 Main Street New York NY" mock_get_address.return_value = device_address_request, True mock_get_days.return_value = ["Monday"] request = MyCityRequestDataModel() request.session_attributes[intent_constants.CURRENT_ADDRESS_KEY] = "10 Main Street Boston MA" result = get_trash_day_info(request) self.assertTrue("Monday" in result.output_speech)
def get_alerts_intent( mycity_request: MyCityRequestDataModel, get_alerts_function_for_test: typing.Callable[[], typing.Dict] = None, prune_normal_responses_function_for_test: typing.Callable[ [], typing.Dict] = None, alerts_to_speech_output_function_for_test: typing.Callable[ [], typing.AnyStr] = None ) -> MyCityResponseDataModel: """ Generate response object with information about citywide alerts :param mycity_request: MyCityRequestDataModel object :param get_alerts_function_for_test: Injectable function for unit tests :param prune_normal_responses_function_for_test: Injectable function for unit tests :param alerts_to_speech_output_function_for_test: Injectable function for unit tests :return: MyCityResponseDataModel object """ logger.debug('MyCityRequestDataModel received:' + mycity_request.get_logger_string()) alerts = get_alerts( ) if get_alerts_function_for_test is None else get_alerts_function_for_test( ) logger.debug("[dictionary with alerts scraped from boston.gov]:\n" + str(alerts)) pruned_alerts = prune_normal_responses(alerts) \ if prune_normal_responses_function_for_test is None else prune_normal_responses_function_for_test(alerts) logger.debug("[dictionary after pruning]:\n" + str(alerts)) mycity_response = _create_response_object() mycity_response.output_speech = alerts_to_speech_output(pruned_alerts) \ if alerts_to_speech_output_function_for_test is None else alerts_to_speech_output_function_for_test(pruned_alerts) return mycity_response
def test_requests_device_address_if_supported(self, mock_get_address): mock_get_address.return_value = (MyCityRequestDataModel(), False) self.request._session_attributes.pop( intent_constants.CURRENT_ADDRESS_KEY, None) self.request.device_has_geolocation = False response = snow_parking.get_snow_emergency_parking_intent(self.request) self.assertEqual(response.card_type, "AskForPermissionsConsent")
def get_inclement_weather_alert( mycity_request: MyCityRequestDataModel, get_alerts_function_for_test: typing.Callable[[], typing.Dict] = None, ) -> MyCityResponseDataModel: """ Generates a response with information about any inclement weather alerts. :param mycity_request: MyCityRequestDataModel object :param get_alerts_function_for_test: Injectable function for unit tests :return: MyCityResponseDataModel object """ logger.debug('MyCityRequestDataModel received:' + mycity_request.get_logger_string()) alerts = get_alerts( ) if get_alerts_function_for_test is None else get_alerts_function_for_test( ) logger.debug("[dictionary with alerts scraped from boston.gov]:\n" + str(alerts)) logger.debug("filtering for inclement weather alerts") output_speech = constants.NO_INCLEMENT_WEATHER_ALERTS if Services.ALERT_HEADER.value in alerts: if any(query in alerts[Services.ALERT_HEADER.value].lower() for query in SNOW_ALERT_QUERY): logger.debug("inclement weather alert found") output_speech = alerts[Services.ALERT_HEADER.value] mycity_response = _create_response_object() mycity_response.session_attributes = mycity_request.session_attributes mycity_response.output_speech = output_speech return mycity_response
def test_does_not_get_device_address_if_desired_address_provided( self, mock_get_days, mock_get_address): mock_get_days.return_value = ["Monday"] request = MyCityRequestDataModel() request.session_attributes[ intent_constants.CURRENT_ADDRESS_KEY] = "10 Main Street Boston MA" get_trash_day_info(request) mock_get_address.assert_not_called()
def test_fallback_to_manual_request_if_no_device_address( self, mock_get_address): mock_get_address.return_value = (MyCityRequestDataModel(), True) self.request._session_attributes.pop( intent_constants.CURRENT_ADDRESS_KEY, None) self.request.device_has_geolocation = False response = snow_parking.get_snow_emergency_parking_intent(self.request) self.assertEqual("Address", response.card_title)
def test_no_ward_voting_response(self, mock_ward, mock_geocode): mycity_request = MyCityRequestDataModel() mycity_request.session_attributes[ intent_constants.CURRENT_ADDRESS_KEY] = "866 Huntington Avenue" mock_geocode.return_value = test_constants.GEOCODE_ADDRESS_CANDIDATES mock_ward.side_effect = ParseError() expected_text = "There doesn't seem to be information for that address in Boston" response = get_voting_location(mycity_request) self.assertTrue(expected_text, response.output_speech)
def test_delegates_if_address_not_provided_no_geolocation_support( self, mock_get_address): self.request.device_has_geolocation = False device_address_request = MyCityRequestDataModel() mock_get_address.return_value = device_address_request, True self.request._session_attributes.pop( intent_constants.CURRENT_ADDRESS_KEY, None) response = self.controller.on_intent(self.request) self.assertEqual(response.dialog_directive['type'], "Dialog.Delegate")
def test_device_address_finds_closest_parking(self, mock_get_address): request_with_address = MyCityRequestDataModel() request_with_address._session_attributes[ intent_constants.CURRENT_ADDRESS_KEY] = "1000 Dorchester Ave" mock_get_address.return_value = (request_with_address, True) self.request._session_attributes.pop( intent_constants.CURRENT_ADDRESS_KEY, None) self.request.device_has_geolocation = False response = snow_parking.get_snow_emergency_parking_intent(self.request) self.assertEqual(self.expected_card_title, response.card_title)
def test_correct_voting_response(self, mock_poll_location, mock_ward, mock_geocode): mycity_request = MyCityRequestDataModel() mycity_request.session_attributes[ intent_constants.CURRENT_ADDRESS_KEY] = "866 Huntington Avenue" mock_geocode.return_value = test_constants.GEOCODE_ADDRESS_CANDIDATES mock_ward.return_value = test_constants.WARD_PRECINCT mock_poll_location.return_value = test_constants.POLL_DATA expected_text = "Your polling location is BACK OF THE HILL APARTMENTS , 100 SOUTH HUNTINGTON AVENUE." response = get_voting_location(mycity_request) self.assertTrue(expected_text, response.output_speech)
def test_requests_user_supplied_address_when_no_device_address_set( self, mock_get_address): mock_get_address.return_value = (MyCityRequestDataModel(), True) request = MyCityRequestDataModel() response = get_trash_day_info(request) self.assertEqual("Address", response.card_title)
def test_requests_device_address_permission(self, mock_get_address): mock_get_address.return_value = (MyCityRequestDataModel(), False) request = MyCityRequestDataModel() response = get_trash_day_info(request) self.assertTrue( "read::alexa:device:all:address" in response.card_permissions)
def test_provided_address_must_be_in_city(self, mock_get_days): mock_get_days.return_value = ["Monday"] request = MyCityRequestDataModel() request.session_attributes[intent_constants.CURRENT_ADDRESS_KEY] = "10 Main Street New York, NY" result = get_trash_day_info(request) self.assertFalse("Monday" in result.output_speech)
def test_provided_address_misunderstood(self): expected_text = speech_constants.ADDRESS_NOT_UNDERSTOOD request = MyCityRequestDataModel() request.session_attributes[intent_constants.CURRENT_ADDRESS_KEY] = "wayne street" result = get_trash_day_info(request) self.assertTrue(expected_text in result.output_speech)
def platform_to_mycity_request(event): """ Translates from Amazon platform request to MyCityRequestDataModel :param event: :return: """ print("\n\n[module: lambda_function]", "[function: platform_to_mycity_request]", "Amazon request received:\n", str(event)) mycity_request = MyCityRequestDataModel() mycity_request.request_type = event['request']['type'] mycity_request.request_id = event['request']['requestId'] mycity_request.is_new_session = event['session']['new'] mycity_request.session_id = event['session']['sessionId'] if 'attributes' in event['session']: mycity_request.session_attributes = event['session']['attributes'] else: mycity_request.session_attributes = {} mycity_request.application_id = event['session']['application'][ 'applicationId'] if 'intent' in event['request']: mycity_request.intent_name = event['request']['intent']['name'] if 'slots' in event['request']['intent']: mycity_request.intent_variables = event['request']['intent'][ 'slots'] else: mycity_request.intent_name = None mycity_request.output_speech = None mycity_request.reprompt_text = None mycity_request.should_end_session = False return mycity_request
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 test_returns_valid_text(self): mycity_request = MyCityRequestDataModel() mycity_reponse = coronavirus_update_intent.get_coronovirus_update( mycity_request) self.assertFalse(coronavirus_update_intent.NO_UPDATE_ERROR in mycity_reponse.output_speech)
def platform_to_mycity_request(event): """ Translates from Amazon platform request to MyCityRequestDataModel :param event: JSON object containing the raw request information received from the Alexa service platform :return: MyCityRequestDataModel object (formatted to be understood and acted on by mycity_controller) """ print("\n\n[module: lambda_function]", "[function: platform_to_mycity_request]", "Amazon request received:\n", str(event)) mycity_request = MyCityRequestDataModel() mycity_request.request_type = event['request']['type'] mycity_request.request_id = event['request']['requestId'] mycity_request.is_new_session = event['session']['new'] mycity_request.session_id = event['session']['sessionId'] if 'attributes' in event['session']: mycity_request.session_attributes = event['session']['attributes'] else: mycity_request.session_attributes = {} mycity_request.application_id = event['session']['application'][ 'applicationId'] if 'intent' in event['request']: mycity_request.intent_name = event['request']['intent']['name'] if 'slots' in event['request']['intent']: mycity_request.intent_variables = event['request']['intent'][ 'slots'] else: mycity_request.intent_name = None mycity_request.output_speech = None mycity_request.reprompt_text = None mycity_request.should_end_session = False return mycity_request
def platform_to_mycity_request(event): """ Translates from Amazon platform request to MyCityRequestDataModel :param event: JSON object containing the raw request information received from the Alexa service platform :return: MyCityRequestDataModel object (formatted to be understood and acted on by mycity_controller) """ logger.debug('Amazon request received: ' + str(event)) mycity_request = MyCityRequestDataModel() # Get base request information mycity_request.request_type = event['request']['type'] mycity_request.request_id = event['request']['requestId'] # Get session information mycity_request.is_new_session = event['session']['new'] mycity_request.session_id = event['session']['sessionId'] mycity_request.application_id = \ event['session']['application']['applicationId'] # Get device information system_context = event['context']['System'] device_context = system_context.get('device', {}) mycity_request.device_id = device_context.get('deviceId', "unknown") mycity_request.api_access_token = \ system_context.get('apiAccessToken', "none") # Get location services info mycity_request = _get_location_services_info(event, mycity_request) if 'attributes' in event['session']: mycity_request.session_attributes = event['session']['attributes'] else: mycity_request.session_attributes = {} if 'intent' in event['request']: mycity_request.intent_name = event['request']['intent']['name'] if 'slots' in event['request']['intent']: mycity_request.intent_variables = \ event['request']['intent']['slots'] else: mycity_request.intent_name = None mycity_request.output_speech = None mycity_request.reprompt_text = None mycity_request.should_end_session = False return mycity_request
def get_alerts_intent( mycity_request: MyCityRequestDataModel, get_alerts_function_for_test: typing.Callable[[], typing.Dict] = None, prune_normal_responses_function_for_test: typing.Callable[[], typing.Dict] = None, alerts_to_speech_output_function_for_test: typing.Callable[[], typing.AnyStr] = None ) -> MyCityResponseDataModel: """ Generate response object with information about citywide alerts :param mycity_request: MyCityRequestDataModel object :param get_alerts_function_for_test: Injectable function for unit tests :param prune_normal_responses_function_for_test: Injectable function for unit tests :param alerts_to_speech_output_function_for_test: Injectable function for unit tests :return: MyCityResponseDataModel object """ logger.debug('MyCityRequestDataModel received:' + mycity_request.get_logger_string()) # get the intent_variables and sessions_attribute object from the request intent_variables = mycity_request.intent_variables session_attributes = mycity_request.session_attributes service_name = intent_variables['ServiceName'].get('value') \ if 'ServiceName' in intent_variables else None if service_name: service_name = service_name.lower() session_alerts = session_attributes.get('alerts', None) if not session_alerts: session_alerts = get_pruned_alerts( get_alerts_function_for_test, prune_normal_responses_function_for_test) # Build the response. mycity_response = _create_response_object() mycity_response.session_attributes = session_attributes.copy() mycity_response.should_end_session = True if session_alerts is None: logger.debug( "Could not get alerts from session attributes or Boston webpage") mycity_response.should_end_session = False mycity_response.output_speech = constants.LAUNCH_REPROMPT_SPEECH return mycity_response if service_name is None: # If the user hasn't give us a service name, check if we should # list alerts if there are only a few, or ask the user to select one if len(session_alerts) > 1: mycity_response.session_attributes['alerts'] = session_alerts mycity_response.dialog_directive = "ElicitSlotServiceName" mycity_response.should_end_session = False mycity_response.output_speech = list_alerts_output(session_alerts) else: mycity_response.output_speech = \ alerts_to_speech_output(session_alerts) \ if alerts_to_speech_output_function_for_test is None \ else alerts_to_speech_output_function_for_test(session_alerts) elif service_name == 'all': # Respond with all alert text mycity_response.output_speech = \ alerts_to_speech_output(session_alerts) \ if alerts_to_speech_output_function_for_test is None \ else alerts_to_speech_output_function_for_test(session_alerts) elif service_name in session_alerts: # Grab the requested service alert alert = {service_name: session_alerts[service_name]} mycity_response.output_speech = alerts_to_speech_output(alert) \ if alerts_to_speech_output_function_for_test is None \ else alerts_to_speech_output_function_for_test(alert) else: # Service not found. Re-ask for the desired service. mycity_response.session_attributes['alerts'] = session_alerts mycity_response.should_end_session = False mycity_response.output_speech = constants.INVALID_SERVICE_NAME_SCRIPT mycity_response.output_speech += list_alerts_output(session_alerts) mycity_response.dialog_directive = "ElicitSlotServiceName" return mycity_response