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