node.py 9.7 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 43 44 45 46
        return self.key == other.key

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

        if self_parent_key == other_parent_key:
            return self.name > other.name
52
        return self_key > other_key
baltery's avatar
baltery 已提交
53 54 55

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

baltery's avatar
baltery 已提交
57 58 59
    @property
    def name(self):
        return self.value
60

61 62 63 64 65 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
    @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)

95 96
    @property
    def full_value(self):
97
        key = self._full_value_cache_key.format(self.key)
baltery's avatar
baltery 已提交
98 99 100
        cached = cache.get(key)
        if cached:
            return cached
101
        if self.is_root():
102
            return self.value
baltery's avatar
baltery 已提交
103 104
        parent_full_value = self.parent.full_value
        value = parent_full_value + ' / ' + self.value
105
        key = self._full_value_cache_key.format(self.key)
baltery's avatar
baltery 已提交
106
        cache.set(key, value, 3600)
107
        return value
baltery's avatar
baltery 已提交
108 109

    def expire_full_value(self):
110
        key = self._full_value_cache_key.format(self.key)
baltery's avatar
baltery 已提交
111
        cache.delete_pattern(key+'*')
baltery's avatar
baltery 已提交
112

baltery's avatar
baltery 已提交
113 114 115 116 117 118 119 120 121
    @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 已提交
122 123
    @property
    def level(self):
baltery's avatar
baltery 已提交
124
        return len(self.key.split(':'))
baltery's avatar
baltery 已提交
125

baltery's avatar
baltery 已提交
126
    def get_next_child_key(self):
baltery's avatar
baltery 已提交
127 128 129
        mark = self.child_mark
        self.child_mark += 1
        self.save()
baltery's avatar
baltery 已提交
130
        return "{}:{}".format(self.key, mark)
baltery's avatar
baltery 已提交
131

baltery's avatar
baltery 已提交
132 133 134 135 136 137 138 139 140 141 142
    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 已提交
143
    def create_child(self, value, _id=None):
baltery's avatar
baltery 已提交
144 145
        with transaction.atomic():
            child_key = self.get_next_child_key()
baltery's avatar
baltery 已提交
146
            child = self.__class__.objects.create(id=_id, key=child_key, value=value)
baltery's avatar
baltery 已提交
147
            return child
baltery's avatar
baltery 已提交
148

baltery's avatar
baltery 已提交
149
    def get_children(self, with_self=False):
baltery's avatar
baltery 已提交
150
        pattern = r'^{0}$|^{0}:[0-9]+$' if with_self else r'^{0}:[0-9]+$'
baltery's avatar
baltery 已提交
151
        return self.__class__.objects.filter(
baltery's avatar
baltery 已提交
152
            key__regex=pattern.format(self.key)
baltery's avatar
baltery 已提交
153
        )
baltery's avatar
baltery 已提交
154

baltery's avatar
baltery 已提交
155
    def get_all_children(self, with_self=False):
Z
zhnxin 已提交
156
        pattern = r'^{0}$|^{0}:' if with_self else r'^{0}:'
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 160
        )

baltery's avatar
baltery 已提交
161 162 163 164 165
    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 已提交
166
        )
baltery's avatar
baltery 已提交
167 168 169
        if not with_self:
            sibling = sibling.exclude(key=self.key)
        return sibling
baltery's avatar
baltery 已提交
170

baltery's avatar
baltery 已提交
171
    def get_family(self):
baltery's avatar
baltery 已提交
172
        ancestor = self.get_ancestor()
baltery's avatar
baltery 已提交
173 174
        children = self.get_all_children()
        return [*tuple(ancestor), self, *tuple(children)]
baltery's avatar
baltery 已提交
175

baltery's avatar
baltery 已提交
176 177
    def get_assets(self):
        from .asset import Asset
baltery's avatar
baltery 已提交
178
        if self.is_default_node():
179
            assets = Asset.objects.filter(Q(nodes__id=self.id) | Q(nodes__isnull=True))
180
        else:
baltery's avatar
baltery 已提交
181
            assets = Asset.objects.filter(nodes__id=self.id)
B
BaiJiangJie 已提交
182
        return assets.distinct()
baltery's avatar
baltery 已提交
183

baltery's avatar
baltery 已提交
184 185
    def get_valid_assets(self):
        return self.get_assets().valid()
186

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

baltery's avatar
baltery 已提交
199 200
    def get_all_valid_assets(self):
        return self.get_all_assets().valid()
201

baltery's avatar
baltery 已提交
202 203 204
    def is_default_node(self):
        return self.is_root() and self.key == '0'

baltery's avatar
baltery 已提交
205
    def is_root(self):
baltery's avatar
baltery 已提交
206
        if self.key.isdigit():
baltery's avatar
baltery 已提交
207 208 209
            return True
        else:
            return False
baltery's avatar
baltery 已提交
210

baltery's avatar
baltery 已提交
211 212 213 214 215
    @property
    def parent_key(self):
        parent_key = ":".join(self.key.split(":")[:-1])
        return parent_key

216 217
    @property
    def parent(self):
baltery's avatar
baltery 已提交
218
        if self.is_root():
baltery's avatar
baltery 已提交
219
            return self
220
        try:
baltery's avatar
baltery 已提交
221
            parent = self.__class__.objects.get(key=self.parent_key)
baltery's avatar
baltery 已提交
222
            return parent
223 224 225
        except Node.DoesNotExist:
            return self.__class__.root()

baltery's avatar
baltery 已提交
226 227
    @parent.setter
    def parent(self, parent):
baltery's avatar
baltery 已提交
228 229 230 231 232 233 234 235 236 237 238
        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 已提交
239

240 241 242
    def get_ancestor_keys(self, with_self=False):
        parent_keys = []
        key_list = self.key.split(":")
baltery's avatar
baltery 已提交
243
        if not with_self:
244 245 246 247 248 249 250 251
            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 已提交
252 253 254
        ancestor = self.__class__.objects.filter(
            key__in=ancestor_keys
        ).order_by('key')
255 256
        return ancestor

baltery's avatar
baltery 已提交
257 258
    @classmethod
    def create_root_node(cls):
259 260
        # 如果使用current_org 在set_current_org时会死循环
        _current_org = get_current_org()
baltery's avatar
baltery 已提交
261
        with transaction.atomic():
baltery's avatar
baltery 已提交
262 263 264 265 266 267 268 269
            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)
270
            root = cls.objects.create(key=key, value=_current_org.name)
baltery's avatar
baltery 已提交
271 272
            return root

baltery's avatar
baltery 已提交
273
    @classmethod
baltery's avatar
baltery 已提交
274
    def root(cls):
baltery's avatar
baltery 已提交
275
        root = cls.objects.filter(key__regex=r'^[0-9]+$')
baltery's avatar
baltery 已提交
276 277
        if root:
            return root[0]
baltery's avatar
baltery 已提交
278 279 280
        else:
            return cls.create_root_node()

281 282 283
    @classmethod
    def default_node(cls):
        defaults = {'value': 'Default'}
baltery's avatar
baltery 已提交
284 285
        obj, created = cls.objects.get_or_create(defaults=defaults, key='1')
        return obj
286

baltery's avatar
baltery 已提交
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
    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 已提交
307 308 309 310 311 312
    @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))