##// END OF EJS Templates
Use cache for PPD value
neko259 -
r410:cf63309c default
parent child Browse files
Show More
@@ -1,316 +1,330 b''
1 from datetime import datetime, timedelta
1 from datetime import datetime, timedelta
2 from datetime import time as dtime
2 from datetime import time as dtime
3 import os
3 import os
4 from random import random
4 from random import random
5 import time
5 import time
6 import math
6 import math
7 import re
7 import re
8 from django.core.cache import cache
8
9
9 from django.db import models
10 from django.db import models
10 from django.http import Http404
11 from django.http import Http404
11 from django.utils import timezone
12 from django.utils import timezone
12 from markupfield.fields import MarkupField
13 from markupfield.fields import MarkupField
13
14
14 from neboard import settings
15 from neboard import settings
15 from boards import thumbs
16 from boards import thumbs
16
17
18 APP_LABEL_BOARDS = 'boards'
19
20 CACHE_KEY_PPD = 'ppd'
21
17 POSTS_PER_DAY_RANGE = range(7)
22 POSTS_PER_DAY_RANGE = range(7)
18
23
19 BAN_REASON_AUTO = 'Auto'
24 BAN_REASON_AUTO = 'Auto'
20
25
21 IMAGE_THUMB_SIZE = (200, 150)
26 IMAGE_THUMB_SIZE = (200, 150)
22
27
23 TITLE_MAX_LENGTH = 50
28 TITLE_MAX_LENGTH = 50
24
29
25 DEFAULT_MARKUP_TYPE = 'markdown'
30 DEFAULT_MARKUP_TYPE = 'markdown'
26
31
27 NO_PARENT = -1
32 NO_PARENT = -1
28 NO_IP = '0.0.0.0'
33 NO_IP = '0.0.0.0'
29 UNKNOWN_UA = ''
34 UNKNOWN_UA = ''
30 ALL_PAGES = -1
35 ALL_PAGES = -1
31 IMAGES_DIRECTORY = 'images/'
36 IMAGES_DIRECTORY = 'images/'
32 FILE_EXTENSION_DELIMITER = '.'
37 FILE_EXTENSION_DELIMITER = '.'
33
38
34 SETTING_MODERATE = "moderate"
39 SETTING_MODERATE = "moderate"
35
40
36 REGEX_REPLY = re.compile('>>(\d+)')
41 REGEX_REPLY = re.compile('>>(\d+)')
37
42
38
43
39 class PostManager(models.Manager):
44 class PostManager(models.Manager):
40
45
41 def create_post(self, title, text, image=None, thread=None,
46 def create_post(self, title, text, image=None, thread=None,
42 ip=NO_IP, tags=None, user=None):
47 ip=NO_IP, tags=None, user=None):
48 cache.delete(CACHE_KEY_PPD)
49
43 posting_time = timezone.now()
50 posting_time = timezone.now()
44 if not thread:
51 if not thread:
45 thread = Thread.objects.create(bump_time=posting_time,
52 thread = Thread.objects.create(bump_time=posting_time,
46 last_edit_time=posting_time)
53 last_edit_time=posting_time)
47 else:
54 else:
48 thread.bump()
55 thread.bump()
49 thread.last_edit_time = posting_time
56 thread.last_edit_time = posting_time
50 thread.save()
57 thread.save()
51
58
52 post = self.create(title=title,
59 post = self.create(title=title,
53 text=text,
60 text=text,
54 pub_time=posting_time,
61 pub_time=posting_time,
55 thread_new=thread,
62 thread_new=thread,
56 image=image,
63 image=image,
57 poster_ip=ip,
64 poster_ip=ip,
58 poster_user_agent=UNKNOWN_UA,
65 poster_user_agent=UNKNOWN_UA,
59 last_edit_time=posting_time,
66 last_edit_time=posting_time,
60 user=user)
67 user=user)
61
68
62 thread.replies.add(post)
69 thread.replies.add(post)
63 if tags:
70 if tags:
64 linked_tags = []
71 linked_tags = []
65 for tag in tags:
72 for tag in tags:
66 tag_linked_tags = tag.get_linked_tags()
73 tag_linked_tags = tag.get_linked_tags()
67 if len(tag_linked_tags) > 0:
74 if len(tag_linked_tags) > 0:
68 linked_tags.extend(tag_linked_tags)
75 linked_tags.extend(tag_linked_tags)
69
76
70 tags.extend(linked_tags)
77 tags.extend(linked_tags)
71 map(thread.add_tag, tags)
78 map(thread.add_tag, tags)
72
79
73 self._delete_old_threads()
80 self._delete_old_threads()
74 self.connect_replies(post)
81 self.connect_replies(post)
75
82
76 return post
83 return post
77
84
78 def delete_post(self, post):
85 def delete_post(self, post):
79 thread = post.thread_new
86 thread = post.thread_new
80 thread.last_edit_time = timezone.now()
87 thread.last_edit_time = timezone.now()
81 thread.save()
88 thread.save()
82
89
83 post.delete()
90 post.delete()
84
91
85 def delete_posts_by_ip(self, ip):
92 def delete_posts_by_ip(self, ip):
86 posts = self.filter(poster_ip=ip)
93 posts = self.filter(poster_ip=ip)
87 map(self.delete_post, posts)
94 map(self.delete_post, posts)
88
95
89 # TODO Move this method to thread manager
96 # TODO Move this method to thread manager
90 def get_threads(self, tag=None, page=ALL_PAGES,
97 def get_threads(self, tag=None, page=ALL_PAGES,
91 order_by='-bump_time'):
98 order_by='-bump_time'):
92 if tag:
99 if tag:
93 threads = tag.threads
100 threads = tag.threads
94
101
95 if not threads.exists():
102 if not threads.exists():
96 raise Http404
103 raise Http404
97 else:
104 else:
98 threads = Thread.objects.all()
105 threads = Thread.objects.all()
99
106
100 threads = threads.order_by(order_by)
107 threads = threads.order_by(order_by)
101
108
102 if page != ALL_PAGES:
109 if page != ALL_PAGES:
103 thread_count = threads.count()
110 thread_count = threads.count()
104
111
105 if page < self._get_page_count(thread_count):
112 if page < self._get_page_count(thread_count):
106 start_thread = page * settings.THREADS_PER_PAGE
113 start_thread = page * settings.THREADS_PER_PAGE
107 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
114 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
108 thread_count)
115 thread_count)
109 threads = threads[start_thread:end_thread]
116 threads = threads[start_thread:end_thread]
110
117
111 return threads
118 return threads
112
119
113 # TODO Move this method to thread manager
120 # TODO Move this method to thread manager
114 def get_thread_page_count(self, tag=None):
121 def get_thread_page_count(self, tag=None):
115 if tag:
122 if tag:
116 threads = Thread.objects.filter(tags=tag)
123 threads = Thread.objects.filter(tags=tag)
117 else:
124 else:
118 threads = Thread.objects.all()
125 threads = Thread.objects.all()
119
126
120 return self._get_page_count(threads.count())
127 return self._get_page_count(threads.count())
121
128
122 # TODO Move this method to thread manager
129 # TODO Move this method to thread manager
123 def _delete_old_threads(self):
130 def _delete_old_threads(self):
124 """
131 """
125 Preserves maximum thread count. If there are too many threads,
132 Preserves maximum thread count. If there are too many threads,
126 delete the old ones.
133 delete the old ones.
127 """
134 """
128
135
129 # TODO Move old threads to the archive instead of deleting them.
136 # TODO Move old threads to the archive instead of deleting them.
130 # Maybe make some 'old' field in the model to indicate the thread
137 # Maybe make some 'old' field in the model to indicate the thread
131 # must not be shown and be able for replying.
138 # must not be shown and be able for replying.
132
139
133 threads = Thread.objects.all()
140 threads = Thread.objects.all()
134 thread_count = threads.count()
141 thread_count = threads.count()
135
142
136 if thread_count > settings.MAX_THREAD_COUNT:
143 if thread_count > settings.MAX_THREAD_COUNT:
137 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
144 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
138 old_threads = threads[thread_count - num_threads_to_delete:]
145 old_threads = threads[thread_count - num_threads_to_delete:]
139
146
140 map(Thread.delete_with_posts, old_threads)
147 map(Thread.delete_with_posts, old_threads)
141
148
142 def connect_replies(self, post):
149 def connect_replies(self, post):
143 """Connect replies to a post to show them as a refmap"""
150 """Connect replies to a post to show them as a refmap"""
144
151
145 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
152 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
146 post_id = reply_number.group(1)
153 post_id = reply_number.group(1)
147 ref_post = self.filter(id=post_id)
154 ref_post = self.filter(id=post_id)
148 if ref_post.count() > 0:
155 if ref_post.count() > 0:
149 referenced_post = ref_post[0]
156 referenced_post = ref_post[0]
150 referenced_post.referenced_posts.add(post)
157 referenced_post.referenced_posts.add(post)
151 referenced_post.last_edit_time = post.pub_time
158 referenced_post.last_edit_time = post.pub_time
152 referenced_post.save()
159 referenced_post.save()
153
160
154 def _get_page_count(self, thread_count):
161 def _get_page_count(self, thread_count):
155 return int(math.ceil(thread_count / float(settings.THREADS_PER_PAGE)))
162 return int(math.ceil(thread_count / float(settings.THREADS_PER_PAGE)))
156
163
157 def get_posts_per_day(self):
164 def get_posts_per_day(self):
158 """Get count of posts for the current day"""
165 """Get count of posts for the current day"""
159
166
167 ppd = cache.get(CACHE_KEY_PPD)
168 if ppd:
169 return ppd
170
160 today = datetime.now().date()
171 today = datetime.now().date()
161
172
162 posts_per_days = []
173 posts_per_days = []
163 for i in POSTS_PER_DAY_RANGE:
174 for i in POSTS_PER_DAY_RANGE:
164 day_end = today + timedelta(i)
175 day_end = today + timedelta(i)
165 day_start = today + timedelta(i - 1)
176 day_start = today + timedelta(i - 1)
166 day_time_start = datetime.combine(day_start, dtime())
177 day_time_start = datetime.combine(day_start, dtime())
167 day_time_end = datetime.combine(day_end, dtime())
178 day_time_end = datetime.combine(day_end, dtime())
168
179
169 posts_per_days.append(float(self.filter(pub_time__lte=day_time_end,
180 posts_per_days.append(float(self.filter(
170 pub_time__gte=day_time_start).count()))
181 pub_time__lte=day_time_end,
182 pub_time__gte=day_time_start).count()))
171
183
172 return sum(posts_per_day for posts_per_day in posts_per_days) / \
184 ppd = (sum(posts_per_day for posts_per_day in posts_per_days) /
173 len(posts_per_days)
185 len(posts_per_days))
186 cache.set(CACHE_KEY_PPD, ppd)
187 return ppd
174
188
175
189
176 class Post(models.Model):
190 class Post(models.Model):
177 """A post is a message."""
191 """A post is a message."""
178
192
179 objects = PostManager()
193 objects = PostManager()
180
194
181 class Meta:
195 class Meta:
182 app_label = 'boards'
196 app_label = APP_LABEL_BOARDS
183
197
184 def _update_image_filename(self, filename):
198 def _update_image_filename(self, filename):
185 """Get unique image filename"""
199 """Get unique image filename"""
186
200
187 path = IMAGES_DIRECTORY
201 path = IMAGES_DIRECTORY
188 new_name = str(int(time.mktime(time.gmtime())))
202 new_name = str(int(time.mktime(time.gmtime())))
189 new_name += str(int(random() * 1000))
203 new_name += str(int(random() * 1000))
190 new_name += FILE_EXTENSION_DELIMITER
204 new_name += FILE_EXTENSION_DELIMITER
191 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
205 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
192
206
193 return os.path.join(path, new_name)
207 return os.path.join(path, new_name)
194
208
195 title = models.CharField(max_length=TITLE_MAX_LENGTH)
209 title = models.CharField(max_length=TITLE_MAX_LENGTH)
196 pub_time = models.DateTimeField()
210 pub_time = models.DateTimeField()
197 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
211 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
198 escape_html=False)
212 escape_html=False)
199
213
200 image_width = models.IntegerField(default=0)
214 image_width = models.IntegerField(default=0)
201 image_height = models.IntegerField(default=0)
215 image_height = models.IntegerField(default=0)
202
216
203 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
217 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
204 blank=True, sizes=(IMAGE_THUMB_SIZE,),
218 blank=True, sizes=(IMAGE_THUMB_SIZE,),
205 width_field='image_width',
219 width_field='image_width',
206 height_field='image_height')
220 height_field='image_height')
207
221
208 poster_ip = models.GenericIPAddressField()
222 poster_ip = models.GenericIPAddressField()
209 poster_user_agent = models.TextField()
223 poster_user_agent = models.TextField()
210
224
211 thread = models.ForeignKey('Post', null=True, default=None)
225 thread = models.ForeignKey('Post', null=True, default=None)
212 thread_new = models.ForeignKey('Thread', null=True, default=None)
226 thread_new = models.ForeignKey('Thread', null=True, default=None)
213 last_edit_time = models.DateTimeField()
227 last_edit_time = models.DateTimeField()
214 user = models.ForeignKey('User', null=True, default=None)
228 user = models.ForeignKey('User', null=True, default=None)
215
229
216 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
230 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
217 null=True,
231 null=True,
218 blank=True, related_name='rfp+')
232 blank=True, related_name='rfp+')
219
233
220 def __unicode__(self):
234 def __unicode__(self):
221 return '#' + str(self.id) + ' ' + self.title + ' (' + \
235 return '#' + str(self.id) + ' ' + self.title + ' (' + \
222 self.text.raw[:50] + ')'
236 self.text.raw[:50] + ')'
223
237
224 def get_title(self):
238 def get_title(self):
225 title = self.title
239 title = self.title
226 if len(title) == 0:
240 if len(title) == 0:
227 title = self.text.raw[:20]
241 title = self.text.raw[:20]
228
242
229 return title
243 return title
230
244
231 def get_sorted_referenced_posts(self):
245 def get_sorted_referenced_posts(self):
232 return self.referenced_posts.order_by('id')
246 return self.referenced_posts.order_by('id')
233
247
234 def is_referenced(self):
248 def is_referenced(self):
235 return self.referenced_posts.all().exists()
249 return self.referenced_posts.all().exists()
236
250
237 def is_opening(self):
251 def is_opening(self):
238 return self.thread_new.get_replies()[0] == self
252 return self.thread_new.get_replies()[0] == self
239
253
240
254
241 class Thread(models.Model):
255 class Thread(models.Model):
242
256
243 class Meta:
257 class Meta:
244 app_label = 'boards'
258 app_label = APP_LABEL_BOARDS
245
259
246 tags = models.ManyToManyField('Tag')
260 tags = models.ManyToManyField('Tag')
247 bump_time = models.DateTimeField()
261 bump_time = models.DateTimeField()
248 last_edit_time = models.DateTimeField()
262 last_edit_time = models.DateTimeField()
249 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
263 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
250 blank=True, related_name='tre+')
264 blank=True, related_name='tre+')
251
265
252 def get_tags(self):
266 def get_tags(self):
253 """Get a sorted tag list"""
267 """Get a sorted tag list"""
254
268
255 return self.tags.order_by('name')
269 return self.tags.order_by('name')
256
270
257 def bump(self):
271 def bump(self):
258 """Bump (move to up) thread"""
272 """Bump (move to up) thread"""
259
273
260 if self.can_bump():
274 if self.can_bump():
261 self.bump_time = timezone.now()
275 self.bump_time = timezone.now()
262
276
263 def get_reply_count(self):
277 def get_reply_count(self):
264 return self.replies.count()
278 return self.replies.count()
265
279
266 def get_images_count(self):
280 def get_images_count(self):
267 return self.replies.filter(image_width__gt=0).count()
281 return self.replies.filter(image_width__gt=0).count()
268
282
269 def can_bump(self):
283 def can_bump(self):
270 """Check if the thread can be bumped by replying"""
284 """Check if the thread can be bumped by replying"""
271
285
272 post_count = self.get_reply_count()
286 post_count = self.get_reply_count()
273
287
274 return post_count <= settings.MAX_POSTS_PER_THREAD
288 return post_count <= settings.MAX_POSTS_PER_THREAD
275
289
276 def delete_with_posts(self):
290 def delete_with_posts(self):
277 """Completely delete thread"""
291 """Completely delete thread"""
278
292
279 if self.replies.count() > 0:
293 if self.replies.count() > 0:
280 map(Post.objects.delete_post, self.replies.all())
294 map(Post.objects.delete_post, self.replies.all())
281
295
282 self.delete()
296 self.delete()
283
297
284 def get_last_replies(self):
298 def get_last_replies(self):
285 """Get last replies, not including opening post"""
299 """Get last replies, not including opening post"""
286
300
287 if settings.LAST_REPLIES_COUNT > 0:
301 if settings.LAST_REPLIES_COUNT > 0:
288 reply_count = self.get_reply_count()
302 reply_count = self.get_reply_count()
289
303
290 if reply_count > 0:
304 if reply_count > 0:
291 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
305 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
292 reply_count - 1)
306 reply_count - 1)
293 last_replies = self.replies.all().order_by('pub_time')[
307 last_replies = self.replies.all().order_by('pub_time')[
294 reply_count - reply_count_to_show:]
308 reply_count - reply_count_to_show:]
295
309
296 return last_replies
310 return last_replies
297
311
298 def get_replies(self):
312 def get_replies(self):
299 """Get sorted thread posts"""
313 """Get sorted thread posts"""
300
314
301 return self.replies.all().order_by('pub_time')
315 return self.replies.all().order_by('pub_time')
302
316
303 def add_tag(self, tag):
317 def add_tag(self, tag):
304 """Connect thread to a tag and tag to a thread"""
318 """Connect thread to a tag and tag to a thread"""
305
319
306 self.tags.add(tag)
320 self.tags.add(tag)
307 tag.threads.add(self)
321 tag.threads.add(self)
308
322
309 def get_opening_post(self):
323 def get_opening_post(self):
310 return self.get_replies()[0]
324 return self.get_replies()[0]
311
325
312 def __unicode__(self):
326 def __unicode__(self):
313 return str(self.get_replies()[0].id)
327 return str(self.get_replies()[0].id)
314
328
315 def get_pub_time(self):
329 def get_pub_time(self):
316 return self.get_opening_post().pub_time No newline at end of file
330 return self.get_opening_post().pub_time
General Comments 0
You need to be logged in to leave comments. Login now