Skip to content

Commit

Permalink
Merge pull request #29 from IDEMSInternational/support-ui
Browse files Browse the repository at this point in the history
Support for UI positions
  • Loading branch information
geoo89 authored Oct 13, 2022
2 parents 0494684 + 1793e63 commit 119c93b
Show file tree
Hide file tree
Showing 7 changed files with 1,069 additions and 53 deletions.
21 changes: 13 additions & 8 deletions parsers/creation/standard_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,27 +170,32 @@ def get_row_node(self, row):
self.rapidpro_container.record_group_uuid(row.mainarg_groups[0], row.obj_id)

node_uuid = row.node_uuid or None
if row.ui_position:
ui_pos = [int(coord) for coord in row.ui_position] # List[str] -> List[int]
assert len(ui_pos) == 2
else:
ui_pos = None
if row.type in ['send_message', 'save_value', 'add_to_group', 'remove_from_group', 'save_flow_result']:
node = BasicNode(uuid=node_uuid)
node = BasicNode(uuid=node_uuid, ui_pos=ui_pos)
node.update_default_exit(None)
return node
elif row.type in ['start_new_flow']:
if row.obj_id:
self.rapidpro_container.record_flow_uuid(row.mainarg_flow_name, row.obj_id)
return EnterFlowNode(row.mainarg_flow_name, uuid=node_uuid)
return EnterFlowNode(row.mainarg_flow_name, uuid=node_uuid, ui_pos=ui_pos)
elif row.type in ['wait_for_response']:
if row.no_response:
return SwitchRouterNode('@input.text', result_name=row.save_name, wait_timeout=int(row.no_response), uuid=node_uuid)
return SwitchRouterNode('@input.text', result_name=row.save_name, wait_timeout=int(row.no_response), uuid=node_uuid, ui_pos=ui_pos)
else:
return SwitchRouterNode('@input.text', result_name=row.save_name, wait_timeout=None, uuid=node_uuid)
return SwitchRouterNode('@input.text', result_name=row.save_name, wait_timeout=0, uuid=node_uuid, ui_pos=ui_pos)
elif row.type in ['split_by_value']:
return SwitchRouterNode(row.mainarg_expression, result_name=row.save_name, wait_timeout=None, uuid=node_uuid)
return SwitchRouterNode(row.mainarg_expression, result_name=row.save_name, wait_timeout=None, uuid=node_uuid, ui_pos=ui_pos)
elif row.type in ['split_by_group']:
return SwitchRouterNode('@contact.groups', result_name=row.save_name, wait_timeout=None, uuid=node_uuid)
return SwitchRouterNode('@contact.groups', result_name=row.save_name, wait_timeout=None, uuid=node_uuid, ui_pos=ui_pos)
elif row.type in ['split_random']:
return RandomRouterNode(result_name=row.save_name, uuid=node_uuid)
return RandomRouterNode(result_name=row.save_name, uuid=node_uuid, ui_pos=ui_pos)
else:
return BasicNode(uuid=node_uuid)
return BasicNode(uuid=node_uuid, ui_pos=ui_pos)

def get_node_name(self, row):
return row.node_uuid or row.node_name
Expand Down
32 changes: 32 additions & 0 deletions parsers/creation/tests/test_standard_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,17 @@ def test_no_switch_node_rows(self):

self.assertIsNone(node_4['exits'][0]['destination_uuid'])

# Check UI positions/types of the first two nodes
render_ui = render_output['_ui']['nodes']
self.assertIn(node_0['uuid'], render_ui)
pos0 = render_ui[node_0['uuid']]['position']
self.assertEqual((280, 73), (pos0['left'], pos0['top']))
self.assertEqual('execute_actions', render_ui[node_0['uuid']]['type'])
self.assertIn(node_1['uuid'], render_ui)
pos1 = render_ui[node_1['uuid']]['position']
self.assertEqual((280, 600), (pos1['left'], pos1['top']))
self.assertEqual('execute_actions', render_ui[node_1['uuid']]['type'])

def test_switch_node_rows(self):
rows = get_dict_from_csv('input/switch_nodes.csv')
switch_node_rows = [self.row_parser.parse_row(row) for row in rows]
Expand All @@ -258,6 +269,27 @@ def test_switch_node_rows(self):
# At least the functionality is covered by the integration tests simulating the flow.
# print(json.dumps(render_output, indent=2))

render_ui = render_output['_ui']['nodes']
f_uuid = lambda i: render_output['nodes'][i]['uuid']
f_uipos_dict = lambda i: render_ui[f_uuid(i)]['position']
f_uipos = lambda i: (f_uipos_dict(i)['left'], f_uipos_dict(i)['top'])
f_uitype = lambda i: render_ui[f_uuid(i)]['type']
self.assertIn(f_uuid(0), render_ui)
self.assertEqual((340, 0), f_uipos(0))
self.assertEqual((360, 180), f_uipos(1))
self.assertEqual((840, 1200), f_uipos(-1))
self.assertEqual("wait_for_response", f_uitype(0))
self.assertEqual("split_by_subflow", f_uitype(1))
self.assertEqual("split_by_expression", f_uitype(2))
self.assertEqual("split_by_contact_field", f_uitype(3))
self.assertEqual("split_by_run_result", f_uitype(4))
self.assertEqual("split_by_groups", f_uitype(5))
self.assertEqual("wait_for_response", f_uitype(6))
self.assertEqual("split_by_random", f_uitype(7))
self.assertEqual("execute_actions", f_uitype(8))
self.assertEqual("execute_actions", f_uitype(9))
self.assertEqual("wait_for_response", f_uitype(-1))

def test_groups_and_flows(self):
# We check that references flows and group are assigned uuids consistently
tiny_uuid = '00000000-acec-434f-bc7c-14c584fc4bc8'
Expand Down
26 changes: 15 additions & 11 deletions rapidpro/models/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ def render(self):


class FlowContainer:
def __init__(self, flow_name, type='messaging', language='eng', uuid=None, spec_version='13.1.0', revision=0, expire_after_minutes=10080, metadata=None, localization=None, ui=None):
def __init__(self, flow_name, type='messaging', language='eng', uuid=None, spec_version='13.1.0', revision=0, expire_after_minutes=10080, metadata=None, localization=None):
# UI is not part of this as it is captured within the nodes.
# Localization/ui may be handled differently in the future (e.g. stored within nodes or similar)
# The field is likely to be dropped from here, and only here temporarily to avoid losing its data.
# Localization may be handled differently in the future (e.g. stored within nodes or similar);
# it is likely to be dropped from here, and only here temporarily to avoid losing its data.
self.uuid = uuid or generate_new_uuid()
self.name = flow_name
self.language = language
Expand All @@ -90,19 +90,18 @@ def __init__(self, flow_name, type='messaging', language='eng', uuid=None, spec_
self.expire_after_minutes = expire_after_minutes
self.metadata = metadata or {}
self.localization = localization or {}
self.ui = ui or {}

def from_dict(data):
data_copy = copy.deepcopy(data)
name = data_copy.pop("name")
data_copy["flow_name"] = name
if "_ui" in data_copy:
ui = data_copy.pop("_ui")
data_copy["ui"] = ui
else:
data_copy["ui"] = {}
nodes = data_copy.pop("nodes")
nodes = [BaseNode.from_dict(node) for node in nodes]
if "_ui" in data_copy:
ui = data_copy.pop("_ui")
if 'nodes' in ui:
for node in nodes:
node.add_ui_from_dict(ui['nodes'])
flow_container = FlowContainer(**data_copy)
flow_container.nodes = nodes
return flow_container
Expand Down Expand Up @@ -131,8 +130,13 @@ def render(self):
"metadata": self.metadata,
"localization": self.localization
}
if self.ui:
render_dict["_ui"] = self.ui
ui_dict = {}
for node in self.nodes:
node_ui = node.render_ui()
if node_ui:
ui_dict[node.uuid] = node_ui
if ui_dict:
render_dict["_ui"] = {'nodes': ui_dict}
return render_dict


Expand Down
Loading

0 comments on commit 119c93b

Please sign in to comment.