##// END OF EJS Templates
Speed up tag local cache
neko259 -
r2021:b8cdd5b1 default
parent child Browse files
Show More
@@ -1,212 +1,212 b''
1 1 import hashlib
2 2 from django.urls import reverse
3 3 from django.db import models
4 4 from django.db.models import Count, Q
5 5 from django.utils.translation import get_language
6 6
7 7 import boards
8 8 from boards.models import Attachment
9 9 from boards.models.attachment import FILE_TYPES_IMAGE
10 10 from boards.models.base import Viewable
11 11 from boards.models.thread import STATUS_ACTIVE, STATUS_BUMPLIMIT, STATUS_ARCHIVE
12 12 from boards.utils import cached_result
13 13
14 14 __author__ = 'neko259'
15 15
16 16
17 17 RELATED_TAGS_COUNT = 5
18 18 DEFAULT_LOCALE = 'default'
19 19
20 20
21 21 class TagAliasManager(models.Manager):
22 22 def filter_localized(self, *args, **kwargs):
23 23 locale = get_language()
24 24 tag_aliases = (self.filter(locale=locale)
25 25 | self.filter(Q(locale=DEFAULT_LOCALE)
26 26 & ~Q(parent__aliases__locale=locale)))
27 27 return tag_aliases.filter(**kwargs)
28 28
29 29
30 30 class TagAlias(models.Model, Viewable):
31 31 class Meta:
32 32 app_label = 'boards'
33 33 ordering = ('name',)
34 34
35 35 objects = TagAliasManager()
36 36
37 37 name = models.CharField(max_length=100, db_index=True)
38 38 locale = models.CharField(max_length=10, db_index=True)
39 39
40 40 parent = models.ForeignKey('Tag', on_delete=models.CASCADE, null=True,
41 41 blank=True, related_name='aliases')
42 42
43 43
44 44 class TagManager(models.Manager):
45 45 def get_not_empty_tags(self):
46 46 """
47 47 Gets tags that have non-archived threads.
48 48 """
49 49
50 50 return self.annotate(num_threads=Count('thread_tags'))\
51 51 .filter(num_threads__gt=0)\
52 52 .filter(aliases__in=TagAlias.objects.filter_localized())\
53 53 .order_by('aliases__name')
54 54
55 55 def get_tag_url_list(self, tags: list) -> str:
56 56 """
57 57 Gets a comma-separated list of tag links.
58 58 """
59 59
60 60 return ', '.join([tag.get_view() for tag in tags])
61 61
62 62 def get_by_alias(self, alias):
63 63 tag = None
64 64 aliases = TagAlias.objects.filter(name=alias).all()
65 65 if aliases:
66 66 tag = aliases[0].parent
67 67
68 68 return tag
69 69
70 70 def get_or_create_with_alias(self, name, required=False):
71 71 tag = self.get_by_alias(name)
72 72 created = False
73 73 if not tag:
74 74 tag = self.create(required=required)
75 75 TagAlias.objects.create(name=name, locale=DEFAULT_LOCALE, parent=tag)
76 76 created = True
77 77 return tag, created
78 78
79 79
80 80 class Tag(models.Model, Viewable):
81 81 """
82 82 A tag is a text node assigned to the thread. The tag serves as a board
83 83 section. There can be multiple tags for each thread
84 84 """
85 85
86 86 objects = TagManager()
87 87
88 88 class Meta:
89 89 app_label = 'boards'
90 90
91 91 required = models.BooleanField(default=False, db_index=True)
92 92 description = models.TextField(blank=True)
93 93
94 94 parent = models.ForeignKey('Tag', on_delete=models.CASCADE, null=True,
95 95 blank=True, related_name='children')
96 96
97 97 def get_name(self):
98 98 return self.aliases.get(locale=DEFAULT_LOCALE).name
99 99
100 100 def __str__(self):
101 101 return self.get_name()
102 102
103 103 def is_empty(self) -> bool:
104 104 """
105 105 Checks if the tag has some threads.
106 106 """
107 107
108 108 return self.get_thread_count() == 0
109 109
110 110 def get_thread_count(self, status=None) -> int:
111 111 threads = self.get_threads()
112 112 if status is not None:
113 113 threads = threads.filter(status=status)
114 114 return threads.count()
115 115
116 116 def get_active_thread_count(self) -> int:
117 117 return self.get_thread_count(status=STATUS_ACTIVE)
118 118
119 119 def get_bumplimit_thread_count(self) -> int:
120 120 return self.get_thread_count(status=STATUS_BUMPLIMIT)
121 121
122 122 def get_archived_thread_count(self) -> int:
123 123 return self.get_thread_count(status=STATUS_ARCHIVE)
124 124
125 125 @cached_result()
126 126 def get_absolute_url(self):
127 127 return reverse('tag', kwargs={'tag_name': self.get_name()})
128 128
129 129 def get_threads(self):
130 130 return self.thread_tags.order_by('-bump_time')
131 131
132 132 def is_required(self):
133 133 return self.required
134 134
135 135 def _get_locale_cache_key(self):
136 return '{}_{}'.format(self.id, get_language())
136 return [get_language()]
137 137
138 138 @cached_result(key_method=_get_locale_cache_key)
139 139 def get_localized_name(self):
140 140 locale = get_language()
141 141
142 142 aliases = self.aliases.filter(Q(locale=locale) | Q(locale=DEFAULT_LOCALE))
143 143
144 144 localized_tag_name = None
145 145 default_tag_name = None
146 146
147 147 for alias in aliases:
148 148 if alias.locale == locale:
149 149 localized_tag_name = alias.name
150 150 elif alias.locale == DEFAULT_LOCALE:
151 151 default_tag_name = alias.name
152 152
153 153 return localized_tag_name if localized_tag_name else default_tag_name
154 154
155 155 def get_view(self):
156 156 name = self.get_localized_name()
157 157 link = '<a class="tag" href="{}">{}</a>'.format(
158 158 self.get_absolute_url(), name)
159 159 if self.is_required():
160 160 link = '<b>{}</b>'.format(link)
161 161 return link
162 162
163 163 @cached_result()
164 164 def get_post_count(self):
165 165 return self.get_threads().aggregate(num_posts=Count('replies'))['num_posts']
166 166
167 167 def get_description(self):
168 168 return self.description
169 169
170 170 def get_random_image_post(self, status=[STATUS_ACTIVE, STATUS_BUMPLIMIT]):
171 171 posts = boards.models.Post.objects.filter(attachments__mimetype__in=FILE_TYPES_IMAGE)\
172 172 .annotate(images_count=Count(
173 173 'attachments')).filter(images_count__gt=0, thread__tags__in=[self])
174 174 if status is not None:
175 175 posts = posts.filter(thread__status__in=status)
176 176 return posts.order_by('?').first()
177 177
178 178 def get_first_letter(self):
179 179 name = self.get_localized_name()
180 180 return name and name[0] or ''
181 181
182 182 def get_related_tags(self):
183 183 return set(Tag.objects.filter(thread_tags__in=self.get_threads()).exclude(
184 184 id=self.id).order_by('?')[:RELATED_TAGS_COUNT])
185 185
186 186 @cached_result()
187 187 def get_color(self):
188 188 """
189 189 Gets color hashed from the tag name.
190 190 """
191 191 return hashlib.md5(self.get_name().encode()).hexdigest()[:6]
192 192
193 193 def get_parent(self):
194 194 return self.parent
195 195
196 196 def get_all_parents(self):
197 197 parents = list()
198 198 parent = self.get_parent()
199 199 if parent and parent not in parents:
200 200 parents.insert(0, parent)
201 201 parents = parent.get_all_parents() + parents
202 202
203 203 return parents
204 204
205 205 def get_children(self):
206 206 return self.children
207 207
208 208 def get_images(self):
209 209 return Attachment.objects.filter(
210 210 attachment_posts__thread__tags__in=[self]).filter(
211 211 mimetype__in=FILE_TYPES_IMAGE).order_by('-attachment_posts__pub_time')
212 212
General Comments 0
You need to be logged in to leave comments. Login now