##// END OF EJS Templates
Made SyncManager's methods static
neko259 -
r1236:124750d7 decentral
parent child Browse files
Show More
@@ -1,59 +1,59 b''
1 import re
1 import re
2 import urllib.parse
2 import urllib.parse
3 import httplib2
3 import httplib2
4 import xml.etree.ElementTree as ET
4 import xml.etree.ElementTree as ET
5
5
6 from django.core.management import BaseCommand
6 from django.core.management import BaseCommand
7 from boards.models import GlobalId
7 from boards.models import GlobalId
8 from boards.models.post.sync import SyncManager
8 from boards.models.post.sync import SyncManager
9
9
10 __author__ = 'neko259'
10 __author__ = 'neko259'
11
11
12
12
13 REGEX_GLOBAL_ID = re.compile(r'(\w+)::([\w\+/]+)::(\d+)')
13 REGEX_GLOBAL_ID = re.compile(r'(\w+)::([\w\+/]+)::(\d+)')
14
14
15
15
16 class Command(BaseCommand):
16 class Command(BaseCommand):
17 help = 'Send a sync or get request to the server.' + \
17 help = 'Send a sync or get request to the server.' + \
18 'sync_with_server <server_url> [post_global_id]'
18 'sync_with_server <server_url> [post_global_id]'
19
19
20 def add_arguments(self, parser):
20 def add_arguments(self, parser):
21 parser.add_argument('url', type=str)
21 parser.add_argument('url', type=str)
22 parser.add_argument('global_id', type=str)
22 parser.add_argument('global_id', type=str)
23
23
24 def handle(self, *args, **options):
24 def handle(self, *args, **options):
25 url = options.get('url')
25 url = options.get('url')
26 global_id_str = options.get('global_id')
26 global_id_str = options.get('global_id')
27 if global_id_str:
27 if global_id_str:
28 match = REGEX_GLOBAL_ID.match(global_id_str)
28 match = REGEX_GLOBAL_ID.match(global_id_str)
29 if match:
29 if match:
30 key_type = match.group(1)
30 key_type = match.group(1)
31 key = match.group(2)
31 key = match.group(2)
32 local_id = match.group(3)
32 local_id = match.group(3)
33
33
34 global_id = GlobalId(key_type=key_type, key=key,
34 global_id = GlobalId(key_type=key_type, key=key,
35 local_id=local_id)
35 local_id=local_id)
36
36
37 xml = GlobalId.objects.generate_request_get([global_id])
37 xml = GlobalId.objects.generate_request_get([global_id])
38 # body = urllib.parse.urlencode(data)
38 # body = urllib.parse.urlencode(data)
39 h = httplib2.Http()
39 h = httplib2.Http()
40 response, content = h.request(url, method="POST", body=xml)
40 response, content = h.request(url, method="POST", body=xml)
41
41
42 SyncManager().parse_response_get(content)
42 SyncManager.parse_response_get(content)
43 else:
43 else:
44 raise Exception('Invalid global ID')
44 raise Exception('Invalid global ID')
45 else:
45 else:
46 h = httplib2.Http()
46 h = httplib2.Http()
47 response, content = h.request(url, method="POST")
47 response, content = h.request(url, method="POST")
48
48
49 print(content)
49 print(content)
50
50
51 root = ET.fromstring(content)
51 root = ET.fromstring(content)
52 status = root.findall('status')[0].text
52 status = root.findall('status')[0].text
53 if status == 'success':
53 if status == 'success':
54 models = root.findall('models')[0]
54 models = root.findall('models')[0]
55 for model in models:
55 for model in models:
56 model_content = model[0]
56 model_content = model[0]
57 print(model_content.findall('text')[0].text)
57 print(model_content.findall('text')[0].text)
58 else:
58 else:
59 raise Exception('Invalid response status')
59 raise Exception('Invalid response status')
@@ -1,128 +1,129 b''
1 import xml.etree.ElementTree as et
1 import xml.etree.ElementTree as et
2 from django.db import transaction
2 from django.db import transaction
3 from boards.models import KeyPair, GlobalId, Signature, Post
3 from boards.models import KeyPair, GlobalId, Signature, Post
4
4
5 ENCODING_UNICODE = 'unicode'
5 ENCODING_UNICODE = 'unicode'
6
6
7 TAG_MODEL = 'model'
7 TAG_MODEL = 'model'
8 TAG_REQUEST = 'request'
8 TAG_REQUEST = 'request'
9 TAG_RESPONSE = 'response'
9 TAG_RESPONSE = 'response'
10 TAG_ID = 'id'
10 TAG_ID = 'id'
11 TAG_STATUS = 'status'
11 TAG_STATUS = 'status'
12 TAG_MODELS = 'models'
12 TAG_MODELS = 'models'
13 TAG_TITLE = 'title'
13 TAG_TITLE = 'title'
14 TAG_TEXT = 'text'
14 TAG_TEXT = 'text'
15 TAG_THREAD = 'thread'
15 TAG_THREAD = 'thread'
16 TAG_PUB_TIME = 'pub-time'
16 TAG_PUB_TIME = 'pub-time'
17 TAG_SIGNATURES = 'signatures'
17 TAG_SIGNATURES = 'signatures'
18 TAG_SIGNATURE = 'signature'
18 TAG_SIGNATURE = 'signature'
19 TAG_CONTENT = 'content'
19 TAG_CONTENT = 'content'
20 TAG_ATTACHMENTS = 'attachments'
20 TAG_ATTACHMENTS = 'attachments'
21 TAG_ATTACHMENT = 'attachment'
21 TAG_ATTACHMENT = 'attachment'
22
22
23 TYPE_GET = 'get'
23 TYPE_GET = 'get'
24
24
25 ATTR_VERSION = 'version'
25 ATTR_VERSION = 'version'
26 ATTR_TYPE = 'type'
26 ATTR_TYPE = 'type'
27 ATTR_NAME = 'name'
27 ATTR_NAME = 'name'
28 ATTR_VALUE = 'value'
28 ATTR_VALUE = 'value'
29 ATTR_MIMETYPE = 'mimetype'
29 ATTR_MIMETYPE = 'mimetype'
30
30
31 STATUS_SUCCESS = 'success'
31 STATUS_SUCCESS = 'success'
32
32
33
33
34 # TODO Make this fully static
35 class SyncManager:
34 class SyncManager:
36 def generate_response_get(self, model_list: list):
35 @staticmethod
36 def generate_response_get(model_list: list):
37 response = et.Element(TAG_RESPONSE)
37 response = et.Element(TAG_RESPONSE)
38
38
39 status = et.SubElement(response, TAG_STATUS)
39 status = et.SubElement(response, TAG_STATUS)
40 status.text = STATUS_SUCCESS
40 status.text = STATUS_SUCCESS
41
41
42 models = et.SubElement(response, TAG_MODELS)
42 models = et.SubElement(response, TAG_MODELS)
43
43
44 for post in model_list:
44 for post in model_list:
45 model = et.SubElement(models, TAG_MODEL)
45 model = et.SubElement(models, TAG_MODEL)
46 model.set(ATTR_NAME, 'post')
46 model.set(ATTR_NAME, 'post')
47
47
48 content_tag = et.SubElement(model, TAG_CONTENT)
48 content_tag = et.SubElement(model, TAG_CONTENT)
49
49
50 tag_id = et.SubElement(content_tag, TAG_ID)
50 tag_id = et.SubElement(content_tag, TAG_ID)
51 post.global_id.to_xml_element(tag_id)
51 post.global_id.to_xml_element(tag_id)
52
52
53 title = et.SubElement(content_tag, TAG_TITLE)
53 title = et.SubElement(content_tag, TAG_TITLE)
54 title.text = post.title
54 title.text = post.title
55
55
56 text = et.SubElement(content_tag, TAG_TEXT)
56 text = et.SubElement(content_tag, TAG_TEXT)
57 text.text = post.get_sync_text()
57 text.text = post.get_sync_text()
58
58
59 if not post.is_opening():
59 if not post.is_opening():
60 thread = et.SubElement(content_tag, TAG_THREAD)
60 thread = et.SubElement(content_tag, TAG_THREAD)
61 thread_id = et.SubElement(thread, TAG_ID)
61 thread_id = et.SubElement(thread, TAG_ID)
62 post.get_thread().get_opening_post().global_id.to_xml_element(thread_id)
62 post.get_thread().get_opening_post().global_id.to_xml_element(thread_id)
63 else:
63 else:
64 # TODO Output tags here
64 # TODO Output tags here
65 pass
65 pass
66
66
67 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
67 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
68 pub_time.text = str(post.get_pub_time_str())
68 pub_time.text = str(post.get_pub_time_str())
69
69
70 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
70 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
71 post_signatures = post.signature.all()
71 post_signatures = post.signature.all()
72 if post_signatures:
72 if post_signatures:
73 signatures = post.signatures
73 signatures = post.signatures
74 else:
74 else:
75 # TODO Maybe the signature can be computed only once after
75 # TODO Maybe the signature can be computed only once after
76 # the post is added? Need to add some on_save signal queue
76 # the post is added? Need to add some on_save signal queue
77 # and add this there.
77 # and add this there.
78 key = KeyPair.objects.get(public_key=post.global_id.key)
78 key = KeyPair.objects.get(public_key=post.global_id.key)
79 signatures = [Signature(
79 signatures = [Signature(
80 key_type=key.key_type,
80 key_type=key.key_type,
81 key=key.public_key,
81 key=key.public_key,
82 signature=key.sign(et.tostring(model, ENCODING_UNICODE)),
82 signature=key.sign(et.tostring(model, ENCODING_UNICODE)),
83 )]
83 )]
84 for signature in signatures:
84 for signature in signatures:
85 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
85 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
86 signature_tag.set(ATTR_TYPE, signature.key_type)
86 signature_tag.set(ATTR_TYPE, signature.key_type)
87 signature_tag.set(ATTR_VALUE, signature.signature)
87 signature_tag.set(ATTR_VALUE, signature.signature)
88
88
89 return et.tostring(response, ENCODING_UNICODE)
89 return et.tostring(response, ENCODING_UNICODE)
90
90
91 @staticmethod
91 @transaction.atomic
92 @transaction.atomic
92 def parse_response_get(self, response_xml):
93 def parse_response_get(response_xml):
93 tag_root = et.fromstring(response_xml)
94 tag_root = et.fromstring(response_xml)
94 tag_status = tag_root.find(TAG_STATUS)
95 tag_status = tag_root.find(TAG_STATUS)
95 if STATUS_SUCCESS == tag_status.text:
96 if STATUS_SUCCESS == tag_status.text:
96 tag_models = tag_root.find(TAG_MODELS)
97 tag_models = tag_root.find(TAG_MODELS)
97 for tag_model in tag_models:
98 for tag_model in tag_models:
98 tag_content = tag_model.find(TAG_CONTENT)
99 tag_content = tag_model.find(TAG_CONTENT)
99 tag_id = tag_content.find(TAG_ID)
100 tag_id = tag_content.find(TAG_ID)
100 global_id, exists = GlobalId.from_xml_element(tag_id)
101 global_id, exists = GlobalId.from_xml_element(tag_id)
101
102
102 if exists:
103 if exists:
103 print('Post with same ID already exists')
104 print('Post with same ID already exists')
104 else:
105 else:
105 global_id.save()
106 global_id.save()
106
107
107 title = tag_content.find(TAG_TITLE).text
108 title = tag_content.find(TAG_TITLE).text
108 text = tag_content.find(TAG_TEXT).text
109 text = tag_content.find(TAG_TEXT).text
109 pub_time = tag_content.find(TAG_PUB_TIME).text
110 pub_time = tag_content.find(TAG_PUB_TIME).text
110
111
111 thread = tag_content.find(TAG_THREAD)
112 thread = tag_content.find(TAG_THREAD)
112 if thread:
113 if thread:
113 opening_post = Post.objects.get(
114 opening_post = Post.objects.get(
114 id=thread.find(TAG_ID).text)
115 id=thread.find(TAG_ID).text)
115 else:
116 else:
116 opening_post = None
117 opening_post = None
117 # TODO Get tags here
118 # TODO Get tags here
118
119
119 # TODO Check that the replied posts are already present
120 # TODO Check that the replied posts are already present
120 # before adding new ones
121 # before adding new ones
121
122
122 post = Post.objects.import_post(
123 post = Post.objects.import_post(
123 title=title, text=text, pub_time=pub_time,
124 title=title, text=text, pub_time=pub_time,
124 opening_post=opening_post)
125 opening_post=opening_post)
125 post.global_id = global_id
126 post.global_id = global_id
126 else:
127 else:
127 # TODO Throw an exception?
128 # TODO Throw an exception?
128 pass
129 pass
@@ -1,87 +1,87 b''
1 from base64 import b64encode
1 from base64 import b64encode
2 import logging
2 import logging
3
3
4 from django.test import TestCase
4 from django.test import TestCase
5 from boards.models import KeyPair, GlobalId, Post
5 from boards.models import KeyPair, GlobalId, Post
6 from boards.models.post.sync import SyncManager
6 from boards.models.post.sync import SyncManager
7
7
8 logger = logging.getLogger(__name__)
8 logger = logging.getLogger(__name__)
9
9
10
10
11 class KeyTest(TestCase):
11 class KeyTest(TestCase):
12 def test_create_key(self):
12 def test_create_key(self):
13 key = KeyPair.objects.generate_key('ecdsa')
13 key = KeyPair.objects.generate_key('ecdsa')
14
14
15 self.assertIsNotNone(key, 'The key was not created.')
15 self.assertIsNotNone(key, 'The key was not created.')
16
16
17 def test_validation(self):
17 def test_validation(self):
18 key = KeyPair.objects.generate_key(key_type='ecdsa')
18 key = KeyPair.objects.generate_key(key_type='ecdsa')
19 message = 'msg'
19 message = 'msg'
20 signature = key.sign(message)
20 signature = key.sign(message)
21 valid = KeyPair.objects.verify(key.public_key, message, signature,
21 valid = KeyPair.objects.verify(key.public_key, message, signature,
22 key_type='ecdsa')
22 key_type='ecdsa')
23
23
24 self.assertTrue(valid, 'Message verification failed.')
24 self.assertTrue(valid, 'Message verification failed.')
25
25
26 def test_primary_constraint(self):
26 def test_primary_constraint(self):
27 KeyPair.objects.generate_key(key_type='ecdsa', primary=True)
27 KeyPair.objects.generate_key(key_type='ecdsa', primary=True)
28
28
29 with self.assertRaises(Exception):
29 with self.assertRaises(Exception):
30 KeyPair.objects.generate_key(key_type='ecdsa', primary=True)
30 KeyPair.objects.generate_key(key_type='ecdsa', primary=True)
31
31
32 def test_model_id_save(self):
32 def test_model_id_save(self):
33 model_id = GlobalId(key_type='test', key='test key', local_id='1')
33 model_id = GlobalId(key_type='test', key='test key', local_id='1')
34 model_id.save()
34 model_id.save()
35
35
36 def test_request_get(self):
36 def test_request_get(self):
37 post = self._create_post_with_key()
37 post = self._create_post_with_key()
38
38
39 request = GlobalId.objects.generate_request_get([post.global_id])
39 request = GlobalId.objects.generate_request_get([post.global_id])
40 logger.debug(request)
40 logger.debug(request)
41
41
42 key = KeyPair.objects.get(primary=True)
42 key = KeyPair.objects.get(primary=True)
43 self.assertTrue('<request type="get" version="1.0">'
43 self.assertTrue('<request type="get" version="1.0">'
44 '<model name="post" version="1.0">'
44 '<model name="post" version="1.0">'
45 '<id key="%s" local-id="1" type="%s" />'
45 '<id key="%s" local-id="1" type="%s" />'
46 '</model>'
46 '</model>'
47 '</request>' % (
47 '</request>' % (
48 key.public_key,
48 key.public_key,
49 key.key_type,
49 key.key_type,
50 ) in request,
50 ) in request,
51 'Wrong XML generated for the GET request.')
51 'Wrong XML generated for the GET request.')
52
52
53 def test_response_get(self):
53 def test_response_get(self):
54 post = self._create_post_with_key()
54 post = self._create_post_with_key()
55 reply_post = Post.objects.create_post(title='test_title',
55 reply_post = Post.objects.create_post(title='test_title',
56 text='[post]%d[/post]' % post.id,
56 text='[post]%d[/post]' % post.id,
57 thread=post.get_thread())
57 thread=post.get_thread())
58
58
59 response = SyncManager().generate_response_get([reply_post])
59 response = SyncManager.generate_response_get([reply_post])
60 logger.debug(response)
60 logger.debug(response)
61
61
62 key = KeyPair.objects.get(primary=True)
62 key = KeyPair.objects.get(primary=True)
63 self.assertTrue('<status>success</status>'
63 self.assertTrue('<status>success</status>'
64 '<models>'
64 '<models>'
65 '<model name="post">'
65 '<model name="post">'
66 '<content>'
66 '<content>'
67 '<id key="%s" local-id="%d" type="%s" />'
67 '<id key="%s" local-id="%d" type="%s" />'
68 '<title>test_title</title>'
68 '<title>test_title</title>'
69 '<text>[post]%s[/post]</text>'
69 '<text>[post]%s[/post]</text>'
70 '<thread><id key="%s" local-id="%d" type="%s" /></thread>'
70 '<thread><id key="%s" local-id="%d" type="%s" /></thread>'
71 '<pub-time>%s</pub-time>'
71 '<pub-time>%s</pub-time>'
72 '</content>' % (
72 '</content>' % (
73 key.public_key,
73 key.public_key,
74 reply_post.id,
74 reply_post.id,
75 key.key_type,
75 key.key_type,
76 str(post.global_id),
76 str(post.global_id),
77 key.public_key,
77 key.public_key,
78 post.id,
78 post.id,
79 key.key_type,
79 key.key_type,
80 str(reply_post.get_pub_time_str()),
80 str(reply_post.get_pub_time_str()),
81 ) in response,
81 ) in response,
82 'Wrong XML generated for the GET response.')
82 'Wrong XML generated for the GET response.')
83
83
84 def _create_post_with_key(self):
84 def _create_post_with_key(self):
85 KeyPair.objects.generate_key(primary=True)
85 KeyPair.objects.generate_key(primary=True)
86
86
87 return Post.objects.create_post(title='test_title', text='test_text')
87 return Post.objects.create_post(title='test_title', text='test_text')
@@ -1,60 +1,60 b''
1 from boards.models import KeyPair, Post
1 from boards.models import KeyPair, Post
2 from boards.models.post.sync import SyncManager
2 from boards.models.post.sync import SyncManager
3 from boards.tests.mocks import MockRequest
3 from boards.tests.mocks import MockRequest
4 from boards.views.sync import response_get
4 from boards.views.sync import response_get
5
5
6 __author__ = 'neko259'
6 __author__ = 'neko259'
7
7
8
8
9 from django.test import TestCase
9 from django.test import TestCase
10
10
11
11
12 class SyncTest(TestCase):
12 class SyncTest(TestCase):
13 def test_get(self):
13 def test_get(self):
14 """
14 """
15 Forms a GET request of a post and checks the response.
15 Forms a GET request of a post and checks the response.
16 """
16 """
17
17
18 KeyPair.objects.generate_key(primary=True)
18 KeyPair.objects.generate_key(primary=True)
19 post = Post.objects.create_post(title='test_title', text='test_text')
19 post = Post.objects.create_post(title='test_title', text='test_text')
20
20
21 request = MockRequest()
21 request = MockRequest()
22 request.body = (
22 request.body = (
23 '<request type="get" version="1.0">'
23 '<request type="get" version="1.0">'
24 '<model name="post" version="1.0">'
24 '<model name="post" version="1.0">'
25 '<id key="%s" local-id="%d" type="%s" />'
25 '<id key="%s" local-id="%d" type="%s" />'
26 '</model>'
26 '</model>'
27 '</request>' % (post.global_id.key,
27 '</request>' % (post.global_id.key,
28 post.id,
28 post.id,
29 post.global_id.key_type)
29 post.global_id.key_type)
30 )
30 )
31
31
32 response = response_get(request).content.decode()
32 response = response_get(request).content.decode()
33 self.assertTrue(
33 self.assertTrue(
34 '<status>success</status>'
34 '<status>success</status>'
35 '<models>'
35 '<models>'
36 '<model name="post">'
36 '<model name="post">'
37 '<content>'
37 '<content>'
38 '<id key="%s" local-id="%d" type="%s" />'
38 '<id key="%s" local-id="%d" type="%s" />'
39 '<title>%s</title>'
39 '<title>%s</title>'
40 '<text>%s</text>'
40 '<text>%s</text>'
41 '<pub-time>%s</pub-time>'
41 '<pub-time>%s</pub-time>'
42 '</content>' % (
42 '</content>' % (
43 post.global_id.key,
43 post.global_id.key,
44 post.id,
44 post.id,
45 post.global_id.key_type,
45 post.global_id.key_type,
46 post.title,
46 post.title,
47 post.get_raw_text(),
47 post.get_raw_text(),
48 post.get_pub_time_str(),
48 post.get_pub_time_str(),
49 ) in response_get(request).content.decode(),
49 ) in response_get(request).content.decode(),
50 'Wrong response generated for the GET request.')
50 'Wrong response generated for the GET request.')
51
51
52 post.delete()
52 post.delete()
53
53
54 SyncManager().parse_response_get(response)
54 SyncManager.parse_response_get(response)
55 self.assertEqual(1, Post.objects.count(),
55 self.assertEqual(1, Post.objects.count(),
56 'Post was not created from XML response.')
56 'Post was not created from XML response.')
57
57
58 SyncManager().parse_response_get(response)
58 SyncManager.parse_response_get(response)
59 self.assertEqual(1, Post.objects.count(),
59 self.assertEqual(1, Post.objects.count(),
60 'The same post was imported twice.')
60 'The same post was imported twice.')
@@ -1,258 +1,258 b''
1 import json
1 import json
2 import logging
2 import logging
3
3
4 import xml.etree.ElementTree as ET
4 import xml.etree.ElementTree as ET
5
5
6 from django.db import transaction
6 from django.db import transaction
7 from django.http import HttpResponse
7 from django.http import HttpResponse
8 from django.shortcuts import get_object_or_404
8 from django.shortcuts import get_object_or_404
9 from django.core import serializers
9 from django.core import serializers
10
10
11 from boards.forms import PostForm, PlainErrorList
11 from boards.forms import PostForm, PlainErrorList
12 from boards.models import Post, Thread, Tag, GlobalId
12 from boards.models import Post, Thread, Tag, GlobalId
13 from boards.models.post.sync import SyncManager
13 from boards.models.post.sync import SyncManager
14 from boards.utils import datetime_to_epoch
14 from boards.utils import datetime_to_epoch
15 from boards.views.thread import ThreadView
15 from boards.views.thread import ThreadView
16 from boards.models.user import Notification
16 from boards.models.user import Notification
17 from boards.mdx_neboard import Parser
17 from boards.mdx_neboard import Parser
18
18
19
19
20 __author__ = 'neko259'
20 __author__ = 'neko259'
21
21
22 PARAMETER_TRUNCATED = 'truncated'
22 PARAMETER_TRUNCATED = 'truncated'
23 PARAMETER_TAG = 'tag'
23 PARAMETER_TAG = 'tag'
24 PARAMETER_OFFSET = 'offset'
24 PARAMETER_OFFSET = 'offset'
25 PARAMETER_DIFF_TYPE = 'type'
25 PARAMETER_DIFF_TYPE = 'type'
26 PARAMETER_POST = 'post'
26 PARAMETER_POST = 'post'
27 PARAMETER_UPDATED = 'updated'
27 PARAMETER_UPDATED = 'updated'
28 PARAMETER_LAST_UPDATE = 'last_update'
28 PARAMETER_LAST_UPDATE = 'last_update'
29 PARAMETER_THREAD = 'thread'
29 PARAMETER_THREAD = 'thread'
30 PARAMETER_UIDS = 'uids'
30 PARAMETER_UIDS = 'uids'
31
31
32 DIFF_TYPE_HTML = 'html'
32 DIFF_TYPE_HTML = 'html'
33 DIFF_TYPE_JSON = 'json'
33 DIFF_TYPE_JSON = 'json'
34
34
35 STATUS_OK = 'ok'
35 STATUS_OK = 'ok'
36 STATUS_ERROR = 'error'
36 STATUS_ERROR = 'error'
37
37
38 logger = logging.getLogger(__name__)
38 logger = logging.getLogger(__name__)
39
39
40
40
41 @transaction.atomic
41 @transaction.atomic
42 def api_get_threaddiff(request):
42 def api_get_threaddiff(request):
43 """
43 """
44 Gets posts that were changed or added since time
44 Gets posts that were changed or added since time
45 """
45 """
46
46
47 thread_id = request.POST.get(PARAMETER_THREAD)
47 thread_id = request.POST.get(PARAMETER_THREAD)
48 uids_str = request.POST.get(PARAMETER_UIDS).strip()
48 uids_str = request.POST.get(PARAMETER_UIDS).strip()
49 uids = uids_str.split(' ')
49 uids = uids_str.split(' ')
50
50
51 thread = get_object_or_404(Post, id=thread_id).get_thread()
51 thread = get_object_or_404(Post, id=thread_id).get_thread()
52
52
53 json_data = {
53 json_data = {
54 PARAMETER_UPDATED: [],
54 PARAMETER_UPDATED: [],
55 PARAMETER_LAST_UPDATE: None, # TODO Maybe this can be removed already?
55 PARAMETER_LAST_UPDATE: None, # TODO Maybe this can be removed already?
56 }
56 }
57 posts = Post.objects.filter(threads__in=[thread]).exclude(uid__in=uids)
57 posts = Post.objects.filter(threads__in=[thread]).exclude(uid__in=uids)
58
58
59 diff_type = request.GET.get(PARAMETER_DIFF_TYPE, DIFF_TYPE_HTML)
59 diff_type = request.GET.get(PARAMETER_DIFF_TYPE, DIFF_TYPE_HTML)
60
60
61 for post in posts:
61 for post in posts:
62 json_data[PARAMETER_UPDATED].append(get_post_data(post.id, diff_type,
62 json_data[PARAMETER_UPDATED].append(get_post_data(post.id, diff_type,
63 request))
63 request))
64 json_data[PARAMETER_LAST_UPDATE] = str(thread.last_edit_time)
64 json_data[PARAMETER_LAST_UPDATE] = str(thread.last_edit_time)
65
65
66 return HttpResponse(content=json.dumps(json_data))
66 return HttpResponse(content=json.dumps(json_data))
67
67
68
68
69 def api_add_post(request, opening_post_id):
69 def api_add_post(request, opening_post_id):
70 """
70 """
71 Adds a post and return the JSON response for it
71 Adds a post and return the JSON response for it
72 """
72 """
73
73
74 opening_post = get_object_or_404(Post, id=opening_post_id)
74 opening_post = get_object_or_404(Post, id=opening_post_id)
75
75
76 logger.info('Adding post via api...')
76 logger.info('Adding post via api...')
77
77
78 status = STATUS_OK
78 status = STATUS_OK
79 errors = []
79 errors = []
80
80
81 if request.method == 'POST':
81 if request.method == 'POST':
82 form = PostForm(request.POST, request.FILES, error_class=PlainErrorList)
82 form = PostForm(request.POST, request.FILES, error_class=PlainErrorList)
83 form.session = request.session
83 form.session = request.session
84
84
85 if form.need_to_ban:
85 if form.need_to_ban:
86 # Ban user because he is suspected to be a bot
86 # Ban user because he is suspected to be a bot
87 # _ban_current_user(request)
87 # _ban_current_user(request)
88 status = STATUS_ERROR
88 status = STATUS_ERROR
89 if form.is_valid():
89 if form.is_valid():
90 post = ThreadView().new_post(request, form, opening_post,
90 post = ThreadView().new_post(request, form, opening_post,
91 html_response=False)
91 html_response=False)
92 if not post:
92 if not post:
93 status = STATUS_ERROR
93 status = STATUS_ERROR
94 else:
94 else:
95 logger.info('Added post #%d via api.' % post.id)
95 logger.info('Added post #%d via api.' % post.id)
96 else:
96 else:
97 status = STATUS_ERROR
97 status = STATUS_ERROR
98 errors = form.as_json_errors()
98 errors = form.as_json_errors()
99
99
100 response = {
100 response = {
101 'status': status,
101 'status': status,
102 'errors': errors,
102 'errors': errors,
103 }
103 }
104
104
105 return HttpResponse(content=json.dumps(response))
105 return HttpResponse(content=json.dumps(response))
106
106
107
107
108 def get_post(request, post_id):
108 def get_post(request, post_id):
109 """
109 """
110 Gets the html of a post. Used for popups. Post can be truncated if used
110 Gets the html of a post. Used for popups. Post can be truncated if used
111 in threads list with 'truncated' get parameter.
111 in threads list with 'truncated' get parameter.
112 """
112 """
113
113
114 post = get_object_or_404(Post, id=post_id)
114 post = get_object_or_404(Post, id=post_id)
115 truncated = PARAMETER_TRUNCATED in request.GET
115 truncated = PARAMETER_TRUNCATED in request.GET
116
116
117 return HttpResponse(content=post.get_view(truncated=truncated))
117 return HttpResponse(content=post.get_view(truncated=truncated))
118
118
119
119
120 def api_get_threads(request, count):
120 def api_get_threads(request, count):
121 """
121 """
122 Gets the JSON thread opening posts list.
122 Gets the JSON thread opening posts list.
123 Parameters that can be used for filtering:
123 Parameters that can be used for filtering:
124 tag, offset (from which thread to get results)
124 tag, offset (from which thread to get results)
125 """
125 """
126
126
127 if PARAMETER_TAG in request.GET:
127 if PARAMETER_TAG in request.GET:
128 tag_name = request.GET[PARAMETER_TAG]
128 tag_name = request.GET[PARAMETER_TAG]
129 if tag_name is not None:
129 if tag_name is not None:
130 tag = get_object_or_404(Tag, name=tag_name)
130 tag = get_object_or_404(Tag, name=tag_name)
131 threads = tag.get_threads().filter(archived=False)
131 threads = tag.get_threads().filter(archived=False)
132 else:
132 else:
133 threads = Thread.objects.filter(archived=False)
133 threads = Thread.objects.filter(archived=False)
134
134
135 if PARAMETER_OFFSET in request.GET:
135 if PARAMETER_OFFSET in request.GET:
136 offset = request.GET[PARAMETER_OFFSET]
136 offset = request.GET[PARAMETER_OFFSET]
137 offset = int(offset) if offset is not None else 0
137 offset = int(offset) if offset is not None else 0
138 else:
138 else:
139 offset = 0
139 offset = 0
140
140
141 threads = threads.order_by('-bump_time')
141 threads = threads.order_by('-bump_time')
142 threads = threads[offset:offset + int(count)]
142 threads = threads[offset:offset + int(count)]
143
143
144 opening_posts = []
144 opening_posts = []
145 for thread in threads:
145 for thread in threads:
146 opening_post = thread.get_opening_post()
146 opening_post = thread.get_opening_post()
147
147
148 # TODO Add tags, replies and images count
148 # TODO Add tags, replies and images count
149 post_data = get_post_data(opening_post.id, include_last_update=True)
149 post_data = get_post_data(opening_post.id, include_last_update=True)
150 post_data['bumpable'] = thread.can_bump()
150 post_data['bumpable'] = thread.can_bump()
151 post_data['archived'] = thread.archived
151 post_data['archived'] = thread.archived
152
152
153 opening_posts.append(post_data)
153 opening_posts.append(post_data)
154
154
155 return HttpResponse(content=json.dumps(opening_posts))
155 return HttpResponse(content=json.dumps(opening_posts))
156
156
157
157
158 # TODO Test this
158 # TODO Test this
159 def api_get_tags(request):
159 def api_get_tags(request):
160 """
160 """
161 Gets all tags or user tags.
161 Gets all tags or user tags.
162 """
162 """
163
163
164 # TODO Get favorite tags for the given user ID
164 # TODO Get favorite tags for the given user ID
165
165
166 tags = Tag.objects.get_not_empty_tags()
166 tags = Tag.objects.get_not_empty_tags()
167
167
168 term = request.GET.get('term')
168 term = request.GET.get('term')
169 if term is not None:
169 if term is not None:
170 tags = tags.filter(name__contains=term)
170 tags = tags.filter(name__contains=term)
171
171
172 tag_names = [tag.name for tag in tags]
172 tag_names = [tag.name for tag in tags]
173
173
174 return HttpResponse(content=json.dumps(tag_names))
174 return HttpResponse(content=json.dumps(tag_names))
175
175
176
176
177 # TODO The result can be cached by the thread last update time
177 # TODO The result can be cached by the thread last update time
178 # TODO Test this
178 # TODO Test this
179 def api_get_thread_posts(request, opening_post_id):
179 def api_get_thread_posts(request, opening_post_id):
180 """
180 """
181 Gets the JSON array of thread posts
181 Gets the JSON array of thread posts
182 """
182 """
183
183
184 opening_post = get_object_or_404(Post, id=opening_post_id)
184 opening_post = get_object_or_404(Post, id=opening_post_id)
185 thread = opening_post.get_thread()
185 thread = opening_post.get_thread()
186 posts = thread.get_replies()
186 posts = thread.get_replies()
187
187
188 json_data = {
188 json_data = {
189 'posts': [],
189 'posts': [],
190 'last_update': None,
190 'last_update': None,
191 }
191 }
192 json_post_list = []
192 json_post_list = []
193
193
194 for post in posts:
194 for post in posts:
195 json_post_list.append(get_post_data(post.id))
195 json_post_list.append(get_post_data(post.id))
196 json_data['last_update'] = datetime_to_epoch(thread.last_edit_time)
196 json_data['last_update'] = datetime_to_epoch(thread.last_edit_time)
197 json_data['posts'] = json_post_list
197 json_data['posts'] = json_post_list
198
198
199 return HttpResponse(content=json.dumps(json_data))
199 return HttpResponse(content=json.dumps(json_data))
200
200
201
201
202 def api_get_notifications(request, username):
202 def api_get_notifications(request, username):
203 last_notification_id_str = request.GET.get('last', None)
203 last_notification_id_str = request.GET.get('last', None)
204 last_id = int(last_notification_id_str) if last_notification_id_str is not None else None
204 last_id = int(last_notification_id_str) if last_notification_id_str is not None else None
205
205
206 posts = Notification.objects.get_notification_posts(username=username,
206 posts = Notification.objects.get_notification_posts(username=username,
207 last=last_id)
207 last=last_id)
208
208
209 json_post_list = []
209 json_post_list = []
210 for post in posts:
210 for post in posts:
211 json_post_list.append(get_post_data(post.id))
211 json_post_list.append(get_post_data(post.id))
212 return HttpResponse(content=json.dumps(json_post_list))
212 return HttpResponse(content=json.dumps(json_post_list))
213
213
214
214
215 def api_get_post(request, post_id):
215 def api_get_post(request, post_id):
216 """
216 """
217 Gets the JSON of a post. This can be
217 Gets the JSON of a post. This can be
218 used as and API for external clients.
218 used as and API for external clients.
219 """
219 """
220
220
221 post = get_object_or_404(Post, id=post_id)
221 post = get_object_or_404(Post, id=post_id)
222
222
223 json = serializers.serialize("json", [post], fields=(
223 json = serializers.serialize("json", [post], fields=(
224 "pub_time", "_text_rendered", "title", "text", "image",
224 "pub_time", "_text_rendered", "title", "text", "image",
225 "image_width", "image_height", "replies", "tags"
225 "image_width", "image_height", "replies", "tags"
226 ))
226 ))
227
227
228 return HttpResponse(content=json)
228 return HttpResponse(content=json)
229
229
230
230
231 # TODO Remove this method and use post method directly
231 # TODO Remove this method and use post method directly
232 def get_post_data(post_id, format_type=DIFF_TYPE_JSON, request=None,
232 def get_post_data(post_id, format_type=DIFF_TYPE_JSON, request=None,
233 include_last_update=False):
233 include_last_update=False):
234 post = get_object_or_404(Post, id=post_id)
234 post = get_object_or_404(Post, id=post_id)
235 return post.get_post_data(format_type=format_type, request=request,
235 return post.get_post_data(format_type=format_type, request=request,
236 include_last_update=include_last_update)
236 include_last_update=include_last_update)
237
237
238
238
239 def api_get_preview(request):
239 def api_get_preview(request):
240 raw_text = request.POST['raw_text']
240 raw_text = request.POST['raw_text']
241
241
242 parser = Parser()
242 parser = Parser()
243 return HttpResponse(content=parser.parse(parser.preparse(raw_text)))
243 return HttpResponse(content=parser.parse(parser.preparse(raw_text)))
244
244
245
245
246 # TODO Make a separate module for sync API methods
246 # TODO Make a separate module for sync API methods
247 def sync_pull(request):
247 def sync_pull(request):
248 """
248 """
249 Return 'pull' request response for all posts.
249 Return 'pull' request response for all posts.
250 """
250 """
251 request_xml = request.get('xml')
251 request_xml = request.get('xml')
252 if request_xml is None:
252 if request_xml is None:
253 posts = Post.objects.all()
253 posts = Post.objects.all()
254 else:
254 else:
255 pass # TODO Parse the XML and get filters from it
255 pass # TODO Parse the XML and get filters from it
256
256
257 xml = SyncManager().generate_response_get(posts)
257 xml = SyncManager.generate_response_get(posts)
258 return HttpResponse(content=xml)
258 return HttpResponse(content=xml)
@@ -1,49 +1,49 b''
1 import xml.etree.ElementTree as et
1 import xml.etree.ElementTree as et
2 from django.http import HttpResponse, Http404
2 from django.http import HttpResponse, Http404
3 from boards.models import GlobalId, Post
3 from boards.models import GlobalId, Post
4 from boards.models.post.sync import SyncManager
4 from boards.models.post.sync import SyncManager
5
5
6
6
7 def response_pull(request):
7 def response_pull(request):
8 pass
8 pass
9
9
10
10
11 def response_get(request):
11 def response_get(request):
12 """
12 """
13 Processes a GET request with post ID list and returns the posts XML list.
13 Processes a GET request with post ID list and returns the posts XML list.
14 Request should contain an 'xml' post attribute with the actual request XML.
14 Request should contain an 'xml' post attribute with the actual request XML.
15 """
15 """
16
16
17 request_xml = request.body
17 request_xml = request.body
18
18
19 if request_xml is None:
19 if request_xml is None:
20 return HttpResponse(content='Use the API')
20 return HttpResponse(content='Use the API')
21
21
22 posts = []
22 posts = []
23
23
24 root_tag = et.fromstring(request_xml)
24 root_tag = et.fromstring(request_xml)
25 model_tag = root_tag[0]
25 model_tag = root_tag[0]
26 for id_tag in model_tag:
26 for id_tag in model_tag:
27 global_id, exists = GlobalId.from_xml_element(id_tag)
27 global_id, exists = GlobalId.from_xml_element(id_tag)
28 if exists:
28 if exists:
29 posts.append(Post.objects.get(global_id=global_id))
29 posts.append(Post.objects.get(global_id=global_id))
30
30
31 response_xml = SyncManager().generate_response_get(posts)
31 response_xml = SyncManager.generate_response_get(posts)
32
32
33 return HttpResponse(content=response_xml)
33 return HttpResponse(content=response_xml)
34
34
35
35
36 def get_post_sync_data(request, post_id):
36 def get_post_sync_data(request, post_id):
37 try:
37 try:
38 post = Post.objects.get(id=post_id)
38 post = Post.objects.get(id=post_id)
39 except Post.DoesNotExist:
39 except Post.DoesNotExist:
40 raise Http404()
40 raise Http404()
41
41
42 content = 'Global ID: %s\n\nXML: %s' \
42 content = 'Global ID: %s\n\nXML: %s' \
43 % (post.global_id, SyncManager().generate_response_get([post]))
43 % (post.global_id, SyncManager.generate_response_get([post]))
44
44
45
45
46 return HttpResponse(
46 return HttpResponse(
47 content_type='text/plain',
47 content_type='text/plain',
48 content=content,
48 content=content,
49 ) No newline at end of file
49 )
General Comments 0
You need to be logged in to leave comments. Login now