Skip to content

Commit

Permalink
Reorganized matching and selecting methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
gunthercox committed Apr 9, 2015
1 parent 32bd597 commit cad01b9
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 94 deletions.
25 changes: 8 additions & 17 deletions chatterbot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,14 @@ def get_response_data(self, user_name, input_text):
* bot: The statement's meta data
"""
from chatterbot.algorithms.engram import engram
from chatterbot.matching import closest

# Check if a name was mentioned
if self.name in input_text:
pass
# Use the closest known matching statement
closest_statement = closest(input_text, self.database)
response_statement = engram(closest_statement, self.database)
self.last_statements.append(response_statement)

bot = {}
statement_text = list(self.get_last_statement().keys())[0]

user = {
input_text: {
Expand All @@ -105,22 +107,11 @@ def get_response_data(self, user_name, input_text):
}
}

# If logging is enabled, add the user's input to the database before selecting a response.
# Add the input to the database before selecting a response if logging is enabled
if self.log:
self.update_log(user)

self.last_statements.append(engram(input_text, self.database))
statement_text = list(self.get_last_statement().keys())[0]

if self.log:
values = self.database[input_text]
if not "in_response_to" in values:
values["in_response_to"] = []
if not statement_text in values["in_response_to"]:
values["in_response_to"].append(statement_text)
self.database[input_text] = values

return {"user": user, "bot": statement_text}
return {user_name: user, "bot": statement_text}

def get_response(self, input_text, user_name="user"):
"""
Expand Down
29 changes: 13 additions & 16 deletions chatterbot/algorithms/engram.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
from chatterbot.algorithms.matching import closest


def engram(text, database):
def engram(closest_statement, database):
"""
Returns the statement after the closest matchng statement in
the conversation.
Returns a statement in response to the closest matching statement in
the database. For each match, the statement with the greatest number
of occurrence will be returned.
"""
import os
import random

# Initialize the matching responce with a random statement from the database
matching_responces = random.choice(list(database))
occurrence_count = database[matching_responces]["occurrence"]
if not closest_statement in database:
raise Exception("A matching statement must exist in the database")

closest_statement = closest(text, database)
# Initialize the matching responce with the first statement in the database
matching_response = database[0].keys()[0]
occurrence_count = database[matching_response]["occurrence"]

for statement in database:

Expand All @@ -24,10 +21,10 @@ def engram(text, database):

# Keep the more common statement
if database[statement]["occurrence"] >= occurrence_count:
matching_responces = statement
matching_response = statement
occurrence_count = database[statement]["occurrence"]

# If the two statements occure equaly in frequency, keep one at random
#TODO? If the two statements occure equaly in frequency, should we keep one at random

# Choose the most common selection of matching responces
return {matching_responces: database[matching_responces]}
# Choose the most common selection of matching response
return {matching_response: database[matching_response]}
39 changes: 0 additions & 39 deletions chatterbot/algorithms/matching.py

This file was deleted.

25 changes: 25 additions & 0 deletions chatterbot/matching.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'''
A collection of methods used to find the closest known match
to an existing statement in the database.
'''

def closest(text, database):
"""
Takes a statement from the current conversation and a database instance.
Returns a the closest known statement that matches by string comparison.
"""
from fuzzywuzzy import process

# Check if an exact match exists
if text in database:
return text

# Get the closest matching statement from the database
return process.extract(text, database[0].keys(), limit=1)[0][0]

def similar(text, database):
"""
Returns the closest known statement based on the meaning of the statement.
"""
# TODO
pass
3 changes: 1 addition & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
fuzzywuzzy==0.5.0
requests==2.5.1
requests==2.6.0
requests-oauthlib==0.4.2
jsondatabase==0.0.3
stemming==1.0.1
35 changes: 29 additions & 6 deletions tests/base_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ def setUp(self):
},
"african or european?": {
"date": "2014-10-15-15-18-41",
"in_response_to": ["How do know so much about swallows?"],
"in_response_to": ["How do you know so much about swallows?"],
"occurrence": 1,
"name": "user"
},
"How do know so much about swallows?": {
"How do you know so much about swallows?": {
"date": "2014-10-15-15-18-40",
"in_response_to": [],
"name": "user",
Expand Down Expand Up @@ -73,12 +73,35 @@ def setUp(self):
}
}

data1 = [
"african or european?",
"Huh? I... I don't know that.",
"How do you know so much about swallows?"
]

data2 = [
"Siri is adorable",
"Who is Seri?",
"Siri is my cat",
]

data3 = [
"What... is your quest?",
"To seek the Holy Grail.",
"What... is your favourite colour?",
"Blue."
]

self.chatbot = ChatBot("Test Bot")
self.chatbot.database.path = "test-database.db"
#self.chatbot.database.path = "test-database.db"

#self.chatbot.train(data1)
#self.chatbot.train(data2)
self.chatbot.train(data3)

database = open(self.chatbot.database.path, "w+")
database.write(json.dumps(data))
database.close()
#database = open(self.chatbot.database.path, "w+")
#database.write(json.dumps(data))
#database.close()

def tearDown(self):
"""
Expand Down
9 changes: 8 additions & 1 deletion tests/test_chatbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ def test_chatbot_returns_answer_to_known_input(self):

self.assertIn("Blue", response)

def test_chatbot_returns_answer_close_to_known_input(self):

input_text = "What is your favourite colour?"
response = self.chatbot.get_response(input_text)

self.assertIn("Blue", response)

def test_match_is_last_line_in_file(self):
"""
Make sure that the if the last line in a file
Expand All @@ -92,7 +99,7 @@ def test_input_text_returned_in_response_data(self):

data = self.chatbot.get_response_data(user_name, user_input)

self.assertIn(user_input, data["user"].keys())
self.assertIn(user_input, data[user_name].keys())

def test_output_text_returned_in_response_data(self):
"""
Expand Down
17 changes: 5 additions & 12 deletions tests/test_engram.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,12 @@ def test_exact_results(self):
output = engram("What... is your quest?", self.chatbot.database)
expected = "To seek the Holy Grail."

self.assertEqual(len(output), 1)
self.assertIn(expected, output.keys())

def test_close_results(self):

output = engram("What is your quest?", self.chatbot.database)
expected = "To seek the Holy Grail."

self.assertEqual(len(output), 1)
self.assertIn(expected, output.keys())

def test_empty_input(self):
"""
If empty input is provided, anything may be returned.
"""
output = self.chatbot.get_response("")

output = engram("", self.chatbot.database)

self.assertEqual(len(output), 1)
self.assertTrue(len(output) > -1)
2 changes: 1 addition & 1 deletion tests/test_matching.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .base_case import ChatBotTestCase
from chatterbot.algorithms.matching import closest
from chatterbot.matching import closest


class MatchingTests(ChatBotTestCase):
Expand Down

0 comments on commit cad01b9

Please sign in to comment.