##// END OF EJS Templates
Quick fix for the 404 page defect. Now 404 page is not shown for tags, this must be investigated further. This refs #50
neko259 -
r87:b8853496 default
parent child Browse files
Show More
@@ -1,288 +1,288 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.http import Http404
9 9 from django.utils import timezone
10 10 from markupfield.fields import MarkupField
11 11
12 12 from neboard import settings
13 13 import thumbs
14 14
15 15 IMAGE_THUMB_SIZE = (200, 150)
16 16
17 17 TITLE_MAX_LENGTH = 50
18 18
19 19 DEFAULT_MARKUP_TYPE = 'markdown'
20 20
21 21 NO_PARENT = -1
22 22 NO_IP = '0.0.0.0'
23 23 UNKNOWN_UA = ''
24 24 ALL_PAGES = -1
25 25 OPENING_POST_POPULARITY_WEIGHT = 2
26 26 IMAGES_DIRECTORY = 'images/'
27 27 FILE_EXTENSION_DELIMITER = '.'
28 28
29 29 REGEX_PRETTY = re.compile(r'^\d(0)+$')
30 30 REGEX_SAME = re.compile(r'^(.)\1+$')
31 31
32 32
33 33 class PostManager(models.Manager):
34 34 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
35 35 ip=NO_IP, tags=None):
36 36 post = self.create(title=title,
37 37 text=text,
38 38 pub_time=timezone.now(),
39 39 parent=parent_id,
40 40 image=image,
41 41 poster_ip=ip,
42 42 poster_user_agent=UNKNOWN_UA,
43 43 last_edit_time=timezone.now())
44 44
45 45 if tags:
46 46 map(post.tags.add, tags)
47 47
48 48 if parent_id != NO_PARENT:
49 49 self._bump_thread(parent_id)
50 50 else:
51 51 self._delete_old_threads()
52 52
53 53 return post
54 54
55 55 def delete_post(self, post):
56 56 children = self.filter(parent=post.id)
57 57 for child in children:
58 58 self.delete_post(child)
59 59 post.delete()
60 60
61 61 def delete_posts_by_ip(self, ip):
62 62 posts = self.filter(poster_ip=ip)
63 63 for post in posts:
64 64 self.delete_post(post)
65 65
66 def get_threads(self, tag=None, page=ALL_PAGES):
66 def get_threads(self, tag=None, page=ALL_PAGES,
67 order_by='-last_edit_time'):
67 68 if tag:
68 69 threads = self.filter(parent=NO_PARENT, tags=tag)
70
71 # TODO Throw error 404 if no threads for tag found?
69 72 else:
70 73 threads = self.filter(parent=NO_PARENT)
71 74
72 if not threads:
73 raise Http404
74
75 threads = threads.order_by('-last_edit_time')
75 threads = threads.order_by(order_by)
76 76
77 77 if page != ALL_PAGES:
78 78 thread_count = len(threads)
79 79
80 80 if page < self.get_thread_page_count(tag=tag):
81 81 start_thread = page * settings.THREADS_PER_PAGE
82 82 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
83 83 thread_count)
84 84 threads = threads[start_thread:end_thread]
85 85
86 86 return threads
87 87
88 88 def get_thread(self, opening_post_id):
89 89 try:
90 90 opening_post = self.get(id=opening_post_id, parent=NO_PARENT)
91 91 except Post.DoesNotExist:
92 92 raise Http404
93 93
94 94 if opening_post.parent == NO_PARENT:
95 95 replies = self.filter(parent=opening_post_id)
96 96
97 97 thread = [opening_post]
98 98 thread.extend(replies)
99 99
100 100 return thread
101 101
102 102 def exists(self, post_id):
103 103 posts = self.filter(id=post_id)
104 104
105 105 return posts.count() > 0
106 106
107 107 def get_thread_page_count(self, tag=None):
108 108 if tag:
109 109 threads = self.filter(parent=NO_PARENT, tags=tag)
110 110 else:
111 111 threads = self.filter(parent=NO_PARENT)
112 112
113 113 return int(math.ceil(threads.count() / float(
114 114 settings.THREADS_PER_PAGE)))
115 115
116 116 def _delete_old_threads(self):
117 117 """
118 118 Preserves maximum thread count. If there are too many threads,
119 119 delete the old ones.
120 120 """
121 121
122 122 # TODO Move old threads to the archive instead of deleting them.
123 123 # Maybe make some 'old' field in the model to indicate the thread
124 124 # must not be shown and be able for replying.
125 125
126 126 threads = self.get_threads()
127 127 thread_count = len(threads)
128 128
129 129 if thread_count > settings.MAX_THREAD_COUNT:
130 130 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
131 131 old_threads = threads[thread_count - num_threads_to_delete:]
132 132
133 133 for thread in old_threads:
134 134 self.delete_post(thread)
135 135
136 136 def _bump_thread(self, thread_id):
137 137 thread = self.get(id=thread_id)
138 138
139 139 if thread.can_bump():
140 140 thread.last_edit_time = timezone.now()
141 141 thread.save()
142 142
143 143
144 144 class TagManager(models.Manager):
145 145 def get_not_empty_tags(self):
146 146 all_tags = self.all().order_by('name')
147 147 tags = []
148 148 for tag in all_tags:
149 149 if not tag.is_empty():
150 150 tags.append(tag)
151 151
152 152 return tags
153 153
154 154 def get_popular_tags(self):
155 155 all_tags = self.get_not_empty_tags()
156 156
157 157 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
158 158 reverse=True)
159 159
160 160 return sorted_tags[:settings.POPULAR_TAGS]
161 161
162 162
163 163 class Tag(models.Model):
164 164 """
165 165 A tag is a text node assigned to the post. The tag serves as a board
166 166 section. There can be multiple tags for each message
167 167 """
168 168
169 169 objects = TagManager()
170 170
171 171 name = models.CharField(max_length=100)
172 172 # TODO Connect the tag to its posts to check the number of threads for
173 173 # the tag.
174 174
175 175 def __unicode__(self):
176 176 return self.name
177 177
178 178 def is_empty(self):
179 179 return self.get_post_count() == 0
180 180
181 181 def get_post_count(self):
182 182 posts_with_tag = Post.objects.get_threads(tag=self)
183 183 return posts_with_tag.count()
184 184
185 185 def get_popularity(self):
186 186 posts_with_tag = Post.objects.get_threads(tag=self)
187 187 reply_count = 0
188 188 for post in posts_with_tag:
189 189 reply_count += post.get_reply_count()
190 190 reply_count += OPENING_POST_POPULARITY_WEIGHT
191 191
192 192 return reply_count
193 193
194 194
195 195 class Post(models.Model):
196 196 """A post is a message."""
197 197
198 198 objects = PostManager()
199 199
200 200 def _update_image_filename(self, filename):
201 201 """Get unique image filename"""
202 202
203 203 path = IMAGES_DIRECTORY
204 204 new_name = str(int(time.mktime(time.gmtime())))
205 205 new_name += str(int(random() * 1000))
206 206 new_name += FILE_EXTENSION_DELIMITER
207 207 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
208 208
209 209 return os.path.join(path, new_name)
210 210
211 211 title = models.CharField(max_length=TITLE_MAX_LENGTH)
212 212 pub_time = models.DateTimeField()
213 213 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
214 214 escape_html=True)
215 215 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
216 216 blank=True, sizes=(IMAGE_THUMB_SIZE,))
217 217 poster_ip = models.IPAddressField()
218 218 poster_user_agent = models.TextField()
219 219 parent = models.BigIntegerField()
220 220 tags = models.ManyToManyField(Tag)
221 221 last_edit_time = models.DateTimeField()
222 222
223 223 def __unicode__(self):
224 224 return '#' + str(self.id) + ' ' + self.title + ' (' + self.text.raw + \
225 225 ')'
226 226
227 227 def _get_replies(self):
228 228 return Post.objects.filter(parent=self.id)
229 229
230 230 def get_reply_count(self):
231 231 return self._get_replies().count()
232 232
233 233 def get_images_count(self):
234 234 images_count = 1 if self.image else 0
235 235 for reply in self._get_replies():
236 236 if reply.image:
237 237 images_count += 1
238 238
239 239 return images_count
240 240
241 241 def get_gets_count(self):
242 242 gets_count = 1 if self.is_get() else 0
243 243 for reply in self._get_replies():
244 244 if reply.is_get():
245 245 gets_count += 1
246 246
247 247 return gets_count
248 248
249 249 def is_get(self):
250 250 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
251 251
252 252 first = self.id == 1
253 253
254 254 id_str = str(self.id)
255 255 pretty = REGEX_PRETTY.match(id_str)
256 256 same_digits = REGEX_SAME.match(id_str)
257 257
258 258 return first or pretty or same_digits
259 259
260 260 def can_bump(self):
261 261 """Check if the thread can be bumped by replying"""
262 262
263 263 replies_count = len(Post.objects.get_thread(self.id))
264 264
265 265 return replies_count <= settings.MAX_POSTS_PER_THREAD
266 266
267 267 def get_last_replies(self):
268 268 if settings.LAST_REPLIES_COUNT > 0:
269 269 reply_count = self.get_reply_count()
270 270
271 271 if reply_count > 0:
272 272 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
273 273 reply_count)
274 274 last_replies = self._get_replies()[reply_count
275 275 - reply_count_to_show:]
276 276
277 277 return last_replies
278 278
279 279
280 280 class Admin(models.Model):
281 281 """
282 282 Model for admin users
283 283 """
284 284 name = models.CharField(max_length=100)
285 285 password = models.CharField(max_length=100)
286 286
287 287 def __unicode__(self):
288 288 return self.name + '/' + '*' * len(self.password)
General Comments 0
You need to be logged in to leave comments. Login now