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