##// END OF EJS Templates
Parse url attachments by the sync manager. Parse each post in a separate transaction. Default number of posts in one GET request is 1
neko259 -
r1800:e64666cc default
parent child Browse files
Show More
@@ -21,7 +21,7 b' class Command(BaseCommand):'
21 parser.add_argument('url', type=str, help='Server root url')
21 parser.add_argument('url', type=str, help='Server root url')
22 parser.add_argument('--global-id', type=str, default='',
22 parser.add_argument('--global-id', type=str, default='',
23 help='Post global ID')
23 help='Post global ID')
24 parser.add_argument('--split-query', type=int, default=10,
24 parser.add_argument('--split-query', type=int, default=1,
25 help='Split GET query into separate by the given'
25 help='Split GET query into separate by the given'
26 ' number of posts in one')
26 ' number of posts in one')
27
27
@@ -138,7 +138,7 b' class PostManager(models.Manager):'
138 @transaction.atomic
138 @transaction.atomic
139 def import_post(self, title: str, text: str, pub_time: str, global_id,
139 def import_post(self, title: str, text: str, pub_time: str, global_id,
140 opening_post=None, tags=list(), files=list(),
140 opening_post=None, tags=list(), files=list(),
141 tripcode=None, version=1):
141 file_urls=list(), tripcode=None, version=1):
142 is_opening = opening_post is None
142 is_opening = opening_post is None
143 if is_opening:
143 if is_opening:
144 thread = boards.models.thread.Thread.objects.create(
144 thread = boards.models.thread.Thread.objects.create(
@@ -160,6 +160,8 b' class PostManager(models.Manager):'
160
160
161 for file in files:
161 for file in files:
162 self._add_file_to_post(file, post)
162 self._add_file_to_post(file, post)
163 for file_url in file_urls:
164 post.attachments.add(Attachment.objects.create_from_url(file_url))
163
165
164 url_to_post = '[post]{}[/post]'.format(str(global_id))
166 url_to_post = '[post]{}[/post]'.format(str(global_id))
165 replies = self.filter(text__contains=url_to_post)
167 replies = self.filter(text__contains=url_to_post)
@@ -168,7 +170,7 b' class PostManager(models.Manager):'
168
170
169 @transaction.atomic
171 @transaction.atomic
170 def update_post(self, post, title: str, text: str, pub_time: str,
172 def update_post(self, post, title: str, text: str, pub_time: str,
171 tags=list(), files=list(), tripcode=None, version=1):
173 tags=list(), files=list(), file_urls=list(), tripcode=None, version=1):
172 post.title = title
174 post.title = title
173 post.text = text
175 post.text = text
174 post.pub_time = pub_time
176 post.pub_time = pub_time
@@ -181,6 +183,8 b' class PostManager(models.Manager):'
181 post.attachments.clear()
183 post.attachments.clear()
182 for file in files:
184 for file in files:
183 self._add_file_to_post(file, post)
185 self._add_file_to_post(file, post)
186 for file_url in file_urls:
187 post.attachments.add(Attachment.objects.create_from_url(file_url))
184
188
185 thread = post.get_thread()
189 thread = post.get_thread()
186 thread.tags.clear()
190 thread.tags.clear()
@@ -8,7 +8,6 b' from boards.utils import get_file_mimety'
8 from django.db import transaction
8 from django.db import transaction
9
9
10 EXCEPTION_NODE = 'Sync node returned an error: {}.'
10 EXCEPTION_NODE = 'Sync node returned an error: {}.'
11 EXCEPTION_OP = 'Load the OP first.'
12 EXCEPTION_DOWNLOAD = 'File was not downloaded.'
11 EXCEPTION_DOWNLOAD = 'File was not downloaded.'
13 EXCEPTION_HASH = 'File hash does not match attachment hash.'
12 EXCEPTION_HASH = 'File hash does not match attachment hash.'
14 EXCEPTION_SIGNATURE = 'Invalid model signature for {}.'
13 EXCEPTION_SIGNATURE = 'Invalid model signature for {}.'
@@ -165,96 +164,105 b' class SyncManager:'
165 return et.tostring(response, ENCODING_UNICODE)
164 return et.tostring(response, ENCODING_UNICODE)
166
165
167 @staticmethod
166 @staticmethod
168 @transaction.atomic
169 def parse_response_get(response_xml, hostname):
167 def parse_response_get(response_xml, hostname):
170 tag_root = et.fromstring(response_xml)
168 tag_root = et.fromstring(response_xml)
171 tag_status = tag_root.find(TAG_STATUS)
169 tag_status = tag_root.find(TAG_STATUS)
172 if STATUS_SUCCESS == tag_status.text:
170 if STATUS_SUCCESS == tag_status.text:
173 tag_models = tag_root.find(TAG_MODELS)
171 tag_models = tag_root.find(TAG_MODELS)
174 for tag_model in tag_models:
172 for tag_model in tag_models:
175 tag_content = tag_model.find(TAG_CONTENT)
173 SyncManager.parse_post(tag_model, hostname)
176
177 content_str = et.tostring(tag_content, ENCODING_UNICODE)
178
179 tag_id = tag_content.find(TAG_ID)
180 global_id, exists = GlobalId.from_xml_element(tag_id)
181 signatures = SyncManager._verify_model(global_id, content_str, tag_model)
182
183 version = int(tag_content.find(TAG_VERSION).text)
184 is_old = exists and global_id.post.version < version
185 if exists and not is_old:
186 print('Post with same ID exists and is up to date.')
187 else:
188 global_id.content = content_str
189 global_id.save()
190 for signature in signatures:
191 signature.global_id = global_id
192 signature.save()
193
194 title = tag_content.find(TAG_TITLE).text or ''
195 text = tag_content.find(TAG_TEXT).text or ''
196 pub_time = tag_content.find(TAG_PUB_TIME).text
197 tripcode_tag = tag_content.find(TAG_TRIPCODE)
198 if tripcode_tag is not None:
199 tripcode = tripcode_tag.text or ''
200 else:
201 tripcode = ''
202
203 thread = tag_content.find(TAG_THREAD)
204 tags = []
205 if thread:
206 thread_id = thread.find(TAG_ID)
207 op_global_id, exists = GlobalId.from_xml_element(thread_id)
208 if exists:
209 opening_post = Post.objects.get(global_id=op_global_id)
210 else:
211 raise SyncException(EXCEPTION_OP)
212 else:
213 opening_post = None
214 tag_tags = tag_content.find(TAG_TAGS)
215 for tag_tag in tag_tags:
216 tag, created = Tag.objects.get_or_create(
217 name=tag_tag.text)
218 tags.append(tag)
219
220 # TODO Check that the replied posts are already present
221 # before adding new ones
222
223 files = []
224 tag_attachments = tag_content.find(TAG_ATTACHMENTS) or list()
225 tag_refs = tag_model.find(TAG_ATTACHMENT_REFS)
226 for attachment in tag_attachments:
227 tag_ref = tag_refs.find("{}[@ref='{}']".format(
228 TAG_ATTACHMENT_REF, attachment.text))
229 url = tag_ref.get(ATTR_URL)
230 attached_file = download(hostname + url)
231 if attached_file is None:
232 raise SyncException(EXCEPTION_DOWNLOAD)
233
234 hash = get_file_hash(attached_file)
235 if hash != attachment.text:
236 raise SyncException(EXCEPTION_HASH)
237
238 files.append(attached_file)
239
240 if is_old:
241 post = global_id.post
242 Post.objects.update_post(
243 post, title=title, text=text, pub_time=pub_time,
244 tags=tags, files=files, tripcode=tripcode,
245 version=version)
246 logger.debug('Parsed updated post {}'.format(global_id))
247 else:
248 Post.objects.import_post(
249 title=title, text=text, pub_time=pub_time,
250 opening_post=opening_post, tags=tags,
251 global_id=global_id, files=files, tripcode=tripcode,
252 version=version)
253 logger.debug('Parsed new post {}'.format(global_id))
254 else:
174 else:
255 raise SyncException(EXCEPTION_NODE.format(tag_status.text))
175 raise SyncException(EXCEPTION_NODE.format(tag_status.text))
256
176
257 @staticmethod
177 @staticmethod
178 @transaction.atomic
179 def parse_post(tag_model, hostname):
180 tag_content = tag_model.find(TAG_CONTENT)
181
182 content_str = et.tostring(tag_content, ENCODING_UNICODE)
183
184 tag_id = tag_content.find(TAG_ID)
185 global_id, exists = GlobalId.from_xml_element(tag_id)
186 signatures = SyncManager._verify_model(global_id, content_str, tag_model)
187
188 version = int(tag_content.find(TAG_VERSION).text)
189 is_old = exists and global_id.post.version < version
190 if exists and not is_old:
191 print('Post with same ID exists and is up to date.')
192 else:
193 global_id.content = content_str
194 global_id.save()
195 for signature in signatures:
196 signature.global_id = global_id
197 signature.save()
198
199 title = tag_content.find(TAG_TITLE).text or ''
200 text = tag_content.find(TAG_TEXT).text or ''
201 pub_time = tag_content.find(TAG_PUB_TIME).text
202 tripcode_tag = tag_content.find(TAG_TRIPCODE)
203 if tripcode_tag is not None:
204 tripcode = tripcode_tag.text or ''
205 else:
206 tripcode = ''
207
208 thread = tag_content.find(TAG_THREAD)
209 tags = []
210 if thread:
211 thread_id = thread.find(TAG_ID)
212 op_global_id, exists = GlobalId.from_xml_element(thread_id)
213 if exists:
214 opening_post = Post.objects.get(global_id=op_global_id)
215 else:
216 logger.debug('No thread exists for post {}'.format(global_id))
217 else:
218 opening_post = None
219 tag_tags = tag_content.find(TAG_TAGS)
220 for tag_tag in tag_tags:
221 tag, created = Tag.objects.get_or_create(
222 name=tag_tag.text)
223 tags.append(tag)
224
225 # TODO Check that the replied posts are already present
226 # before adding new ones
227
228 files = []
229 urls = []
230 tag_attachments = tag_content.find(TAG_ATTACHMENTS) or list()
231 tag_refs = tag_model.find(TAG_ATTACHMENT_REFS)
232 for attachment in tag_attachments:
233 if attachment.get(ATTR_ID_TYPE) == ID_TYPE_URL:
234 urls.append(attachment.text)
235 else:
236 tag_ref = tag_refs.find("{}[@ref='{}']".format(
237 TAG_ATTACHMENT_REF, attachment.text))
238 url = tag_ref.get(ATTR_URL)
239 attached_file = download(hostname + url)
240 if attached_file is None:
241 raise SyncException(EXCEPTION_DOWNLOAD)
242
243 hash = get_file_hash(attached_file)
244 if hash != attachment.text:
245 raise SyncException(EXCEPTION_HASH)
246
247 files.append(attached_file)
248
249 if is_old:
250 post = global_id.post
251 Post.objects.update_post(
252 post, title=title, text=text, pub_time=pub_time,
253 tags=tags, files=files, file_urls=urls,
254 tripcode=tripcode, version=version)
255 logger.debug('Parsed updated post {}'.format(global_id))
256 else:
257 Post.objects.import_post(
258 title=title, text=text, pub_time=pub_time,
259 opening_post=opening_post, tags=tags,
260 global_id=global_id, files=files,
261 file_urls=urls, tripcode=tripcode,
262 version=version)
263 logger.debug('Parsed new post {}'.format(global_id))
264
265 @staticmethod
258 def generate_response_list():
266 def generate_response_list():
259 response = et.Element(TAG_RESPONSE)
267 response = et.Element(TAG_RESPONSE)
260
268
@@ -152,6 +152,7 b' Sample response:'
152 -->
152 -->
153 <attachments>
153 <attachments>
154 <attachment mimetype="image/png" id-type="md5">TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5I</attachment>
154 <attachment mimetype="image/png" id-type="md5">TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5I</attachment>
155 <attachment id-type="url">http://example.com/</attachment>
155 </attachments>
156 </attachments>
156 </content>
157 </content>
157 <!--
158 <!--
General Comments 0
You need to be logged in to leave comments. Login now