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