Пример #1
0
def start_chat(userentry):
    try:
        input_statement = Statement(text=userentry,
                                    conversation='training',
                                    persona='user:Amira')
        input_statement.add_tags('Amira')
        response = Bot.get_response(input_statement)
    except IOError:
        return str(IOError)
    return response.text
class StatementIntegrationTestCase(TestCase):
    """
    Test case to make sure that the Django Statement model
    and ChatterBot Statement object have a common interface.
    """

    def setUp(self):
        super(StatementIntegrationTestCase, self).setUp()

        from datetime import datetime
        from pytz import UTC

        now = datetime(2020, 2, 15, 3, 14, 10, 0, UTC)

        self.object = StatementObject(text='_', created_at=now)
        self.model = StatementModel(text='_', created_at=now)

        # Simulate both statements being saved
        self.model.save()
        self.object.id = self.model.id

    def test_text(self):
        self.assertTrue(hasattr(self.object, 'text'))
        self.assertTrue(hasattr(self.model, 'text'))

    def test_in_response_to(self):
        self.assertTrue(hasattr(self.object, 'in_response_to'))
        self.assertTrue(hasattr(self.model, 'in_response_to'))

    def test_conversation(self):
        self.assertTrue(hasattr(self.object, 'conversation'))
        self.assertTrue(hasattr(self.model, 'conversation'))

    def test_tags(self):
        self.assertTrue(hasattr(self.object, 'tags'))
        self.assertTrue(hasattr(self.model, 'tags'))

    def test__str__(self):
        self.assertTrue(hasattr(self.object, '__str__'))
        self.assertTrue(hasattr(self.model, '__str__'))

        self.assertEqual(str(self.object), str(self.model))

    def test_add_tags(self):
        self.object.add_tags('a', 'b')
        self.model.add_tags('a', 'b')

        self.assertIn('a', self.object.get_tags())
        self.assertIn('a', self.model.get_tags())

    def test_serialize(self):
        object_data = self.object.serialize()
        model_data = self.model.serialize()

        self.assertEqual(object_data, model_data)
class StatementIntegrationTestCase(TestCase):
    """
    Test case to make sure that the Django Statement model
    and ChatterBot Statement object have a common interface.
    """

    def setUp(self):
        super().setUp()

        from datetime import datetime
        from pytz import UTC

        now = datetime(2020, 2, 15, 3, 14, 10, 0, UTC)

        self.object = StatementObject(text='_', created_at=now)
        self.model = StatementModel(text='_', created_at=now)

        # Simulate both statements being saved
        self.model.save()
        self.object.id = self.model.id

    def test_text(self):
        self.assertTrue(hasattr(self.object, 'text'))
        self.assertTrue(hasattr(self.model, 'text'))

    def test_in_response_to(self):
        self.assertTrue(hasattr(self.object, 'in_response_to'))
        self.assertTrue(hasattr(self.model, 'in_response_to'))

    def test_conversation(self):
        self.assertTrue(hasattr(self.object, 'conversation'))
        self.assertTrue(hasattr(self.model, 'conversation'))

    def test_tags(self):
        self.assertTrue(hasattr(self.object, 'tags'))
        self.assertTrue(hasattr(self.model, 'tags'))

    def test__str__(self):
        self.assertTrue(hasattr(self.object, '__str__'))
        self.assertTrue(hasattr(self.model, '__str__'))

        self.assertEqual(str(self.object), str(self.model))

    def test_add_tags(self):
        self.object.add_tags('a', 'b')
        self.model.add_tags('a', 'b')

        self.assertIn('a', self.object.get_tags())
        self.assertIn('a', self.model.get_tags())

    def test_serialize(self):
        object_data = self.object.serialize()
        model_data = self.model.serialize()

        self.assertEqual(object_data, model_data)
Пример #4
0
def test_can_process_tags(testbot):
    """It should not process when the adapter is disabled in the request."""
    responses.add(
        responses.GET,
        "https://api.wolframalpha.com/v2/validatequery",
        body=validate_result_success,
        status=200,
    )
    wa = wolframalpha.WolframAlpha(testbot, wolfram_alpha_app_id="abcd")
    statement = Statement("hello")
    statement.add_tags("disable_wolfram")
    assert not wa.can_process(statement)
Пример #5
0
    def train(self, *corpus_paths):
        from chatterbot.corpus import load_corpus, list_corpus_files

        data_file_paths = []

        # Get the paths to each file the bot will be trained with
        for corpus_path in corpus_paths:
            data_file_paths.extend(list_corpus_files(corpus_path))

        for corpus, categories, file_path in load_corpus(*data_file_paths):

            statements_to_create = []

            # Train the chat bot with each statement and response pair
            for conversation_count, conversation in enumerate(corpus):

                if self.show_training_progress:
                    utils.print_progress_bar(
                        'Training ' + str(os.path.basename(file_path)),
                        conversation_count + 1, len(corpus))

                previous_statement_text = None
                previous_statement_search_text = ''

                for text in conversation:

                    statement_search_text = self.stemmer.stem(text)

                    _statement = Statement(
                        text=text,
                        search_text=statement_search_text,
                        in_response_to=previous_statement_text,
                        search_in_response_to=previous_statement_search_text,
                        conversation='training')

                    _statement.add_tags(*categories)

                    statement = self.get_preprocessed_statement(_statement)

                    previous_statement_text = statement.text
                    previous_statement_search_text = statement_search_text

                    statements_to_create.append({
                        'text': statement.text,
                        'in_response_to': statement.in_response_to,
                        'conversation': statement.conversation,
                        'tags': statement.tags
                    })

            self.chatbot.storage.create_many(statements_to_create)
Пример #6
0
    def train(self, *corpus_paths):
        from chatterbot.corpus import load_corpus, list_corpus_files

        data_file_paths = []

        # Get the paths to each file the bot will be trained with
        for corpus_path in corpus_paths:
            data_file_paths.extend(list_corpus_files(corpus_path))

        for corpus, categories, file_path in load_corpus(*data_file_paths):

            statements_to_create = []

            # Train the chat bot with each statement and response pair
            for conversation_count, conversation in enumerate(corpus):

                if self.show_training_progress:
                    utils.print_progress_bar(
                        'Training ' + str(os.path.basename(file_path)),
                        conversation_count + 1,
                        len(corpus)
                    )

                previous_statement_text = None
                previous_statement_search_text = ''

                for text in conversation:

                    statement_search_text = self.stemmer.get_bigram_pair_string(text)

                    statement = Statement(
                        text=text,
                        search_text=statement_search_text,
                        in_response_to=previous_statement_text,
                        search_in_response_to=previous_statement_search_text,
                        conversation='training'
                    )

                    statement.add_tags(*categories)

                    statement = self.get_preprocessed_statement(statement)

                    previous_statement_text = statement.text
                    previous_statement_search_text = statement_search_text

                    statements_to_create.append(statement)

            self.chatbot.storage.create_many(statements_to_create)
Пример #7
0
    def train(self, *corpus_paths):

        # Allow a list of corpora to be passed instead of arguments
        if len(corpus_paths) == 1:
            if isinstance(corpus_paths[0], list):
                corpus_paths = corpus_paths[0]

        # Train the chat bot with each statement and response pair
        for corpus_path in corpus_paths:

            corpora = self.corpus.load_corpus(corpus_path)

            corpus_files = self.corpus.list_corpus_files(corpus_path)
            for corpus_count, corpus in enumerate(corpora):
                for conversation_count, conversation in enumerate(corpus):

                    if self.show_training_progress:
                        utils.print_progress_bar(
                            str(os.path.basename(corpus_files[corpus_count])) +
                            ' Training', conversation_count + 1, len(corpus))

                    previous_statement_text = None

                    for text in conversation:

                        _statement = Statement(
                            text=text,
                            in_response_to=previous_statement_text,
                            conversation='training')

                        _statement.add_tags(*corpus.categories)

                        statement = self.get_preprocessed_statement(_statement)

                        previous_statement_text = statement.text

                        self.chatbot.storage.create(
                            text=statement.text,
                            in_response_to=statement.in_response_to,
                            conversation=statement.conversation,
                            tags=statement.tags)
Пример #8
0
    def process_input(self, statement):
        """
        Process input from the HipChat room.
        """
        new_message = False

        conversation = self.chatbot.storage.filter(
            conversation=statement.conversation,
            order_by=['id']
        )

        response_statement = conversation[-1] if conversation else None

        if response_statement:
            tags = response_statement.get_tags()
            last_message_id = None

            for tag in tags:
                if tag.startswith('hipchat_message_id:'):
                    last_message_id = tag.split('hipchat_message_id:')[-1]
                    break

            if last_message_id:
                self.recent_message_ids.add(last_message_id)

        while not new_message:
            data = self.get_most_recent_message(self.hipchat_room)

            if data and data['id'] not in self.recent_message_ids:
                self.recent_message_ids.add(data['id'])
                new_message = True
            else:
                pass
            sleep(3.5)

        text = data['message']

        statement = Statement(text)
        statement.add_tags('hipchat_message_id' + data['id'])

        return statement
Пример #9
0
    def train(self, *corpus_paths):
        from chatterbot.corpus import load_corpus, list_corpus_files

        data_file_paths = []

        # Get the paths to each file the bot will be trained with
        for corpus_path in corpus_paths:
            data_file_paths.extend(list_corpus_files(corpus_path))

        for corpus, categories, file_path in load_corpus(*data_file_paths):

            statements_to_create = []

            # Train the chat bot with each statement and response pair
            for conversation_count, conversations in enumerate(corpus):

                if self.show_training_progress:
                    utils.print_progress_bar(
                        'Training ' + str(os.path.basename(file_path)),
                        conversation_count + 1, len(corpus))

                previous_statements_texts = [None]
                previous_statements_search_texts = ['']

                for conversation in conversations:

                    if isinstance(conversation, str):
                        conversation = [conversation]

                    statements_texts = []
                    statements_search_texts = []

                    for previous_statement_text, previous_statement_search_text in zip(
                            previous_statements_texts,
                            previous_statements_search_texts):

                        for text in conversation:
                            statement_search_text = self.chatbot.storage.tagger.get_bigram_pair_string(
                                text)

                            statement = Statement(
                                text=text,
                                search_text=statement_search_text,
                                in_response_to=previous_statement_text,
                                search_in_response_to=
                                previous_statement_search_text,
                                conversation='training')

                            statement.add_tags(*categories)

                            statement = self.get_preprocessed_statement(
                                statement)

                            statements_texts.append(statement.text)
                            statements_search_texts.append(
                                statement_search_text)

                            statements_to_create.append(statement)

                    previous_statements_texts = statements_texts
                    previous_statements_search_texts = statements_search_texts

            if statements_to_create:
                self.chatbot.storage.create_many(statements_to_create)
Пример #10
0
class ChatterBotResponseTestCase(ChatBotTestCase):
    def setUp(self):
        super(ChatterBotResponseTestCase, self).setUp()

        self.test_statement = Statement('Hello', in_response_to='Hi')

    def test_empty_database(self):
        """
        If there is no statements in the database, then the
        user's input is the only thing that can be returned.
        """
        response = self.chatbot.get_response('How are you?')

        self.assertEqual('How are you?', response)

    def test_statement_saved_empty_database(self):
        """
        Test that when database is empty, the first
        statement is saved and returned as a response.
        """
        statement_text = 'Wow!'
        response = self.chatbot.get_response(statement_text)

        results = self.chatbot.storage.filter(text=statement_text)

        self.assertIsLength(results, 1)
        self.assertEqual(response, statement_text)

    def test_statement_added_to_conversation(self):
        """
        An input statement should be added to the recent response list.
        """
        statement = Statement(text='Wow!', conversation='test')
        response = self.chatbot.get_response(statement)

        self.assertEqual(statement.text, response)
        self.assertEqual(response.conversation, 'test')

    def test_response_known(self):
        self.chatbot.storage.update(self.test_statement)

        response = self.chatbot.get_response('Hi')

        self.assertEqual(response, self.test_statement.text)

    def test_response_format(self):
        self.chatbot.storage.update(self.test_statement)

        response = self.chatbot.get_response('Hi')
        results = self.chatbot.storage.filter(text=response.text)

        self.assertEqual(response, self.test_statement.text)
        self.assertIsLength(results, 1)
        self.assertEqual(results[0].in_response_to, 'Hi')

    def test_second_response_format(self):
        self.chatbot.storage.update(self.test_statement)

        response = self.chatbot.get_response('Hi')
        self.assertEqual(response.text, 'Hello')

        second_response = self.chatbot.get_response('How are you?')
        results = self.chatbot.storage.filter(text=second_response.text)

        # Make sure that the second response was saved to the database
        self.assertIsLength(self.chatbot.storage.filter(text='How are you?'),
                            1)

        self.assertEqual(second_response, self.test_statement.text)
        self.assertIsLength(results, 1)
        self.assertEqual(results[0].in_response_to, 'Hi')

    def test_get_response_unicode(self):
        """
        Test the case that a unicode string is passed in.
        """
        response = self.chatbot.get_response(u'سلام')
        self.assertGreater(len(response.text), 0)

    def test_get_response_emoji(self):
        """
        Test the case that the input string contains an emoji.
        """
        response = self.chatbot.get_response(u'💩 ')
        self.assertGreater(len(response.text), 0)

    def test_get_response_non_whitespace(self):
        """
        Test the case that a non-whitespace C1 control string is passed in.
        """
        response = self.chatbot.get_response(u'€Ž‘’')
        self.assertGreater(len(response.text), 0)

    def test_get_response_two_byte_characters(self):
        """
        Test the case that a string containing two-byte characters is passed in.
        """
        response = self.chatbot.get_response(u'田中さんにあげて下さい')
        self.assertGreater(len(response.text), 0)

    def test_get_response_corrupted_text(self):
        """
        Test the case that a string contains "corrupted" text.
        """
        response = self.chatbot.get_response(
            u'Ṱ̺̺̕h̼͓̲̦̳̘̲e͇̣̰̦̬͎ ̢̼̻̱̘h͚͎͙̜̣̲ͅi̦̲̣̰̤v̻͍e̺̭̳̪̰-m̢iͅn̖̺̞̲̯̰d̵̼̟͙̩̼̘̳.̨̹͈̣'
        )
        self.assertGreater(len(response.text), 0)

    def test_response_with_tags_added(self):
        """
        If an input statement has tags added to it,
        that data should saved with the input statement.
        """
        self.test_statement.add_tags('test')
        self.chatbot.get_response(self.test_statement)

        results = self.chatbot.storage.filter(text=self.test_statement.text)

        self.assertIsLength(results, 1)
        self.assertIn('test', results[0].get_tags())

    def test_generate_response(self):
        statement = Statement(
            'Many insects adopt a tripedal gait for rapid yet stable walking.')
        response = self.chatbot.generate_response(statement)

        self.assertEqual(response, statement)
        self.assertEqual(response.confidence, 1)

    def test_learn_response(self):
        previous_response = Statement('Define Hemoglobin.')
        statement = Statement(
            'Hemoglobin is an oxygen-transport metalloprotein.')
        self.chatbot.learn_response(statement, previous_response)
        results = self.chatbot.storage.filter(text=statement.text)

        self.assertIsLength(results, 1)

    def test_get_response_does_not_add_new_statement(self):
        """
        Test that a new statement is not learned if `read_only` is set to True.
        """
        self.chatbot.read_only = True
        self.chatbot.get_response('Hi!')
        results = self.chatbot.storage.filter(text='Hi!')

        self.assertIsLength(results, 0)

    def test_get_latest_response_from_zero_responses(self):
        response = self.chatbot.get_latest_response('invalid')

        self.assertIsNone(response)

    def test_get_latest_response_from_one_responses(self):
        self.chatbot.storage.create(text='A', conversation='test')
        self.chatbot.storage.create(text='B',
                                    conversation='test',
                                    in_response_to='A')

        response = self.chatbot.get_latest_response('test')

        self.assertEqual(response.text, 'A')

    def test_get_latest_response_from_two_responses(self):
        self.chatbot.storage.create(text='A', conversation='test')
        self.chatbot.storage.create(text='B',
                                    conversation='test',
                                    in_response_to='A')
        self.chatbot.storage.create(text='C',
                                    conversation='test',
                                    in_response_to='B')

        response = self.chatbot.get_latest_response('test')

        self.assertEqual(response.text, 'B')

    def test_get_latest_response_from_three_responses(self):
        self.chatbot.storage.create(text='A', conversation='test')
        self.chatbot.storage.create(text='B',
                                    conversation='test',
                                    in_response_to='A')
        self.chatbot.storage.create(text='C',
                                    conversation='test',
                                    in_response_to='B')
        self.chatbot.storage.create(text='D',
                                    conversation='test',
                                    in_response_to='C')

        response = self.chatbot.get_latest_response('test')

        self.assertEqual(response.text, 'C')
Пример #11
0
    def get_response(self, statement=None, **kwargs):
        """
        Return the bot's response based on the input.

        :param statement: An statement object or string.
        :returns: A response to the input.
        :rtype: Statement

        :param additional_response_selection_parameters: Parameters to pass to the
            chat bot's logic adapters to control response selection.
        :type additional_response_selection_parameters: dict

        :param persist_values_to_response: Values that should be saved to the response
            that the chat bot generates.
        :type persist_values_to_response: dict
        """
        Statement = self.storage.get_object('statement')

        additional_response_selection_parameters = kwargs.pop(
            'additional_response_selection_parameters', {})

        persist_values_to_response = kwargs.pop('persist_values_to_response',
                                                {})

        if isinstance(statement, str):
            kwargs['text'] = statement

        if isinstance(statement, dict):
            kwargs.update(statement)

        if statement is None and 'text' not in kwargs:
            raise self.ChatBotException(
                'Either a statement object or a "text" keyword '
                'argument is required. Neither was provided.')

        if hasattr(statement, 'serialize'):
            kwargs.update(**statement.serialize())

        tags = kwargs.pop('tags', [])

        text = kwargs.pop('text')

        input_statement = Statement(text=text, **kwargs)

        input_statement.add_tags(*tags)

        # Preprocess the input statement
        for preprocessor in self.preprocessors:
            input_statement = preprocessor(input_statement)

        # Make sure the input statement has its search text saved

        if not input_statement.search_text:
            input_statement.search_text = self.storage.tagger.get_bigram_pair_string(
                input_statement.text)

        if not input_statement.search_in_response_to and input_statement.in_response_to:
            input_statement.search_in_response_to = self.storage.tagger.get_bigram_pair_string(
                input_statement.in_response_to)

        response = self.generate_response(
            input_statement, additional_response_selection_parameters)

        # Update any response data that needs to be changed
        if persist_values_to_response:
            for response_key in persist_values_to_response:
                response_value = persist_values_to_response[response_key]
                if response_key == 'tags':
                    input_statement.add_tags(*response_value)
                    response.add_tags(*response_value)
                else:
                    setattr(input_statement, response_key, response_value)
                    setattr(response, response_key, response_value)

        if not self.read_only:
            self.learn_response(input_statement)

            # Save the response generated for the input
            self.storage.create(**response.serialize())

        return response
Пример #12
0
    def train(self, *corpus_paths):
        from chatterbot.corpus import load_corpus, list_corpus_files

        data_file_paths = []

        # Get the paths to each file the bot will be trained with
        for corpus_path in corpus_paths:
            data_file_paths.extend(list_corpus_files(corpus_path))

        for corpus, categories, file_path in load_corpus(*data_file_paths):

            statements_to_create = []

            # Train the chat bot with each statement and response pair
            for conversation_count, conversation in enumerate(corpus):

                if self.show_training_progress:
                    utils.print_progress_bar(
                        'Training ' + str(os.path.basename(file_path)),
                        conversation_count + 1, len(corpus))

                previous_statement_text = None
                previous_statement_search_text = ''

                for text in conversation:
                    suggestion_tags = []
                    if text.strip('.?!/;:\'\"') in constants.AFFIRMATIVES:
                        text = 'AFF'
                    elif text.strip('.?!/;:\'\"') in constants.NEGATIVES:
                        text = 'NEG'
                    elif text[0] is '^':
                        (suggestion, text) = text.split(maxsplit=1)
                        suggestion = suggestion[1:]
                        if not suggestion.find('/'):
                            suggestion_tags.append(suggestion)
                        else:
                            for suggestion in suggestion.split('/'):
                                suggestion_tags.append(suggestion)

                    statement_search_text = self.chatbot.storage.tagger.get_bigram_pair_string(
                        text)

                    statement = Statement(
                        text=text,
                        search_text=statement_search_text,
                        in_response_to=previous_statement_text,
                        search_in_response_to=previous_statement_search_text,
                        conversation='training')

                    # YesNoLogicAdapter deals with responses to AFF/NEG via statement tags.
                    # No need for statements in_response_to = AFF/NEG   In fact, it was causing
                    # erroneous responses
                    if statement.in_response_to in ['AFF', 'NEG']:
                        statement.in_response_to = None
                        statement.search_in_response_to = None

                    statement.add_tags(*categories)

                    if suggestion_tags:
                        for suggestion in suggestion_tags:
                            statement.add_tags('SUGGESTION:' + suggestion)

                    if previous_statement_text:
                        if previous_statement_text == 'AFF':
                            statements_to_create[-2].add_tags('AFF:' +
                                                              statement.text)
                        elif previous_statement_text == 'NEG':
                            statements_to_create[-2].add_tags('NEG:' +
                                                              statement.text)

                    statement = self.get_preprocessed_statement(statement)
                    previous_statement_text = statement.text
                    previous_statement_search_text = statement_search_text

                    statements_to_create.append(statement)

            # Using update() because create_many() makes duplicate statements. AFF/NEG tag data was lost on some.
            for stmnts in statements_to_create:
                self.chatbot.storage.update(stmnts)
Пример #13
0
class ChatterBotResponseTestCase(ChatBotTestCase):

    def setUp(self):
        super(ChatterBotResponseTestCase, self).setUp()

        self.test_statement = Statement(text='Hello', in_response_to='Hi')

    def test_empty_database(self):
        """
        If there is no statements in the database, then the
        user's input is the only thing that can be returned.
        """
        response = self.chatbot.get_response('How are you?')

        self.assertEqual('How are you?', response)

    def test_statement_saved_empty_database(self):
        """
        Test that when database is empty, the first
        statement is saved and returned as a response.
        """
        statement_text = 'Wow!'
        response = self.chatbot.get_response(statement_text)

        results = self.chatbot.storage.filter(text=statement_text)

        self.assertIsLength(results, 1)
        self.assertEqual(response, statement_text)

    def test_statement_added_to_conversation(self):
        """
        An input statement should be added to the recent response list.
        """
        statement = Statement(text='Wow!', conversation='test')
        response = self.chatbot.get_response(statement)

        self.assertEqual(statement.text, response)
        self.assertEqual(response.conversation, 'test')

    def test_response_known(self):
        self.chatbot.storage.update(self.test_statement)

        response = self.chatbot.get_response('Hi')

        self.assertEqual(response, self.test_statement.text)

    def test_response_format(self):
        self.chatbot.storage.update(self.test_statement)

        response = self.chatbot.get_response('Hi')
        results = self.chatbot.storage.filter(text=response.text)

        self.assertEqual(response, self.test_statement.text)
        self.assertIsLength(results, 1)
        self.assertEqual(results[0].in_response_to, 'Hi')

    def test_second_response_format(self):
        self.chatbot.storage.update(self.test_statement)

        response = self.chatbot.get_response('Hi')
        self.assertEqual(response.text, 'Hello')

        second_response = self.chatbot.get_response('How are you?')
        results = self.chatbot.storage.filter(text=second_response.text)

        # Make sure that the second response was saved to the database
        self.assertIsLength(self.chatbot.storage.filter(text='How are you?'), 1)

        self.assertEqual(second_response, self.test_statement.text)
        self.assertIsLength(results, 1)
        self.assertEqual(results[0].in_response_to, 'Hi')

    def test_get_response_unicode(self):
        """
        Test the case that a unicode string is passed in.
        """
        response = self.chatbot.get_response(u'سلام')
        self.assertGreater(len(response.text), 0)

    def test_get_response_emoji(self):
        """
        Test the case that the input string contains an emoji.
        """
        response = self.chatbot.get_response(u'💩 ')
        self.assertGreater(len(response.text), 0)

    def test_get_response_non_whitespace(self):
        """
        Test the case that a non-whitespace C1 control string is passed in.
        """
        response = self.chatbot.get_response(u'€Ž‘’')
        self.assertGreater(len(response.text), 0)

    def test_get_response_two_byte_characters(self):
        """
        Test the case that a string containing two-byte characters is passed in.
        """
        response = self.chatbot.get_response(u'田中さんにあげて下さい')
        self.assertGreater(len(response.text), 0)

    def test_get_response_corrupted_text(self):
        """
        Test the case that a string contains "corrupted" text.
        """
        response = self.chatbot.get_response(u'Ṱ̺̺̕h̼͓̲̦̳̘̲e͇̣̰̦̬͎ ̢̼̻̱̘h͚͎͙̜̣̲ͅi̦̲̣̰̤v̻͍e̺̭̳̪̰-m̢iͅn̖̺̞̲̯̰d̵̼̟͙̩̼̘̳.̨̹͈̣')
        self.assertGreater(len(response.text), 0)

    def test_response_with_tags_added(self):
        """
        If an input statement has tags added to it,
        that data should saved with the input statement.
        """
        self.test_statement.add_tags('test')
        self.chatbot.get_response(
            self.test_statement
        )

        results = self.chatbot.storage.filter(text=self.test_statement.text)

        self.assertIsLength(results, 1)
        self.assertIn('test', results[0].get_tags())

    def test_get_response_with_text_and_kwargs(self):
        self.chatbot.get_response('Hello', conversation='greetings')

        results = self.chatbot.storage.filter(text='Hello')

        self.assertIsLength(results, 1)
        self.assertEqual(results[0].conversation, 'greetings')

    def test_get_response_missing_text(self):
        with self.assertRaises(self.chatbot.ChatBotException):
            self.chatbot.get_response()

    def test_get_response_missing_text_with_conversation(self):
        with self.assertRaises(self.chatbot.ChatBotException):
            self.chatbot.get_response(conversation='test')

    def test_generate_response(self):
        statement = Statement(text='Many insects adopt a tripedal gait for rapid yet stable walking.')
        response = self.chatbot.generate_response(statement)

        self.assertEqual(response, statement)
        self.assertEqual(response.confidence, 1)

    def test_learn_response(self):
        previous_response = Statement(text='Define Hemoglobin.')
        statement = Statement(text='Hemoglobin is an oxygen-transport metalloprotein.')
        self.chatbot.learn_response(statement, previous_response)
        results = self.chatbot.storage.filter(text=statement.text)

        self.assertIsLength(results, 1)

    def test_get_response_does_not_add_new_statement(self):
        """
        Test that a new statement is not learned if `read_only` is set to True.
        """
        self.chatbot.read_only = True
        self.chatbot.get_response('Hi!')
        results = self.chatbot.storage.filter(text='Hi!')

        self.assertIsLength(results, 0)

    def test_get_latest_response_from_zero_responses(self):
        response = self.chatbot.get_latest_response('invalid')

        self.assertIsNone(response)

    def test_get_latest_response_from_one_responses(self):
        self.chatbot.storage.create(text='A', conversation='test')
        self.chatbot.storage.create(text='B', conversation='test', in_response_to='A')

        response = self.chatbot.get_latest_response('test')

        self.assertEqual(response.text, 'A')

    def test_get_latest_response_from_two_responses(self):
        self.chatbot.storage.create(text='A', conversation='test')
        self.chatbot.storage.create(text='B', conversation='test', in_response_to='A')
        self.chatbot.storage.create(text='C', conversation='test', in_response_to='B')

        response = self.chatbot.get_latest_response('test')

        self.assertEqual(response.text, 'B')

    def test_get_latest_response_from_three_responses(self):
        self.chatbot.storage.create(text='A', conversation='test')
        self.chatbot.storage.create(text='B', conversation='test', in_response_to='A')
        self.chatbot.storage.create(text='C', conversation='test', in_response_to='B')
        self.chatbot.storage.create(text='D', conversation='test', in_response_to='C')

        response = self.chatbot.get_latest_response('test')

        self.assertEqual(response.text, 'C')