Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
 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
Beispiel #4
0
 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
Beispiel #5
0
    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
Beispiel #6
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
Beispiel #7
0
    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)
Beispiel #8
0
    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
Beispiel #9
0
    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]
Beispiel #10
0
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'