11from flask import Flask , render_template , request
22import json
33
4+ # ===============================================
5+ # app.py
6+ # ------------
7+ # reads json data to send to viewer
8+ # ===============================================
9+
410app = Flask (__name__ , static_folder = './static' , template_folder = './static' )
511
612nodes = {}
713edges = []
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
1318schema_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
1927def 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-
12789def 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():
257261def 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