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