delete_node()   B
last analyzed

Complexity

Conditions 4

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
c 1
b 0
f 0
dl 0
loc 24
rs 8.6845
1
# Copyright (c) 2016 Fabian Kochem
2
3
4
import json
5
import uuid
6
7
from libtree import exceptions
8
from libtree.core.node_data import NodeData
9
from libtree.core.positioning import (ensure_free_position,
10
                                      find_highest_position, shift_positions)
11
from libtree.core.query import (get_children, get_descendant_ids, get_node,
12
                                get_root_node)
13
14
15
def print_tree(cur, start_node=None, indent='  ', _level=0):
16
    """
17
    Print tree to stdout.
18
19
    :param start_node: Starting point for tree output.
20
                       If ``None``, start at root node.
21
    :type start_node: int, Node, NodaData or None
22
    :param str indent: String to print per level (default: '  ')
23
    """
24
    if start_node is None:
25
        start_node = get_root_node(cur)
26
27
    print('{}{}'.format(indent * _level, repr(start_node)))  # noqa
28
29
    for child in list(get_children(cur, start_node)):
30
        print_tree(cur, child, indent=indent, _level=_level + 1)
31
32
33
def insert_node(cur, parent, properties=None, position=None,
34
                auto_position=True, id=None):
35
    """
36
    Create a ``Node`` object, insert it into the tree and then return
37
    it.
38
39
    :param parent: Reference to its parent node. If `None`, this will
40
                   be the root node.
41
    :type parent: Node or uuid4
42
    :param dict properties: Inheritable key/value pairs
43
                            (see :ref:`core-properties`)
44
    :param int position: Position in between siblings. If 0, the node
45
                         will be inserted at the beginning of the
46
                         parents children. If -1, the node will be
47
                         inserted the the end of the parents children.
48
                         If `auto_position` is disabled, this is just a
49
                         value.
50
    :param bool auto_position: See :ref:`core-positioning`
51
    :param uuid4 id: Use this ID instead of automatically generating
52
                     one.
53
    """
54
    if id is None:
55
        id = str(uuid.uuid4())
56
57
    parent_id = None
58
    if parent is not None:
59
        parent_id = str(parent)
60
61
    if properties is None:
62
        properties = {}
63
64
    # Can't run set_position() because the node doesn't exist yet
65
    if auto_position:
66
        if isinstance(position, int) and position >= 0:
67
            ensure_free_position(cur, parent, position)
68
        else:
69
            position = find_highest_position(cur, parent) + 1
70
71
    sql = """
72
        INSERT INTO
73
          nodes
74
          (id, parent, position, properties)
75
        VALUES
76
          (%s, %s, %s, %s);
77
    """
78
    cur.execute(sql, (id, parent_id, position, json.dumps(properties)))
79
80
    return NodeData(id, parent_id, position, properties)
81
82
83
def delete_node(cur, node, auto_position=True):
84
    """
85
    Delete node and its subtree.
86
87
    :param node:
88
    :type node: Node or uuid4
89
    :param bool auto_position: See :ref:`core-positioning`
90
    """
91
    id = str(node)
92
93
    # Get Node object if integer (ID) was passed
94
    if auto_position and not isinstance(node, NodeData):
95
        node = get_node(cur, id)
96
97
    sql = """
98
        DELETE FROM
99
          nodes
100
        WHERE
101
          id=%s;
102
    """
103
    cur.execute(sql, (id, ))
104
105
    if auto_position:
106
        shift_positions(cur, node.parent, node.position, -1)
107
108
109
def change_parent(cur, node, new_parent, position=None, auto_position=True):
110
    """
111
    Move node and its subtree from its current to another parent node.
112
    Return updated ``Node`` object with new parent set. Raise
113
    ``ValueError`` if ``new_parent`` is inside ``node`` s subtree.
114
115
    :param node:
116
    :type node: Node or uuid4
117
    :param new_parent: Reference to the new parent node
118
    :type new_parent: Node or uuid4
119
    :param int position: Position in between siblings. If 0, the node
120
                         will be inserted at the beginning of the
121
                         parents children. If -1, the node will be
122
                         inserted the the end of the parents children.
123
                         If `auto_position` is disabled, this is just a
124
                         value.
125
    :param bool auto_position: See :ref:`core-positioning`.
126
    """
127
    new_parent_id = str(new_parent)
128
    if new_parent_id in get_descendant_ids(cur, node):
129
        raise exceptions.CantMoveIntoOwnSubtree()
130
131
    # Can't run set_position() here because the node hasn't been moved yet,
132
    # must do it manually
133
    if auto_position:
134
        if isinstance(position, int) and position >= 0:
135
            ensure_free_position(cur, new_parent_id, position)
136
        else:
137
            position = find_highest_position(cur, new_parent_id) + 1
138
139
    sql = """
140
        UPDATE
141
          nodes
142
        SET
143
          parent=%s,
144
          position=%s
145
        WHERE
146
          id=%s;
147
    """
148
    cur.execute(sql, (new_parent_id, position, str(node)))
149
150
    if isinstance(node, str):
151
        node = get_node(cur, node)
152
153
    kwargs = node.to_dict()
154
    kwargs['parent'] = new_parent_id
155
    kwargs['position'] = position
156
    return NodeData(**kwargs)
157