##// END OF EJS Templates
Optimized some post count calls.
neko259 -
r58:e922d63e default
parent child Browse files
Show More
@@ -1,265 +1,267 b''
1 1 import os
2 2 from random import random
3 3 import re
4 4 from django.db import models
5 5 from django.utils import timezone
6 6 import time
7 7 import math
8 8 import markdown
9 9 from markdown.inlinepatterns import Pattern
10 10 from markdown.util import etree
11 11
12 12 from neboard import settings
13 13 from markupfield.fields import MarkupField
14 14
15 15 import thumbs
16 16
17 17 NO_PARENT = -1
18 18 NO_IP = '0.0.0.0'
19 19 UNKNOWN_UA = ''
20 20
21 21
22 22 class PostManager(models.Manager):
23 23 ALL_PAGES = -1
24 24
25 25 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
26 26 ip=NO_IP, tags=None):
27 27 post = self.create(title=title,
28 28 text=text,
29 29 pub_time=timezone.now(),
30 30 parent=parent_id,
31 31 image=image,
32 32 poster_ip=ip,
33 33 poster_user_agent=UNKNOWN_UA,
34 34 last_edit_time=timezone.now())
35 35
36 36 if tags:
37 37 for tag in tags:
38 38 post.tags.add(tag)
39 39
40 40 if parent_id != NO_PARENT:
41 41 self._bump_thread(parent_id)
42 42 else:
43 43 self._delete_old_threads()
44 44
45 45 return post
46 46
47 47 def delete_post(self, post):
48 48 children = self.filter(parent=post.id)
49 49 for child in children:
50 50 self.delete_post(child)
51 51 post.delete()
52 52
53 53 def delete_posts_by_ip(self, ip):
54 54 posts = self.filter(poster_ip=ip)
55 55 for post in posts:
56 56 self.delete_post(post)
57 57
58 58 def get_threads(self, tag=None, page=ALL_PAGES):
59 59 if tag:
60 60 threads = self.filter(parent=NO_PARENT, tags=tag)
61 61 else:
62 62 threads = self.filter(parent=NO_PARENT)
63 threads = list(threads.order_by('-last_edit_time'))
63 threads = threads.order_by('-last_edit_time')
64 64
65 65 if page != self.ALL_PAGES:
66 66 thread_count = len(threads)
67 67
68 68 if page < self.get_thread_page_count(tag=tag):
69 69 start_thread = page * settings.THREADS_PER_PAGE
70 70 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
71 71 thread_count)
72 72 threads = threads[start_thread:end_thread]
73 73
74 74 return threads
75 75
76 76 def get_thread(self, opening_post_id):
77 77 opening_post = self.get(id=opening_post_id)
78 78
79 79 if opening_post.parent == NO_PARENT:
80 80 replies = self.filter(parent=opening_post_id)
81 81
82 82 thread = [opening_post]
83 83 thread.extend(replies)
84 84
85 85 return thread
86 86
87 87 def exists(self, post_id):
88 88 posts = self.filter(id=post_id)
89 89
90 return len(posts) > 0
90 return posts.count() > 0
91 91
92 92 def get_thread_page_count(self, tag=None):
93 93 if tag:
94 94 threads = self.filter(parent=NO_PARENT, tags=tag)
95 95 else:
96 96 threads = self.filter(parent=NO_PARENT)
97 97
98 return int(math.ceil(len(threads) / float(settings.THREADS_PER_PAGE)))
98 return int(math.ceil(threads.count() / float(
99 settings.THREADS_PER_PAGE)))
99 100
100 101 def _delete_old_threads(self):
101 102 """
102 103 Preserves maximum thread count. If there are too many threads,
103 104 delete the old ones.
104 105 """
105 106
106 # TODO Try to find a better way to get the active thread count.
107
108 107 # TODO Move old threads to the archive instead of deleting them.
109 108 # Maybe make some 'old' field in the model to indicate the thread
110 109 # must not be shown and be able for replying.
111 110
112 111 threads = self.get_threads()
113 112 thread_count = len(threads)
114 113
115 114 if thread_count > settings.MAX_THREAD_COUNT:
116 115 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
117 old_threads = threads[-num_threads_to_delete:]
116 old_threads = threads[thread_count - num_threads_to_delete:]
118 117
119 118 for thread in old_threads:
120 119 self.delete_post(thread)
121 120
122 121 def _bump_thread(self, thread_id):
123 122 thread = self.get(id=thread_id)
124 123
125 124 if thread.can_bump():
126 125 thread.last_edit_time = timezone.now()
127 126 thread.save()
128 127
129 128
130 129 class TagManager(models.Manager):
131 130 def get_not_empty_tags(self):
132 131 all_tags = self.all().order_by('name')
133 132 tags = []
134 133 for tag in all_tags:
135 134 if not tag.is_empty():
136 135 tags.append(tag)
137 136
138 137 return tags
139 138
140 139 def get_popular_tags(self):
141 140 all_tags = self.get_not_empty_tags()
142 141
143 142 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
144 143 reverse=True)
145 144
146 145 return sorted_tags[:settings.POPULAR_TAGS]
147 146
148 147
149 148 class Tag(models.Model):
150 149 """
151 150 A tag is a text node assigned to the post. The tag serves as a board
152 151 section. There can be multiple tags for each message
153 152 """
154 153
154 OPENING_POST_WEIGHT = 5
155
155 156 objects = TagManager()
156 157
157 158 name = models.CharField(max_length=100)
158 159 # TODO Connect the tag to its posts to check the number of threads for
159 160 # the tag.
160 161
161 162 def __unicode__(self):
162 163 return self.name
163 164
164 165 def is_empty(self):
165 166 return self.get_post_count() == 0
166 167
167 168 def get_post_count(self):
168 169 posts_with_tag = Post.objects.get_threads(tag=self)
169 return len(posts_with_tag)
170 return posts_with_tag.count()
170 171
171 172 def get_popularity(self):
172 173 posts_with_tag = Post.objects.get_threads(tag=self)
173 174 reply_count = 0
174 175 for post in posts_with_tag:
175 176 reply_count += post.get_reply_count()
177 reply_count += self.OPENING_POST_WEIGHT
176 178
177 179 return reply_count
178 180
179 181
180 182 class Post(models.Model):
181 183 """A post is a message."""
182 184
183 185 IMAGES_DIRECTORY = 'images/'
184 186 FILE_EXTENSION_DELIMITER = '.'
185 187
186 188 objects = PostManager()
187 189
188 190 def _update_image_filename(self, filename):
189 191 """Get unique image filename"""
190 192
191 193 path = self.IMAGES_DIRECTORY
192 194 new_name = str(int(time.mktime(time.gmtime())))
193 195 new_name += str(int(random() * 1000))
194 196 new_name += self.FILE_EXTENSION_DELIMITER
195 197 new_name += filename.split(self.FILE_EXTENSION_DELIMITER)[-1:][0]
196 198
197 199 return os.path.join(path, new_name)
198 200
199 201 title = models.CharField(max_length=50)
200 202 pub_time = models.DateTimeField()
201 203 text = MarkupField(default_markup_type='markdown', escape_html=True)
202 204 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
203 205 blank=True, sizes=((200, 150),))
204 206 poster_ip = models.IPAddressField()
205 207 poster_user_agent = models.TextField()
206 208 parent = models.BigIntegerField()
207 209 tags = models.ManyToManyField(Tag)
208 210 last_edit_time = models.DateTimeField()
209 211
210 212 regex_pretty = re.compile(r'^\d(0)+$')
211 213 regex_same = re.compile(r'^(.)\1+$')
212 214
213 215 def __unicode__(self):
214 216 return self.title + ' (' + self.text.raw + ')'
215 217
216 218 def _get_replies(self):
217 219 return Post.objects.filter(parent=self.id)
218 220
219 221 def get_reply_count(self):
220 return len(self._get_replies())
222 return self._get_replies().count()
221 223
222 224 def get_images_count(self):
223 225 images_count = 1 if self.image else 0
224 226 for reply in self._get_replies():
225 227 if reply.image:
226 228 images_count += 1
227 229
228 230 return images_count
229 231
230 232 def get_gets_count(self):
231 233 gets_count = 1 if self.is_get() else 0
232 234 for reply in self._get_replies():
233 235 if reply.is_get():
234 236 gets_count += 1
235 237
236 238 return gets_count
237 239
238 240 def is_get(self):
239 241 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
240 242
241 243 first = self.id == 1
242 244
243 245 id_str = str(self.id)
244 246 pretty = self.regex_pretty.match(id_str)
245 247 same_digits = self.regex_same.match(id_str)
246 248
247 249 return first or pretty or same_digits
248 250
249 251 def can_bump(self):
250 252 """Check if the thread can be bumped by replying"""
251 253
252 254 replies_count = len(Post.objects.get_thread(self.id))
253 255
254 256 return replies_count <= settings.MAX_POSTS_PER_THREAD
255 257
256 258
257 259 class Admin(models.Model):
258 260 """
259 261 Model for admin users
260 262 """
261 263 name = models.CharField(max_length=100)
262 264 password = models.CharField(max_length=100)
263 265
264 266 def __unicode__(self):
265 267 return self.name + '/' + '*' * len(self.password) No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now