##// END OF EJS Templates
Show only sections in the image tags list in random images
neko259 -
r1257:ff69f54a default
parent child Browse files
Show More
@@ -1,227 +1,230 b''
1 1 import logging
2 2 from adjacent import Client
3 3
4 4 from django.db.models import Count, Sum, QuerySet
5 5 from django.utils import timezone
6 6 from django.db import models
7 7
8 8 from boards import settings
9 9 import boards
10 10 from boards.utils import cached_result, datetime_to_epoch
11 11 from boards.models.post import Post
12 12 from boards.models.tag import Tag
13 13
14 14
15 15 __author__ = 'neko259'
16 16
17 17
18 18 logger = logging.getLogger(__name__)
19 19
20 20
21 21 WS_NOTIFICATION_TYPE_NEW_POST = 'new_post'
22 22 WS_NOTIFICATION_TYPE = 'notification_type'
23 23
24 24 WS_CHANNEL_THREAD = "thread:"
25 25
26 26
27 27 class ThreadManager(models.Manager):
28 28 def process_oldest_threads(self):
29 29 """
30 30 Preserves maximum thread count. If there are too many threads,
31 31 archive or delete the old ones.
32 32 """
33 33
34 34 threads = Thread.objects.filter(archived=False).order_by('-bump_time')
35 35 thread_count = threads.count()
36 36
37 37 max_thread_count = settings.get_int('Messages', 'MaxThreadCount')
38 38 if thread_count > max_thread_count:
39 39 num_threads_to_delete = thread_count - max_thread_count
40 40 old_threads = threads[thread_count - num_threads_to_delete:]
41 41
42 42 for thread in old_threads:
43 43 if settings.get_bool('Storage', 'ArchiveThreads'):
44 44 self._archive_thread(thread)
45 45 else:
46 46 thread.delete()
47 47
48 48 logger.info('Processed %d old threads' % num_threads_to_delete)
49 49
50 50 def _archive_thread(self, thread):
51 51 thread.archived = True
52 52 thread.bumpable = False
53 53 thread.last_edit_time = timezone.now()
54 54 thread.update_posts_time()
55 55 thread.save(update_fields=['archived', 'last_edit_time', 'bumpable'])
56 56
57 57
58 58 def get_thread_max_posts():
59 59 return settings.get_int('Messages', 'MaxPostsPerThread')
60 60
61 61
62 62 class Thread(models.Model):
63 63 objects = ThreadManager()
64 64
65 65 class Meta:
66 66 app_label = 'boards'
67 67
68 68 tags = models.ManyToManyField('Tag')
69 69 bump_time = models.DateTimeField(db_index=True)
70 70 last_edit_time = models.DateTimeField()
71 71 archived = models.BooleanField(default=False)
72 72 bumpable = models.BooleanField(default=True)
73 73 max_posts = models.IntegerField(default=get_thread_max_posts)
74 74
75 75 def get_tags(self) -> QuerySet:
76 76 """
77 77 Gets a sorted tag list.
78 78 """
79 79
80 80 return self.tags.order_by('name')
81 81
82 82 def bump(self):
83 83 """
84 84 Bumps (moves to up) thread if possible.
85 85 """
86 86
87 87 if self.can_bump():
88 88 self.bump_time = self.last_edit_time
89 89
90 90 self.update_bump_status()
91 91
92 92 logger.info('Bumped thread %d' % self.id)
93 93
94 94 def has_post_limit(self) -> bool:
95 95 return self.max_posts > 0
96 96
97 97 def update_bump_status(self, exclude_posts=None):
98 98 if self.has_post_limit() and self.get_reply_count() >= self.max_posts:
99 99 self.bumpable = False
100 100 self.update_posts_time(exclude_posts=exclude_posts)
101 101
102 102 def _get_cache_key(self):
103 103 return [datetime_to_epoch(self.last_edit_time)]
104 104
105 105 @cached_result(key_method=_get_cache_key)
106 106 def get_reply_count(self) -> int:
107 107 return self.get_replies().count()
108 108
109 109 @cached_result(key_method=_get_cache_key)
110 110 def get_images_count(self) -> int:
111 111 return self.get_replies().annotate(images_count=Count(
112 112 'images')).aggregate(Sum('images_count'))['images_count__sum']
113 113
114 114 def can_bump(self) -> bool:
115 115 """
116 116 Checks if the thread can be bumped by replying to it.
117 117 """
118 118
119 119 return self.bumpable and not self.archived
120 120
121 121 def get_last_replies(self) -> QuerySet:
122 122 """
123 123 Gets several last replies, not including opening post
124 124 """
125 125
126 126 last_replies_count = settings.get_int('View', 'LastRepliesCount')
127 127
128 128 if last_replies_count > 0:
129 129 reply_count = self.get_reply_count()
130 130
131 131 if reply_count > 0:
132 132 reply_count_to_show = min(last_replies_count,
133 133 reply_count - 1)
134 134 replies = self.get_replies()
135 135 last_replies = replies[reply_count - reply_count_to_show:]
136 136
137 137 return last_replies
138 138
139 139 def get_skipped_replies_count(self) -> int:
140 140 """
141 141 Gets number of posts between opening post and last replies.
142 142 """
143 143 reply_count = self.get_reply_count()
144 144 last_replies_count = min(settings.get_int('View', 'LastRepliesCount'),
145 145 reply_count - 1)
146 146 return reply_count - last_replies_count - 1
147 147
148 148 def get_replies(self, view_fields_only=False) -> QuerySet:
149 149 """
150 150 Gets sorted thread posts
151 151 """
152 152
153 153 query = Post.objects.filter(threads__in=[self])
154 154 query = query.order_by('pub_time').prefetch_related('images', 'thread', 'threads')
155 155 if view_fields_only:
156 156 query = query.defer('poster_ip')
157 157 return query.all()
158 158
159 159 def get_top_level_replies(self) -> QuerySet:
160 160 return self.get_replies().exclude(refposts__threads__in=[self])
161 161
162 162 def get_replies_with_images(self, view_fields_only=False) -> QuerySet:
163 163 """
164 164 Gets replies that have at least one image attached
165 165 """
166 166
167 167 return self.get_replies(view_fields_only).annotate(images_count=Count(
168 168 'images')).filter(images_count__gt=0)
169 169
170 170 def get_opening_post(self, only_id=False) -> Post:
171 171 """
172 172 Gets the first post of the thread
173 173 """
174 174
175 175 query = self.get_replies().order_by('pub_time')
176 176 if only_id:
177 177 query = query.only('id')
178 178 opening_post = query.first()
179 179
180 180 return opening_post
181 181
182 182 @cached_result()
183 183 def get_opening_post_id(self) -> int:
184 184 """
185 185 Gets ID of the first thread post.
186 186 """
187 187
188 188 return self.get_opening_post(only_id=True).id
189 189
190 190 def get_pub_time(self):
191 191 """
192 192 Gets opening post's pub time because thread does not have its own one.
193 193 """
194 194
195 195 return self.get_opening_post().pub_time
196 196
197 197 def __str__(self):
198 198 return 'T#{}/{}'.format(self.id, self.get_opening_post_id())
199 199
200 200 def get_tag_url_list(self) -> list:
201 201 return boards.models.Tag.objects.get_tag_url_list(self.get_tags())
202 202
203 203 def update_posts_time(self, exclude_posts=None):
204 204 last_edit_time = self.last_edit_time
205 205
206 206 for post in self.post_set.all():
207 207 if exclude_posts is None or post not in exclude_posts:
208 208 # Manual update is required because uids are generated on save
209 209 post.last_edit_time = last_edit_time
210 210 post.save(update_fields=['last_edit_time'])
211 211
212 212 post.get_threads().update(last_edit_time=last_edit_time)
213 213
214 214 def notify_clients(self):
215 215 if not settings.get_bool('External', 'WebsocketsEnabled'):
216 216 return
217 217
218 218 client = Client()
219 219
220 220 channel_name = WS_CHANNEL_THREAD + str(self.get_opening_post_id())
221 221 client.publish(channel_name, {
222 222 WS_NOTIFICATION_TYPE: WS_NOTIFICATION_TYPE_NEW_POST,
223 223 })
224 224 client.send()
225 225
226 226 def get_absolute_url(self):
227 227 return self.get_opening_post().get_absolute_url()
228
229 def get_required_tags(self):
230 return self.get_tags().filter(required=True)
@@ -1,39 +1,39 b''
1 1 {% extends "boards/base.html" %}
2 2
3 3 {% load i18n %}
4 4
5 5 {% block head %}
6 6 <title>{% trans 'Random images' %} - {{ site_name }}</title>
7 7 {% endblock %}
8 8
9 9 {% block content %}
10 10
11 11 {% if images %}
12 12 <div class="random-images-table">
13 13 <div>
14 14 {% for image in images %}
15 15 <div class="gallery_image">
16 16 {% autoescape off %}
17 17 {{ image.get_view }}
18 18 {% endautoescape %}
19 19 {% with image.get_random_associated_post as post %}
20 20 <a href="{{ post.get_absolute_url }}">>>{{ post.id }}</a>
21 21 <div>
22 {% for tag in post.get_thread.tags.all %}
22 {% for tag in post.get_thread.get_required_tags.all %}
23 23 {% autoescape off %}
24 24 {{ tag.get_view }}{% if not forloop.last %},{% endif %}
25 25 {% endautoescape %}
26 26 {% endfor %}
27 27 </div>
28 28 {% endwith %}
29 29 </div>
30 30 {% if forloop.counter|divisibleby:"3" %}
31 31 </div>
32 32 <div>
33 33 {% endif %}
34 34 {% endfor %}
35 35 </div>
36 36 </div>
37 37 {% endif %}
38 38
39 39 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now