node.py 10.0 KB
Newer Older
baltery's avatar
baltery 已提交
1 2
# -*- coding: utf-8 -*-
#
baltery's avatar
baltery 已提交
3
import uuid
baltery's avatar
baltery 已提交
4

5
from django.db import models, transaction
6
from django.db.models import Q
baltery's avatar
baltery 已提交
7
from django.utils.translation import ugettext_lazy as _
baltery's avatar
baltery 已提交
8
from django.utils.translation import ugettext
baltery's avatar
baltery 已提交
9
from django.core.cache import cache
baltery's avatar
baltery 已提交
10 11

from orgs.mixins import OrgModelMixin
baltery's avatar
baltery 已提交
12
from orgs.utils import set_current_org, get_current_org
baltery's avatar
baltery 已提交
13
from orgs.models import Organization
baltery's avatar
baltery 已提交
14 15 16 17

__all__ = ['Node']


baltery's avatar
baltery 已提交
18
class Node(OrgModelMixin):
baltery's avatar
baltery 已提交
19 20
    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
    key = models.CharField(unique=True, max_length=64, verbose_name=_("Key"))  # '1:1:1:1'
21
    value = models.CharField(max_length=128, verbose_name=_("Value"))
baltery's avatar
baltery 已提交
22 23 24
    child_mark = models.IntegerField(default=0)
    date_create = models.DateTimeField(auto_now_add=True)

25
    is_node = True
26 27 28
    _assets_amount = None
    _full_value_cache_key = '_NODE_VALUE_{}'
    _assets_amount_cache_key = '_NODE_ASSETS_AMOUNT_{}'
baltery's avatar
baltery 已提交
29

baltery's avatar
baltery 已提交
30 31
    class Meta:
        verbose_name = _("Node")
baltery's avatar
baltery 已提交
32
        ordering = ['key']
baltery's avatar
baltery 已提交
33

baltery's avatar
baltery 已提交
34
    def __str__(self):
baltery's avatar
baltery 已提交
35
        return self.full_value
baltery's avatar
baltery 已提交
36

baltery's avatar
baltery 已提交
37
    def __eq__(self, other):
38 39
        if not other:
            return False
baltery's avatar
baltery 已提交
40 41 42
        return self.key == other.key

    def __gt__(self, other):
43
        if self.is_root() and not other.is_root():
baltery's avatar
baltery 已提交
44
            return True
45 46
        elif not self.is_root() and other.is_root():
            return False
baltery's avatar
baltery 已提交
47 48
        self_key = [int(k) for k in self.key.split(':')]
        other_key = [int(k) for k in other.key.split(':')]
baltery's avatar
baltery 已提交
49 50 51 52 53
        self_parent_key = self_key[:-1]
        other_parent_key = other_key[:-1]

        if self_parent_key == other_parent_key:
            return self.name > other.name
54 55 56 57
        if len(self_parent_key) < len(other_parent_key):
            return True
        elif len(self_parent_key) > len(other_parent_key):
            return False
58
        return self_key > other_key
baltery's avatar
baltery 已提交
59 60 61

    def __lt__(self, other):
        return not self.__gt__(other)
baltery's avatar
baltery 已提交
62

baltery's avatar
baltery 已提交
63 64 65
    @property
    def name(self):
        return self.value
66

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
    @property
    def assets_amount(self):
        """
        获取节点下所有资产数量速度太慢,所以需要重写,使用cache等方案
        :return:
        """
        if self._assets_amount is not None:
            return self._assets_amount
        cache_key = self._assets_amount_cache_key.format(self.key)
        cached = cache.get(cache_key)
        if cached is not None:
            return cached
        assets_amount = self.get_all_assets().count()
        cache.set(cache_key, assets_amount, 3600)
        return assets_amount

    @assets_amount.setter
    def assets_amount(self, value):
        self._assets_amount = value

    def expire_assets_amount(self):
        ancestor_keys = self.get_ancestor_keys(with_self=True)
        cache_keys = [self._assets_amount_cache_key.format(k) for k in ancestor_keys]
        cache.delete_many(cache_keys)

    @classmethod
    def expire_nodes_assets_amount(cls, nodes=None):
        if nodes:
            for node in nodes:
                node.expire_assets_amount()
            return
        key = cls._assets_amount_cache_key.format('*')
        cache.delete_pattern(key)

101 102
    @property
    def full_value(self):
103
        key = self._full_value_cache_key.format(self.key)
baltery's avatar
baltery 已提交
104 105 106
        cached = cache.get(key)
        if cached:
            return cached
107
        if self.is_root():
108
            return self.value
baltery's avatar
baltery 已提交
109 110
        parent_full_value = self.parent.full_value
        value = parent_full_value + ' / ' + self.value
111
        key = self._full_value_cache_key.format(self.key)
baltery's avatar
baltery 已提交
112
        cache.set(key, value, 3600)
113
        return value
baltery's avatar
baltery 已提交
114 115

    def expire_full_value(self):
116
        key = self._full_value_cache_key.format(self.key)
baltery's avatar
baltery 已提交
117
        cache.delete_pattern(key+'*')
baltery's avatar
baltery 已提交
118

baltery's avatar
baltery 已提交
119 120 121 122 123 124 125 126 127
    @classmethod
    def expire_nodes_full_value(cls, nodes=None):
        if nodes:
            for node in nodes:
                node.expire_full_value()
            return
        key = cls._full_value_cache_key.format('*')
        cache.delete_pattern(key+'*')

baltery's avatar
baltery 已提交
128 129
    @property
    def level(self):
baltery's avatar
baltery 已提交
130
        return len(self.key.split(':'))
baltery's avatar
baltery 已提交
131

baltery's avatar
baltery 已提交
132
    def get_next_child_key(self):
baltery's avatar
baltery 已提交
133 134 135
        mark = self.child_mark
        self.child_mark += 1
        self.save()
baltery's avatar
baltery 已提交
136
        return "{}:{}".format(self.key, mark)
baltery's avatar
baltery 已提交
137

baltery's avatar
baltery 已提交
138 139 140 141 142 143 144 145 146 147 148
    def get_next_child_preset_name(self):
        name = ugettext("New node")
        values = [
            child.value[child.value.rfind(' '):]
            for child in self.get_children()
            if child.value.startswith(name)
        ]
        values = [int(value) for value in values if value.strip().isdigit()]
        count = max(values) + 1 if values else 1
        return '{} {}'.format(name, count)

baltery's avatar
baltery 已提交
149
    def create_child(self, value, _id=None):
baltery's avatar
baltery 已提交
150 151
        with transaction.atomic():
            child_key = self.get_next_child_key()
baltery's avatar
baltery 已提交
152
            child = self.__class__.objects.create(id=_id, key=child_key, value=value)
baltery's avatar
baltery 已提交
153
            return child
baltery's avatar
baltery 已提交
154

baltery's avatar
baltery 已提交
155
    def get_children(self, with_self=False):
baltery's avatar
baltery 已提交
156
        pattern = r'^{0}$|^{0}:[0-9]+$' if with_self else r'^{0}:[0-9]+$'
baltery's avatar
baltery 已提交
157
        return self.__class__.objects.filter(
baltery's avatar
baltery 已提交
158
            key__regex=pattern.format(self.key)
baltery's avatar
baltery 已提交
159
        )
baltery's avatar
baltery 已提交
160

baltery's avatar
baltery 已提交
161
    def get_all_children(self, with_self=False):
Z
zhnxin 已提交
162
        pattern = r'^{0}$|^{0}:' if with_self else r'^{0}:'
baltery's avatar
baltery 已提交
163
        return self.__class__.objects.filter(
baltery's avatar
baltery 已提交
164
            key__regex=pattern.format(self.key)
baltery's avatar
baltery 已提交
165 166
        )

baltery's avatar
baltery 已提交
167 168 169 170 171
    def get_sibling(self, with_self=False):
        key = ':'.join(self.key.split(':')[:-1])
        pattern = r'^{}:[0-9]+$'.format(key)
        sibling = self.__class__.objects.filter(
            key__regex=pattern.format(self.key)
baltery's avatar
baltery 已提交
172
        )
baltery's avatar
baltery 已提交
173 174 175
        if not with_self:
            sibling = sibling.exclude(key=self.key)
        return sibling
baltery's avatar
baltery 已提交
176

baltery's avatar
baltery 已提交
177
    def get_family(self):
baltery's avatar
baltery 已提交
178
        ancestor = self.get_ancestor()
baltery's avatar
baltery 已提交
179 180
        children = self.get_all_children()
        return [*tuple(ancestor), self, *tuple(children)]
baltery's avatar
baltery 已提交
181

baltery's avatar
baltery 已提交
182 183
    def get_assets(self):
        from .asset import Asset
baltery's avatar
baltery 已提交
184
        if self.is_default_node():
185
            assets = Asset.objects.filter(Q(nodes__id=self.id) | Q(nodes__isnull=True))
186
        else:
baltery's avatar
baltery 已提交
187
            assets = Asset.objects.filter(nodes__id=self.id)
B
BaiJiangJie 已提交
188
        return assets.distinct()
baltery's avatar
baltery 已提交
189

baltery's avatar
baltery 已提交
190 191
    def get_valid_assets(self):
        return self.get_assets().valid()
192

baltery's avatar
baltery 已提交
193 194
    def get_all_assets(self):
        from .asset import Asset
baltery's avatar
baltery 已提交
195 196 197
        pattern = r'^{0}$|^{0}:'.format(self.key)
        args = []
        kwargs = {}
baltery's avatar
baltery 已提交
198
        if self.is_root():
199
            args.append(Q(nodes__key__regex=pattern) | Q(nodes=None))
baltery's avatar
baltery 已提交
200
        else:
baltery's avatar
baltery 已提交
201
            kwargs['nodes__key__regex'] = pattern
baltery's avatar
baltery 已提交
202
        assets = Asset.objects.filter(*args, **kwargs).distinct()
baltery's avatar
baltery 已提交
203 204
        return assets

baltery's avatar
baltery 已提交
205 206
    def get_all_valid_assets(self):
        return self.get_all_assets().valid()
207

baltery's avatar
baltery 已提交
208 209 210
    def is_default_node(self):
        return self.is_root() and self.key == '0'

baltery's avatar
baltery 已提交
211
    def is_root(self):
baltery's avatar
baltery 已提交
212
        if self.key.isdigit():
baltery's avatar
baltery 已提交
213 214 215
            return True
        else:
            return False
baltery's avatar
baltery 已提交
216

baltery's avatar
baltery 已提交
217 218 219 220 221
    @property
    def parent_key(self):
        parent_key = ":".join(self.key.split(":")[:-1])
        return parent_key

222 223
    @property
    def parent(self):
baltery's avatar
baltery 已提交
224
        if self.is_root():
baltery's avatar
baltery 已提交
225
            return self
226
        try:
baltery's avatar
baltery 已提交
227
            parent = self.__class__.objects.get(key=self.parent_key)
baltery's avatar
baltery 已提交
228
            return parent
229 230 231
        except Node.DoesNotExist:
            return self.__class__.root()

baltery's avatar
baltery 已提交
232 233
    @parent.setter
    def parent(self, parent):
baltery's avatar
baltery 已提交
234 235 236 237 238 239 240 241 242 243 244
        if not self.is_node:
            self.key = parent.key + ':fake'
            return
        children = self.get_all_children()
        old_key = self.key
        with transaction.atomic():
            self.key = parent.get_next_child_key()
            for child in children:
                child.key = child.key.replace(old_key, self.key, 1)
                child.save()
            self.save()
baltery's avatar
baltery 已提交
245

246 247 248
    def get_ancestor_keys(self, with_self=False):
        parent_keys = []
        key_list = self.key.split(":")
baltery's avatar
baltery 已提交
249
        if not with_self:
250 251 252 253 254 255 256 257
            key_list.pop()
        for i in range(len(key_list)):
            parent_keys.append(":".join(key_list))
            key_list.pop()
        return parent_keys

    def get_ancestor(self, with_self=False):
        ancestor_keys = self.get_ancestor_keys(with_self=with_self)
baltery's avatar
baltery 已提交
258 259 260
        ancestor = self.__class__.objects.filter(
            key__in=ancestor_keys
        ).order_by('key')
261 262
        return ancestor

baltery's avatar
baltery 已提交
263 264
    @classmethod
    def create_root_node(cls):
265 266
        # 如果使用current_org 在set_current_org时会死循环
        _current_org = get_current_org()
baltery's avatar
baltery 已提交
267
        with transaction.atomic():
baltery's avatar
baltery 已提交
268 269 270 271 272 273 274 275
            if not _current_org.is_real():
                return cls.default_node()
            set_current_org(Organization.root())
            org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$')
            org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True) or ['1']
            key = max([int(k) for k in org_nodes_roots_keys])
            key = str(key + 1) if key != 0 else '2'
            set_current_org(_current_org)
276
            root = cls.objects.create(key=key, value=_current_org.name)
baltery's avatar
baltery 已提交
277 278
            return root

baltery's avatar
baltery 已提交
279
    @classmethod
baltery's avatar
baltery 已提交
280
    def root(cls):
baltery's avatar
baltery 已提交
281
        root = cls.objects.filter(key__regex=r'^[0-9]+$')
baltery's avatar
baltery 已提交
282 283
        if root:
            return root[0]
baltery's avatar
baltery 已提交
284 285 286
        else:
            return cls.create_root_node()

287 288 289
    @classmethod
    def default_node(cls):
        defaults = {'value': 'Default'}
baltery's avatar
baltery 已提交
290 291
        obj, created = cls.objects.get_or_create(defaults=defaults, key='1')
        return obj
292

baltery's avatar
baltery 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
    def as_tree_node(self):
        from common.tree import TreeNode
        from ..serializers import NodeSerializer
        name = '{} ({})'.format(self.value, self.assets_amount)
        node_serializer = NodeSerializer(instance=self)
        data = {
            'id': self.key,
            'name': name,
            'title': name,
            'pId': self.parent_key,
            'isParent': True,
            'open': self.is_root(),
            'meta': {
                'node': node_serializer.data,
                'type': 'node'
            }
        }
        tree_node = TreeNode(**data)
        return tree_node

baltery's avatar
baltery 已提交
313 314 315 316 317 318
    @classmethod
    def generate_fake(cls, count=100):
        import random
        for i in range(count):
            node = random.choice(cls.objects.all())
            node.create_child('Node {}'.format(i))