bots: Improve response at 3 wrong answers in trivia bot.
This commit is contained in:
		
							parent
							
								
									f25772d1dc
								
							
						
					
					
						commit
						b28cfcac3d
					
				
					 2 changed files with 103 additions and 11 deletions
				
			
		| 
						 | 
				
			
			@ -2,12 +2,13 @@ import json
 | 
			
		|||
import html
 | 
			
		||||
 | 
			
		||||
from unittest.mock import patch
 | 
			
		||||
from typing import Optional
 | 
			
		||||
from typing import Optional, Tuple, Any, Dict
 | 
			
		||||
 | 
			
		||||
from zulip_bots.test_lib import (
 | 
			
		||||
    BotTestCase,
 | 
			
		||||
    DefaultTests,
 | 
			
		||||
    read_bot_fixture_data,
 | 
			
		||||
    StubBotHandler,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from zulip_bots.request_test_lib import (
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +18,9 @@ from zulip_bots.request_test_lib import (
 | 
			
		|||
from zulip_bots.bots.trivia_quiz.trivia_quiz import (
 | 
			
		||||
    get_quiz_from_payload,
 | 
			
		||||
    fix_quotes,
 | 
			
		||||
    get_quiz_from_id,
 | 
			
		||||
    update_quiz,
 | 
			
		||||
    handle_answer,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
class TestTriviaQuizBot(BotTestCase, DefaultTests):
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +33,13 @@ class TestTriviaQuizBot(BotTestCase, DefaultTests):
 | 
			
		|||
        '* **D** Mammals\n' + \
 | 
			
		||||
        '**reply**: answer Q001 <letter>'
 | 
			
		||||
 | 
			
		||||
    def get_test_quiz(self) -> Tuple[Dict[str, Any], Any]:
 | 
			
		||||
        bot_handler = StubBotHandler()
 | 
			
		||||
        quiz_payload = read_bot_fixture_data('trivia_quiz', 'test_new_question')['response']
 | 
			
		||||
        with patch('random.shuffle'):
 | 
			
		||||
            quiz = get_quiz_from_payload(quiz_payload)
 | 
			
		||||
        return quiz, bot_handler
 | 
			
		||||
 | 
			
		||||
    def _test(self, message: str, response: str, fixture: Optional[str]=None) -> None:
 | 
			
		||||
        if fixture:
 | 
			
		||||
            with self.mock_http_conversation(fixture):
 | 
			
		||||
| 
						 | 
				
			
			@ -74,9 +85,12 @@ class TestTriviaQuizBot(BotTestCase, DefaultTests):
 | 
			
		|||
        with patch('random.shuffle'):
 | 
			
		||||
            quiz = get_quiz_from_payload(quiz_payload)
 | 
			
		||||
 | 
			
		||||
        # Test initial storage
 | 
			
		||||
        self.assertEqual(quiz['question'], 'Which class of animals are newts members of?')
 | 
			
		||||
        self.assertEqual(quiz['correct_letter'], 'A')
 | 
			
		||||
        self.assertEqual(quiz['answers']['D'], 'Mammals')
 | 
			
		||||
        self.assertEqual(quiz['answered_options'], [])
 | 
			
		||||
        self.assertEqual(quiz['pending'], True)
 | 
			
		||||
 | 
			
		||||
        # test incorrect answer
 | 
			
		||||
        with patch('zulip_bots.bots.trivia_quiz.trivia_quiz.get_quiz_from_id',
 | 
			
		||||
| 
						 | 
				
			
			@ -88,3 +102,56 @@ class TestTriviaQuizBot(BotTestCase, DefaultTests):
 | 
			
		|||
                   return_value=json.dumps(quiz)):
 | 
			
		||||
            with patch('zulip_bots.bots.trivia_quiz.trivia_quiz.start_new_quiz') as mock_new_quiz:
 | 
			
		||||
                self._test('answer Q001 A', '**CORRECT!** Amphibian :tada:')
 | 
			
		||||
 | 
			
		||||
    def test_update_quiz(self) -> None:
 | 
			
		||||
        quiz, bot_handler = self.get_test_quiz()
 | 
			
		||||
        update_quiz(quiz, 'Q001', bot_handler)
 | 
			
		||||
        test_quiz = json.loads(bot_handler.storage.get('Q001'))
 | 
			
		||||
        self.assertEqual(test_quiz, quiz)
 | 
			
		||||
 | 
			
		||||
    def test_get_quiz_from_id(self) -> None:
 | 
			
		||||
        quiz, bot_handler = self.get_test_quiz()
 | 
			
		||||
        bot_handler.storage.put('Q001', quiz)
 | 
			
		||||
        self.assertEqual(get_quiz_from_id('Q001', bot_handler), quiz)
 | 
			
		||||
 | 
			
		||||
    def test_handle_answer(self) -> None:
 | 
			
		||||
        quiz, bot_handler = self.get_test_quiz()
 | 
			
		||||
        # create test initial storage
 | 
			
		||||
        update_quiz(quiz, 'Q001', bot_handler)
 | 
			
		||||
 | 
			
		||||
        # test for a correct answer
 | 
			
		||||
        start_new_question, response = handle_answer(quiz, 'A', 'Q001', bot_handler)
 | 
			
		||||
        self.assertTrue(start_new_question)
 | 
			
		||||
        self.assertEqual(response, '**CORRECT!** Amphibian :tada:')
 | 
			
		||||
 | 
			
		||||
        # test for an incorrect answer
 | 
			
		||||
        start_new_question, response = handle_answer(quiz, 'D', 'Q001', bot_handler)
 | 
			
		||||
        self.assertFalse(start_new_question)
 | 
			
		||||
        self.assertEqual(response, '**WRONG!** D is not correct :disappointed:')
 | 
			
		||||
 | 
			
		||||
    def test_handle_answer_three_failed_attempts(self) -> None:
 | 
			
		||||
        quiz, bot_handler = self.get_test_quiz()
 | 
			
		||||
        # create test storage for a question which has been incorrectly answered twice
 | 
			
		||||
        quiz['answered_options'] = ['C', 'B']
 | 
			
		||||
        update_quiz(quiz, 'Q001', bot_handler)
 | 
			
		||||
 | 
			
		||||
        # test response  and storage after three failed attempts
 | 
			
		||||
        start_new_question, response = handle_answer(quiz, 'D', 'Q001', bot_handler)
 | 
			
		||||
        self.assertEqual(response, '**WRONG!** :disappointed: The correct answer is Amphibian.')
 | 
			
		||||
        self.assertTrue(start_new_question)
 | 
			
		||||
        quiz_reset = json.loads(bot_handler.storage.get('Q001'))
 | 
			
		||||
        self.assertEqual(quiz_reset['pending'], False)
 | 
			
		||||
 | 
			
		||||
        # test response after question has ended
 | 
			
		||||
        incorrect_answers = ['B', 'C', 'D']
 | 
			
		||||
        for ans in incorrect_answers:
 | 
			
		||||
            start_new_question, response = handle_answer(quiz, ans, 'Q001', bot_handler)
 | 
			
		||||
            self.assertEqual(response, '**WRONG!** :disappointed: The correct answer is Amphibian.')
 | 
			
		||||
            self.assertFalse(start_new_question)
 | 
			
		||||
        start_new_question, response = handle_answer(quiz, 'A', 'Q001', bot_handler)
 | 
			
		||||
        self.assertEqual(response, '**CORRECT!** Amphibian :tada:')
 | 
			
		||||
        self.assertFalse(start_new_question)
 | 
			
		||||
 | 
			
		||||
        # test storage after question has ended
 | 
			
		||||
        quiz_reset = json.loads(bot_handler.storage.get('Q001'))
 | 
			
		||||
        self.assertEqual(quiz_reset['pending'], False)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,9 +43,9 @@ class TriviaQuizHandler:
 | 
			
		|||
                bot_handler.send_reply(message, bot_response)
 | 
			
		||||
                return
 | 
			
		||||
            quiz = json.loads(quiz_payload)
 | 
			
		||||
            correct, bot_response = grade_question(quiz, answer)
 | 
			
		||||
            start_new_question, bot_response = handle_answer(quiz, answer, quiz_id, bot_handler)
 | 
			
		||||
            bot_handler.send_reply(message, bot_response)
 | 
			
		||||
            if correct:
 | 
			
		||||
            if start_new_question:
 | 
			
		||||
                start_new_quiz(message, bot_handler)
 | 
			
		||||
            return
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -125,6 +125,8 @@ def get_quiz_from_payload(payload: Dict[str, Any]) -> Dict[str, Any]:
 | 
			
		|||
    quiz = dict(
 | 
			
		||||
        question=fix_quotes(question),
 | 
			
		||||
        answers=answers,
 | 
			
		||||
        answered_options=[],
 | 
			
		||||
        pending=True,
 | 
			
		||||
        correct_letter=correct_letter,
 | 
			
		||||
    )
 | 
			
		||||
    return quiz
 | 
			
		||||
| 
						 | 
				
			
			@ -196,16 +198,39 @@ Q: {question}
 | 
			
		|||
    )
 | 
			
		||||
    return content
 | 
			
		||||
 | 
			
		||||
def grade_question(quiz: Dict[str, Any], answer: str) -> Tuple[bool, str]:
 | 
			
		||||
    correct = (answer == quiz['correct_letter'])
 | 
			
		||||
def update_quiz(quiz: Dict[str, Any], quiz_id: str, bot_handler: Any) -> None:
 | 
			
		||||
    bot_handler.storage.put(quiz_id, json.dumps(quiz))
 | 
			
		||||
 | 
			
		||||
    if correct:
 | 
			
		||||
        long_answer = quiz['answers'][answer]
 | 
			
		||||
        response = '**CORRECT!** {long_answer} :tada:'.format(long_answer=long_answer)
 | 
			
		||||
        return correct, response
 | 
			
		||||
def build_response(is_correct: bool, num_answers: int) -> str:
 | 
			
		||||
    if is_correct:
 | 
			
		||||
        response = '**CORRECT!** {answer} :tada:'
 | 
			
		||||
    else:
 | 
			
		||||
        if num_answers >= 3:
 | 
			
		||||
            response = '**WRONG!** :disappointed: The correct answer is {answer}.'
 | 
			
		||||
        else:
 | 
			
		||||
            response = '**WRONG!** {option} is not correct :disappointed:'
 | 
			
		||||
    return response
 | 
			
		||||
 | 
			
		||||
    response = '**WRONG!** {answer} is not correct :disappointed:'.format(answer=answer)
 | 
			
		||||
    return correct, response
 | 
			
		||||
def handle_answer(quiz: Dict[str, Any], option: str, quiz_id: str,
 | 
			
		||||
                  bot_handler: Any) -> Tuple[bool, str]:
 | 
			
		||||
    answer = quiz['answers'][quiz['correct_letter']]
 | 
			
		||||
    is_new_answer = (option not in quiz['answered_options'])
 | 
			
		||||
    if is_new_answer:
 | 
			
		||||
        quiz['answered_options'].append(option)
 | 
			
		||||
 | 
			
		||||
    num_answers = len(quiz['answered_options'])
 | 
			
		||||
    is_correct = (option == quiz['correct_letter'])
 | 
			
		||||
 | 
			
		||||
    start_new_question = quiz['pending'] and (is_correct or num_answers >= 3)
 | 
			
		||||
    if start_new_question or is_correct:
 | 
			
		||||
        quiz['pending'] = False
 | 
			
		||||
 | 
			
		||||
    if is_new_answer or start_new_question:
 | 
			
		||||
        update_quiz(quiz, quiz_id, bot_handler)
 | 
			
		||||
 | 
			
		||||
    response = build_response(is_correct, num_answers).format(
 | 
			
		||||
        option=option, answer=answer, id=quiz_id)
 | 
			
		||||
    return start_new_question, response
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
handler_class = TriviaQuizHandler
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue