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