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