Skip to content

Commit

Permalink
Makes hinting on flows useable (errbotio#1062)
Browse files Browse the repository at this point in the history
It needs to be attached to a specific node so it will be set at
initialization phase after the fact with a 'hints' parameter.
  • Loading branch information
gbin authored Aug 12, 2017
1 parent 75638b3 commit d5d4151
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 12 deletions.
18 changes: 9 additions & 9 deletions errbot/flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ class FlowNode(object):
The predicate is a function that takes one parameter, the context of the conversation.
"""

def __init__(self, command: str=None):
def __init__(self, command: str=None, hints: bool=True):
"""
Creates a FlowNone, takes the command to which the Node is linked to.
:param command: the command this Node is linked to. Can only be None if this Node is a Root.
:param hints: hints the users for the next steps in chat.
"""
self.command = command
self.children = [] # (predicate, node)
self.hints = hints

def connect(self, node_or_command: Union['FlowNode', str], predicate: Predicate=lambda _: False):
"""
Expand All @@ -42,6 +44,7 @@ def connect(self, node_or_command: Union['FlowNode', str], predicate: Predicate=
(this node or command will be the follow up of this one)
:param predicate: function with one parameter, the context, to determine of the flow executor can continue
automatically this flow with no user intervention.
:param hints: hints the user on the next step possible.
:return: the newly created node if you passed a command or the node you gave it to be easily chainable.
"""
node_to_connect_to = node_or_command if isinstance(node_or_command, FlowNode) else FlowNode(node_or_command)
Expand Down Expand Up @@ -72,11 +75,13 @@ def __init__(self, name: str, description: str):
:param name: The name of the conversation/flow.
:param description: A human description of what this flow does.
:param hints: Hints for the next steps when triggered.
"""
super().__init__()
self.name = name
self.description = description
self.auto_triggers = set()
self.room_flow = False

def connect(self,
node_or_command: Union['FlowNode', str],
Expand Down Expand Up @@ -122,21 +127,17 @@ class Flow(object):
This is a live Flow. It keeps context of the conversation (requestor and context).
Context is just a python dictionary representing the state of the conversation.
"""
def __init__(self, root: FlowRoot, requestor: Identifier, initial_context: Mapping[str, Any],
next_step_hinting: int=True):
def __init__(self, root: FlowRoot, requestor: Identifier, initial_context: Mapping[str, Any]):
"""
:param root: the root of this flow.
:param requestor: the user requesting this flow.
:param initial_context: any data we already have that could help executing this flow automatically.
:param next_step_hinting: toggle display of the "you are now in the flow xxx, you may continue with yyy, zzz"
messages after each flow step
"""
self._root = root
self._current_step = self._root
self.ctx = dict(initial_context)
self.requestor = requestor
self.next_step_hinting = next_step_hinting

def next_autosteps(self) -> List[FlowNode]:
"""
Expand Down Expand Up @@ -390,7 +391,7 @@ def execute(self, flow: Flow):
self.in_flight.remove(flow)
break

if not autosteps:
if not autosteps and flow.current_step.hints:
possible_next_steps = ["You are in the flow **%s**, you can continue with:\n\n" % flow.name]
for step in steps:
cmd = step.command
Expand All @@ -405,8 +406,7 @@ def execute(self, flow: Flow):
if syntax_args:
syntax += syntax_args
possible_next_steps.append("- %s" % syntax)
if flow.next_step_hinting:
self._bot.send(flow.requestor, "\n".join(possible_next_steps))
self._bot.send(flow.requestor, "\n".join(possible_next_steps))
break

log.debug("Steps triggered automatically %s", ', '.join(str(node) for node in autosteps))
Expand Down
13 changes: 12 additions & 1 deletion tests/flow_e2e_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@


def test_list_flows(testbot):
assert len(testbot.bot.flow_executor.flow_roots) == 3
assert len(testbot.bot.flow_executor.flow_roots) == 4
testbot.bot.push_message('!flows list')
result = testbot.pop_message()
assert 'documentation of W1' in result
assert 'documentation of W2' in result
assert 'documentation of W3' in result
assert 'documentation of W4' in result
assert 'w1' in result
assert 'w2' in result
assert 'w3' in result
assert 'w4' in result


def test_no_autotrigger(testbot):
Expand Down Expand Up @@ -62,6 +64,15 @@ def test_manual_flow(testbot):
assert '!c' in flow_message


def test_manual_flow_with_or_without_hinting(testbot):
assert 'Flow w4 started' in testbot.exec_command('!flows start w4')
assert 'a' in testbot.exec_command('!a')
assert 'b' in testbot.exec_command('!b')
flow_message = testbot.pop_message()
assert 'You are in the flow w4, you can continue with' in flow_message
assert '!c' in flow_message


def test_no_flyby_trigger_flow(testbot):
testbot.bot.push_message('!flows start w1')
# One message or the other can arrive first.
Expand Down
23 changes: 21 additions & 2 deletions tests/flow_plugin/flowtest_flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,35 @@ def w1(self, flow: FlowRoot):
c_node = a_node.connect('c') # crosses the autotrigger of w2
d_node = c_node.connect('d')

assert a_node.hints
assert b_node.hints
assert c_node.hints
assert d_node.hints

@botflow
def w2(self, flow: FlowRoot):
"documentation of W2"
"""documentation of W2"""
c_node = flow.connect('c', auto_trigger=True)
b_node = c_node.connect('b')
e_node = flow.connect('e', auto_trigger=True) # 2 autotriggers for the same workflow
d_node = e_node.connect('d')

@botflow
def w3(self, flow: FlowRoot):
"documentation of W3"
"""documentation of W3"""
c_node = flow.connect('a', room_flow=True)
b_node = c_node.connect('b')

@botflow
def w4(self, flow: FlowRoot):
"""documentation of W4"""
a_node = flow.connect('a')
b_node = a_node.connect('b')
c_node = b_node.connect('c')
c_node.connect('d')

# set some hinting.
flow.hints = False
a_node.hints = False
b_node.hints = True
c_node.hint = False

0 comments on commit d5d4151

Please sign in to comment.