##// END OF EJS Templates
Fixed typo in getting last replies
neko259 -
r175:2f4adb2c default
parent child Browse files
Show More
@@ -1,341 +1,341 b''
1 import os
1 import os
2 from random import random
2 from random import random
3 import re
3 import re
4 import time
4 import time
5 import math
5 import math
6
6
7 from django.db import models
7 from django.db import models
8 from django.db.models import Count
8 from django.db.models import Count
9 from django.http import Http404
9 from django.http import Http404
10 from django.utils import timezone
10 from django.utils import timezone
11 from markupfield.fields import MarkupField
11 from markupfield.fields import MarkupField
12
12
13 from neboard import settings
13 from neboard import settings
14 import thumbs
14 import thumbs
15
15
16 IMAGE_THUMB_SIZE = (200, 150)
16 IMAGE_THUMB_SIZE = (200, 150)
17
17
18 TITLE_MAX_LENGTH = 50
18 TITLE_MAX_LENGTH = 50
19
19
20 DEFAULT_MARKUP_TYPE = 'markdown'
20 DEFAULT_MARKUP_TYPE = 'markdown'
21
21
22 NO_PARENT = -1
22 NO_PARENT = -1
23 NO_IP = '0.0.0.0'
23 NO_IP = '0.0.0.0'
24 UNKNOWN_UA = ''
24 UNKNOWN_UA = ''
25 ALL_PAGES = -1
25 ALL_PAGES = -1
26 OPENING_POST_POPULARITY_WEIGHT = 2
26 OPENING_POST_POPULARITY_WEIGHT = 2
27 IMAGES_DIRECTORY = 'images/'
27 IMAGES_DIRECTORY = 'images/'
28 FILE_EXTENSION_DELIMITER = '.'
28 FILE_EXTENSION_DELIMITER = '.'
29
29
30 RANK_ADMIN = 0
30 RANK_ADMIN = 0
31 RANK_MODERATOR = 10
31 RANK_MODERATOR = 10
32 RANK_USER = 100
32 RANK_USER = 100
33
33
34
34
35 class PostManager(models.Manager):
35 class PostManager(models.Manager):
36 def create_post(self, title, text, image=None, thread=None,
36 def create_post(self, title, text, image=None, thread=None,
37 ip=NO_IP, tags=None, user=None):
37 ip=NO_IP, tags=None, user=None):
38 post = self.create(title=title,
38 post = self.create(title=title,
39 text=text,
39 text=text,
40 pub_time=timezone.now(),
40 pub_time=timezone.now(),
41 thread=thread,
41 thread=thread,
42 image=image,
42 image=image,
43 poster_ip=ip,
43 poster_ip=ip,
44 poster_user_agent=UNKNOWN_UA,
44 poster_user_agent=UNKNOWN_UA,
45 last_edit_time=timezone.now(),
45 last_edit_time=timezone.now(),
46 user=user)
46 user=user)
47
47
48 if thread:
48 if thread:
49 thread.replies.add(post)
49 thread.replies.add(post)
50
50
51 if tags:
51 if tags:
52 map(post.tags.add, tags)
52 map(post.tags.add, tags)
53
53
54 if thread:
54 if thread:
55 thread.bump()
55 thread.bump()
56 else:
56 else:
57 self._delete_old_threads()
57 self._delete_old_threads()
58
58
59 return post
59 return post
60
60
61 def delete_post(self, post):
61 def delete_post(self, post):
62 if post.replies.count() > 0:
62 if post.replies.count() > 0:
63 map(self.delete_post, post.replies.all())
63 map(self.delete_post, post.replies.all())
64 post.delete()
64 post.delete()
65
65
66 def delete_posts_by_ip(self, ip):
66 def delete_posts_by_ip(self, ip):
67 posts = self.filter(poster_ip=ip)
67 posts = self.filter(poster_ip=ip)
68 map(self.delete_post, posts)
68 map(self.delete_post, posts)
69
69
70 def get_threads(self, tag=None, page=ALL_PAGES,
70 def get_threads(self, tag=None, page=ALL_PAGES,
71 order_by='-last_edit_time'):
71 order_by='-last_edit_time'):
72 if tag:
72 if tag:
73 threads = self.filter(thread=None, tags=tag)
73 threads = self.filter(thread=None, tags=tag)
74
74
75 # TODO This needs to be uncommented when 'all tags' view won't
75 # TODO This needs to be uncommented when 'all tags' view won't
76 # use this method to get threads for tag
76 # use this method to get threads for tag
77
77
78 # if threads.count() == 0:
78 # if threads.count() == 0:
79 # raise Http404
79 # raise Http404
80 else:
80 else:
81 threads = self.filter(thread=None)
81 threads = self.filter(thread=None)
82
82
83 threads = threads.order_by(order_by)
83 threads = threads.order_by(order_by)
84
84
85 if page != ALL_PAGES:
85 if page != ALL_PAGES:
86 thread_count = threads.count()
86 thread_count = threads.count()
87
87
88 if page < self.get_thread_page_count(tag=tag):
88 if page < self.get_thread_page_count(tag=tag):
89 start_thread = page * settings.THREADS_PER_PAGE
89 start_thread = page * settings.THREADS_PER_PAGE
90 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
90 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
91 thread_count)
91 thread_count)
92 threads = threads[start_thread:end_thread]
92 threads = threads[start_thread:end_thread]
93
93
94 return threads
94 return threads
95
95
96 def get_thread(self, opening_post_id):
96 def get_thread(self, opening_post_id):
97 try:
97 try:
98 opening_post = self.get(id=opening_post_id, thread=None)
98 opening_post = self.get(id=opening_post_id, thread=None)
99 except Post.DoesNotExist:
99 except Post.DoesNotExist:
100 raise Http404
100 raise Http404
101
101
102 if opening_post.replies:
102 if opening_post.replies:
103 thread = [opening_post]
103 thread = [opening_post]
104 thread.extend(opening_post.replies.all())
104 thread.extend(opening_post.replies.all())
105
105
106 return thread
106 return thread
107
107
108 def exists(self, post_id):
108 def exists(self, post_id):
109 posts = self.filter(id=post_id)
109 posts = self.filter(id=post_id)
110
110
111 return posts.count() > 0
111 return posts.count() > 0
112
112
113 def get_thread_page_count(self, tag=None):
113 def get_thread_page_count(self, tag=None):
114 if tag:
114 if tag:
115 threads = self.filter(thread=None, tags=tag)
115 threads = self.filter(thread=None, tags=tag)
116 else:
116 else:
117 threads = self.filter(thread=None)
117 threads = self.filter(thread=None)
118
118
119 return int(math.ceil(threads.count() / float(
119 return int(math.ceil(threads.count() / float(
120 settings.THREADS_PER_PAGE)))
120 settings.THREADS_PER_PAGE)))
121
121
122 def _delete_old_threads(self):
122 def _delete_old_threads(self):
123 """
123 """
124 Preserves maximum thread count. If there are too many threads,
124 Preserves maximum thread count. If there are too many threads,
125 delete the old ones.
125 delete the old ones.
126 """
126 """
127
127
128 # TODO Move old threads to the archive instead of deleting them.
128 # TODO Move old threads to the archive instead of deleting them.
129 # Maybe make some 'old' field in the model to indicate the thread
129 # Maybe make some 'old' field in the model to indicate the thread
130 # must not be shown and be able for replying.
130 # must not be shown and be able for replying.
131
131
132 threads = self.get_threads()
132 threads = self.get_threads()
133 thread_count = len(threads)
133 thread_count = len(threads)
134
134
135 if thread_count > settings.MAX_THREAD_COUNT:
135 if thread_count > settings.MAX_THREAD_COUNT:
136 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
136 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
137 old_threads = threads[thread_count - num_threads_to_delete:]
137 old_threads = threads[thread_count - num_threads_to_delete:]
138
138
139 map(self.delete_post, old_threads)
139 map(self.delete_post, old_threads)
140
140
141 def _bump_thread(self, thread_id):
141 def _bump_thread(self, thread_id):
142 thread = self.get(id=thread_id)
142 thread = self.get(id=thread_id)
143
143
144 if thread.can_bump():
144 if thread.can_bump():
145 thread.last_edit_time = timezone.now()
145 thread.last_edit_time = timezone.now()
146 thread.save()
146 thread.save()
147
147
148
148
149 class TagManager(models.Manager):
149 class TagManager(models.Manager):
150 def get_not_empty_tags(self):
150 def get_not_empty_tags(self):
151 all_tags = self.all().order_by('name')
151 all_tags = self.all().order_by('name')
152 tags = []
152 tags = []
153 for tag in all_tags:
153 for tag in all_tags:
154 if not tag.is_empty():
154 if not tag.is_empty():
155 tags.append(tag)
155 tags.append(tag)
156
156
157 return tags
157 return tags
158
158
159 def get_popular_tags(self):
159 def get_popular_tags(self):
160 all_tags = self.get_not_empty_tags()
160 all_tags = self.get_not_empty_tags()
161
161
162 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
162 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
163 reverse=True)
163 reverse=True)
164
164
165 return sorted_tags[:settings.POPULAR_TAGS]
165 return sorted_tags[:settings.POPULAR_TAGS]
166
166
167
167
168 class Tag(models.Model):
168 class Tag(models.Model):
169 """
169 """
170 A tag is a text node assigned to the post. The tag serves as a board
170 A tag is a text node assigned to the post. The tag serves as a board
171 section. There can be multiple tags for each message
171 section. There can be multiple tags for each message
172 """
172 """
173
173
174 objects = TagManager()
174 objects = TagManager()
175
175
176 name = models.CharField(max_length=100)
176 name = models.CharField(max_length=100)
177
177
178 def __unicode__(self):
178 def __unicode__(self):
179 return self.name
179 return self.name
180
180
181 def is_empty(self):
181 def is_empty(self):
182 return self.get_post_count() == 0
182 return self.get_post_count() == 0
183
183
184 def get_post_count(self):
184 def get_post_count(self):
185 posts_with_tag = Post.objects.get_threads(tag=self)
185 posts_with_tag = Post.objects.get_threads(tag=self)
186 return posts_with_tag.count()
186 return posts_with_tag.count()
187
187
188 def get_popularity(self):
188 def get_popularity(self):
189 posts_with_tag = Post.objects.get_threads(tag=self)
189 posts_with_tag = Post.objects.get_threads(tag=self)
190 reply_count = 0
190 reply_count = 0
191 for post in posts_with_tag:
191 for post in posts_with_tag:
192 reply_count += post.get_reply_count()
192 reply_count += post.get_reply_count()
193 reply_count += OPENING_POST_POPULARITY_WEIGHT
193 reply_count += OPENING_POST_POPULARITY_WEIGHT
194
194
195 return reply_count
195 return reply_count
196
196
197
197
198 class Post(models.Model):
198 class Post(models.Model):
199 """A post is a message."""
199 """A post is a message."""
200
200
201 objects = PostManager()
201 objects = PostManager()
202
202
203 def _update_image_filename(self, filename):
203 def _update_image_filename(self, filename):
204 """Get unique image filename"""
204 """Get unique image filename"""
205
205
206 path = IMAGES_DIRECTORY
206 path = IMAGES_DIRECTORY
207 new_name = str(int(time.mktime(time.gmtime())))
207 new_name = str(int(time.mktime(time.gmtime())))
208 new_name += str(int(random() * 1000))
208 new_name += str(int(random() * 1000))
209 new_name += FILE_EXTENSION_DELIMITER
209 new_name += FILE_EXTENSION_DELIMITER
210 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
210 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
211
211
212 return os.path.join(path, new_name)
212 return os.path.join(path, new_name)
213
213
214 title = models.CharField(max_length=TITLE_MAX_LENGTH)
214 title = models.CharField(max_length=TITLE_MAX_LENGTH)
215 pub_time = models.DateTimeField()
215 pub_time = models.DateTimeField()
216 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
216 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
217 escape_html=False)
217 escape_html=False)
218
218
219 image_width = models.IntegerField(default=0)
219 image_width = models.IntegerField(default=0)
220 image_height = models.IntegerField(default=0)
220 image_height = models.IntegerField(default=0)
221
221
222 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
222 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
223 blank=True, sizes=(IMAGE_THUMB_SIZE,),
223 blank=True, sizes=(IMAGE_THUMB_SIZE,),
224 width_field='image_width',
224 width_field='image_width',
225 height_field='image_height')
225 height_field='image_height')
226
226
227 poster_ip = models.GenericIPAddressField()
227 poster_ip = models.GenericIPAddressField()
228 poster_user_agent = models.TextField()
228 poster_user_agent = models.TextField()
229
229
230 # TODO Convert this field to ForeignKey
230 # TODO Convert this field to ForeignKey
231 parent = models.BigIntegerField(default=NO_PARENT)
231 parent = models.BigIntegerField(default=NO_PARENT)
232
232
233 thread = models.ForeignKey('Post', null=True, default=None)
233 thread = models.ForeignKey('Post', null=True, default=None)
234 tags = models.ManyToManyField(Tag)
234 tags = models.ManyToManyField(Tag)
235 last_edit_time = models.DateTimeField()
235 last_edit_time = models.DateTimeField()
236 user = models.ForeignKey('User', null=True, default=None)
236 user = models.ForeignKey('User', null=True, default=None)
237
237
238 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
238 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
239 blank=True, related_name='re+')
239 blank=True, related_name='re+')
240
240
241 def __unicode__(self):
241 def __unicode__(self):
242 return '#' + str(self.id) + ' ' + self.title + ' (' + \
242 return '#' + str(self.id) + ' ' + self.title + ' (' + \
243 self.text.raw[:50] + ')'
243 self.text.raw[:50] + ')'
244
244
245 def get_title(self):
245 def get_title(self):
246 title = self.title
246 title = self.title
247 if len(title) == 0:
247 if len(title) == 0:
248 title = self.text.raw[:20]
248 title = self.text.raw[:20]
249
249
250 return title
250 return title
251
251
252 def _get_replies(self):
252 def _get_replies(self):
253 return self.replies
253 return self.replies
254
254
255 def get_reply_count(self):
255 def get_reply_count(self):
256 return self.replies.count()
256 return self.replies.count()
257
257
258 def get_images_count(self):
258 def get_images_count(self):
259 images_count = 1 if self.image else 0
259 images_count = 1 if self.image else 0
260
260
261 for reply in self.replies:
261 for reply in self.replies:
262 if reply.image:
262 if reply.image:
263 images_count += 1
263 images_count += 1
264
264
265 return images_count
265 return images_count
266
266
267 def can_bump(self):
267 def can_bump(self):
268 """Check if the thread can be bumped by replying"""
268 """Check if the thread can be bumped by replying"""
269
269
270 post_count = self.get_reply_count() + 1
270 post_count = self.get_reply_count() + 1
271
271
272 return post_count <= settings.MAX_POSTS_PER_THREAD
272 return post_count <= settings.MAX_POSTS_PER_THREAD
273
273
274 def bump(self):
274 def bump(self):
275 if self.can_bump():
275 if self.can_bump():
276 self.last_edit_time = timezone.now()
276 self.last_edit_time = timezone.now()
277 self.save()
277 self.save()
278
278
279 def get_last_replies(self):
279 def get_last_replies(self):
280 if settings.LAST_REPLIES_COUNT > 0:
280 if settings.LAST_REPLIES_COUNT > 0:
281 reply_count = self.get_reply_count()
281 reply_count = self.get_reply_count()
282
282
283 if reply_count > 0:
283 if reply_count > 0:
284 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
284 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
285 reply_count)
285 reply_count)
286 last_replies = self.replies.all()[reply_count -
286 last_replies = self.replies.all()[reply_count -
287 reply_count_to_show:]
287 reply_count_to_show:]
288
288
289 return last_replies
289 return last_replies
290
290
291
291
292 class User(models.Model):
292 class User(models.Model):
293
293
294 user_id = models.CharField(max_length=50)
294 user_id = models.CharField(max_length=50)
295 rank = models.IntegerField()
295 rank = models.IntegerField()
296
296
297 registration_time = models.DateTimeField()
297 registration_time = models.DateTimeField()
298 last_access_time = models.DateTimeField()
298 last_access_time = models.DateTimeField()
299
299
300 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
300 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
301 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
301 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
302 blank=True)
302 blank=True)
303
303
304 def save_setting(self, name, value):
304 def save_setting(self, name, value):
305 setting, created = Setting.objects.get_or_create(name=name, user=self)
305 setting, created = Setting.objects.get_or_create(name=name, user=self)
306 setting.value = value
306 setting.value = value
307 setting.save()
307 setting.save()
308
308
309 return setting
309 return setting
310
310
311 def get_setting(self, name):
311 def get_setting(self, name):
312 if Setting.objects.filter(name=name, user=self).exists():
312 if Setting.objects.filter(name=name, user=self).exists():
313 setting = Setting.objects.get(name=name, user=self)
313 setting = Setting.objects.get(name=name, user=self)
314 setting_value = setting.value
314 setting_value = setting.value
315 else:
315 else:
316 setting_value = None
316 setting_value = None
317
317
318 return setting_value
318 return setting_value
319
319
320 def is_moderator(self):
320 def is_moderator(self):
321 return RANK_MODERATOR >= self.rank
321 return RANK_MODERATOR >= self.rank
322
322
323 def get_sorted_fav_tags(self):
323 def get_sorted_fav_tags(self):
324 return self.fav_tags.order_by('name')
324 return self.fav_tags.order_by('name')
325
325
326 def __unicode__(self):
326 def __unicode__(self):
327 return self.user_id + '(' + str(self.rank) + ')'
327 return self.user_id + '(' + str(self.rank) + ')'
328
328
329
329
330 class Setting(models.Model):
330 class Setting(models.Model):
331
331
332 name = models.CharField(max_length=50)
332 name = models.CharField(max_length=50)
333 value = models.CharField(max_length=50)
333 value = models.CharField(max_length=50)
334 user = models.ForeignKey(User)
334 user = models.ForeignKey(User)
335
335
336
336
337 class Ban(models.Model):
337 class Ban(models.Model):
338 ip = models.GenericIPAddressField()
338 ip = models.GenericIPAddressField()
339
339
340 def __unicode__(self):
340 def __unicode__(self):
341 return self.ip
341 return self.ip
General Comments 0
You need to be logged in to leave comments. Login now