Show More
@@ -0,0 +1,31 b'' | |||||
|
1 | import xml.etree.ElementTree as et | |||
|
2 | ||||
|
3 | from boards.models import Post | |||
|
4 | ||||
|
5 | TAG_THREAD = 'thread' | |||
|
6 | ||||
|
7 | ||||
|
8 | class PostFilter: | |||
|
9 | def __init__(self, content=None): | |||
|
10 | self.content = content | |||
|
11 | ||||
|
12 | def filter(self, posts): | |||
|
13 | return posts | |||
|
14 | ||||
|
15 | def add_filter(self, model_tag, value): | |||
|
16 | return model_tag | |||
|
17 | ||||
|
18 | ||||
|
19 | class ThreadFilter(PostFilter): | |||
|
20 | def filter(self, posts): | |||
|
21 | op_id = self.content.text | |||
|
22 | ||||
|
23 | op = Post.objects.filter(opening=True, id=op_id).first() | |||
|
24 | if op: | |||
|
25 | return posts.filter(thread=op.get_thread()) | |||
|
26 | else: | |||
|
27 | return posts.none() | |||
|
28 | ||||
|
29 | def add_filter(self, model_tag, value): | |||
|
30 | thread_tag = et.SubElement(model_tag, TAG_THREAD) | |||
|
31 | thread_tag.text = str(value) |
@@ -24,6 +24,8 b' class Command(BaseCommand):' | |||||
24 | parser.add_argument('--split-query', type=int, default=1, |
|
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 | parser.add_argument('--thread', type=int, | |||
|
28 | help='Get posts of one specific thread') | |||
27 |
|
29 | |||
28 | def handle(self, *args, **options): |
|
30 | def handle(self, *args, **options): | |
29 | logger = logging.getLogger('boards.sync') |
|
31 | logger = logging.getLogger('boards.sync') | |
@@ -45,7 +47,7 b' class Command(BaseCommand):' | |||||
45 | global_id = GlobalId(key_type=key_type, key=key, |
|
47 | global_id = GlobalId(key_type=key_type, key=key, | |
46 | local_id=local_id) |
|
48 | local_id=local_id) | |
47 |
|
49 | |||
48 |
xml = |
|
50 | xml = SyncManager.generate_request_get([global_id]) | |
49 | h = httplib2.Http() |
|
51 | h = httplib2.Http() | |
50 | response, content = h.request(get_url, method="POST", body=xml) |
|
52 | response, content = h.request(get_url, method="POST", body=xml) | |
51 |
|
53 | |||
@@ -55,7 +57,8 b' class Command(BaseCommand):' | |||||
55 | else: |
|
57 | else: | |
56 | logger.info('Running LIST request...') |
|
58 | logger.info('Running LIST request...') | |
57 | h = httplib2.Http() |
|
59 | h = httplib2.Http() | |
58 |
xml = |
|
60 | xml = SyncManager.generate_request_list( | |
|
61 | opening_post=options.get('thread')) | |||
59 | response, content = h.request(list_url, method="POST", body=xml) |
|
62 | response, content = h.request(list_url, method="POST", body=xml) | |
60 | logger.info('Processing response...') |
|
63 | logger.info('Processing response...') | |
61 |
|
64 | |||
@@ -83,7 +86,7 b' class Command(BaseCommand):' | |||||
83 | if len(ids_to_sync) > 0: |
|
86 | if len(ids_to_sync) > 0: | |
84 | limit = options.get('split_query', len(ids_to_sync)) |
|
87 | limit = options.get('split_query', len(ids_to_sync)) | |
85 | for offset in range(0, len(ids_to_sync), limit): |
|
88 | for offset in range(0, len(ids_to_sync), limit): | |
86 |
xml = |
|
89 | xml = SyncManager.generate_request_get(ids_to_sync[offset:offset + limit]) | |
87 | h = httplib2.Http() |
|
90 | h = httplib2.Http() | |
88 | logger.info('Running GET request...') |
|
91 | logger.info('Running GET request...') | |
89 | response, content = h.request(get_url, method="POST", body=xml) |
|
92 | response, content = h.request(get_url, method="POST", body=xml) |
@@ -1,9 +1,13 b'' | |||||
1 | import xml.etree.ElementTree as et |
|
1 | import xml.etree.ElementTree as et | |
2 | import logging |
|
2 | import logging | |
|
3 | from xml.etree import ElementTree | |||
3 |
|
4 | |||
4 | from boards.abstracts.exceptions import SyncException |
|
5 | from boards.abstracts.exceptions import SyncException | |
|
6 | from boards.abstracts.sync_filters import ThreadFilter | |||
5 | from boards.models import KeyPair, GlobalId, Signature, Post, Tag |
|
7 | from boards.models import KeyPair, GlobalId, Signature, Post, Tag | |
6 | from boards.models.attachment.downloaders import download |
|
8 | from boards.models.attachment.downloaders import download | |
|
9 | from boards.models.signature import TAG_REQUEST, ATTR_TYPE, TYPE_GET, \ | |||
|
10 | ATTR_VERSION, TAG_MODEL, ATTR_NAME, TAG_ID, TYPE_LIST | |||
7 | from boards.utils import get_file_mimetype, get_file_hash |
|
11 | from boards.utils import get_file_mimetype, get_file_hash | |
8 | from django.db import transaction |
|
12 | from django.db import transaction | |
9 |
|
13 | |||
@@ -263,7 +267,7 b' class SyncManager:' | |||||
263 | logger.debug('Parsed new post {}'.format(global_id)) |
|
267 | logger.debug('Parsed new post {}'.format(global_id)) | |
264 |
|
268 | |||
265 | @staticmethod |
|
269 | @staticmethod | |
266 | def generate_response_list(): |
|
270 | def generate_response_list(filters): | |
267 | response = et.Element(TAG_RESPONSE) |
|
271 | response = et.Element(TAG_RESPONSE) | |
268 |
|
272 | |||
269 | status = et.SubElement(response, TAG_STATUS) |
|
273 | status = et.SubElement(response, TAG_STATUS) | |
@@ -271,7 +275,11 b' class SyncManager:' | |||||
271 |
|
275 | |||
272 | models = et.SubElement(response, TAG_MODELS) |
|
276 | models = et.SubElement(response, TAG_MODELS) | |
273 |
|
277 | |||
274 |
|
|
278 | posts = Post.objects.prefetch_related('global_id') | |
|
279 | for post_filter in filters: | |||
|
280 | posts = post_filter.filter(posts) | |||
|
281 | ||||
|
282 | for post in posts: | |||
275 | tag_model = et.SubElement(models, TAG_MODEL) |
|
283 | tag_model = et.SubElement(models, TAG_MODEL) | |
276 | tag_id = et.SubElement(tag_model, TAG_ID) |
|
284 | tag_id = et.SubElement(tag_model, TAG_ID) | |
277 | post.global_id.to_xml_element(tag_id) |
|
285 | post.global_id.to_xml_element(tag_id) | |
@@ -329,3 +337,42 b' class SyncManager:' | |||||
329 | attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF) |
|
337 | attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF) | |
330 | attachment_ref.set(ATTR_REF, attachment.hash) |
|
338 | attachment_ref.set(ATTR_REF, attachment.hash) | |
331 | attachment_ref.set(ATTR_URL, attachment.file.url) |
|
339 | attachment_ref.set(ATTR_URL, attachment.file.url) | |
|
340 | ||||
|
341 | @staticmethod | |||
|
342 | def generate_request_get(global_id_list: list): | |||
|
343 | """ | |||
|
344 | Form a get request from a list of ModelId objects. | |||
|
345 | """ | |||
|
346 | ||||
|
347 | request = et.Element(TAG_REQUEST) | |||
|
348 | request.set(ATTR_TYPE, TYPE_GET) | |||
|
349 | request.set(ATTR_VERSION, '1.0') | |||
|
350 | ||||
|
351 | model = et.SubElement(request, TAG_MODEL) | |||
|
352 | model.set(ATTR_VERSION, '1.0') | |||
|
353 | model.set(ATTR_NAME, 'post') | |||
|
354 | ||||
|
355 | for global_id in global_id_list: | |||
|
356 | tag_id = et.SubElement(model, TAG_ID) | |||
|
357 | global_id.to_xml_element(tag_id) | |||
|
358 | ||||
|
359 | return et.tostring(request, 'unicode') | |||
|
360 | ||||
|
361 | @staticmethod | |||
|
362 | def generate_request_list(opening_post=None): | |||
|
363 | """ | |||
|
364 | Form a pull request from a list of ModelId objects. | |||
|
365 | """ | |||
|
366 | ||||
|
367 | request = et.Element(TAG_REQUEST) | |||
|
368 | request.set(ATTR_TYPE, TYPE_LIST) | |||
|
369 | request.set(ATTR_VERSION, '1.0') | |||
|
370 | ||||
|
371 | model = et.SubElement(request, TAG_MODEL) | |||
|
372 | model.set(ATTR_VERSION, '1.0') | |||
|
373 | model.set(ATTR_NAME, 'post') | |||
|
374 | ||||
|
375 | if opening_post: | |||
|
376 | ThreadFilter().add_filter(model, opening_post) | |||
|
377 | ||||
|
378 | return et.tostring(request, 'unicode') No newline at end of file |
@@ -1,8 +1,9 b'' | |||||
1 | import xml.etree.ElementTree as et |
|
1 | import xml.etree.ElementTree as et | |
|
2 | ||||
2 | from django.db import models |
|
3 | from django.db import models | |
|
4 | ||||
3 | from boards.models import KeyPair |
|
5 | from boards.models import KeyPair | |
4 |
|
6 | |||
5 |
|
||||
6 | TAG_MODEL = 'model' |
|
7 | TAG_MODEL = 'model' | |
7 | TAG_REQUEST = 'request' |
|
8 | TAG_REQUEST = 'request' | |
8 | TAG_ID = 'id' |
|
9 | TAG_ID = 'id' | |
@@ -20,40 +21,6 b" ATTR_LOCAL_ID = 'local-id'" | |||||
20 |
|
21 | |||
21 |
|
22 | |||
22 | class GlobalIdManager(models.Manager): |
|
23 | class GlobalIdManager(models.Manager): | |
23 | def generate_request_get(self, global_id_list: list): |
|
|||
24 | """ |
|
|||
25 | Form a get request from a list of ModelId objects. |
|
|||
26 | """ |
|
|||
27 |
|
||||
28 | request = et.Element(TAG_REQUEST) |
|
|||
29 | request.set(ATTR_TYPE, TYPE_GET) |
|
|||
30 | request.set(ATTR_VERSION, '1.0') |
|
|||
31 |
|
||||
32 | model = et.SubElement(request, TAG_MODEL) |
|
|||
33 | model.set(ATTR_VERSION, '1.0') |
|
|||
34 | model.set(ATTR_NAME, 'post') |
|
|||
35 |
|
||||
36 | for global_id in global_id_list: |
|
|||
37 | tag_id = et.SubElement(model, TAG_ID) |
|
|||
38 | global_id.to_xml_element(tag_id) |
|
|||
39 |
|
||||
40 | return et.tostring(request, 'unicode') |
|
|||
41 |
|
||||
42 | def generate_request_list(self): |
|
|||
43 | """ |
|
|||
44 | Form a pull request from a list of ModelId objects. |
|
|||
45 | """ |
|
|||
46 |
|
||||
47 | request = et.Element(TAG_REQUEST) |
|
|||
48 | request.set(ATTR_TYPE, TYPE_LIST) |
|
|||
49 | request.set(ATTR_VERSION, '1.0') |
|
|||
50 |
|
||||
51 | model = et.SubElement(request, TAG_MODEL) |
|
|||
52 | model.set(ATTR_VERSION, '1.0') |
|
|||
53 | model.set(ATTR_NAME, 'post') |
|
|||
54 |
|
||||
55 | return et.tostring(request, 'unicode') |
|
|||
56 |
|
||||
57 | def global_id_exists(self, global_id): |
|
24 | def global_id_exists(self, global_id): | |
58 | """ |
|
25 | """ | |
59 | Checks if the same global id already exists in the system. |
|
26 | Checks if the same global id already exists in the system. |
@@ -1,4 +1,3 b'' | |||||
1 | from base64 import b64encode |
|
|||
2 |
|
|
1 | import logging | |
3 |
|
2 | |||
4 | from django.test import TestCase |
|
3 | from django.test import TestCase | |
@@ -38,7 +37,7 b' class KeyTest(TestCase):' | |||||
38 | def test_request_get(self): |
|
37 | def test_request_get(self): | |
39 | post = self._create_post_with_key() |
|
38 | post = self._create_post_with_key() | |
40 |
|
39 | |||
41 |
request = |
|
40 | request = SyncManager.generate_request_get([post.global_id]) | |
42 | logger.debug(request) |
|
41 | logger.debug(request) | |
43 |
|
42 | |||
44 | key = KeyPair.objects.get(primary=True) |
|
43 | key = KeyPair.objects.get(primary=True) |
@@ -3,7 +3,7 b' from django.test import TestCase' | |||||
3 | from boards.models import KeyPair, Post, Tag |
|
3 | from boards.models import KeyPair, Post, Tag | |
4 | from boards.models.post.sync import SyncManager |
|
4 | from boards.models.post.sync import SyncManager | |
5 | from boards.tests.mocks import MockRequest |
|
5 | from boards.tests.mocks import MockRequest | |
6 | from boards.views.sync import response_get |
|
6 | from boards.views.sync import response_get, response_list | |
7 |
|
7 | |||
8 | __author__ = 'neko259' |
|
8 | __author__ = 'neko259' | |
9 |
|
9 | |||
@@ -103,3 +103,112 b' class SyncTest(TestCase):' | |||||
103 | post.version, |
|
103 | post.version, | |
104 | ) in response, |
|
104 | ) in response, | |
105 | 'Wrong response generated for the GET request.') |
|
105 | 'Wrong response generated for the GET request.') | |
|
106 | ||||
|
107 | def test_list_all(self): | |||
|
108 | key = KeyPair.objects.generate_key(primary=True) | |||
|
109 | tag = Tag.objects.create(name='tag1') | |||
|
110 | post = Post.objects.create_post(title='test_title', | |||
|
111 | text='test_text\rline two', | |||
|
112 | tags=[tag]) | |||
|
113 | post2 = Post.objects.create_post(title='test title 2', | |||
|
114 | text='test text 2', | |||
|
115 | tags=[tag]) | |||
|
116 | ||||
|
117 | request_all = MockRequest() | |||
|
118 | request_all.body = ( | |||
|
119 | '<request type="list" version="1.0">' | |||
|
120 | '<model name="post" version="1.0">' | |||
|
121 | '</model>' | |||
|
122 | '</request>' | |||
|
123 | ) | |||
|
124 | ||||
|
125 | response_all = response_list(request_all).content.decode() | |||
|
126 | self.assertTrue( | |||
|
127 | '<status>success</status>' | |||
|
128 | '<models>' | |||
|
129 | '<model>' | |||
|
130 | '<id key="{}" local-id="{}" type="{}" />' | |||
|
131 | '<version>{}</version>' | |||
|
132 | '</model>' | |||
|
133 | '<model>' | |||
|
134 | '<id key="{}" local-id="{}" type="{}" />' | |||
|
135 | '<version>{}</version>' | |||
|
136 | '</model>' | |||
|
137 | '</models>'.format( | |||
|
138 | post.global_id.key, | |||
|
139 | post.global_id.local_id, | |||
|
140 | post.global_id.key_type, | |||
|
141 | post.version, | |||
|
142 | post2.global_id.key, | |||
|
143 | post2.global_id.local_id, | |||
|
144 | post2.global_id.key_type, | |||
|
145 | post2.version, | |||
|
146 | ) in response_all, | |||
|
147 | 'Wrong response generated for the LIST request for all posts.') | |||
|
148 | ||||
|
149 | def test_list_existing_thread(self): | |||
|
150 | key = KeyPair.objects.generate_key(primary=True) | |||
|
151 | tag = Tag.objects.create(name='tag1') | |||
|
152 | post = Post.objects.create_post(title='test_title', | |||
|
153 | text='test_text\rline two', | |||
|
154 | tags=[tag]) | |||
|
155 | post2 = Post.objects.create_post(title='test title 2', | |||
|
156 | text='test text 2', | |||
|
157 | tags=[tag]) | |||
|
158 | ||||
|
159 | request_thread = MockRequest() | |||
|
160 | request_thread.body = ( | |||
|
161 | '<request type="list" version="1.0">' | |||
|
162 | '<model name="post" version="1.0">' | |||
|
163 | '<thread>{}</thread>' | |||
|
164 | '</model>' | |||
|
165 | '</request>'.format( | |||
|
166 | post.id, | |||
|
167 | ) | |||
|
168 | ) | |||
|
169 | ||||
|
170 | response_thread = response_list(request_thread).content.decode() | |||
|
171 | self.assertTrue( | |||
|
172 | '<status>success</status>' | |||
|
173 | '<models>' | |||
|
174 | '<model>' | |||
|
175 | '<id key="{}" local-id="{}" type="{}" />' | |||
|
176 | '<version>{}</version>' | |||
|
177 | '</model>' | |||
|
178 | '</models>'.format( | |||
|
179 | post.global_id.key, | |||
|
180 | post.global_id.local_id, | |||
|
181 | post.global_id.key_type, | |||
|
182 | post.version, | |||
|
183 | ) in response_thread, | |||
|
184 | 'Wrong response generated for the LIST request for posts of ' | |||
|
185 | 'existing thread.') | |||
|
186 | ||||
|
187 | def test_list_non_existing_thread(self): | |||
|
188 | key = KeyPair.objects.generate_key(primary=True) | |||
|
189 | tag = Tag.objects.create(name='tag1') | |||
|
190 | post = Post.objects.create_post(title='test_title', | |||
|
191 | text='test_text\rline two', | |||
|
192 | tags=[tag]) | |||
|
193 | post2 = Post.objects.create_post(title='test title 2', | |||
|
194 | text='test text 2', | |||
|
195 | tags=[tag]) | |||
|
196 | ||||
|
197 | request_thread = MockRequest() | |||
|
198 | request_thread.body = ( | |||
|
199 | '<request type="list" version="1.0">' | |||
|
200 | '<model name="post" version="1.0">' | |||
|
201 | '<thread>{}</thread>' | |||
|
202 | '</model>' | |||
|
203 | '</request>'.format( | |||
|
204 | 0, | |||
|
205 | ) | |||
|
206 | ) | |||
|
207 | ||||
|
208 | response_thread = response_list(request_thread).content.decode() | |||
|
209 | self.assertTrue( | |||
|
210 | '<status>success</status>' | |||
|
211 | '<models />' | |||
|
212 | in response_thread, | |||
|
213 | 'Wrong response generated for the LIST request for posts of ' | |||
|
214 | 'non-existing thread.') |
@@ -1,17 +1,41 b'' | |||||
|
1 | import logging | |||
|
2 | ||||
1 | import xml.etree.ElementTree as et |
|
3 | import xml.etree.ElementTree as et | |
2 |
|
4 | |||
3 | from django.http import HttpResponse, Http404 |
|
5 | from django.http import HttpResponse, Http404 | |
|
6 | ||||
|
7 | from boards.abstracts.sync_filters import ThreadFilter, TAG_THREAD | |||
4 | from boards.models import GlobalId, Post |
|
8 | from boards.models import GlobalId, Post | |
5 | from boards.models.post.sync import SyncManager |
|
9 | from boards.models.post.sync import SyncManager | |
6 |
|
10 | |||
7 |
|
11 | |||
|
12 | logger = logging.getLogger('boards.sync') | |||
|
13 | ||||
|
14 | ||||
|
15 | FILTERS = { | |||
|
16 | TAG_THREAD: ThreadFilter, | |||
|
17 | } | |||
|
18 | ||||
|
19 | ||||
8 | def response_list(request): |
|
20 | def response_list(request): | |
9 | request_xml = request.body |
|
21 | request_xml = request.body | |
10 |
|
22 | |||
|
23 | filters = [] | |||
|
24 | ||||
11 | if request_xml is None or len(request_xml) == 0: |
|
25 | if request_xml is None or len(request_xml) == 0: | |
12 | return HttpResponse(content='Use the API') |
|
26 | return HttpResponse(content='Use the API') | |
|
27 | else: | |||
|
28 | root_tag = et.fromstring(request_xml) | |||
|
29 | model_tag = root_tag[0] | |||
13 |
|
30 | |||
14 | response_xml = SyncManager.generate_response_list() |
|
31 | for tag_filter in model_tag: | |
|
32 | filter_name = tag_filter.tag | |||
|
33 | model_filter = FILTERS.get(filter_name)(tag_filter) | |||
|
34 | if not model_filter: | |||
|
35 | logger.warning('Unavailable filter: {}'.format(filter_name)) | |||
|
36 | filters.append(model_filter) | |||
|
37 | ||||
|
38 | response_xml = SyncManager.generate_response_list(filters) | |||
15 |
|
39 | |||
16 | return HttpResponse(content=response_xml) |
|
40 | return HttpResponse(content=response_xml) | |
17 |
|
41 |
General Comments 0
You need to be logged in to leave comments.
Login now