##// END OF EJS Templates
Added ability to filter posts in the LIST request
neko259 -
r1834:e64fc2ba default
parent child Browse files
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 = GlobalId.objects.generate_request_get([global_id])
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 = GlobalId.objects.generate_request_list()
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 = GlobalId.objects.generate_request_get(ids_to_sync[offset:offset+limit])
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 for post in Post.objects.prefetch_related('global_id').all():
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 import logging
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 = GlobalId.objects.generate_request_get([post.global_id])
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