Skip to content

Commit 7a5f202

Browse files
authored
Merge pull request #5 from iris-liu0312/master
Json Editor
2 parents 3f8f380 + 19736e0 commit 7a5f202

14 files changed

Lines changed: 815 additions & 113 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Introduction
44

5-
This is a web tool to visualize KAIROS Schema Format generated schemas using Cytoscape.js and React.js. The tool also allows editing of these schemas for curation purpose. Current supported SDF version is **1.2**.
5+
This is a web tool to visualize KAIROS Schema Format generated schemas using Cytoscape.js and React.js. The tool also allows editing of these schemas for curation purpose. Current supported SDF version is **1.3**.
66

77
**This project is currently a work in progress and is in alpha testing. Feedbacks and suggestions are welcome.**
88

app.py

Lines changed: 85 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
from flask import Flask, render_template, request
22
import json
33

4+
# ===============================================
5+
# app.py
6+
# ------------
7+
# reads json data to send to viewer
8+
# ===============================================
9+
410
app = Flask(__name__, static_folder='./static', template_folder='./static')
511

612
nodes = {}
713
edges = []
814

9-
# TODO: look through SDF version documentation to decide what should be in the schema_key_dict
10-
# TODO: move support to SDF version 1.3
15+
# TODO: handle multiple root nodes
1116

12-
# SDF version 1.2
17+
# SDF version 1.3
1318
schema_key_dict = {
14-
'root': ['@id', 'name', 'description', 'comment', 'qnode', '@type', 'minDuration', 'maxDuration', 'repeatable', 'TA1explanation', 'importance', 'qlabel'],
19+
'root': ['@id', 'name', 'comment', 'description', 'aka', 'qnode', 'qlabel', 'minDuration',
20+
'maxDuration', 'goal', 'ta1explanation', 'importance', 'children_gate'],
21+
# TODO: handle xor children_gates
1522
'participant': ['@id', 'roleName', 'entity'],
16-
'child': ['child', 'comment', 'optional', 'importance', 'outlinks', 'outlink_gate', ]
23+
'child': ['child', 'comment', 'optional', 'importance', 'outlinks', 'outlink_gate'],
24+
'privateData': ['@type', 'template', 'repeatable', 'importance']
1725
}
1826

1927
def create_node(_id, _label, _type, _shape=''):
@@ -72,58 +80,12 @@ def extend_node(node, obj):
7280
node['classes'] = 'optional'
7381
else:
7482
node['data'][key] = obj[key]
83+
if 'privateData' in obj.keys():
84+
for key in obj['privateData'].keys():
85+
if key in schema_key_dict['privateData']:
86+
node['data'][key] = obj['privateData'][key]
7587
return node
7688

77-
def handle_precondition(order, node_set, label='Precondition'):
78-
"""Adds edges between multiple previous and next nodes.
79-
80-
Parameters:
81-
order (dict): links to "before" and "after" nodes
82-
node_set (dict): nodes to be linked
83-
label (str): shows order on graph
84-
85-
"""
86-
e = []
87-
if isinstance(order['before'], list):
88-
for before_id in order['before']:
89-
if isinstance(order['after'], list):
90-
for after_id in order['after']:
91-
if before_id in node_set and after_id in node_set:
92-
e.append(create_edge(before_id, after_id, label, 'step_child'))
93-
else:
94-
if before_id in node_set and order['after'] in node_set:
95-
e.append(create_edge(before_id, order['after'], label, 'step_child'))
96-
else:
97-
if isinstance(order['after'], list):
98-
for after_id in order['after']:
99-
if order['before'] in node_set and after_id in node_set:
100-
e.append(create_edge(order['before'], after_id, label, 'step_child'))
101-
else:
102-
if order['before'] in node_set and order['after'] in node_set:
103-
e.append(create_edge(order['before'], order['after'], label, 'step_child'))
104-
return e
105-
106-
def handle_optional(_order, node_set):
107-
"""Calls handle_precondition with thet optional label.
108-
109-
"""
110-
return handle_precondition(_order, node_set, 'Optional')
111-
112-
def handle_flags(_flag, _order, node_set):
113-
"""Calls handle_precondition or handle_optional based on flag.
114-
115-
Parameters:
116-
_flag (str): flag raised in order dictionary
117-
_node_set (dict): set of nodes to be handled
118-
119-
"""
120-
switcher={
121-
'precondition': handle_precondition,
122-
'optional': handle_optional
123-
}
124-
func = switcher.get(_flag.lower(), lambda *args: None)
125-
return func(_order, node_set)
126-
12789
def get_nodes_and_edges(schema):
12890
"""Creates lists of nodes and edges, through references and relations.
12991
@@ -137,55 +99,97 @@ def get_nodes_and_edges(schema):
13799
"""
138100
nodes = {}
139101
edges = []
102+
containers = []
140103

141104
for scheme in schema:
142-
# top node
105+
# create event node
143106
_label = scheme['name'].split('/')[-1].replace('_', ' ').replace('-', ' ')
144-
nodes[scheme['@id']] = extend_node(create_node(scheme['@id'], _label, 'root', 'diamond'), scheme)
145-
# not root node, change node type
146-
if '@type' in nodes[scheme['@id']]['data']:
147-
nodes[scheme['@id']]['data']['_type'] = 'parent'
148-
# not hierarchical node, change node shape
107+
scheme_id = scheme['@id']
108+
# node already exists
109+
if scheme_id in nodes:
110+
# add information
111+
nodes[scheme_id]['data']['_type'] = 'root'
112+
nodes[scheme_id]['data']['_label'] = _label
113+
nodes[scheme_id] = extend_node(nodes[scheme_id], scheme)
114+
# change type back
149115
if 'children' not in scheme:
150-
nodes[scheme['@id']]['data']['_type'] = 'child'
151-
nodes[scheme['@id']]['data']['_shape'] = 'ellipse'
152-
if scheme['repeatable']:
153-
edges.append(create_edge(scheme['@id'], scheme['@id'], _edge_type='child_outlink'))
116+
nodes[scheme_id]['data']['_type'] = 'child'
117+
elif 'outlinks' in nodes[scheme_id]['data']['name']:
118+
nodes[scheme_id]['data']['_type'] = 'container'
119+
containers.append(scheme_id)
120+
else:
121+
nodes[scheme_id]['data']['_type'] = 'parent'
122+
nodes[scheme_id]['data']['_shape'] = 'diamond'
123+
# new node
124+
else:
125+
nodes[scheme_id] = extend_node(create_node(scheme_id, _label, 'root', 'diamond'), scheme)
126+
127+
if '@type' in nodes[scheme_id]['data']:
128+
# not root node, change node type
129+
nodes[scheme_id]['data']['_type'] = 'parent'
130+
nodes[scheme_id]['data']['_shape'] = 'diamond'
131+
# not hierarchical node, change node shape
132+
if 'children' not in scheme:
133+
nodes[scheme_id]['data']['_type'] = 'child'
134+
nodes[scheme_id]['data']['_shape'] = 'ellipse'
135+
# handle repeatable
136+
if nodes[scheme_id]['data']['repeatable']:
137+
edges.append(create_edge(scheme_id, scheme_id, _edge_type='child_outlink'))
154138

155139
# participants
156140
if 'participants' in scheme:
157141
for participant in scheme['participants']:
158142
_label = participant['roleName'].split('/')[-1].replace('_', '')
159143
nodes[participant['@id']] = extend_node(create_node(participant['@id'], _label, 'participant', 'square'), participant)
160144

161-
edges.append(create_edge(scheme['@id'], participant['@id'], _edge_type='step_participant'))
145+
edges.append(create_edge(scheme_id, participant['@id'], _edge_type='step_participant'))
162146

163147
# children
164148
if 'children' in scheme:
165149
for child in scheme['children']:
166-
nodes[child['child']] = extend_node(create_node(child['child'], child['comment'], 'child', 'ellipse'), child)
167-
edges.append(create_edge(scheme['@id'], child['child'], _edge_type='step_child'))
150+
151+
child_id = child['child']
152+
# node already exists
153+
if child_id in nodes:
154+
prev_type = nodes[child_id]['data']['_type']
155+
nodes[child_id]['data']['_type'] = 'child'
156+
nodes[child_id] = extend_node(nodes[child_id], child)
157+
nodes[child_id]['data']['_type'] = prev_type
158+
# new node
159+
else:
160+
nodes[child_id] = extend_node(create_node(child_id, child['comment'], 'child', 'ellipse'), child)
161+
edges.append(create_edge(scheme_id, child_id, _edge_type='step_child'))
168162

169163
# check for outlinks
170164
if len(child['outlinks']):
171165
for outlink in child['outlinks']:
172166
if outlink not in nodes:
173167
_label = outlink.split('/')[-1].replace('_', '')
174168
nodes[outlink] = create_node(outlink, _label, 'child', 'ellipse')
175-
edges.append(create_edge(child['child'], outlink, _edge_type='child_outlink'))
176-
177-
# TODO: and, xor gate
178-
# TODO: optional nodes -- the option is in the cy-style json
179-
# bug? it doesn't show up on parent node
180-
# found: parent node does not have optional key and overwrites child node keys
169+
edges.append(create_edge(child_id, outlink, _edge_type='child_outlink'))
170+
171+
# handle containers, ie. connect previous node to all their successors
172+
edges_to_remove = []
173+
for container in containers:
174+
for edge in edges:
175+
if 'searched' in edge['data'] and edge['data']['searched']:
176+
continue
177+
if edge['data']['target'] == container:
178+
source_id = edge['data']['source']
179+
nodes[source_id]['data']['children_gate'] = nodes[container]['data']['children_gate']
180+
edges_to_remove.append(edge)
181+
edge['data']['searched'] = True
182+
if edge['data']['source'] == container:
183+
edges.append(create_edge(source_id, edge['data']['target'], _edge_type='step_child'))
184+
edges_to_remove.append(edge)
185+
edge['data']['searched'] = True
186+
187+
for index in edges_to_remove:
188+
edges.remove(index)
181189

182190
# === are these two necessary? / what are these for ===
183191
# TODO: entities
184192
# TODO: relations
185-
# if 'relations' in schema and len(schema['relations']):
186-
# # generalize, although at the moment UIUC Q7 only has these two predicates
187-
# predicates = {'Q19267375':'proximity', 'Q6498684':'ownership'}
188-
# for relation in schema['relations']:
189193

190194
# if 'entityRelations' in schema and isinstance(schema['entityRelations'], list):
191195
# for entityRelation in schema['entityRelations']:
@@ -257,8 +261,8 @@ def homepage():
257261
def upload():
258262
file = request.files['file']
259263
schema_string = file.read().decode("utf-8")
260-
schemaJson = json.loads(schema_string)['events']
261-
schema = schemaJson
264+
schemaJson = json.loads(schema_string)
265+
schema = schemaJson['events']
262266
global nodes
263267
global edges
264268
nodes, edges = get_nodes_and_edges(schema)
@@ -284,7 +288,7 @@ def reload_schema():
284288
"""Reloads schema; does the same thing as upload."""
285289
schema_string = request.data.decode("utf-8")
286290
schemaJson = json.loads(schema_string)
287-
schema = schemaJson
291+
schema = schemaJson['events']
288292
global nodes
289293
global edges
290294
nodes, edges = get_nodes_and_edges(schema)

0 commit comments

Comments
 (0)