Show More
@@ -1,66 +1,66 | |||||
1 | import xml.etree.ElementTree as et |
|
1 | import xml.etree.ElementTree as et | |
2 |
|
2 | |||
3 | from boards.models import Post, Tag |
|
3 | from boards.models import Post, Tag | |
4 |
|
4 | |||
5 | TAG_THREAD = 'thread' |
|
5 | TAG_THREAD = 'thread' | |
6 | TAG_TAGS = 'tags' |
|
6 | TAG_TAGS = 'tags' | |
7 | TAG_TAG = 'tag' |
|
7 | TAG_TAG = 'tag' | |
8 | TAG_TIME_FROM = 'timestamp_from' |
|
8 | TAG_TIME_FROM = 'timestamp_from' | |
9 |
|
9 | |||
10 |
|
10 | |||
11 | class PostFilter: |
|
11 | class PostFilter: | |
12 | def __init__(self, content=None): |
|
12 | def __init__(self, content=None): | |
13 | self.content = content |
|
13 | self.content = content | |
14 |
|
14 | |||
15 | def filter(self, posts): |
|
15 | def filter(self, posts): | |
16 | return posts |
|
16 | return posts | |
17 |
|
17 | |||
18 | def add_filter(self, model_tag, value): |
|
18 | def add_filter(self, model_tag, value): | |
19 | return model_tag |
|
19 | return model_tag | |
20 |
|
20 | |||
21 |
|
21 | |||
22 | class ThreadFilter(PostFilter): |
|
22 | class ThreadFilter(PostFilter): | |
23 | def filter(self, posts): |
|
23 | def filter(self, posts): | |
24 | op_id = self.content.text |
|
24 | op_id = self.content.text | |
25 |
|
25 | |||
26 | op = Post.objects.filter(opening=True, id=op_id).first() |
|
26 | op = Post.objects.filter(opening=True, id=op_id).first() | |
27 | if op: |
|
27 | if op: | |
28 | return posts.filter(thread=op.get_thread()) |
|
28 | return posts.filter(thread=op.get_thread()) | |
29 | else: |
|
29 | else: | |
30 | return posts.none() |
|
30 | return posts.none() | |
31 |
|
31 | |||
32 | def add_filter(self, model_tag, value): |
|
32 | def add_filter(self, model_tag, value): | |
33 | thread_tag = et.SubElement(model_tag, TAG_THREAD) |
|
33 | thread_tag = et.SubElement(model_tag, TAG_THREAD) | |
34 | thread_tag.text = str(value) |
|
34 | thread_tag.text = str(value) | |
35 |
|
35 | |||
36 |
|
36 | |||
37 | class TagsFilter(PostFilter): |
|
37 | class TagsFilter(PostFilter): | |
38 | def filter(self, posts): |
|
38 | def filter(self, posts): | |
39 | tags = [] |
|
39 | tags = [] | |
40 | for tag_tag in self.content: |
|
40 | for tag_tag in self.content: | |
41 | try: |
|
41 | try: | |
42 | tags.append(Tag.objects.get_by_alias(tag_tag.text)) |
|
42 | tags.append(Tag.objects.get_by_alias(tag_tag.text)) | |
43 | except Tag.DoesNotExist: |
|
43 | except Tag.DoesNotExist: | |
44 | pass |
|
44 | pass | |
45 |
|
45 | |||
46 | if tags: |
|
46 | if tags: | |
47 | return posts.filter(thread__tags__in=tags) |
|
47 | return posts.filter(thread__tags__in=tags) | |
48 | else: |
|
48 | else: | |
49 | return posts.none() |
|
49 | return posts.none() | |
50 |
|
50 | |||
51 | def add_filter(self, model_tag, value): |
|
51 | def add_filter(self, model_tag, value): | |
52 | tags_tag = et.SubElement(model_tag, TAG_TAGS) |
|
52 | tags_tag = et.SubElement(model_tag, TAG_TAGS) | |
53 | for tag_name in value: |
|
53 | for tag_name in value: | |
54 | tag_tag = et.SubElement(tags_tag, TAG_TAG) |
|
54 | tag_tag = et.SubElement(tags_tag, TAG_TAG) | |
55 | tag_tag.text = tag_name |
|
55 | tag_tag.text = tag_name | |
56 |
|
56 | |||
57 |
|
57 | |||
58 | class TimestampFromFilter(PostFilter): |
|
58 | class TimestampFromFilter(PostFilter): | |
59 | def filter(self, posts): |
|
59 | def filter(self, posts): | |
60 | from_time = self.content.text |
|
60 | from_time = self.content.text | |
61 | return posts.filter(pub_time__gt=from_time) |
|
61 | return posts.filter(pub_time__gt=from_time) | |
62 |
|
62 | |||
63 | def add_filter(self, model_tag, value): |
|
63 | def add_filter(self, model_tag, value): | |
64 | tags_from_time = et.SubElement(model_tag, TAG_TIME_FROM) |
|
64 | tags_from_time = et.SubElement(model_tag, TAG_TIME_FROM) | |
65 | tags_from_time.text = value |
|
65 | tags_from_time.text = str(value) | |
66 |
|
66 |
@@ -1,137 +1,140 | |||||
1 | import re |
|
1 | import re | |
2 | import logging |
|
2 | import logging | |
3 | import xml.etree.ElementTree as ET |
|
3 | import xml.etree.ElementTree as ET | |
4 |
|
4 | |||
5 | import httplib2 |
|
5 | import httplib2 | |
6 | from django.core.management import BaseCommand |
|
6 | from django.core.management import BaseCommand | |
7 | from django.utils.dateparse import parse_datetime |
|
7 | from django.utils.dateparse import parse_datetime | |
8 |
|
8 | |||
9 | from boards.models import GlobalId, KeyPair |
|
9 | from boards.models import GlobalId, KeyPair | |
10 | from boards.models.post.sync import SyncManager, TAG_ID, TAG_UPDATE_TIME |
|
10 | from boards.models.post.sync import SyncManager, TAG_ID, TAG_UPDATE_TIME | |
11 |
|
11 | |||
12 | __author__ = 'neko259' |
|
12 | __author__ = 'neko259' | |
13 |
|
13 | |||
14 |
|
14 | |||
15 | REGEX_GLOBAL_ID = re.compile(r'(\w+)::([\w\+/]+)::(\d+)') |
|
15 | REGEX_GLOBAL_ID = re.compile(r'(\w+)::([\w\+/]+)::(\d+)') | |
16 |
|
16 | |||
17 |
|
17 | |||
18 | class Command(BaseCommand): |
|
18 | class Command(BaseCommand): | |
19 | help = 'Send a sync or get request to the server.' |
|
19 | help = 'Send a sync or get request to the server.' | |
20 |
|
20 | |||
21 | def add_arguments(self, parser): |
|
21 | def add_arguments(self, parser): | |
22 | parser.add_argument('url', type=str, help='Server root url') |
|
22 | parser.add_argument('url', type=str, help='Server root url') | |
23 | parser.add_argument('--global-id', type=str, default='', |
|
23 | parser.add_argument('--global-id', type=str, default='', | |
24 | help='Post global ID') |
|
24 | help='Post global ID') | |
25 | parser.add_argument('--split-query', type=int, default=1, |
|
25 | parser.add_argument('--split-query', type=int, default=1, | |
26 | help='Split GET query into separate by the given' |
|
26 | help='Split GET query into separate by the given' | |
27 | ' number of posts in one') |
|
27 | ' number of posts in one') | |
28 | parser.add_argument('--thread', type=int, |
|
28 | parser.add_argument('--thread', type=int, | |
29 | help='Get posts of one specific thread') |
|
29 | help='Get posts of one specific thread') | |
30 | parser.add_argument('--tags', type=str, |
|
30 | parser.add_argument('--tags', type=str, | |
31 | help='Get posts of the tags, comma-separated') |
|
31 | help='Get posts of the tags, comma-separated') | |
32 | parser.add_argument('--time-from', type=str, |
|
32 | parser.add_argument('--time-from', type=str, | |
33 | help='Get posts from the given timestamp') |
|
33 | help='Get posts from the given timestamp') | |
34 |
|
34 | |||
35 | def handle(self, *args, **options): |
|
35 | def handle(self, *args, **options): | |
36 | logger = logging.getLogger('boards.sync') |
|
36 | logger = logging.getLogger('boards.sync') | |
37 |
|
37 | |||
38 | url = options.get('url') |
|
38 | url = options.get('url') | |
39 |
|
39 | |||
40 | list_url = url + 'api/sync/list/' |
|
40 | list_url = url + 'api/sync/list/' | |
41 | get_url = url + 'api/sync/get/' |
|
41 | get_url = url + 'api/sync/get/' | |
42 | file_url = url[:-1] |
|
42 | file_url = url[:-1] | |
43 |
|
43 | |||
44 | global_id_str = options.get('global_id') |
|
44 | global_id_str = options.get('global_id') | |
45 | if global_id_str: |
|
45 | if global_id_str: | |
46 | match = REGEX_GLOBAL_ID.match(global_id_str) |
|
46 | match = REGEX_GLOBAL_ID.match(global_id_str) | |
47 | if match: |
|
47 | if match: | |
48 | key_type = match.group(1) |
|
48 | key_type = match.group(1) | |
49 | key = match.group(2) |
|
49 | key = match.group(2) | |
50 | local_id = match.group(3) |
|
50 | local_id = match.group(3) | |
51 |
|
51 | |||
52 | global_id = GlobalId(key_type=key_type, key=key, |
|
52 | global_id = GlobalId(key_type=key_type, key=key, | |
53 | local_id=local_id) |
|
53 | local_id=local_id) | |
54 |
|
54 | |||
55 | xml = SyncManager.generate_request_get([global_id]) |
|
55 | xml = SyncManager.generate_request_get([global_id]) | |
56 | h = httplib2.Http() |
|
56 | h = httplib2.Http() | |
57 | response, content = h.request(get_url, method="POST", body=xml) |
|
57 | response, content = h.request(get_url, method="POST", body=xml) | |
58 |
|
58 | |||
59 | SyncManager.parse_response_get(content, file_url) |
|
59 | SyncManager.parse_response_get(content, file_url) | |
60 | else: |
|
60 | else: | |
61 | raise Exception('Invalid global ID') |
|
61 | raise Exception('Invalid global ID') | |
62 | else: |
|
62 | else: | |
63 | logger.info('Running LIST request...') |
|
63 | logger.info('Running LIST request...') | |
64 | h = httplib2.Http() |
|
64 | h = httplib2.Http() | |
65 |
|
65 | |||
66 | tags = [] |
|
66 | tags = [] | |
67 | tags_str = options.get('tags') |
|
67 | tags_str = options.get('tags') | |
68 | if tags_str: |
|
68 | if tags_str: | |
69 | tags = tags_str.split(',') |
|
69 | tags = tags_str.split(',') | |
70 |
|
70 | |||
71 | timestamp_str = options.get('time_from') |
|
71 | timestamp_str = options.get('time_from') | |
72 | timestamp = None |
|
72 | timestamp = None | |
73 | if timestamp_str: |
|
73 | if timestamp_str: | |
74 | timestamp = parse_datetime(timestamp_str) |
|
74 | timestamp = parse_datetime(timestamp_str) | |
|
75 | if not timestamp: | |||
|
76 | raise Exception('Timestamp {} cannot be parsed'.format( | |||
|
77 | timestamp_str)) | |||
75 |
|
78 | |||
76 | xml = SyncManager.generate_request_list( |
|
79 | xml = SyncManager.generate_request_list( | |
77 | opening_post=options.get('thread'), tags=tags, |
|
80 | opening_post=options.get('thread'), tags=tags, | |
78 | timestamp_from=timestamp).encode() |
|
81 | timestamp_from=timestamp).encode() | |
79 | response, content = h.request(list_url, method="POST", body=xml) |
|
82 | response, content = h.request(list_url, method="POST", body=xml) | |
80 | if response.status != 200: |
|
83 | if response.status != 200: | |
81 | raise Exception('Server returned error {}'.format(response.status)) |
|
84 | raise Exception('Server returned error {}'.format(response.status)) | |
82 |
|
85 | |||
83 | logger.info('Processing response...') |
|
86 | logger.info('Processing response...') | |
84 |
|
87 | |||
85 | root = ET.fromstring(content) |
|
88 | root = ET.fromstring(content) | |
86 | status = root.findall('status')[0].text |
|
89 | status = root.findall('status')[0].text | |
87 | if status == 'success': |
|
90 | if status == 'success': | |
88 | ids_to_sync = list() |
|
91 | ids_to_sync = list() | |
89 |
|
92 | |||
90 | models = root.findall('models')[0] |
|
93 | models = root.findall('models')[0] | |
91 | for model in models: |
|
94 | for model in models: | |
92 | self.add_to_sync_list(ids_to_sync, logger, model) |
|
95 | self.add_to_sync_list(ids_to_sync, logger, model) | |
93 | logger.info('Starting sync...') |
|
96 | logger.info('Starting sync...') | |
94 |
|
97 | |||
95 | if len(ids_to_sync) > 0: |
|
98 | if len(ids_to_sync) > 0: | |
96 | limit = options.get('split_query', len(ids_to_sync)) |
|
99 | limit = options.get('split_query', len(ids_to_sync)) | |
97 | for offset in range(0, len(ids_to_sync), limit): |
|
100 | for offset in range(0, len(ids_to_sync), limit): | |
98 | xml = SyncManager.generate_request_get(ids_to_sync[offset:offset + limit]) |
|
101 | xml = SyncManager.generate_request_get(ids_to_sync[offset:offset + limit]) | |
99 | h = httplib2.Http() |
|
102 | h = httplib2.Http() | |
100 | logger.info('Running GET request...') |
|
103 | logger.info('Running GET request...') | |
101 | response, content = h.request(get_url, method="POST", body=xml) |
|
104 | response, content = h.request(get_url, method="POST", body=xml) | |
102 | logger.info('Processing response...') |
|
105 | logger.info('Processing response...') | |
103 |
|
106 | |||
104 | SyncManager.parse_response_get(content, file_url) |
|
107 | SyncManager.parse_response_get(content, file_url) | |
105 |
|
108 | |||
106 | logger.info('Sync completed successfully') |
|
109 | logger.info('Sync completed successfully') | |
107 | else: |
|
110 | else: | |
108 | logger.info('Nothing to get, everything synced') |
|
111 | logger.info('Nothing to get, everything synced') | |
109 | else: |
|
112 | else: | |
110 | raise Exception('Invalid response status') |
|
113 | raise Exception('Invalid response status') | |
111 |
|
114 | |||
112 | def add_to_sync_list(self, ids_to_sync, logger, model): |
|
115 | def add_to_sync_list(self, ids_to_sync, logger, model): | |
113 | tag_id = model.find(TAG_ID) |
|
116 | tag_id = model.find(TAG_ID) | |
114 | global_id, exists = GlobalId.from_xml_element(tag_id) |
|
117 | global_id, exists = GlobalId.from_xml_element(tag_id) | |
115 | from_this_board = self._is_from_this_board(global_id) |
|
118 | from_this_board = self._is_from_this_board(global_id) | |
116 | if from_this_board: |
|
119 | if from_this_board: | |
117 | # If the post originates from this board, no need to process |
|
120 | # If the post originates from this board, no need to process | |
118 | # it again, nobody else could modify it |
|
121 | # it again, nobody else could modify it | |
119 | logger.debug('NO SYNC Processed post {}'.format(global_id)) |
|
122 | logger.debug('NO SYNC Processed post {}'.format(global_id)) | |
120 | else: |
|
123 | else: | |
121 | tag_update_time = model.find(TAG_UPDATE_TIME) |
|
124 | tag_update_time = model.find(TAG_UPDATE_TIME) | |
122 | if tag_update_time: |
|
125 | if tag_update_time: | |
123 | update_time = tag_update_time.text |
|
126 | update_time = tag_update_time.text | |
124 | else: |
|
127 | else: | |
125 | update_time = None |
|
128 | update_time = None | |
126 | if not exists or update_time is None or global_id.post.last_edit_time < parse_datetime( |
|
129 | if not exists or update_time is None or global_id.post.last_edit_time < parse_datetime( | |
127 | update_time): |
|
130 | update_time): | |
128 | logger.debug('SYNC Processed post {}'.format(global_id)) |
|
131 | logger.debug('SYNC Processed post {}'.format(global_id)) | |
129 | ids_to_sync.append(global_id) |
|
132 | ids_to_sync.append(global_id) | |
130 | else: |
|
133 | else: | |
131 | logger.debug('NO SYNC Processed post {}'.format(global_id)) |
|
134 | logger.debug('NO SYNC Processed post {}'.format(global_id)) | |
132 |
|
135 | |||
133 | def _is_from_this_board(self, global_id): |
|
136 | def _is_from_this_board(self, global_id): | |
134 | from_this_board = KeyPair.objects.filter( |
|
137 | from_this_board = KeyPair.objects.filter( | |
135 | key_type=global_id.key_type, |
|
138 | key_type=global_id.key_type, | |
136 | public_key=global_id.key).exists() |
|
139 | public_key=global_id.key).exists() | |
137 | return from_this_board |
|
140 | return from_this_board |
General Comments 0
You need to be logged in to leave comments.
Login now