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