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