##// END OF EJS Templates
Removed poster user agent field
neko259 -
r1078:d6ba9a1d default
parent child Browse files
Show More
@@ -0,0 +1,30 b''
1 # -*- coding: utf-8 -*-
2 from __future__ import unicode_literals
3
4 from django.db import models, migrations
5
6
7 class Migration(migrations.Migration):
8
9 dependencies = [
10 ('boards', '0012_thread_max_posts'),
11 ]
12
13 operations = [
14 migrations.RemoveField(
15 model_name='post',
16 name='poster_user_agent',
17 ),
18 migrations.AlterField(
19 model_name='post',
20 name='title',
21 field=models.CharField(null=True, blank=True, max_length=200),
22 preserve_default=True,
23 ),
24 migrations.AlterField(
25 model_name='thread',
26 name='max_posts',
27 field=models.IntegerField(default=12),
28 preserve_default=True,
29 ),
30 ]
@@ -1,444 +1,438 b''
1 from datetime import datetime, timedelta, date
1 from datetime import datetime, timedelta, date
2 from datetime import time as dtime
2 from datetime import time as dtime
3 import logging
3 import logging
4 import re
4 import re
5
5
6 from adjacent import Client
6 from adjacent import Client
7 from django.core.exceptions import ObjectDoesNotExist
7 from django.core.exceptions import ObjectDoesNotExist
8 from django.core.urlresolvers import reverse
8 from django.core.urlresolvers import reverse
9 from django.db import models, transaction
9 from django.db import models, transaction
10 from django.db.models import TextField
10 from django.db.models import TextField
11 from django.template.loader import render_to_string
11 from django.template.loader import render_to_string
12 from django.utils import timezone
12 from django.utils import timezone
13
13
14 from boards import settings
14 from boards import settings
15 from boards.mdx_neboard import Parser
15 from boards.mdx_neboard import Parser
16 from boards.models import PostImage
16 from boards.models import PostImage
17 from boards.models.base import Viewable
17 from boards.models.base import Viewable
18 from boards.utils import datetime_to_epoch, cached_result
18 from boards.utils import datetime_to_epoch, cached_result
19 from boards.models.user import Notification
19 from boards.models.user import Notification
20 import boards.models.thread
20 import boards.models.thread
21
21
22
22
23 WS_NOTIFICATION_TYPE_NEW_POST = 'new_post'
23 WS_NOTIFICATION_TYPE_NEW_POST = 'new_post'
24 WS_NOTIFICATION_TYPE = 'notification_type'
24 WS_NOTIFICATION_TYPE = 'notification_type'
25
25
26 WS_CHANNEL_THREAD = "thread:"
26 WS_CHANNEL_THREAD = "thread:"
27
27
28 APP_LABEL_BOARDS = 'boards'
28 APP_LABEL_BOARDS = 'boards'
29
29
30 POSTS_PER_DAY_RANGE = 7
30 POSTS_PER_DAY_RANGE = 7
31
31
32 BAN_REASON_AUTO = 'Auto'
32 BAN_REASON_AUTO = 'Auto'
33
33
34 IMAGE_THUMB_SIZE = (200, 150)
34 IMAGE_THUMB_SIZE = (200, 150)
35
35
36 TITLE_MAX_LENGTH = 200
36 TITLE_MAX_LENGTH = 200
37
37
38 # TODO This should be removed
38 # TODO This should be removed
39 NO_IP = '0.0.0.0'
39 NO_IP = '0.0.0.0'
40
40
41 # TODO Real user agent should be saved instead of this
42 UNKNOWN_UA = ''
43
44 REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]')
41 REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]')
45 REGEX_NOTIFICATION = re.compile(r'\[user\](\w+)\[/user\]')
42 REGEX_NOTIFICATION = re.compile(r'\[user\](\w+)\[/user\]')
46
43
47 PARAMETER_TRUNCATED = 'truncated'
44 PARAMETER_TRUNCATED = 'truncated'
48 PARAMETER_TAG = 'tag'
45 PARAMETER_TAG = 'tag'
49 PARAMETER_OFFSET = 'offset'
46 PARAMETER_OFFSET = 'offset'
50 PARAMETER_DIFF_TYPE = 'type'
47 PARAMETER_DIFF_TYPE = 'type'
51 PARAMETER_BUMPABLE = 'bumpable'
48 PARAMETER_BUMPABLE = 'bumpable'
52 PARAMETER_THREAD = 'thread'
49 PARAMETER_THREAD = 'thread'
53 PARAMETER_IS_OPENING = 'is_opening'
50 PARAMETER_IS_OPENING = 'is_opening'
54 PARAMETER_MODERATOR = 'moderator'
51 PARAMETER_MODERATOR = 'moderator'
55 PARAMETER_POST = 'post'
52 PARAMETER_POST = 'post'
56 PARAMETER_OP_ID = 'opening_post_id'
53 PARAMETER_OP_ID = 'opening_post_id'
57 PARAMETER_NEED_OPEN_LINK = 'need_open_link'
54 PARAMETER_NEED_OPEN_LINK = 'need_open_link'
58 PARAMETER_REPLY_LINK = 'reply_link'
55 PARAMETER_REPLY_LINK = 'reply_link'
59
56
60 DIFF_TYPE_HTML = 'html'
57 DIFF_TYPE_HTML = 'html'
61 DIFF_TYPE_JSON = 'json'
58 DIFF_TYPE_JSON = 'json'
62
59
63
60
64 class PostManager(models.Manager):
61 class PostManager(models.Manager):
65 @transaction.atomic
62 @transaction.atomic
66 def create_post(self, title: str, text: str, image=None, thread=None,
63 def create_post(self, title: str, text: str, image=None, thread=None,
67 ip=NO_IP, tags: list=None, threads: list=None):
64 ip=NO_IP, tags: list=None, threads: list=None):
68 """
65 """
69 Creates new post
66 Creates new post
70 """
67 """
71
68
72 if not tags:
69 if not tags:
73 tags = []
70 tags = []
74 if not threads:
71 if not threads:
75 threads = []
72 threads = []
76
73
77 posting_time = timezone.now()
74 posting_time = timezone.now()
78 if not thread:
75 if not thread:
79 thread = boards.models.thread.Thread.objects.create(
76 thread = boards.models.thread.Thread.objects.create(
80 bump_time=posting_time, last_edit_time=posting_time)
77 bump_time=posting_time, last_edit_time=posting_time)
81 new_thread = True
78 new_thread = True
82 else:
79 else:
83 new_thread = False
80 new_thread = False
84
81
85 pre_text = Parser().preparse(text)
82 pre_text = Parser().preparse(text)
86
83
87 post = self.create(title=title,
84 post = self.create(title=title,
88 text=pre_text,
85 text=pre_text,
89 pub_time=posting_time,
86 pub_time=posting_time,
90 poster_ip=ip,
87 poster_ip=ip,
91 thread=thread,
88 thread=thread,
92 poster_user_agent=UNKNOWN_UA, # TODO Get UA at
93 # last!
94 last_edit_time=posting_time)
89 last_edit_time=posting_time)
95 post.threads.add(thread)
90 post.threads.add(thread)
96
91
97 logger = logging.getLogger('boards.post.create')
92 logger = logging.getLogger('boards.post.create')
98
93
99 logger.info('Created post {} by {}'.format(
94 logger.info('Created post {} by {}'.format(
100 post, post.poster_ip))
95 post, post.poster_ip))
101
96
102 if image:
97 if image:
103 post.images.add(PostImage.objects.create_with_hash(image))
98 post.images.add(PostImage.objects.create_with_hash(image))
104
99
105 list(map(thread.add_tag, tags))
100 list(map(thread.add_tag, tags))
106
101
107 if new_thread:
102 if new_thread:
108 boards.models.thread.Thread.objects.process_oldest_threads()
103 boards.models.thread.Thread.objects.process_oldest_threads()
109 else:
104 else:
110 thread.last_edit_time = posting_time
105 thread.last_edit_time = posting_time
111 thread.bump()
106 thread.bump()
112 thread.save()
107 thread.save()
113
108
114 post.connect_replies()
109 post.connect_replies()
115 post.connect_threads(threads)
110 post.connect_threads(threads)
116 post.connect_notifications()
111 post.connect_notifications()
117
112
118 return post
113 return post
119
114
120 def delete_posts_by_ip(self, ip):
115 def delete_posts_by_ip(self, ip):
121 """
116 """
122 Deletes all posts of the author with same IP
117 Deletes all posts of the author with same IP
123 """
118 """
124
119
125 posts = self.filter(poster_ip=ip)
120 posts = self.filter(poster_ip=ip)
126 for post in posts:
121 for post in posts:
127 post.delete()
122 post.delete()
128
123
129 @cached_result
124 @cached_result
130 def get_posts_per_day(self):
125 def get_posts_per_day(self):
131 """
126 """
132 Gets average count of posts per day for the last 7 days
127 Gets average count of posts per day for the last 7 days
133 """
128 """
134
129
135 day_end = date.today()
130 day_end = date.today()
136 day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
131 day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
137
132
138 day_time_start = timezone.make_aware(datetime.combine(
133 day_time_start = timezone.make_aware(datetime.combine(
139 day_start, dtime()), timezone.get_current_timezone())
134 day_start, dtime()), timezone.get_current_timezone())
140 day_time_end = timezone.make_aware(datetime.combine(
135 day_time_end = timezone.make_aware(datetime.combine(
141 day_end, dtime()), timezone.get_current_timezone())
136 day_end, dtime()), timezone.get_current_timezone())
142
137
143 posts_per_period = float(self.filter(
138 posts_per_period = float(self.filter(
144 pub_time__lte=day_time_end,
139 pub_time__lte=day_time_end,
145 pub_time__gte=day_time_start).count())
140 pub_time__gte=day_time_start).count())
146
141
147 ppd = posts_per_period / POSTS_PER_DAY_RANGE
142 ppd = posts_per_period / POSTS_PER_DAY_RANGE
148
143
149 return ppd
144 return ppd
150
145
151
146
152 class Post(models.Model, Viewable):
147 class Post(models.Model, Viewable):
153 """A post is a message."""
148 """A post is a message."""
154
149
155 objects = PostManager()
150 objects = PostManager()
156
151
157 class Meta:
152 class Meta:
158 app_label = APP_LABEL_BOARDS
153 app_label = APP_LABEL_BOARDS
159 ordering = ('id',)
154 ordering = ('id',)
160
155
161 title = models.CharField(max_length=TITLE_MAX_LENGTH)
156 title = models.CharField(max_length=TITLE_MAX_LENGTH, null=True, blank=True)
162 pub_time = models.DateTimeField()
157 pub_time = models.DateTimeField()
163 text = TextField(blank=True, null=True)
158 text = TextField(blank=True, null=True)
164 _text_rendered = TextField(blank=True, null=True, editable=False)
159 _text_rendered = TextField(blank=True, null=True, editable=False)
165
160
166 images = models.ManyToManyField(PostImage, null=True, blank=True,
161 images = models.ManyToManyField(PostImage, null=True, blank=True,
167 related_name='ip+', db_index=True)
162 related_name='ip+', db_index=True)
168
163
169 poster_ip = models.GenericIPAddressField()
164 poster_ip = models.GenericIPAddressField()
170 poster_user_agent = models.TextField()
171
165
172 last_edit_time = models.DateTimeField()
166 last_edit_time = models.DateTimeField()
173
167
174 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
168 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
175 null=True,
169 null=True,
176 blank=True, related_name='rfp+',
170 blank=True, related_name='rfp+',
177 db_index=True)
171 db_index=True)
178 refmap = models.TextField(null=True, blank=True)
172 refmap = models.TextField(null=True, blank=True)
179 threads = models.ManyToManyField('Thread', db_index=True)
173 threads = models.ManyToManyField('Thread', db_index=True)
180 thread = models.ForeignKey('Thread', db_index=True, related_name='pt+')
174 thread = models.ForeignKey('Thread', db_index=True, related_name='pt+')
181
175
182 def __str__(self):
176 def __str__(self):
183 return 'P#{}/{}'.format(self.id, self.title)
177 return 'P#{}/{}'.format(self.id, self.title)
184
178
185 def get_title(self) -> str:
179 def get_title(self) -> str:
186 """
180 """
187 Gets original post title or part of its text.
181 Gets original post title or part of its text.
188 """
182 """
189
183
190 title = self.title
184 title = self.title
191 if not title:
185 if not title:
192 title = self.get_text()
186 title = self.get_text()
193
187
194 return title
188 return title
195
189
196 def build_refmap(self) -> None:
190 def build_refmap(self) -> None:
197 """
191 """
198 Builds a replies map string from replies list. This is a cache to stop
192 Builds a replies map string from replies list. This is a cache to stop
199 the server from recalculating the map on every post show.
193 the server from recalculating the map on every post show.
200 """
194 """
201
195
202 post_urls = ['<a href="{}">&gt;&gt;{}</a>'.format(
196 post_urls = ['<a href="{}">&gt;&gt;{}</a>'.format(
203 refpost.get_url(), refpost.id) for refpost in self.referenced_posts.all()]
197 refpost.get_url(), refpost.id) for refpost in self.referenced_posts.all()]
204
198
205 self.refmap = ', '.join(post_urls)
199 self.refmap = ', '.join(post_urls)
206
200
207 def get_sorted_referenced_posts(self):
201 def get_sorted_referenced_posts(self):
208 return self.refmap
202 return self.refmap
209
203
210 def is_referenced(self) -> bool:
204 def is_referenced(self) -> bool:
211 return self.refmap and len(self.refmap) > 0
205 return self.refmap and len(self.refmap) > 0
212
206
213 def is_opening(self) -> bool:
207 def is_opening(self) -> bool:
214 """
208 """
215 Checks if this is an opening post or just a reply.
209 Checks if this is an opening post or just a reply.
216 """
210 """
217
211
218 return self.get_thread().get_opening_post_id() == self.id
212 return self.get_thread().get_opening_post_id() == self.id
219
213
220 @cached_result
214 @cached_result
221 def get_url(self):
215 def get_url(self):
222 """
216 """
223 Gets full url to the post.
217 Gets full url to the post.
224 """
218 """
225
219
226 thread = self.get_thread()
220 thread = self.get_thread()
227
221
228 opening_id = thread.get_opening_post_id()
222 opening_id = thread.get_opening_post_id()
229
223
230 if self.id != opening_id:
224 if self.id != opening_id:
231 link = reverse('thread', kwargs={
225 link = reverse('thread', kwargs={
232 'post_id': opening_id}) + '#' + str(self.id)
226 'post_id': opening_id}) + '#' + str(self.id)
233 else:
227 else:
234 link = reverse('thread', kwargs={'post_id': self.id})
228 link = reverse('thread', kwargs={'post_id': self.id})
235
229
236 return link
230 return link
237
231
238 def get_thread(self):
232 def get_thread(self):
239 return self.thread
233 return self.thread
240
234
241 def get_threads(self):
235 def get_threads(self):
242 """
236 """
243 Gets post's thread.
237 Gets post's thread.
244 """
238 """
245
239
246 return self.threads
240 return self.threads
247
241
248 def get_referenced_posts(self):
242 def get_referenced_posts(self):
249 return self.referenced_posts.only('id', 'threads')
243 return self.referenced_posts.only('id', 'threads')
250
244
251 def get_view(self, moderator=False, need_open_link=False,
245 def get_view(self, moderator=False, need_open_link=False,
252 truncated=False, *args, **kwargs):
246 truncated=False, *args, **kwargs):
253 """
247 """
254 Renders post's HTML view. Some of the post params can be passed over
248 Renders post's HTML view. Some of the post params can be passed over
255 kwargs for the means of caching (if we view the thread, some params
249 kwargs for the means of caching (if we view the thread, some params
256 are same for every post and don't need to be computed over and over.
250 are same for every post and don't need to be computed over and over.
257 """
251 """
258
252
259 thread = self.get_thread()
253 thread = self.get_thread()
260 is_opening = kwargs.get(PARAMETER_IS_OPENING, self.is_opening())
254 is_opening = kwargs.get(PARAMETER_IS_OPENING, self.is_opening())
261 can_bump = kwargs.get(PARAMETER_BUMPABLE, thread.can_bump())
255 can_bump = kwargs.get(PARAMETER_BUMPABLE, thread.can_bump())
262
256
263 if is_opening:
257 if is_opening:
264 opening_post_id = self.id
258 opening_post_id = self.id
265 else:
259 else:
266 opening_post_id = thread.get_opening_post_id()
260 opening_post_id = thread.get_opening_post_id()
267
261
268 return render_to_string('boards/post.html', {
262 return render_to_string('boards/post.html', {
269 PARAMETER_POST: self,
263 PARAMETER_POST: self,
270 PARAMETER_MODERATOR: moderator,
264 PARAMETER_MODERATOR: moderator,
271 PARAMETER_IS_OPENING: is_opening,
265 PARAMETER_IS_OPENING: is_opening,
272 PARAMETER_THREAD: thread,
266 PARAMETER_THREAD: thread,
273 PARAMETER_BUMPABLE: can_bump,
267 PARAMETER_BUMPABLE: can_bump,
274 PARAMETER_NEED_OPEN_LINK: need_open_link,
268 PARAMETER_NEED_OPEN_LINK: need_open_link,
275 PARAMETER_TRUNCATED: truncated,
269 PARAMETER_TRUNCATED: truncated,
276 PARAMETER_OP_ID: opening_post_id,
270 PARAMETER_OP_ID: opening_post_id,
277 })
271 })
278
272
279 def get_search_view(self, *args, **kwargs):
273 def get_search_view(self, *args, **kwargs):
280 return self.get_view(args, kwargs)
274 return self.get_view(args, kwargs)
281
275
282 def get_first_image(self) -> PostImage:
276 def get_first_image(self) -> PostImage:
283 return self.images.earliest('id')
277 return self.images.earliest('id')
284
278
285 def delete(self, using=None):
279 def delete(self, using=None):
286 """
280 """
287 Deletes all post images and the post itself.
281 Deletes all post images and the post itself.
288 """
282 """
289
283
290 for image in self.images.all():
284 for image in self.images.all():
291 image_refs_count = Post.objects.filter(images__in=[image]).count()
285 image_refs_count = Post.objects.filter(images__in=[image]).count()
292 if image_refs_count == 1:
286 if image_refs_count == 1:
293 image.delete()
287 image.delete()
294
288
295 thread = self.get_thread()
289 thread = self.get_thread()
296 thread.last_edit_time = timezone.now()
290 thread.last_edit_time = timezone.now()
297 thread.save()
291 thread.save()
298
292
299 super(Post, self).delete(using)
293 super(Post, self).delete(using)
300
294
301 logging.getLogger('boards.post.delete').info(
295 logging.getLogger('boards.post.delete').info(
302 'Deleted post {}'.format(self))
296 'Deleted post {}'.format(self))
303
297
304 def get_post_data(self, format_type=DIFF_TYPE_JSON, request=None,
298 def get_post_data(self, format_type=DIFF_TYPE_JSON, request=None,
305 include_last_update=False):
299 include_last_update=False):
306 """
300 """
307 Gets post HTML or JSON data that can be rendered on a page or used by
301 Gets post HTML or JSON data that can be rendered on a page or used by
308 API.
302 API.
309 """
303 """
310
304
311 if format_type == DIFF_TYPE_HTML:
305 if format_type == DIFF_TYPE_HTML:
312 params = dict()
306 params = dict()
313 params['post'] = self
307 params['post'] = self
314 if PARAMETER_TRUNCATED in request.GET:
308 if PARAMETER_TRUNCATED in request.GET:
315 params[PARAMETER_TRUNCATED] = True
309 params[PARAMETER_TRUNCATED] = True
316 else:
310 else:
317 params[PARAMETER_REPLY_LINK] = True
311 params[PARAMETER_REPLY_LINK] = True
318
312
319 return render_to_string('boards/api_post.html', params)
313 return render_to_string('boards/api_post.html', params)
320 elif format_type == DIFF_TYPE_JSON:
314 elif format_type == DIFF_TYPE_JSON:
321 post_json = {
315 post_json = {
322 'id': self.id,
316 'id': self.id,
323 'title': self.title,
317 'title': self.title,
324 'text': self._text_rendered,
318 'text': self._text_rendered,
325 }
319 }
326 if self.images.exists():
320 if self.images.exists():
327 post_image = self.get_first_image()
321 post_image = self.get_first_image()
328 post_json['image'] = post_image.image.url
322 post_json['image'] = post_image.image.url
329 post_json['image_preview'] = post_image.image.url_200x150
323 post_json['image_preview'] = post_image.image.url_200x150
330 if include_last_update:
324 if include_last_update:
331 post_json['bump_time'] = datetime_to_epoch(
325 post_json['bump_time'] = datetime_to_epoch(
332 self.get_thread().bump_time)
326 self.get_thread().bump_time)
333 return post_json
327 return post_json
334
328
335 def send_to_websocket(self, request, recursive=True):
329 def send_to_websocket(self, request, recursive=True):
336 """
330 """
337 Sends post HTML data to the thread web socket.
331 Sends post HTML data to the thread web socket.
338 """
332 """
339
333
340 if not settings.WEBSOCKETS_ENABLED:
334 if not settings.WEBSOCKETS_ENABLED:
341 return
335 return
342
336
343 client = Client()
337 client = Client()
344
338
345 logger = logging.getLogger('boards.post.websocket')
339 logger = logging.getLogger('boards.post.websocket')
346
340
347 thread_ids = list()
341 thread_ids = list()
348 for thread in self.get_threads().all():
342 for thread in self.get_threads().all():
349 thread_ids.append(thread.id)
343 thread_ids.append(thread.id)
350
344
351 channel_name = WS_CHANNEL_THREAD + str(thread.get_opening_post_id())
345 channel_name = WS_CHANNEL_THREAD + str(thread.get_opening_post_id())
352 client.publish(channel_name, {
346 client.publish(channel_name, {
353 WS_NOTIFICATION_TYPE: WS_NOTIFICATION_TYPE_NEW_POST,
347 WS_NOTIFICATION_TYPE: WS_NOTIFICATION_TYPE_NEW_POST,
354 })
348 })
355 client.send()
349 client.send()
356
350
357 logger.info('Sent notification from post #{} to channel {}'.format(
351 logger.info('Sent notification from post #{} to channel {}'.format(
358 self.id, channel_name))
352 self.id, channel_name))
359
353
360 if recursive:
354 if recursive:
361 for reply_number in re.finditer(REGEX_REPLY, self.get_raw_text()):
355 for reply_number in re.finditer(REGEX_REPLY, self.get_raw_text()):
362 post_id = reply_number.group(1)
356 post_id = reply_number.group(1)
363
357
364 try:
358 try:
365 ref_post = Post.objects.get(id=post_id)
359 ref_post = Post.objects.get(id=post_id)
366
360
367 if ref_post.get_threads().exclude(id__in=thread_ids).exists():
361 if ref_post.get_threads().exclude(id__in=thread_ids).exists():
368 # If post is in this thread, its thread was already notified.
362 # If post is in this thread, its thread was already notified.
369 # Otherwise, notify its thread separately.
363 # Otherwise, notify its thread separately.
370 ref_post.send_to_websocket(request, recursive=False)
364 ref_post.send_to_websocket(request, recursive=False)
371 except ObjectDoesNotExist:
365 except ObjectDoesNotExist:
372 pass
366 pass
373
367
374 def save(self, force_insert=False, force_update=False, using=None,
368 def save(self, force_insert=False, force_update=False, using=None,
375 update_fields=None):
369 update_fields=None):
376 self._text_rendered = Parser().parse(self.get_raw_text())
370 self._text_rendered = Parser().parse(self.get_raw_text())
377
371
378 super().save(force_insert, force_update, using, update_fields)
372 super().save(force_insert, force_update, using, update_fields)
379
373
380 def get_text(self) -> str:
374 def get_text(self) -> str:
381 return self._text_rendered
375 return self._text_rendered
382
376
383 def get_raw_text(self) -> str:
377 def get_raw_text(self) -> str:
384 return self.text
378 return self.text
385
379
386 def get_absolute_id(self) -> str:
380 def get_absolute_id(self) -> str:
387 """
381 """
388 If the post has many threads, shows its main thread OP id in the post
382 If the post has many threads, shows its main thread OP id in the post
389 ID.
383 ID.
390 """
384 """
391
385
392 if self.get_threads().count() > 1:
386 if self.get_threads().count() > 1:
393 return '{}/{}'.format(self.get_thread().get_opening_post_id(), self.id)
387 return '{}/{}'.format(self.get_thread().get_opening_post_id(), self.id)
394 else:
388 else:
395 return str(self.id)
389 return str(self.id)
396
390
397 def connect_notifications(self):
391 def connect_notifications(self):
398 for reply_number in re.finditer(REGEX_NOTIFICATION, self.get_raw_text()):
392 for reply_number in re.finditer(REGEX_NOTIFICATION, self.get_raw_text()):
399 user_name = reply_number.group(1).lower()
393 user_name = reply_number.group(1).lower()
400 Notification.objects.get_or_create(name=user_name, post=self)
394 Notification.objects.get_or_create(name=user_name, post=self)
401
395
402 def connect_replies(self):
396 def connect_replies(self):
403 """
397 """
404 Connects replies to a post to show them as a reflink map
398 Connects replies to a post to show them as a reflink map
405 """
399 """
406
400
407 for reply_number in re.finditer(REGEX_REPLY, self.get_raw_text()):
401 for reply_number in re.finditer(REGEX_REPLY, self.get_raw_text()):
408 post_id = reply_number.group(1)
402 post_id = reply_number.group(1)
409
403
410 try:
404 try:
411 referenced_post = Post.objects.get(id=post_id)
405 referenced_post = Post.objects.get(id=post_id)
412
406
413 referenced_post.referenced_posts.add(self)
407 referenced_post.referenced_posts.add(self)
414 referenced_post.last_edit_time = self.pub_time
408 referenced_post.last_edit_time = self.pub_time
415 referenced_post.build_refmap()
409 referenced_post.build_refmap()
416 referenced_post.save(update_fields=['refmap', 'last_edit_time'])
410 referenced_post.save(update_fields=['refmap', 'last_edit_time'])
417
411
418 referenced_threads = referenced_post.get_threads().all()
412 referenced_threads = referenced_post.get_threads().all()
419 for thread in referenced_threads:
413 for thread in referenced_threads:
420 if thread.can_bump():
414 if thread.can_bump():
421 thread.update_bump_status()
415 thread.update_bump_status()
422
416
423 thread.last_edit_time = self.pub_time
417 thread.last_edit_time = self.pub_time
424 thread.save(update_fields=['last_edit_time', 'bumpable'])
418 thread.save(update_fields=['last_edit_time', 'bumpable'])
425 except ObjectDoesNotExist:
419 except ObjectDoesNotExist:
426 pass
420 pass
427
421
428 def connect_threads(self, threads):
422 def connect_threads(self, threads):
429 """
423 """
430 If the referenced post is an OP in another thread,
424 If the referenced post is an OP in another thread,
431 make this post multi-thread.
425 make this post multi-thread.
432 """
426 """
433
427
434 for referenced_post in threads:
428 for referenced_post in threads:
435 if referenced_post.is_opening():
429 if referenced_post.is_opening():
436 referenced_threads = referenced_post.get_threads().all()
430 referenced_threads = referenced_post.get_threads().all()
437 for thread in referenced_threads:
431 for thread in referenced_threads:
438 if thread.can_bump():
432 if thread.can_bump():
439 thread.update_bump_status()
433 thread.update_bump_status()
440
434
441 thread.last_edit_time = self.pub_time
435 thread.last_edit_time = self.pub_time
442 thread.save(update_fields=['last_edit_time', 'bumpable'])
436 thread.save(update_fields=['last_edit_time', 'bumpable'])
443
437
444 self.threads.add(thread)
438 self.threads.add(thread)
General Comments 0
You need to be logged in to leave comments. Login now