##// END OF EJS Templates
Fixed tags counting
neko259 -
r178:3569fa99 default
parent child Browse files
Show More
@@ -1,324 +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 posts_with_tag = Post.objects.filter(tag=self)
171 posts_with_tag = Post.objects.get_threads(tag=self)
172 172 return posts_with_tag.count()
173 173
174 174 def get_popularity(self):
175 posts_with_tag = Post.objects.filter(tag=self)
175 posts_with_tag = Post.objects.get_threads(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 243 images_count += self.replies.filter(image_width__gt=0).count()
244 244
245 245 return images_count
246 246
247 247 def can_bump(self):
248 248 """Check if the thread can be bumped by replying"""
249 249
250 250 post_count = self.get_reply_count() + 1
251 251
252 252 return post_count <= settings.MAX_POSTS_PER_THREAD
253 253
254 254 def bump(self):
255 255 """Bump (move to up) thread"""
256 256
257 257 if self.can_bump():
258 258 self.last_edit_time = timezone.now()
259 259 self.save()
260 260
261 261 def get_last_replies(self):
262 262 if settings.LAST_REPLIES_COUNT > 0:
263 263 reply_count = self.get_reply_count()
264 264
265 265 if reply_count > 0:
266 266 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
267 267 reply_count)
268 268 last_replies = self.replies.all()[reply_count -
269 269 reply_count_to_show:]
270 270
271 271 return last_replies
272 272
273 273
274 274 class User(models.Model):
275 275
276 276 user_id = models.CharField(max_length=50)
277 277 rank = models.IntegerField()
278 278
279 279 registration_time = models.DateTimeField()
280 280 last_access_time = models.DateTimeField()
281 281
282 282 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
283 283 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
284 284 blank=True)
285 285
286 286 def save_setting(self, name, value):
287 287 setting, created = Setting.objects.get_or_create(name=name, user=self)
288 288 setting.value = value
289 289 setting.save()
290 290
291 291 return setting
292 292
293 293 def get_setting(self, name):
294 294 if Setting.objects.filter(name=name, user=self).exists():
295 295 setting = Setting.objects.get(name=name, user=self)
296 296 setting_value = setting.value
297 297 else:
298 298 setting_value = None
299 299
300 300 return setting_value
301 301
302 302 def is_moderator(self):
303 303 return RANK_MODERATOR >= self.rank
304 304
305 305 def get_sorted_fav_tags(self):
306 306 return self.fav_tags.order_by('name')
307 307
308 308 def __unicode__(self):
309 309 return self.user_id + '(' + str(self.rank) + ')'
310 310
311 311
312 312 class Setting(models.Model):
313 313
314 314 name = models.CharField(max_length=50)
315 315 value = models.CharField(max_length=50)
316 316 user = models.ForeignKey(User)
317 317
318 318
319 319 class Ban(models.Model):
320 320
321 321 ip = models.GenericIPAddressField()
322 322
323 323 def __unicode__(self):
324 324 return self.ip
General Comments 0
You need to be logged in to leave comments. Login now