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