def _clone_update_in_place(node, node_clone_policy): if node.node_running_status == NodeRunningStatus.SPECIAL: for output in node.outputs: output.values = [] return node old_node_id = node._id node._id = ObjectId() node.successor_node_id = None if node_clone_policy == NodeClonePolicy.NODE_TO_NODE: node.parent_node_id = old_node_id node.original_node_id = None elif node_clone_policy == NodeClonePolicy.NODE_TO_RUN: node.parent_node_id = None node.original_node_id = node.original_node_id or old_node_id elif node_clone_policy == NodeClonePolicy.RUN_TO_NODE: node.parent_node_id = node.original_node_id node.original_node_id = None else: raise Exception('Unknown clone policy `{}`'.format(node_clone_policy)) if node.node_running_status == NodeRunningStatus.STATIC: return node node.node_running_status = NodeRunningStatus.READY node.node_status = NodeStatus.CREATED sub_nodes = node.get_parameter_by_name('_nodes', throw=False) if sub_nodes: object_id_mapping = {} for sub_node in sub_nodes.value.value: prev_id = ObjectId(sub_node._id) _clone_update_in_place(sub_node, node_clone_policy) object_id_mapping[prev_id] = sub_node._id for sub_node in sub_nodes.value.value: for input in sub_node.inputs: input.values = [] for input_reference in input.input_references: input_reference.node_id = object_id_mapping[ObjectId( input_reference.node_id)] for parameter in sub_node.parameters: if not parameter.reference: continue parameter.value = node.get_parameter_by_name( parameter.reference, throw=True).value if sub_node.node_running_status == NodeRunningStatus.STATIC: # do not copy the rest of the elements because they don't change continue for output in sub_node.outputs: output.values = [] for log in sub_node.logs: log.values = [] for output_or_log in node.outputs + node.logs: output_or_log.resource_id = None return node
def _find_nodes(graph, *node_ids): res = [None] * len(node_ids) node_ids_map = {ObjectId(node_id): ii for ii, node_id in enumerate(node_ids)} if len(node_ids_map) != len(node_ids): raise ValueError("Duplicated node ids found") for node in graph.nodes: node_id = ObjectId(node._id) if node_id in node_ids_map: res[node_ids_map[node_id]] = node return res
def cancel_run(run_id): """Cancel Run. Args: run_id (ObjectId, str) RunID """ run_cancellation = RunCancellation() run_cancellation.run_id = ObjectId(run_id) run_cancellation.save() return True
def create_demo_graphs(user): res = [] for graph_id in DemoUserManager.demo_config.graph_ids: graph = Graph.load(graph_id) graph._id = ObjectId() graph.author = user._id graph.save() res.append(graph._id) return res
def cancel_graph(graph_id): """Cancel Graph. Args: graph_id (ObjectId, str) GraphID """ graph_cancellation = GraphCancellation() graph_cancellation.graph_id = ObjectId(graph_id) graph_cancellation.save() return True
def get_index_helper(node, level): if level < 0: return 0 parent_node_ids = set() for input in node.inputs: for input_reference in input.input_references: parent_node_ids.add(ObjectId(input_reference.node_id)) for index, node_id in enumerate(level_to_node_ids[level]): if node_id in parent_node_ids: return index return -1
def load(cls, _id): """Load object from db. Args: _id (str, ObjectId): ID of the object in DB """ obj_dict = getattr(get_db_connector(), cls.DB_COLLECTION).find_one({'_id': ObjectId(_id)}) if not obj_dict: raise DBObjectNotFound( 'Object `{_id}` not found in `{collection}` collection'.format( _id=_id, collection=cls.DB_COLLECTION, )) return cls.from_dict(obj_dict)
def clone(self): graph = Graph.from_dict(self.to_dict()) graph._id = ObjectId() graph.graph_running_status = GraphRunningStatus.CREATED for node in graph.nodes: if node.node_running_status != NodeRunningStatus.STATIC: node.node_running_status = NodeRunningStatus.CREATED for output in node.outputs: output.resource_id = None for input in node.inputs: for value in input.values: value.resource_id = None for log in node.logs: log.resource_id = None return graph
def arrange_auto_layout(self, readonly=False): """Use heuristic to rearange nodes.""" HEADER_HEIGHT = 23 TITLE_HEIGHT = 20 FOOTER_HEIGHT = 10 BORDERS_HEIGHT = 2 ITEM_HEIGHT = 20 SPACE_HEIGHT = 50 LEFT_PADDING = 30 TOP_PADDING = 80 LEVEL_WIDTH = 252 SPECIAL_PARAMETER_HEIGHT = 20 SPECIAL_PARAMETER_TYPES = [ParameterTypes.CODE] min_node_height = HEADER_HEIGHT + TITLE_HEIGHT + FOOTER_HEIGHT + BORDERS_HEIGHT node_id_to_level = defaultdict(lambda: -1) node_id_to_node = {} queued_node_ids = set() children_ids = defaultdict(set) sub_nodes = self.get_parameter_by_name('_nodes').value.value if len(sub_nodes) == 0: return node_ids = set([node._id for node in sub_nodes]) non_zero_node_ids = set() for node in sub_nodes: node_id_to_node[node._id] = node for input in node.inputs: for input_reference in input.input_references: parent_node_id = ObjectId(input_reference.node_id) non_zero_node_ids.add(parent_node_id) children_ids[parent_node_id].add(node._id) leaves = node_ids - non_zero_node_ids to_visit = deque() # Alwasy put Output Node in the end push_special = True if SpecialNodeId.OUTPUT in leaves and len(leaves) > 1 else False for leaf_id in leaves: node_id_to_level[leaf_id] = 1 if push_special and leaf_id != SpecialNodeId.OUTPUT else 0 to_visit.append(leaf_id) while to_visit: node_id = to_visit.popleft() node = node_id_to_node[node_id] node_level = max([node_id_to_level[node_id]] + [node_id_to_level[child_id] + 1 for child_id in children_ids[node_id]]) node_id_to_level[node_id] = node_level for input in node.inputs: for input_reference in input.input_references: parent_node_id = ObjectId(input_reference.node_id) parent_level = node_id_to_level[parent_node_id] node_id_to_level[parent_node_id] = max(node_level + 1, parent_level) if parent_node_id not in queued_node_ids: to_visit.append(parent_node_id) queued_node_ids.add(parent_node_id) max_level = max(node_id_to_level.values()) level_to_node_ids = defaultdict(list) row_heights = defaultdict(lambda: 0) def get_index_helper(node, level): if level < 0: return 0 parent_node_ids = set() for input in node.inputs: for input_reference in input.input_references: parent_node_ids.add(ObjectId(input_reference.node_id)) for index, node_id in enumerate(level_to_node_ids[level]): if node_id in parent_node_ids: return index return -1 def get_index(node, max_level, level): return tuple( [get_index_helper(node, lvl) for lvl in range(max_level, level, -1)] ) for node_id, level in node_id_to_level.items(): level_to_node_ids[level].append(node_id) # Push Input Node up the level if SpecialNodeId.INPUT in node_id_to_level and \ (node_id_to_level[SpecialNodeId.INPUT] != max_level or len(level_to_node_ids[max_level]) > 1): input_level = node_id_to_level[SpecialNodeId.INPUT] level_to_node_ids[input_level] = [node_id for node_id in level_to_node_ids[input_level] if node_id != SpecialNodeId.INPUT] max_level += 1 node_id_to_level[SpecialNodeId.INPUT] = max_level level_to_node_ids[max_level] = [SpecialNodeId.INPUT] for level in range(max_level, -1, -1): level_node_ids = level_to_node_ids[level] index_to_node_id = [] for node_id in level_node_ids: node = node_id_to_node[node_id] index = get_index(node, max_level, level) index_to_node_id.append((index, node_id)) index_to_node_id.sort() level_to_node_ids[level] = [node_id for _, node_id in index_to_node_id] for index, node_id in enumerate(level_to_node_ids[level]): node = node_id_to_node[node_id] special_parameters_count = sum( 1 if parameter.parameter_type in SPECIAL_PARAMETER_TYPES and parameter.widget else 0 for parameter in node.parameters ) node_height = sum([ min_node_height, ITEM_HEIGHT * max(len(node.inputs), len(node.outputs)), special_parameters_count * SPECIAL_PARAMETER_HEIGHT ]) row_heights[index] = max(row_heights[index], node_height) # TODO compute grid in a separate function if readonly: return level_to_node_ids, node_id_to_node cum_heights = [0] for index in range(len(row_heights)): cum_heights.append(cum_heights[-1] + row_heights[index] + SPACE_HEIGHT) max_height = max(cum_heights) for level in range(max_level, -1, -1): level_node_ids = level_to_node_ids[level] level_height = cum_heights[len(level_node_ids)] level_padding = (max_height - level_height) // 2 for index, node_id in enumerate(level_node_ids): node = node_id_to_node[node_id] node.x = LEFT_PADDING + (max_level - level) * LEVEL_WIDTH node.y = TOP_PADDING + level_padding + cum_heights[index]
def post_graph_node_action(graph_id, action): graph_dict = graph_collection_manager.get_db_graph(graph_id, g.user._id) if not graph_dict: return make_fail_response('Graph was not found'), 404 if graph_dict['_readonly']: return make_fail_response('Permission denied'), 403 if not request.data: return make_fail_response('Empty body'), 400 data = json.loads(request.data) graph = Graph.from_dict(graph_dict) if action == GraphNodePostAction.INSERT_NODE: node_id = data.get('node_id', None) x, y = int(data.get('x', 0)), int(data.get('y', 0)) node_dict = node_collection_manager.get_db_node(node_id) if not node_dict: return make_fail_response('Node was not found'), 404 node = Node.from_dict(node_dict) node.x, node.y = x, y node.parent_node = node._id node._id = ObjectId() graph.nodes.append(node) graph.save() return make_success_response(node=node.to_dict()) elif action == GraphNodePostAction.REMOVE_NODE: node_id = ObjectId(data.get('node_id', None)) node_index = -1 for index, node in enumerate(graph.nodes): for input in node.inputs: input.values = [value for value in input.values if ObjectId(value.node_id) != node_id] if ObjectId(node._id) == node_id: node_index = index if node_index < 0: return make_fail_response('Node was not found'), 404 del graph.nodes[node_index] graph.save() return make_success_response('Node removed') elif action == GraphNodePostAction.CHANGE_PARAMETER: node_id = data.get('node_id', None) parameter_name = data.get('parameter_name', None) parameter_value = data.get('parameter_value', None) if parameter_name is None: return make_fail_response('No parameter name'), 400 if parameter_value is None: return make_fail_response('No parameter value'), 400 node, = _find_nodes(graph, node_id) if not node: return make_fail_response('Node was not found'), 404 for parameter in node.parameters: if parameter.name == parameter_name: parameter_dict = parameter.to_dict() parameter_dict['value'] = parameter_value parameter.value = Parameter(obj_dict=parameter_dict).value graph.save() return make_success_response('Parameter updated') elif action in (GraphNodePostAction.CREATE_LINK, GraphNodePostAction.REMOVE_LINK): for field in ['from', 'to']: for sub_field in ['node_id', 'resource']: if field not in data: return make_fail_response('`{}` is missing'.format(field)), 400 if sub_field not in data[field]: return make_fail_response('`{}.{}` is missing'.format(field, sub_field)), 400 from_node_id = data['from']['node_id'] from_resource = data['from']['resource'] to_node_id = data['to']['node_id'] to_resource = data['to']['resource'] from_node, to_node = _find_nodes(graph, from_node_id, to_node_id) if not from_node or not to_node: return make_fail_response('Node was not found'), 404 from_output = None to_input = None for output in from_node.outputs: if output.name == from_resource: from_output = output break for input in to_node.inputs: if input.name == to_resource: to_input = input break if not from_output or not to_input: return make_fail_response('Input or output not found'), 404 if action == GraphNodePostAction.CREATE_LINK: # TODO graph.validate() it if from_output.file_type not in to_input.file_types and 'file' not in to_input.file_types: return make_fail_response('Incompatible types'), 400 # TODO graph.validate() it if to_input.max_count > 0 and len(to_input.values) >= to_input.max_count: return make_fail_response('Number of inputs reached the limit'), 400 new_input_value = InputValue() new_input_value.node_id = from_node_id new_input_value.output_id = from_resource # TODO graph.validate() it for value in to_input.values: if value.node_id == from_node_id and value.output_id == from_resource: return make_fail_response('Link already exists'), 400 to_input.values.append(new_input_value) elif action == GraphNodePostAction.REMOVE_LINK: rm_index = -1 # TODO graph.validate() it for index, value in enumerate(to_input.values): if value.node_id == from_node_id and value.output_id == from_resource: rm_index = index break if rm_index < 0: return make_fail_response('Link not found'), 404 del to_input.values[rm_index] graph.save() return make_success_response('Completed') else: return make_fail_response('Unknown action `{}`'.format(action)), 400 return 'ok'