Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace ChatterBot's "sessions" with "Conversations" #828

Merged
merged 28 commits into from
Aug 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3352a2a
Add Conversation model to SQL Alchemy
gunthercox Jul 7, 2017
64e05ac
Save conversations to the database
gunthercox Jul 8, 2017
d9ae366
Add conversation methods to mongodb adapter
gunthercox Jul 8, 2017
52f1b24
Remove conversation_sessions variable
gunthercox Jul 8, 2017
bf07247
Add method to django adapter for creating conversations
gunthercox Jul 11, 2017
c9a0f45
Fix order of latest statement response query in sql adapter
gunthercox Jul 11, 2017
3efc100
Properly order returned conversation statements from mongodb
gunthercox Jul 11, 2017
b65f2bb
Update django view to use conversations
gunthercox Jul 11, 2017
c5e06f7
Add remaining conversation methods to django storage adapter
gunthercox Jul 12, 2017
f3aba21
Update Django app
gunthercox Jul 14, 2017
5ecec3e
A new conversation will be created if it has no statements
gunthercox Jul 14, 2017
67775fc
Don't check read-only status in learn function
gunthercox Jul 14, 2017
5a6da8f
Allow repeat statements to be saved in a conversation
gunthercox Jul 15, 2017
551a902
Remove session module
gunthercox Jul 15, 2017
60c5cce
No longer need to clear response queue after tests
gunthercox Jul 15, 2017
b940a6a
Update test response values
gunthercox Jul 15, 2017
2c04a68
Rename session variables
gunthercox Jul 15, 2017
e376887
Remove session documentation
gunthercox Jul 15, 2017
d7df50c
Update variables in tests
gunthercox Jul 15, 2017
807fbd6
Fail at connecting conversations and responses
gunthercox Jul 15, 2017
44c8565
Update migrations
gunthercox Jul 18, 2017
dc4a9e6
Remove created_at attribute from the statement object
gunthercox Jul 20, 2017
aa2da71
Add check for if conversation exists
gunthercox Jul 20, 2017
176fec0
Properly create Responses for testing
gunthercox Jul 20, 2017
e67735b
Correct PEP8 issues
gunthercox Jul 20, 2017
2f9b736
Remove ResponseQueue clas
gunthercox Aug 8, 2017
f88f786
Remove unused fixed-size queue class
gunthercox Aug 8, 2017
a5d1228
Remove old session methods from Hipchat adapter
gunthercox Aug 8, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions chatterbot/chatterbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ class ChatBot(object):
"""

def __init__(self, name, **kwargs):
from .conversation.session import ConversationSessionManager
from .logic import MultiLogicAdapter

self.name = name
kwargs['name'] = name
kwargs['chatbot'] = self

self.conversation_sessions = ConversationSessionManager()
self.default_session = self.conversation_sessions.new()
self.default_session = None

storage_adapter = kwargs.get('storage_adapter', 'chatterbot.storage.SQLStorageAdapter')

Expand Down Expand Up @@ -76,6 +74,8 @@ def __init__(self, name, **kwargs):
self.trainer = TrainerClass(self.storage, **kwargs)
self.training_data = kwargs.get('training_data')

self.default_conversation_id = None

self.logger = kwargs.get('logger', logging.getLogger(__name__))

# Allow the bot to save input it receives so that it can learn
Expand All @@ -90,41 +90,42 @@ def initialize(self):
"""
self.logic.initialize()

def get_response(self, input_item, session_id=None):
def get_response(self, input_item, conversation_id=None):
"""
Return the bot's response based on the input.

:param input_item: An input value.
:returns: A response to the input.
:rtype: Statement
"""
if not session_id:
session_id = str(self.default_session.uuid)
if not conversation_id:
if not self.default_conversation_id:
self.default_conversation_id = self.storage.create_conversation()
conversation_id = self.default_conversation_id

input_statement = self.input.process_input_statement(input_item)

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

statement, response = self.generate_response(input_statement, session_id)
statement, response = self.generate_response(input_statement, conversation_id)

# Learn that the user's input was a valid response to the chat bot's previous output
previous_statement = self.conversation_sessions.get(
session_id
).conversation.get_last_response_statement()
self.learn_response(statement, previous_statement)
previous_statement = self.storage.get_latest_response(conversation_id)

self.conversation_sessions.update(session_id, (statement, response, ))
if not self.read_only:
self.learn_response(statement, previous_statement)
self.storage.add_to_converation(conversation_id, statement, response)

# Process the response output with the output adapter
return self.output.process_response(response, session_id)
return self.output.process_response(response, conversation_id)

def generate_response(self, input_statement, session_id):
def generate_response(self, input_statement, conversation_id):
"""
Return a response based on a given input statement.
"""
self.storage.generate_base_query(self, session_id)
self.storage.generate_base_query(self, conversation_id)

# Select a response to the input statement
response = self.logic.process(input_statement)
Expand All @@ -147,8 +148,7 @@ def learn_response(self, statement, previous_statement):
))

# Save the statement after selecting a response
if not self.read_only:
self.storage.update(statement)
self.storage.update(statement)

def set_trainer(self, training_class, **kwargs):
"""
Expand Down
51 changes: 0 additions & 51 deletions chatterbot/conversation/session.py

This file was deleted.

5 changes: 0 additions & 5 deletions chatterbot/conversation/statement.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from .response import Response
from datetime import datetime


class Statement(object):
Expand All @@ -20,9 +19,6 @@ def __init__(self, text, **kwargs):
self.text = text
self.in_response_to = kwargs.pop('in_response_to', [])

# The date and time that this statement was created at
self.created_at = kwargs.pop('created_at', datetime.now())

self.extra_data = kwargs.pop('extra_data', {})

# This is the confidence with which the chat bot believes
Expand Down Expand Up @@ -139,7 +135,6 @@ def serialize(self):

data['text'] = self.text
data['in_response_to'] = []
data['created_at'] = self.created_at
data['extra_data'] = self.extra_data

for response in self.in_response_to:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-07-18 11:25
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('django_chatterbot', '0007_response_created_at'),
]

operations = [
migrations.RemoveField(
model_name='conversation',
name='statements',
),
migrations.RemoveField(
model_name='response',
name='occurrence',
),
migrations.RemoveField(
model_name='statement',
name='created_at',
),
migrations.AddField(
model_name='conversation',
name='responses',
field=models.ManyToManyField(help_text='The responses in this conversation.', related_name='conversations', to='django_chatterbot.Response'),
),
]
41 changes: 23 additions & 18 deletions chatterbot/ext/django_chatterbot/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ class AbstractBaseStatement(models.Model):
max_length=255
)

created_at = models.DateTimeField(
default=timezone.now,
help_text='The date and time that this statement was created at.'
)

extra_data = models.CharField(max_length=500)

# This is the confidence with which the chat bot believes
Expand Down Expand Up @@ -98,11 +93,7 @@ def get_response_count(self, statement):
:returns: Return the number of times the statement has been used as a response.
:rtype: int
"""
try:
response = self.in_response.get(response__text=statement.text)
return response.occurrence
except Response.DoesNotExist:
return 0
return self.in_response.filter(response__text=statement.text).count()

def serialize(self):
"""
Expand All @@ -117,7 +108,6 @@ def serialize(self):

data['text'] = self.text
data['in_response_to'] = []
data['created_at'] = self.created_at
data['extra_data'] = json.loads(self.extra_data)

for response in self.in_response.all():
Expand All @@ -143,9 +133,10 @@ class AbstractBaseResponse(models.Model):
related_name='responses'
)

unique_together = (('statement', 'response'),)

occurrence = models.PositiveIntegerField(default=1)
created_at = models.DateTimeField(
default=timezone.now,
help_text='The date and time that this statement was created at.'
)

created_at = models.DateTimeField(
default=timezone.now,
Expand All @@ -155,6 +146,20 @@ class AbstractBaseResponse(models.Model):
class Meta:
abstract = True

@property
def occurrence(self):
"""
Return a count of the number of times this response has occurred.
"""
from django.apps import apps

response = apps.get_model('django_chatterbot', self.__class__.__name__)

return response.objects.filter(
statement__text=self.statement.text,
response__text=self.response.text
).count()

def __str__(self):
statement = self.statement.text
response = self.response.text
Expand Down Expand Up @@ -184,10 +189,10 @@ class AbstractBaseConversation(models.Model):
default models.
"""

statements = models.ManyToManyField(
'Statement',
related_name='conversation',
help_text='The statements in this conversation.'
responses = models.ManyToManyField(
'Response',
related_name='conversations',
help_text='The responses in this conversation.'
)

class Meta:
Expand Down
59 changes: 35 additions & 24 deletions chatterbot/ext/django_chatterbot/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,48 @@ def validate(self, data):
if 'text' not in data:
raise ValidationError('The attribute "text" is required.')

def get_chat_session(self, request):
def get_conversation(self, request):
"""
Return the current session for the chat if one exists.
Create a new session if one does not exist.
Return the conversation for the session if one exists.
Create a new conversation if one does not exist.
"""
chat_session_id = request.session.get('chat_session_id', None)
chat_session = self.chatterbot.conversation_sessions.get(chat_session_id, None)
from chatterbot.ext.django_chatterbot.models import Conversation, Response

if not chat_session:
chat_session = self.chatterbot.conversation_sessions.new()
request.session['chat_session_id'] = chat_session.id_string
class Obj(object):
def __init__(self):
self.id = None
self.statements = []

return chat_session
conversation = Obj()

conversation.id = request.session.get('conversation_id', 0)
existing_conversation = False
try:
Conversation.objects.get(id=conversation.id)
existing_conversation = True

class ChatterBotView(ChatterBotViewMixin, View):
"""
Provide an API endpoint to interact with ChatterBot.
"""

def _serialize_conversation(self, session):
if session.conversation.empty():
return []
except Conversation.DoesNotExist:
conversation_id = self.chatterbot.storage.create_conversation()
request.session['conversation_id'] = conversation_id
conversation.id = conversation_id

conversation = []
if existing_conversation:
responses = Response.objects.filter(
conversations__id=conversation.id
)

for statement, response in session.conversation:
conversation.append([statement.serialize(), response.serialize()])
for response in responses:
conversation.statements.append(response.statement.serialize())
conversation.statements.append(response.response.serialize())

return conversation


class ChatterBotView(ChatterBotViewMixin, View):
"""
Provide an API endpoint to interact with ChatterBot.
"""

def post(self, request, *args, **kwargs):
"""
Return a response to the statement in the posted data.
Expand All @@ -62,9 +73,9 @@ def post(self, request, *args, **kwargs):

self.validate(input_data)

chat_session = self.get_chat_session(request)
conversation = self.get_conversation(request)

response = self.chatterbot.get_response(input_data, chat_session.id_string)
response = self.chatterbot.get_response(input_data, conversation.id)
response_data = response.serialize()

return JsonResponse(response_data, status=200)
Expand All @@ -73,12 +84,12 @@ def get(self, request, *args, **kwargs):
"""
Return data corresponding to the current conversation.
"""
chat_session = self.get_chat_session(request)
conversation = self.get_conversation(request)

data = {
'detail': 'You should make a POST request to this endpoint.',
'name': self.chatterbot.name,
'conversation': self._serialize_conversation(chat_session)
'conversation': conversation.statements
}

# Return a method not allowed response
Expand Down
Loading