node.py 8.2 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.core.cache import cache
baltery's avatar
baltery 已提交
9 10

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

__all__ = ['Node']


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

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

baltery's avatar
baltery 已提交
29 30 31
    class Meta:
        verbose_name = _("Node")

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

baltery's avatar
baltery 已提交
35
    def __eq__(self, other):
36 37
        if not other:
            return False
baltery's avatar
baltery 已提交
38 39 40 41 42 43 44
        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 已提交
45 46 47 48
        return self_key.__lt__(other_key)

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

baltery's avatar
baltery 已提交
50 51 52
    @property
    def name(self):
        return self.value
53

54 55 56 57 58 59 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
    @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)

88 89
    @property
    def full_value(self):
90
        key = self._full_value_cache_key.format(self.key)
baltery's avatar
baltery 已提交
91 92 93
        cached = cache.get(key)
        if cached:
            return cached
94
        if self.is_root():
95
            return self.value
baltery's avatar
baltery 已提交
96 97
        parent_full_value = self.parent.full_value
        value = parent_full_value + ' / ' + self.value
98
        key = self._full_value_cache_key.format(self.key)
baltery's avatar
baltery 已提交
99
        cache.set(key, value, 3600)
100
        return value
baltery's avatar
baltery 已提交
101 102

    def expire_full_value(self):
103
        key = self._full_value_cache_key.format(self.key)
baltery's avatar
baltery 已提交
104
        cache.delete_pattern(key+'*')
baltery's avatar
baltery 已提交
105 106 107

    @property
    def level(self):
baltery's avatar
baltery 已提交
108
        return len(self.key.split(':'))
baltery's avatar
baltery 已提交
109

baltery's avatar
baltery 已提交
110
    def get_next_child_key(self):
baltery's avatar
baltery 已提交
111 112 113
        mark = self.child_mark
        self.child_mark += 1
        self.save()
baltery's avatar
baltery 已提交
114
        return "{}:{}".format(self.key, mark)
baltery's avatar
baltery 已提交
115

baltery's avatar
baltery 已提交
116
    def create_child(self, value):
baltery's avatar
baltery 已提交
117 118 119 120
        with transaction.atomic():
            child_key = self.get_next_child_key()
            child = self.__class__.objects.create(key=child_key, value=value)
            return child
baltery's avatar
baltery 已提交
121

baltery's avatar
baltery 已提交
122
    def get_children(self, with_self=False):
baltery's avatar
baltery 已提交
123
        pattern = r'^{0}$|^{0}:[0-9]+$' if with_self else r'^{0}:[0-9]+$'
baltery's avatar
baltery 已提交
124
        return self.__class__.objects.filter(
baltery's avatar
baltery 已提交
125
            key__regex=pattern.format(self.key)
baltery's avatar
baltery 已提交
126
        )
baltery's avatar
baltery 已提交
127

baltery's avatar
baltery 已提交
128 129
    def get_all_children(self, with_self=False):
        pattern = r'^{0}$|^{0}:' if with_self else r'^{0}'
baltery's avatar
baltery 已提交
130
        return self.__class__.objects.filter(
baltery's avatar
baltery 已提交
131
            key__regex=pattern.format(self.key)
baltery's avatar
baltery 已提交
132 133
        )

baltery's avatar
baltery 已提交
134 135 136 137 138
    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 已提交
139
        )
baltery's avatar
baltery 已提交
140 141 142
        if not with_self:
            sibling = sibling.exclude(key=self.key)
        return sibling
baltery's avatar
baltery 已提交
143

baltery's avatar
baltery 已提交
144
    def get_family(self):
baltery's avatar
baltery 已提交
145
        ancestor = self.get_ancestor()
baltery's avatar
baltery 已提交
146 147
        children = self.get_all_children()
        return [*tuple(ancestor), self, *tuple(children)]
baltery's avatar
baltery 已提交
148

baltery's avatar
baltery 已提交
149 150
    def get_assets(self):
        from .asset import Asset
baltery's avatar
baltery 已提交
151
        if self.is_default_node():
152
            assets = Asset.objects.filter(Q(nodes__id=self.id) | Q(nodes__isnull=True))
153
        else:
baltery's avatar
baltery 已提交
154
            assets = Asset.objects.filter(nodes__id=self.id)
B
BaiJiangJie 已提交
155
        return assets.distinct()
baltery's avatar
baltery 已提交
156

baltery's avatar
baltery 已提交
157 158
    def get_valid_assets(self):
        return self.get_assets().valid()
159

baltery's avatar
baltery 已提交
160 161
    def get_all_assets(self):
        from .asset import Asset
baltery's avatar
baltery 已提交
162 163 164 165
        pattern = r'^{0}$|^{0}:'.format(self.key)
        args = []
        kwargs = {}
        if self.is_default_node():
166
            args.append(Q(nodes__key__regex=pattern) | Q(nodes=None))
baltery's avatar
baltery 已提交
167
        else:
baltery's avatar
baltery 已提交
168
            kwargs['nodes__key__regex'] = pattern
baltery's avatar
baltery 已提交
169
        assets = Asset.objects.filter(*args, **kwargs).distinct()
baltery's avatar
baltery 已提交
170 171
        return assets

baltery's avatar
baltery 已提交
172 173
    def get_all_valid_assets(self):
        return self.get_all_assets().valid()
174

baltery's avatar
baltery 已提交
175 176 177
    def is_default_node(self):
        return self.is_root() and self.key == '0'

baltery's avatar
baltery 已提交
178
    def is_root(self):
baltery's avatar
baltery 已提交
179
        if self.key.isdigit():
baltery's avatar
baltery 已提交
180 181 182
            return True
        else:
            return False
baltery's avatar
baltery 已提交
183

baltery's avatar
baltery 已提交
184 185 186 187 188
    @property
    def parent_key(self):
        parent_key = ":".join(self.key.split(":")[:-1])
        return parent_key

189 190
    @property
    def parent(self):
baltery's avatar
baltery 已提交
191
        if self.is_root():
baltery's avatar
baltery 已提交
192
            return self
193
        try:
baltery's avatar
baltery 已提交
194
            parent = self.__class__.objects.get(key=self.parent_key)
baltery's avatar
baltery 已提交
195
            return parent
196 197 198
        except Node.DoesNotExist:
            return self.__class__.root()

baltery's avatar
baltery 已提交
199 200
    @parent.setter
    def parent(self, parent):
baltery's avatar
baltery 已提交
201 202 203 204 205 206 207 208 209 210 211
        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 已提交
212

213 214 215
    def get_ancestor_keys(self, with_self=False):
        parent_keys = []
        key_list = self.key.split(":")
baltery's avatar
baltery 已提交
216
        if not with_self:
217 218 219 220 221 222 223 224
            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 已提交
225 226 227
        ancestor = self.__class__.objects.filter(
            key__in=ancestor_keys
        ).order_by('key')
228 229
        return ancestor

baltery's avatar
baltery 已提交
230 231
    @classmethod
    def create_root_node(cls):
232 233
        # 如果使用current_org 在set_current_org时会死循环
        _current_org = get_current_org()
baltery's avatar
baltery 已提交
234
        with transaction.atomic():
baltery's avatar
baltery 已提交
235 236 237 238 239 240 241 242
            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)
243
            root = cls.objects.create(key=key, value=_current_org.name)
baltery's avatar
baltery 已提交
244 245
            return root

baltery's avatar
baltery 已提交
246
    @classmethod
baltery's avatar
baltery 已提交
247
    def root(cls):
baltery's avatar
baltery 已提交
248
        root = cls.objects.filter(key__regex=r'^[0-9]+$')
baltery's avatar
baltery 已提交
249 250
        if root:
            return root[0]
baltery's avatar
baltery 已提交
251 252 253
        else:
            return cls.create_root_node()

254 255 256
    @classmethod
    def default_node(cls):
        defaults = {'value': 'Default'}
baltery's avatar
baltery 已提交
257
        return cls.objects.get_or_create(defaults=defaults, key='1')
258

baltery's avatar
baltery 已提交
259 260 261 262 263 264 265
    @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))

baltery's avatar
baltery 已提交
266