##// END OF EJS Templates
Use aggregation to get the thread images count
neko259 -
r891:72a58333 default
parent child Browse files
Show More
@@ -1,191 +1,187 b''
1 import logging
1 import logging
2 from django.db.models import Count
2 from django.db.models import Count, Sum
3 from django.utils import timezone
3 from django.utils import timezone
4 from django.core.cache import cache
4 from django.core.cache import cache
5 from django.db import models
5 from django.db import models
6 from boards import settings
6 from boards import settings
7
7
8 __author__ = 'neko259'
8 __author__ = 'neko259'
9
9
10
10
11 logger = logging.getLogger(__name__)
11 logger = logging.getLogger(__name__)
12
12
13
13
14 CACHE_KEY_OPENING_POST = 'opening_post_id'
14 CACHE_KEY_OPENING_POST = 'opening_post_id'
15
15
16
16
17 class ThreadManager(models.Manager):
17 class ThreadManager(models.Manager):
18 def process_oldest_threads(self):
18 def process_oldest_threads(self):
19 """
19 """
20 Preserves maximum thread count. If there are too many threads,
20 Preserves maximum thread count. If there are too many threads,
21 archive or delete the old ones.
21 archive or delete the old ones.
22 """
22 """
23
23
24 threads = Thread.objects.filter(archived=False).order_by('-bump_time')
24 threads = Thread.objects.filter(archived=False).order_by('-bump_time')
25 thread_count = threads.count()
25 thread_count = threads.count()
26
26
27 if thread_count > settings.MAX_THREAD_COUNT:
27 if thread_count > settings.MAX_THREAD_COUNT:
28 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
28 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
29 old_threads = threads[thread_count - num_threads_to_delete:]
29 old_threads = threads[thread_count - num_threads_to_delete:]
30
30
31 for thread in old_threads:
31 for thread in old_threads:
32 if settings.ARCHIVE_THREADS:
32 if settings.ARCHIVE_THREADS:
33 self._archive_thread(thread)
33 self._archive_thread(thread)
34 else:
34 else:
35 thread.delete()
35 thread.delete()
36
36
37 logger.info('Processed %d old threads' % num_threads_to_delete)
37 logger.info('Processed %d old threads' % num_threads_to_delete)
38
38
39 def _archive_thread(self, thread):
39 def _archive_thread(self, thread):
40 thread.archived = True
40 thread.archived = True
41 thread.bumpable = False
41 thread.bumpable = False
42 thread.last_edit_time = timezone.now()
42 thread.last_edit_time = timezone.now()
43 thread.save(update_fields=['archived', 'last_edit_time', 'bumpable'])
43 thread.save(update_fields=['archived', 'last_edit_time', 'bumpable'])
44
44
45
45
46 class Thread(models.Model):
46 class Thread(models.Model):
47 objects = ThreadManager()
47 objects = ThreadManager()
48
48
49 class Meta:
49 class Meta:
50 app_label = 'boards'
50 app_label = 'boards'
51
51
52 tags = models.ManyToManyField('Tag')
52 tags = models.ManyToManyField('Tag')
53 bump_time = models.DateTimeField()
53 bump_time = models.DateTimeField()
54 last_edit_time = models.DateTimeField()
54 last_edit_time = models.DateTimeField()
55 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
55 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
56 blank=True, related_name='tre+')
56 blank=True, related_name='tre+')
57 archived = models.BooleanField(default=False)
57 archived = models.BooleanField(default=False)
58 bumpable = models.BooleanField(default=True)
58 bumpable = models.BooleanField(default=True)
59
59
60 def get_tags(self):
60 def get_tags(self):
61 """
61 """
62 Gets a sorted tag list.
62 Gets a sorted tag list.
63 """
63 """
64
64
65 return self.tags.order_by('name')
65 return self.tags.order_by('name')
66
66
67 def bump(self):
67 def bump(self):
68 """
68 """
69 Bumps (moves to up) thread if possible.
69 Bumps (moves to up) thread if possible.
70 """
70 """
71
71
72 if self.can_bump():
72 if self.can_bump():
73 self.bump_time = timezone.now()
73 self.bump_time = timezone.now()
74
74
75 if self.get_reply_count() >= settings.MAX_POSTS_PER_THREAD:
75 if self.get_reply_count() >= settings.MAX_POSTS_PER_THREAD:
76 self.bumpable = False
76 self.bumpable = False
77
77
78 logger.info('Bumped thread %d' % self.id)
78 logger.info('Bumped thread %d' % self.id)
79
79
80 def get_reply_count(self):
80 def get_reply_count(self):
81 return self.replies.count()
81 return self.replies.count()
82
82
83 def get_images_count(self):
83 def get_images_count(self):
84 # TODO Use sum
84 return self.replies.annotate(images_count=Count(
85 total_count = 0
85 'images')).aggregate(Sum('images_count'))['images_count__sum']
86 for post_with_image in self.replies.annotate(images_count=Count(
87 'images')):
88 total_count += post_with_image.images_count
89 return total_count
90
86
91 def can_bump(self):
87 def can_bump(self):
92 """
88 """
93 Checks if the thread can be bumped by replying to it.
89 Checks if the thread can be bumped by replying to it.
94 """
90 """
95
91
96 return self.bumpable
92 return self.bumpable
97
93
98 def get_last_replies(self):
94 def get_last_replies(self):
99 """
95 """
100 Gets several last replies, not including opening post
96 Gets several last replies, not including opening post
101 """
97 """
102
98
103 if settings.LAST_REPLIES_COUNT > 0:
99 if settings.LAST_REPLIES_COUNT > 0:
104 reply_count = self.get_reply_count()
100 reply_count = self.get_reply_count()
105
101
106 if reply_count > 0:
102 if reply_count > 0:
107 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
103 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
108 reply_count - 1)
104 reply_count - 1)
109 replies = self.get_replies()
105 replies = self.get_replies()
110 last_replies = replies[reply_count - reply_count_to_show:]
106 last_replies = replies[reply_count - reply_count_to_show:]
111
107
112 return last_replies
108 return last_replies
113
109
114 def get_skipped_replies_count(self):
110 def get_skipped_replies_count(self):
115 """
111 """
116 Gets number of posts between opening post and last replies.
112 Gets number of posts between opening post and last replies.
117 """
113 """
118 reply_count = self.get_reply_count()
114 reply_count = self.get_reply_count()
119 last_replies_count = min(settings.LAST_REPLIES_COUNT,
115 last_replies_count = min(settings.LAST_REPLIES_COUNT,
120 reply_count - 1)
116 reply_count - 1)
121 return reply_count - last_replies_count - 1
117 return reply_count - last_replies_count - 1
122
118
123 def get_replies(self, view_fields_only=False):
119 def get_replies(self, view_fields_only=False):
124 """
120 """
125 Gets sorted thread posts
121 Gets sorted thread posts
126 """
122 """
127
123
128 query = self.replies.order_by('pub_time').prefetch_related('images')
124 query = self.replies.order_by('pub_time').prefetch_related('images')
129 if view_fields_only:
125 if view_fields_only:
130 query = query.defer('poster_user_agent')
126 query = query.defer('poster_user_agent')
131 return query.all()
127 return query.all()
132
128
133 def get_replies_with_images(self, view_fields_only=False):
129 def get_replies_with_images(self, view_fields_only=False):
134 return self.get_replies(view_fields_only).annotate(images_count=Count(
130 return self.get_replies(view_fields_only).annotate(images_count=Count(
135 'images')).filter(images_count__gt=0)
131 'images')).filter(images_count__gt=0)
136
132
137 def add_tag(self, tag):
133 def add_tag(self, tag):
138 """
134 """
139 Connects thread to a tag and tag to a thread
135 Connects thread to a tag and tag to a thread
140 """
136 """
141
137
142 self.tags.add(tag)
138 self.tags.add(tag)
143 tag.threads.add(self)
139 tag.threads.add(self)
144
140
145 def remove_tag(self, tag):
141 def remove_tag(self, tag):
146 self.tags.remove(tag)
142 self.tags.remove(tag)
147 tag.threads.remove(self)
143 tag.threads.remove(self)
148
144
149 def get_opening_post(self, only_id=False):
145 def get_opening_post(self, only_id=False):
150 """
146 """
151 Gets the first post of the thread
147 Gets the first post of the thread
152 """
148 """
153
149
154 query = self.replies.order_by('pub_time')
150 query = self.replies.order_by('pub_time')
155 if only_id:
151 if only_id:
156 query = query.only('id')
152 query = query.only('id')
157 opening_post = query.first()
153 opening_post = query.first()
158
154
159 return opening_post
155 return opening_post
160
156
161 def get_opening_post_id(self):
157 def get_opening_post_id(self):
162 """
158 """
163 Gets ID of the first thread post.
159 Gets ID of the first thread post.
164 """
160 """
165
161
166 cache_key = CACHE_KEY_OPENING_POST + str(self.id)
162 cache_key = CACHE_KEY_OPENING_POST + str(self.id)
167 opening_post_id = cache.get(cache_key)
163 opening_post_id = cache.get(cache_key)
168 if not opening_post_id:
164 if not opening_post_id:
169 opening_post_id = self.get_opening_post(only_id=True).id
165 opening_post_id = self.get_opening_post(only_id=True).id
170 cache.set(cache_key, opening_post_id)
166 cache.set(cache_key, opening_post_id)
171
167
172 return opening_post_id
168 return opening_post_id
173
169
174 def __unicode__(self):
170 def __unicode__(self):
175 return str(self.id)
171 return str(self.id)
176
172
177 def get_pub_time(self):
173 def get_pub_time(self):
178 """
174 """
179 Gets opening post's pub time because thread does not have its own one.
175 Gets opening post's pub time because thread does not have its own one.
180 """
176 """
181
177
182 return self.get_opening_post().pub_time
178 return self.get_opening_post().pub_time
183
179
184 def delete(self, using=None):
180 def delete(self, using=None):
185 if self.replies.exists():
181 if self.replies.exists():
186 self.replies.all().delete()
182 self.replies.all().delete()
187
183
188 super(Thread, self).delete(using)
184 super(Thread, self).delete(using)
189
185
190 def __str__(self):
186 def __str__(self):
191 return 'T#{}/{}'.format(self.id, self.get_opening_post_id())
187 return 'T#{}/{}'.format(self.id, self.get_opening_post_id())
General Comments 0
You need to be logged in to leave comments. Login now