##// END OF EJS Templates
Added support for partial import: when the reply is imported but the original post is not, the reply will be connected after the original post is ready
neko259 -
r1588:b69fb396 default
parent child Browse files
Show More
@@ -1,184 +1,193 b''
1 1 import logging
2 2
3 3 from datetime import datetime, timedelta, date
4 4 from datetime import time as dtime
5 5
6 6 from django.db import models, transaction
7 7 from django.utils import timezone
8 from django.dispatch import Signal
8 9
9 10 import boards
10 11
11 12 from boards.models.user import Ban
12 13 from boards.mdx_neboard import Parser
13 14 from boards.models import PostImage, Attachment
14 15 from boards import utils
15 16
16 17 __author__ = 'neko259'
17 18
18 19 IMAGE_TYPES = (
19 20 'jpeg',
20 21 'jpg',
21 22 'png',
22 23 'bmp',
23 24 'gif',
24 25 )
25 26
26 27 POSTS_PER_DAY_RANGE = 7
27 28 NO_IP = '0.0.0.0'
28 29
29 30
31 post_import_deps = Signal()
32
33
30 34 class PostManager(models.Manager):
31 35 @transaction.atomic
32 36 def create_post(self, title: str, text: str, file=None, thread=None,
33 37 ip=NO_IP, tags: list=None, opening_posts: list=None,
34 38 tripcode='', monochrome=False, images=[]):
35 39 """
36 40 Creates new post
37 41 """
38 42
39 43 if thread is not None and thread.is_archived():
40 44 raise Exception('Cannot post into an archived thread')
41 45
42 46 if not utils.is_anonymous_mode():
43 47 is_banned = Ban.objects.filter(ip=ip).exists()
44 48 else:
45 49 is_banned = False
46 50
47 51 # TODO Raise specific exception and catch it in the views
48 52 if is_banned:
49 53 raise Exception("This user is banned")
50 54
51 55 if not tags:
52 56 tags = []
53 57 if not opening_posts:
54 58 opening_posts = []
55 59
56 60 posting_time = timezone.now()
57 61 new_thread = False
58 62 if not thread:
59 63 thread = boards.models.thread.Thread.objects.create(
60 64 bump_time=posting_time, last_edit_time=posting_time,
61 65 monochrome=monochrome)
62 66 list(map(thread.tags.add, tags))
63 67 boards.models.thread.Thread.objects.process_oldest_threads()
64 68 new_thread = True
65 69
66 70 pre_text = Parser().preparse(text)
67 71
68 72 post = self.create(title=title,
69 73 text=pre_text,
70 74 pub_time=posting_time,
71 75 poster_ip=ip,
72 76 thread=thread,
73 77 last_edit_time=posting_time,
74 78 tripcode=tripcode,
75 79 opening=new_thread)
76 80 post.threads.add(thread)
77 81
78 82 logger = logging.getLogger('boards.post.create')
79 83
80 84 logger.info('Created post [{}] with text [{}] by {}'.format(post,
81 85 post.get_text(),post.poster_ip))
82 86
83 87 if file:
84 88 self._add_file_to_post(file, post)
85 89 for image in images:
86 90 post.images.add(image)
87 91
88 92 post.connect_threads(opening_posts)
89 93 post.set_global_id()
90 94
91 95 # Thread needs to be bumped only when the post is already created
92 96 if not new_thread:
93 97 thread.last_edit_time = posting_time
94 98 thread.bump()
95 99 thread.save()
96 100
97 101 return post
98 102
99 103 def delete_posts_by_ip(self, ip):
100 104 """
101 105 Deletes all posts of the author with same IP
102 106 """
103 107
104 108 posts = self.filter(poster_ip=ip)
105 109 for post in posts:
106 110 post.delete()
107 111
108 112 @utils.cached_result()
109 113 def get_posts_per_day(self) -> float:
110 114 """
111 115 Gets average count of posts per day for the last 7 days
112 116 """
113 117
114 118 day_end = date.today()
115 119 day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
116 120
117 121 day_time_start = timezone.make_aware(datetime.combine(
118 122 day_start, dtime()), timezone.get_current_timezone())
119 123 day_time_end = timezone.make_aware(datetime.combine(
120 124 day_end, dtime()), timezone.get_current_timezone())
121 125
122 126 posts_per_period = float(self.filter(
123 127 pub_time__lte=day_time_end,
124 128 pub_time__gte=day_time_start).count())
125 129
126 130 ppd = posts_per_period / POSTS_PER_DAY_RANGE
127 131
128 132 return ppd
129 133
130 134 @transaction.atomic
131 135 def import_post(self, title: str, text: str, pub_time: str, global_id,
132 136 opening_post=None, tags=list(), files=list(),
133 137 tripcode=None, version=1):
134 138 is_opening = opening_post is None
135 139 if is_opening:
136 140 thread = boards.models.thread.Thread.objects.create(
137 141 bump_time=pub_time, last_edit_time=pub_time)
138 142 list(map(thread.tags.add, tags))
139 143 else:
140 144 thread = opening_post.get_thread()
141 145
142 146 post = self.create(title=title,
143 147 text=text,
144 148 pub_time=pub_time,
145 149 poster_ip=NO_IP,
146 150 last_edit_time=pub_time,
147 151 global_id=global_id,
148 152 opening=is_opening,
149 153 thread=thread,
150 154 tripcode=tripcode,
151 155 version=version)
152 156
153 157 for file in files:
154 158 self._add_file_to_post(file, post)
155 159
156 160 post.threads.add(thread)
157 161
162 url_to_post = '[post]{}[/post]'.format(str(global_id))
163 replies = self.filter(text__contains=url_to_post)
164 for reply in replies:
165 post_import_deps.send(reply.__class__)
166
158 167 @transaction.atomic
159 168 def update_post(self, post, title: str, text: str, pub_time: str,
160 169 tags=list(), files=list(), tripcode=None, version=1):
161 170 post.title = title
162 171 post.text = text
163 172 post.pub_time = pub_time
164 173 post.tripcode = tripcode
165 174 post.version = version
166 175 post.save()
167 176
168 177 post.clear_cache()
169 178
170 179 post.images.clear()
171 180 post.attachments.clear()
172 181 for file in files:
173 182 self._add_file_to_post(file, post)
174 183
175 184 thread = post.get_thread()
176 185 thread.tags.clear()
177 186 list(map(thread.tags.add, tags))
178 187
179 188 def _add_file_to_post(self, file, post):
180 189 file_type = file.name.split('.')[-1].lower()
181 190 if file_type in IMAGE_TYPES:
182 191 post.images.add(PostImage.objects.create_with_hash(file))
183 192 else:
184 193 post.attachments.add(Attachment.objects.create_with_hash(file))
@@ -1,89 +1,92 b''
1 1 import re
2 2 from boards.mdx_neboard import get_parser
3 3
4 4 from boards.models import Post, GlobalId
5 from boards.models.post import REGEX_NOTIFICATION
6 from boards.models.post import REGEX_REPLY, REGEX_GLOBAL_REPLY
5 from boards.models.post import REGEX_NOTIFICATION, REGEX_REPLY,\
6 REGEX_GLOBAL_REPLY
7 from boards.models.post.manager import post_import_deps
7 8 from boards.models.user import Notification
8 9 from django.db.models.signals import post_save, pre_save, pre_delete, \
9 10 post_delete
10 11 from django.dispatch import receiver
11 12 from django.utils import timezone
12 13
13 14
14 15 @receiver(post_save, sender=Post)
15 16 def connect_replies(instance, **kwargs):
16 17 for reply_number in re.finditer(REGEX_REPLY, instance.get_raw_text()):
17 18 post_id = reply_number.group(1)
18 19
19 20 try:
20 21 referenced_post = Post.objects.get(id=post_id)
21 22
22 23 if not referenced_post.referenced_posts.filter(
23 24 id=instance.id).exists():
24 25 referenced_post.referenced_posts.add(instance)
25 26 referenced_post.last_edit_time = instance.pub_time
26 27 referenced_post.build_refmap()
27 28 referenced_post.save(update_fields=['refmap', 'last_edit_time'])
28 29 except Post.DoesNotExist:
29 30 pass
30 31
31 32
32 33 @receiver(post_save, sender=Post)
34 @receiver(post_import_deps, sender=Post)
33 35 def connect_global_replies(instance, **kwargs):
34 36 for reply_number in re.finditer(REGEX_GLOBAL_REPLY, instance.get_raw_text()):
35 37 key_type = reply_number.group(1)
36 38 key = reply_number.group(2)
37 39 local_id = reply_number.group(3)
38 40
39 41 try:
40 42 global_id = GlobalId.objects.get(key_type=key_type, key=key,
41 43 local_id=local_id)
42 44 referenced_post = Post.objects.get(global_id=global_id)
43 45 referenced_post.referenced_posts.add(instance)
44 46 referenced_post.last_edit_time = instance.pub_time
45 47 referenced_post.build_refmap()
46 48 referenced_post.save(update_fields=['refmap', 'last_edit_time'])
47 49 except (GlobalId.DoesNotExist, Post.DoesNotExist):
48 50 pass
49 51
50 52
51 53 @receiver(post_save, sender=Post)
52 54 def connect_notifications(instance, **kwargs):
53 55 for reply_number in re.finditer(REGEX_NOTIFICATION, instance.get_raw_text()):
54 56 user_name = reply_number.group(1).lower()
55 57 Notification.objects.get_or_create(name=user_name, post=instance)
56 58
57 59
58 60 @receiver(pre_save, sender=Post)
59 def preparse_text(instance, **kwargs):
61 @receiver(post_import_deps, sender=Post)
62 def parse_text(instance, **kwargs):
60 63 instance._text_rendered = get_parser().parse(instance.get_raw_text())
61 64
62 65
63 66 @receiver(pre_delete, sender=Post)
64 67 def delete_images(instance, **kwargs):
65 68 for image in instance.images.all():
66 69 image_refs_count = image.post_images.count()
67 70 if image_refs_count == 1:
68 71 image.delete()
69 72
70 73
71 74 @receiver(pre_delete, sender=Post)
72 75 def delete_attachments(instance, **kwargs):
73 76 for attachment in instance.attachments.all():
74 77 attachment_refs_count = attachment.attachment_posts.count()
75 78 if attachment_refs_count == 1:
76 79 attachment.delete()
77 80
78 81
79 82 @receiver(post_delete, sender=Post)
80 83 def update_thread_on_delete(instance, **kwargs):
81 84 thread = instance.get_thread()
82 85 thread.last_edit_time = timezone.now()
83 86 thread.save()
84 87
85 88
86 89 @receiver(post_delete, sender=Post)
87 90 def delete_global_id(instance, **kwargs):
88 91 if instance.global_id and instance.global_id.id:
89 92 instance.global_id.delete()
General Comments 0
You need to be logged in to leave comments. Login now