Skip to content

Commit

Permalink
Scroll chat to bottom and add options interface
Browse files Browse the repository at this point in the history
  • Loading branch information
JoaoFelipe committed Feb 9, 2022
1 parent 2d1faba commit f671bcb
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 175 deletions.
184 changes: 16 additions & 168 deletions anachat/core/ana.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import math
import traceback
from lunr import lunr

from .states import OptionsState, SubjectState


class SubjectTree:

Expand All @@ -21,19 +22,21 @@ def display(self, n=0):
def __repr__(self):
return self.display()

class TypeConversionDecisionState:

class TypeConversionDecisionState(OptionsState):

def __init__(self, comm, previousstate, subjectstate):
self.label = f"What are the types?"
self.subjectstate = subjectstate
self.previousstate = previousstate
self.order = {
"1": ("DataFrame", self.text("Convert to Numpy Arrays")),
"2": ("SparseMatrix", self.text("Convert to PandasSparseSeries")),
"3": ("Image", self.text("Use TensorFlow extract_image_patches")),
"4": ("(Back)", self.back),
"0": ('(Go back to subject search)', self.backsubject),
}
self.initial(comm)
options = [
("1", "DataFrame", self.text("Convert to Numpy Arrays")),
("2", "SparseMatrix", self.text("Convert to PandasSparseSeries")),
("3", "Image", self.text("Use TensorFlow extract_image_patches")),
("4", "(Back)", self.back),
("0", '(Go back to subject search)', self.backsubject),
]
super().__init__(comm, options)

def text(self, text):
def text_display(comm):
Expand All @@ -49,23 +52,6 @@ def back(self, comm):
def backsubject(self, comm):
return self.subjectstate

def initial(self, comm):
text = f"What are the types?"
order_text = "\n".join(
f"{i}. {order_tup[0]}" for i, order_tup in self.order.items()
)
comm.reply(f"{text}\n{order_text}")

def process_message(self, comm, text):
strip = text.strip()
if strip in self.order:
return self.order[strip][1](comm)
for order_tup in self.order.values():
if strip.lower() == order_tup[0].lower():
return order_tup[1](comm)
comm.reply("I could not understand this option. Please, try again.")
return self


TREE = SubjectTree(
"",
Expand Down Expand Up @@ -121,38 +107,11 @@ def process_message(self, comm, text):
),
)


def build_document_list(tree):
docmap = {}
documents = []

cid = 1
visit = [("", tree)]
while visit:
current = visit.pop()
newpath = current[0] + " " + current[1].name
document = {
'id': cid,
'name': current[1].name,
'path': newpath,
'node': current[1]
}
documents.append(document)
docmap[str(cid)] = document
cid += 1
for child in current[1].children:
child.parent = current[1]
visit.append((newpath, child))
return docmap, documents




class AnaCore(object):
"""Implements ana chat"""

def __init__(self):
self.state = SubjectState()
self.state = SubjectState(TREE)

def refresh(self, comm):
comm.send({
Expand All @@ -164,116 +123,5 @@ def process_message(self, comm, text):
try:
self.state = self.state.process_message(comm, text)
except:
self.state = SubjectState()
self.state = SubjectState(TREE)
comm.reply("Something is wrong: " + traceback.format_exc(), "error")


class SubjectState:

def __init__(self):
self.docmap, documents = build_document_list(TREE)
self.idx = lunr(ref='id', fields=('name', 'path'), documents=documents)

def process_message(self, comm, text):
matches = self.idx.search(text)
if not matches:
comm.reply("I could not find this subject. Please, try a different query")
return self
return SubjectChoiceState(matches, comm, self)


class SubjectChoiceState:

def __init__(self, matches, comm, subjectstate):
self.order = {str(i + 1): subjectstate.docmap[match['ref']] for i, match in enumerate(matches)}
self.subjectstate = subjectstate
self.initial(comm)

def initial(self, comm):
order_text = "\n".join(
f"{i}. {match['name']}" for i, match in self.order.items()
)
order_text += "\n0. (Go back to subject search)"
comm.reply(f"I found {len(self.order)} subjects. Which one of these best describe your query?\n{order_text}")

def process_message(self, comm, text):
strip = text.strip()
if strip == "0":
return self.subjectstate
if strip in self.order:
return SubjectInfoState(comm, self.order[strip]["node"], self.subjectstate, self)
for match in self.order.values():
if strip.lower() == match['name'].lower():
return SubjectInfoState(comm, match["node"], self.subjectstate, self)
comm.reply("I could not understand this option. Please, try again.")
return self


class SubjectInfoState:
def __init__(self, comm, node, subjectstate, previousstate):
self.node = node
self.subjectstate = subjectstate
self.previousstate = previousstate
cid = 1
self.order = {}
for key in self.node.attr:
self.order[str(cid)] = (key.replace("_", " ").capitalize(), self.attr(key))
cid += 1
if self.node.parent is not None:
self.order[str(cid)] = (f"{self.node.parent.name} (parent)", self.subject(self.node.parent))
cid += 1
for child in self.node.children:
self.order[str(cid)] = (f"{child.name} (child)", self.subject(child))
cid += 1

self.order[str(cid)] = ('(Back)', self.back)
self.order["0"] = ('(Go back to subject search)', self.backsubject)

self.initial(comm)

def attr(self, attr):
def attr_display(comm):
value = self.node.attr[attr]
if isinstance(value, type):
return value(comm, self, self.subjectstate)
else:
comm.reply(value)
self.initial(comm)
return self
return attr_display

def subject(self, child):
def child_display(comm):
return SubjectInfoState(comm, child, self.subjectstate, self)
return child_display

def back(self, comm):
self.previousstate.initial(comm)
return self.previousstate

def backsubject(self, comm):
return self.subjectstate

def initial(self, comm):
text = f"What do you want to know about {self.node.name}?"
order_text = "\n".join(
f"{i}. {order_tup[0]}" for i, order_tup in self.order.items()
)
comm.reply(f"{text}\n{order_text}")

def process_message(self, comm, text):
strip = text.strip()
if strip in self.order:
return self.order[strip][1](comm)
for order_tup in self.order.values():
if strip.lower() == order_tup[0].lower():
return order_tup[1](comm)
comm.reply("I could not understand this option. Please, try again.")
return self

class DummyState:

def process_message(self, comm, text):
comm.reply(text + ", ditto")
return self

146 changes: 146 additions & 0 deletions anachat/core/states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
from lunr import lunr

class OptionsState:

label = "Please, choose an option:"
invalid = "I could not understand this option. Please, try again."

def __init__(self, comm, options=None):
self.options = options or []
self.matches = {}
for key, label, function in self.options:
pkey, plabel = self.preprocess(key), self.preprocess(label)
self.matches[pkey] = function
self.matches[plabel] = function
self.matches[f"{pkey}. {plabel}"] = function
self.initial(comm)

def initial(self, comm):
comm.reply(self.label)
comm.reply([
{'key': item[0], 'label': item[1]} for item in self.options
], "options")

def preprocess(self, text):
return str(text).strip().lower()

def process_message(self, comm, text):
newtext = self.preprocess(text)
if newtext in self.matches:
function = self.matches[newtext]
return function(comm)
comm.reply(self.invalid)
return self


def build_document_list(tree):
docmap = {}
documents = []

cid = 1
visit = [("", tree)]
while visit:
current = visit.pop()
newpath = current[0] + " " + current[1].name
document = {
'id': cid,
'name': current[1].name,
'path': newpath,
'node': current[1]
}
documents.append(document)
docmap[str(cid)] = document
cid += 1
for child in current[1].children:
child.parent = current[1]
visit.append((newpath, child))
return docmap, documents


class SubjectState:

def __init__(self, tree):
self.docmap, documents = build_document_list(tree)
self.idx = lunr(ref='id', fields=('name', 'path'), documents=documents)

def process_message(self, comm, text):
matches = self.idx.search(text)
if not matches:
comm.reply("I could not find this subject. Please, try a different query")
return self
return SubjectChoiceState(matches, comm, self)


class SubjectChoiceState(OptionsState):

def __init__(self, matches, comm, subjectstate):
self.subjectstate = subjectstate
self.label = f"I found {len(matches)} subjects. Which one of these best describe your query?"
options = []
for i, match in enumerate(matches):
label = subjectstate.docmap[match['ref']]['name']
options.append((str(i + 1), label, self.load_subject_info(match, subjectstate)))
options.append(('0', '(Go back to subject search)', self.load_subjectstate))
super().__init__(comm, options)

def load_subject_info(self, match, subjectstate):
def load_info(comm):
return SubjectInfoState(comm, subjectstate.docmap[match['ref']]['node'], subjectstate, self)
return load_info

def load_subjectstate(self, comm):
return self.subjectstate


class SubjectInfoState(OptionsState):
def __init__(self, comm, node, subjectstate, previousstate):
self.label = f"What do you want to know about {node.name}?"
self.node = node
self.subjectstate = subjectstate
self.previousstate = previousstate
options = []
cid = 1
self.order = {}
for key in self.node.attr:
options.append((str(cid), key.replace("_", " ").capitalize(), self.attr(key)))
cid += 1
if self.node.parent is not None:
options.append((str(cid), f"{self.node.parent.name} (parent)", self.subject(self.node.parent)))
cid += 1
for child in self.node.children:
options.append((str(cid), f"{child.name} (child)", self.subject(child)))
cid += 1
options.append((str(cid), '(Back)', self.back))
options.append(("0", '(Go back to subject search)', self.backsubject))
super().__init__(comm, options)

def attr(self, attr):
def attr_display(comm):
value = self.node.attr[attr]
if isinstance(value, type):
return value(comm, self, self.subjectstate)
else:
comm.reply(value)
self.initial(comm)
return self
return attr_display

def subject(self, child):
def child_display(comm):
return SubjectInfoState(comm, child, self.subjectstate, self)
return child_display

def back(self, comm):
self.previousstate.initial(comm)
return self.previousstate

def backsubject(self, comm):
return self.subjectstate


class DummyState:

def process_message(self, comm, text):
comm.reply(text + ", ditto")
return self

5 changes: 3 additions & 2 deletions src/anachat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,14 @@ export class AnaChat extends Panel {
this._eh,
this.refreshAnaChat.bind(this)
);
this._chatWidget = new ChatWidget({ messages });
const sendText = this.sendText.bind(this);
this._chatWidget = new ChatWidget({ messages, sendText });
this._mainWidget = new Panel();
this._mainWidget.addClass('jp-AnaChat');
this._mainWidget.addWidget(headerWidget);
this._mainWidget.addWidget(this._chatWidget);
if (showInput) {
this._mainWidget.addWidget(new InputWidget(this.sendText.bind(this)));
this._mainWidget.addWidget(new InputWidget(sendText));
}
this.addWidget(this._mainWidget);
} catch (error) {
Expand Down
Loading

0 comments on commit f671bcb

Please sign in to comment.