Skip to content

Commit

Permalink
Merge pull request #51 from gunthercox/mongo
Browse files Browse the repository at this point in the history
Created MongoDB adapter
  • Loading branch information
gunthercox committed Sep 6, 2015
2 parents 3742f14 + d2b43b2 commit c3232ab
Show file tree
Hide file tree
Showing 8 changed files with 434 additions and 37 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ python:

install:
- pip install coveralls
- pip install pymongo
- pip install -r requirements.txt

services:
- mongodb

script:
- nosetests --with-coverage --cover-package=chatterbot

Expand Down
1 change: 1 addition & 0 deletions chatterbot/adapters/storage/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .database import DatabaseAdapter
from .jsondatabase import JsonDatabaseAdapter
from .mongodb import MongoDatabaseAdapter

120 changes: 87 additions & 33 deletions chatterbot/adapters/storage/mongodb.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,112 @@
from chatterbot.adapters.storage import DatabaseAdapter
from chatterbot.adapters.exceptions import EmptyDatabaseException
from chatterbot.conversation import Statement
from pymongo import MongoClient

# Use the default host and port
client = MongoClient()

# We can also specify the host and port explicitly
#client = MongoClient('localhost', 27017)

# Specify the name of the database
db = client['test-database']

# The mongo collection of statement documents
statements = db['statements']


class MongoDatabaseAdapter(DatabaseAdapter):

def __init__(self, **kwargs):
pass
super(MongoDatabaseAdapter, self).__init__(**kwargs)

def find(self, statement):
#def find(self, key):
return statements.find_one(statement)
self.database_name = self.kwargs.get("database", "chatterbot-database")

def insert(self, key, values):
statement_id = self.statements.insert_one(statement).inserted_id
# Use the default host and port
self.client = MongoClient()

return statement_id
# Specify the name of the database
self.database = self.client[self.database_name]

def update(self, key, **kwargs):
# The mongo collection of statement documents
self.statements = self.database['statements']

values = self.database.data(key=key)
def count(self):
return self.statements.count()

def find(self, statement_text):
values = self.statements.find_one({'text': statement_text})

# Create the statement if it doesn't exist in the database
if not values:
self.database[key] = {}
values = {}
return None

del(values['text'])
return Statement(statement_text, **values)

def filter(self, **kwargs):
"""
Returns a list of statements in the database
that match the parameters specified.
"""
filter_parameters = kwargs.copy()
contains_parameters = {}

# Exclude special arguments from the kwargs
for parameter in kwargs:
values[parameter] = kwargs.get(parameter)
if "__" in parameter:
del(filter_parameters[parameter])

kwarg_parts = parameter.split("__")

if kwarg_parts[1] == "contains":
key = kwarg_parts[0]
value = kwargs[parameter]
contains_parameters[key] = value

filter_parameters.update(contains_parameters)

matches = self.statements.find(filter_parameters)
matches = list(matches)

self.database[key] = values
results = []

return values
for match in matches:
statement_text = match['text']
del(match['text'])
results.append(Statement(statement_text, **match))

def keys(self):
# The value has to be cast as a list for Python 3 compatibility
return list(self.database[0].keys())
return results

def update(self, statement):
# Do not alter the database unless writing is enabled
if not self.read_only:
data = statement.serialize()

# Remove the text key from the data
self.statements.update({'text': statement.text}, data, True)

# Make sure that an entry for each response is saved
for response_statement in statement.in_response_to:
response = self.find(response_statement)
if not response:
response = Statement(response_statement)
self.update(response)

return statement

def get_random(self):
"""
Returns a random statement from the database
"""
from random import choice
from random import randint

count = self.count()

random_integer = randint(0, count -1)

if self.count() < 1:
raise EmptyDatabaseException()

statement = self.statements.find().limit(1).skip(random_integer * count)

values = list(statement)[0]
statement_text = values['text']

del(values['text'])
return Statement(statement_text, **values)

def drop(self):
"""
Remove the database.
"""
self.client.drop_database(self.database_name)

statement = choice(self.keys())
return {statement: self.find(statement)}
9 changes: 6 additions & 3 deletions chatterbot/chatterbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,10 @@ def get_response(self, input_text):
"""
Return the bot's response based on the input.
"""
text_of_all_statements = self.storage._keys()

input_statement = Statement(input_text)

# If no responses exist, use the input text
if not text_of_all_statements:
if not self.storage.count():
response = Statement(input_text)
self.storage.update(response)
self.recent_statements.append(response)
Expand All @@ -100,6 +98,11 @@ def get_response(self, input_text):

return response

all_statements = self.storage.filter()
text_of_all_statements = []
for statement in all_statements:
text_of_all_statements.append(statement.text)

# Select the closest match to the input statement
closest_match = self.logic.get(
input_text, text_of_all_statements
Expand Down
40 changes: 40 additions & 0 deletions examples/terminal_mongo_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from chatterbot import ChatBot


# Create a new instance of a ChatBot
bot = ChatBot("Terminal",
storage_adapter="chatterbot.adapters.storage.MongoDatabaseAdapter",
logic_adapter="chatterbot.adapters.logic.ClosestMatchAdapter",
io_adapter="chatterbot.adapters.io.TerminalAdapter",
database="chatterbot-database")

user_input = "Type something to begin..."

print(user_input)

'''
In this example we use a while loop combined with a try-except statement.
This allows us to have a conversation with the chat bot until we press
ctrl-c or ctrl-d on the keyboard.
'''

while True:
try:
'''
ChatterBot's get_input method uses io adapter to get new input for
the bot to respond to. In this example, the TerminalAdapter gets the
input from the user's terminal. Other io adapters might retrieve input
differently, such as from various web APIs.
'''
user_input = bot.get_input()

'''
The get_response method also uses the io adapter to determine how
the bot's output should be returned. In the case of the TerminalAdapter,
the output is printed to the user's terminal.
'''
bot_input = bot.get_response(user_input)

except (KeyboardInterrupt, EOFError, SystemExit):
break

3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fuzzywuzzy==0.6.0
jsondatabase>=0.0.6
pymongo==3.0.3
requests==2.7.0
requests-oauthlib==0.5.0
jsondatabase==0.0.6
14 changes: 14 additions & 0 deletions tests/storage_adapter_tests/test_jsondb_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,20 @@ def test_filter_multiple_parameters_no_results(self):

self.assertEqual(len(results), 0)

def test_filter_no_parameters(self):
"""
If not parameters are provided to the filter,
then all statements should be returned.
"""
statement1 = Statement("Testing...")
statement2 = Statement("Testing one, two, three.")
self.adapter.update(statement1)
self.adapter.update(statement2)

results = self.adapter.filter()

self.assertEqual(len(results), 2)


class ReadOnlyJsonDatabaseAdapterTestCase(BaseJsonDatabaseAdapterTestCase):

Expand Down
Loading

0 comments on commit c3232ab

Please sign in to comment.