def test_failed_and_not_abandoned(self): profile = UserProfile(validated=True) event = FailedPrompt( phone_number="123456789", user_profile=profile, prompt=DRILL.prompts[2], drill_instance_id=uuid.uuid4(), response="b", abandoned=False, ) dialog_state = DialogState( "123456789", seq="0", user_profile=profile, current_drill=DRILL, drill_instance_id=event.drill_instance_id, current_prompt_state=PromptState(DRILL.prompts[2].slug, start_time=NOW), ) event.apply_to(dialog_state) self.assertEqual( PromptState( slug=DRILL.prompts[2].slug, start_time=NOW, last_response_time=event.created_time, failures=1, ), dialog_state.current_prompt_state, )
def test_idempotence(self): user_id = self._make_user_and_get_id() event = DrillStarted( phone_number=self.phone_number, user_profile=UserProfile(True), drill=self.drill, first_prompt=self.prompt, ) batch1 = self._make_batch([event]) self.repo.update_user(batch1) user = self.repo.get_user(user_id) self.assertEqual(event.created_time, user.last_interacted_time) event2 = FailedPrompt( phone_number=self.phone_number, user_profile=UserProfile(True), prompt=self.prompt, drill_instance_id=event.drill_instance_id, response="go", abandoned=True, ) batch2 = self._make_batch([event2]) batch2.seq = batch1.seq self.repo.update_user(batch2) user = self.repo.get_user(user_id) self.assertEqual(event.created_time, user.last_interacted_time)
def test_failed_prompt(self): original = FailedPrompt( phone_number="123456789", user_profile=UserProfile(True), prompt=self.prompt, response="hello", abandoned=True, drill_instance_id=uuid.uuid4(), ) serialized = original.to_dict() deserialized: FailedPrompt = event_from_dict( serialized) # type: ignore self._make_base_assertions(original, deserialized) self.assertEqual(original.prompt.slug, deserialized.prompt.slug) for original_message, deserialized_message in zip( original.prompt.messages, deserialized.prompt.messages): self.assertEqual(original_message.text, deserialized_message.text) self.assertEqual(original_message.media_url, deserialized_message.media_url) self.assertEqual(original.response, deserialized.response) self.assertEqual(original.abandoned, deserialized.abandoned) self.assertEqual(original.drill_instance_id, deserialized.drill_instance_id)
def _check_response( self, dialog_state: DialogState, base_args: Dict[str, Any] ) -> Optional[List[stopcovid.dialog.models.events.DialogEvent]]: prompt = dialog_state.get_prompt() if prompt is None: return events = [] if prompt.should_advance_with_answer( self.content_lower, dialog_state.user_profile.language): events.append( CompletedPrompt( prompt=prompt, drill_instance_id=dialog_state. drill_instance_id, # type: ignore response=self.content, **base_args, )) should_advance = True else: should_advance = dialog_state.current_prompt_state.failures >= prompt.max_failures events.append( FailedPrompt( prompt=prompt, response=self.content, drill_instance_id=dialog_state. drill_instance_id, # type: ignore abandoned=should_advance, **base_args, )) if should_advance: next_prompt = dialog_state.get_next_prompt() if next_prompt is not None: events.append( AdvancedToNextPrompt( prompt=next_prompt, drill_instance_id=dialog_state. drill_instance_id, # type: ignore **base_args, )) if dialog_state.is_next_prompt_last(): # assume the last prompt doesn't wait for an answer events.append( DrillCompleted( drill_instance_id=dialog_state. drill_instance_id, # type: ignore **base_args, )) return events
def test_prompt_failed(self): self.repo._save_drill_instance(self.drill_instance) event = FailedPrompt( phone_number=self.phone_number, user_profile=UserProfile(True), prompt=self.prompt1, drill_instance_id=self.drill_instance.drill_instance_id, response="go", abandoned=False, ) self.repo.update_user(self._make_batch([event])) retrieved = self.repo.get_drill_instance( self.drill_instance.drill_instance_id) self.assertEqual(event.created_time, retrieved.current_prompt_last_response_time)
def test_non_abandoned_failed_prompt_event(self): dialog_events: List[DialogEvent] = [ FailedPrompt( phone_number=self.phone, user_profile=self.validated_user_profile, prompt=self.drill.prompts[1], response="a", drill_instance_id=uuid.uuid4(), abandoned=False, ) ] outbound_messages = get_outbound_sms_commands(dialog_events) self.assertEqual(len(outbound_messages), 1) message = outbound_messages[0] self.assertEqual(message.phone_number, self.phone) self.assertEqual(message.event_id, dialog_events[0].event_id) self.assertEqual(message.body, "🤖 Sorry, not correct. Try again one more time.")
def test_abandoned_failed_prompt_event(self): dialog_events: List[DialogEvent] = [ FailedPrompt( self.phone, self.validated_user_profile, prompt=self.drill.prompts[1], response="a", drill_instance_id=uuid.uuid4(), abandoned=True, ) ] outbound_messages = get_outbound_sms_commands(dialog_events) self.assertEqual(len(outbound_messages), 1) message = outbound_messages[0] self.assertEqual(message.phone_number, self.phone) self.assertEqual(message.event_id, dialog_events[0].event_id) self.assertEqual( message.body, "🤖 The correct answer is *a*.\n\nLets move to the next one.")
def _check_response( self, dialog_state: DialogState, base_args: Dict[str, Any]) -> Optional[List[DialogEvent]]: prompt = dialog_state.get_prompt() if prompt is None: return None events: List[DialogEvent] = [] if prompt.should_advance_with_answer(self.content_lower): user_profile_updates = None if prompt.response_user_profile_key: user_profile_updates = { prompt.response_user_profile_key: self.content } events.append( CompletedPrompt( prompt=prompt, drill_instance_id=dialog_state.drill_instance_id, response=self.content, user_profile_updates=user_profile_updates, **base_args, )) should_advance = True else: assert dialog_state.current_prompt_state should_advance = dialog_state.current_prompt_state.failures >= ( prompt.max_failures or 1) events.append( FailedPrompt( prompt=prompt, response=self.content or None, drill_instance_id=dialog_state.drill_instance_id, abandoned=should_advance, **base_args, )) if should_advance: assert dialog_state.current_drill next_prompt = dialog_state.get_next_prompt() if next_prompt is not None: events.append( AdvancedToNextPrompt( prompt=next_prompt, drill_instance_id=dialog_state.drill_instance_id, **base_args, )) if dialog_state.is_next_prompt_last(): # assume the last prompt doesn't wait for an answer events.append( DrillCompleted( drill_instance_id=dialog_state.drill_instance_id, **base_args, )) elif len(dialog_state.current_drill.prompts) == 1: events.append( DrillCompleted( drill_instance_id=dialog_state.drill_instance_id, last_prompt_response=self.content or None, **base_args, )) return events