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