bots: Improve response at 3 wrong answers in trivia bot.
This commit is contained in:
parent
f25772d1dc
commit
b28cfcac3d
|
@ -2,12 +2,13 @@ import json
|
||||||
import html
|
import html
|
||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from typing import Optional
|
from typing import Optional, Tuple, Any, Dict
|
||||||
|
|
||||||
from zulip_bots.test_lib import (
|
from zulip_bots.test_lib import (
|
||||||
BotTestCase,
|
BotTestCase,
|
||||||
DefaultTests,
|
DefaultTests,
|
||||||
read_bot_fixture_data,
|
read_bot_fixture_data,
|
||||||
|
StubBotHandler,
|
||||||
)
|
)
|
||||||
|
|
||||||
from zulip_bots.request_test_lib import (
|
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 (
|
from zulip_bots.bots.trivia_quiz.trivia_quiz import (
|
||||||
get_quiz_from_payload,
|
get_quiz_from_payload,
|
||||||
fix_quotes,
|
fix_quotes,
|
||||||
|
get_quiz_from_id,
|
||||||
|
update_quiz,
|
||||||
|
handle_answer,
|
||||||
)
|
)
|
||||||
|
|
||||||
class TestTriviaQuizBot(BotTestCase, DefaultTests):
|
class TestTriviaQuizBot(BotTestCase, DefaultTests):
|
||||||
|
@ -29,6 +33,13 @@ class TestTriviaQuizBot(BotTestCase, DefaultTests):
|
||||||
'* **D** Mammals\n' + \
|
'* **D** Mammals\n' + \
|
||||||
'**reply**: answer Q001 <letter>'
|
'**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:
|
def _test(self, message: str, response: str, fixture: Optional[str]=None) -> None:
|
||||||
if fixture:
|
if fixture:
|
||||||
with self.mock_http_conversation(fixture):
|
with self.mock_http_conversation(fixture):
|
||||||
|
@ -74,9 +85,12 @@ class TestTriviaQuizBot(BotTestCase, DefaultTests):
|
||||||
with patch('random.shuffle'):
|
with patch('random.shuffle'):
|
||||||
quiz = get_quiz_from_payload(quiz_payload)
|
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['question'], 'Which class of animals are newts members of?')
|
||||||
self.assertEqual(quiz['correct_letter'], 'A')
|
self.assertEqual(quiz['correct_letter'], 'A')
|
||||||
self.assertEqual(quiz['answers']['D'], 'Mammals')
|
self.assertEqual(quiz['answers']['D'], 'Mammals')
|
||||||
|
self.assertEqual(quiz['answered_options'], [])
|
||||||
|
self.assertEqual(quiz['pending'], True)
|
||||||
|
|
||||||
# test incorrect answer
|
# test incorrect answer
|
||||||
with patch('zulip_bots.bots.trivia_quiz.trivia_quiz.get_quiz_from_id',
|
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)):
|
return_value=json.dumps(quiz)):
|
||||||
with patch('zulip_bots.bots.trivia_quiz.trivia_quiz.start_new_quiz') as mock_new_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:')
|
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)
|
bot_handler.send_reply(message, bot_response)
|
||||||
return
|
return
|
||||||
quiz = json.loads(quiz_payload)
|
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)
|
bot_handler.send_reply(message, bot_response)
|
||||||
if correct:
|
if start_new_question:
|
||||||
start_new_quiz(message, bot_handler)
|
start_new_quiz(message, bot_handler)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
@ -125,6 +125,8 @@ def get_quiz_from_payload(payload: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
quiz = dict(
|
quiz = dict(
|
||||||
question=fix_quotes(question),
|
question=fix_quotes(question),
|
||||||
answers=answers,
|
answers=answers,
|
||||||
|
answered_options=[],
|
||||||
|
pending=True,
|
||||||
correct_letter=correct_letter,
|
correct_letter=correct_letter,
|
||||||
)
|
)
|
||||||
return quiz
|
return quiz
|
||||||
|
@ -196,16 +198,39 @@ Q: {question}
|
||||||
)
|
)
|
||||||
return content
|
return content
|
||||||
|
|
||||||
def grade_question(quiz: Dict[str, Any], answer: str) -> Tuple[bool, str]:
|
def update_quiz(quiz: Dict[str, Any], quiz_id: str, bot_handler: Any) -> None:
|
||||||
correct = (answer == quiz['correct_letter'])
|
bot_handler.storage.put(quiz_id, json.dumps(quiz))
|
||||||
|
|
||||||
if correct:
|
def build_response(is_correct: bool, num_answers: int) -> str:
|
||||||
long_answer = quiz['answers'][answer]
|
if is_correct:
|
||||||
response = '**CORRECT!** {long_answer} :tada:'.format(long_answer=long_answer)
|
response = '**CORRECT!** {answer} :tada:'
|
||||||
return correct, response
|
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)
|
def handle_answer(quiz: Dict[str, Any], option: str, quiz_id: str,
|
||||||
return correct, response
|
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
|
handler_class = TriviaQuizHandler
|
||||||
|
|
Loading…
Reference in a new issue