Show More
@@ -0,0 +1,19 | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | # Generated by Django 1.11 on 2017-09-27 12:38 | |||
|
3 | from __future__ import unicode_literals | |||
|
4 | ||||
|
5 | from django.db import migrations | |||
|
6 | ||||
|
7 | ||||
|
8 | class Migration(migrations.Migration): | |||
|
9 | ||||
|
10 | dependencies = [ | |||
|
11 | ('boards', '0063_auto_20170301_1058'), | |||
|
12 | ] | |||
|
13 | ||||
|
14 | operations = [ | |||
|
15 | migrations.RemoveField( | |||
|
16 | model_name='post', | |||
|
17 | name='version', | |||
|
18 | ), | |||
|
19 | ] |
@@ -16,7 +16,7 class PostAdmin(admin.ModelAdmin): | |||||
16 | exclude = ('referenced_posts', 'refmap', 'images', 'global_id') |
|
16 | exclude = ('referenced_posts', 'refmap', 'images', 'global_id') | |
17 | readonly_fields = ('poster_ip', 'thread', 'linked_images', |
|
17 | readonly_fields = ('poster_ip', 'thread', 'linked_images', | |
18 | 'attachments', 'uid', 'url', 'pub_time', 'opening', 'linked_global_id', |
|
18 | 'attachments', 'uid', 'url', 'pub_time', 'opening', 'linked_global_id', | |
19 |
|
|
19 | 'foreign', 'tags') | |
20 |
|
20 | |||
21 | def ban_poster(self, request, queryset): |
|
21 | def ban_poster(self, request, queryset): | |
22 | bans = 0 |
|
22 | bans = 0 | |
@@ -63,7 +63,6 class PostAdmin(admin.ModelAdmin): | |||||
63 | return ', '.join([tag.get_name() for tag in obj.get_tags()]) |
|
63 | return ', '.join([tag.get_name() for tag in obj.get_tags()]) | |
64 |
|
64 | |||
65 | def save_model(self, request, obj, form, change): |
|
65 | def save_model(self, request, obj, form, change): | |
66 | obj.increment_version() |
|
|||
67 | obj.save() |
|
66 | obj.save() | |
68 | obj.clear_cache() |
|
67 | obj.clear_cache() | |
69 |
|
68 | |||
@@ -127,8 +126,6 class ThreadAdmin(admin.ModelAdmin): | |||||
127 |
|
126 | |||
128 | def save_model(self, request, obj, form, change): |
|
127 | def save_model(self, request, obj, form, change): | |
129 | op = obj.get_opening_post() |
|
128 | op = obj.get_opening_post() | |
130 | op.increment_version() |
|
|||
131 | op.save(update_fields=['version']) |
|
|||
132 | obj.save() |
|
129 | obj.save() | |
133 | op.clear_cache() |
|
130 | op.clear_cache() | |
134 |
|
131 |
@@ -4,9 +4,10 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 |
|
8 | |||
8 | from boards.models import GlobalId |
|
9 | from boards.models import GlobalId | |
9 |
from boards.models.post.sync import SyncManager, TAG_ID, TAG_ |
|
10 | from boards.models.post.sync import SyncManager, TAG_ID, TAG_UPDATE_TIME | |
10 |
|
11 | |||
11 | __author__ = 'neko259' |
|
12 | __author__ = 'neko259' | |
12 |
|
13 | |||
@@ -85,12 +86,12 class Command(BaseCommand): | |||||
85 | for model in models: |
|
86 | for model in models: | |
86 | tag_id = model.find(TAG_ID) |
|
87 | tag_id = model.find(TAG_ID) | |
87 | global_id, exists = GlobalId.from_xml_element(tag_id) |
|
88 | global_id, exists = GlobalId.from_xml_element(tag_id) | |
88 |
tag_ |
|
89 | tag_update_time = model.find(TAG_UPDATE_TIME) | |
89 |
if tag_ |
|
90 | if tag_update_time: | |
90 |
|
|
91 | update_time = tag_update_time.text | |
91 | else: |
|
92 | else: | |
92 |
|
|
93 | update_time = None | |
93 |
if not exists or global_id.post. |
|
94 | if not exists or update_time is None or global_id.post.last_edit_time < parse_datetime(update_time): | |
94 | logger.debug('Processed (+) post {}'.format(global_id)) |
|
95 | logger.debug('Processed (+) post {}'.format(global_id)) | |
95 | ids_to_sync.append(global_id) |
|
96 | ids_to_sync.append(global_id) | |
96 | else: |
|
97 | else: |
@@ -100,7 +100,6 class Post(models.Model, Viewable): | |||||
100 | tripcode = models.CharField(max_length=50, blank=True, default='') |
|
100 | tripcode = models.CharField(max_length=50, blank=True, default='') | |
101 | opening = models.BooleanField(db_index=True) |
|
101 | opening = models.BooleanField(db_index=True) | |
102 | hidden = models.BooleanField(default=False) |
|
102 | hidden = models.BooleanField(default=False) | |
103 | version = models.IntegerField(default=1) |
|
|||
104 |
|
103 | |||
105 | def __str__(self): |
|
104 | def __str__(self): | |
106 | return 'P#{}/{}'.format(self.id, self.get_title()) |
|
105 | return 'P#{}/{}'.format(self.id, self.get_title()) | |
@@ -341,9 +340,6 class Post(models.Model, Viewable): | |||||
341 | def set_hidden(self, hidden): |
|
340 | def set_hidden(self, hidden): | |
342 | self.hidden = hidden |
|
341 | self.hidden = hidden | |
343 |
|
342 | |||
344 | def increment_version(self): |
|
|||
345 | self.version = F('version') + 1 |
|
|||
346 |
|
||||
347 | def clear_cache(self): |
|
343 | def clear_cache(self): | |
348 | """ |
|
344 | """ | |
349 | Clears sync data (content cache, signatures etc). |
|
345 | Clears sync data (content cache, signatures etc). |
@@ -138,7 +138,7 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 |
file_urls=list(), tripcode=None, |
|
141 | file_urls=list(), tripcode=None, last_edit_time=None): | |
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( | |
@@ -151,12 +151,11 class PostManager(models.Manager): | |||||
151 | text=text, |
|
151 | text=text, | |
152 | pub_time=pub_time, |
|
152 | pub_time=pub_time, | |
153 | poster_ip=NO_IP, |
|
153 | poster_ip=NO_IP, | |
154 | last_edit_time=pub_time, |
|
154 | last_edit_time=last_edit_time or pub_time, | |
155 | global_id=global_id, |
|
155 | global_id=global_id, | |
156 | opening=is_opening, |
|
156 | opening=is_opening, | |
157 | thread=thread, |
|
157 | thread=thread, | |
158 |
tripcode=tripcode |
|
158 | tripcode=tripcode) | |
159 | version=version) |
|
|||
160 |
|
159 | |||
161 | for file in files: |
|
160 | for file in files: | |
162 | self._add_file_to_post(file, post) |
|
161 | self._add_file_to_post(file, post) | |
@@ -170,12 +169,11 class PostManager(models.Manager): | |||||
170 |
|
169 | |||
171 | @transaction.atomic |
|
170 | @transaction.atomic | |
172 | def update_post(self, post, title: str, text: str, pub_time: str, |
|
171 | def update_post(self, post, title: str, text: str, pub_time: str, | |
173 |
tags=list(), files=list(), file_urls=list(), tripcode=None |
|
172 | tags=list(), files=list(), file_urls=list(), tripcode=None): | |
174 | post.title = title |
|
173 | post.title = title | |
175 | post.text = text |
|
174 | post.text = text | |
176 | post.pub_time = pub_time |
|
175 | post.pub_time = pub_time | |
177 | post.tripcode = tripcode |
|
176 | post.tripcode = tripcode | |
178 | post.version = version |
|
|||
179 | post.save() |
|
177 | post.save() | |
180 |
|
178 | |||
181 | post.clear_cache() |
|
179 | post.clear_cache() |
@@ -1,6 +1,8 | |||||
|
1 | import logging | |||
1 | import xml.etree.ElementTree as et |
|
2 | import xml.etree.ElementTree as et | |
2 | import logging |
|
3 | ||
3 | from xml.etree import ElementTree |
|
4 | from django.db import transaction | |
|
5 | from django.utils.dateparse import parse_datetime | |||
4 |
|
6 | |||
5 | from boards.abstracts.exceptions import SyncException |
|
7 | from boards.abstracts.exceptions import SyncException | |
6 | from boards.abstracts.sync_filters import ThreadFilter, TagsFilter,\ |
|
8 | from boards.abstracts.sync_filters import ThreadFilter, TagsFilter, \ | |
@@ -10,7 +12,6 from boards.models.attachment.downloader | |||||
10 | from boards.models.signature import TAG_REQUEST, ATTR_TYPE, TYPE_GET, \ |
|
12 | from boards.models.signature import TAG_REQUEST, ATTR_TYPE, TYPE_GET, \ | |
11 | ATTR_VERSION, TAG_MODEL, ATTR_NAME, TAG_ID, TYPE_LIST |
|
13 | ATTR_VERSION, TAG_MODEL, ATTR_NAME, TAG_ID, TYPE_LIST | |
12 | from boards.utils import get_file_mimetype, get_file_hash |
|
14 | from boards.utils import get_file_mimetype, get_file_hash | |
13 | from django.db import transaction |
|
|||
14 |
|
15 | |||
15 | EXCEPTION_NODE = 'Sync node returned an error: {}.' |
|
16 | EXCEPTION_NODE = 'Sync node returned an error: {}.' | |
16 | EXCEPTION_DOWNLOAD = 'File was not downloaded.' |
|
17 | EXCEPTION_DOWNLOAD = 'File was not downloaded.' | |
@@ -30,6 +31,7 TAG_TITLE = 'title' | |||||
30 | TAG_TEXT = 'text' |
|
31 | TAG_TEXT = 'text' | |
31 | TAG_THREAD = 'thread' |
|
32 | TAG_THREAD = 'thread' | |
32 | TAG_PUB_TIME = 'pub-time' |
|
33 | TAG_PUB_TIME = 'pub-time' | |
|
34 | TAG_UPDATE_TIME = 'update-time' | |||
33 | TAG_SIGNATURES = 'signatures' |
|
35 | TAG_SIGNATURES = 'signatures' | |
34 | TAG_SIGNATURE = 'signature' |
|
36 | TAG_SIGNATURE = 'signature' | |
35 | TAG_CONTENT = 'content' |
|
37 | TAG_CONTENT = 'content' | |
@@ -59,6 +61,8 ID_TYPE_URL = 'url' | |||||
59 |
|
61 | |||
60 | STATUS_SUCCESS = 'success' |
|
62 | STATUS_SUCCESS = 'success' | |
61 |
|
63 | |||
|
64 | CURRENT_MODEL_VERSION = '1.1' | |||
|
65 | ||||
62 |
|
66 | |||
63 | logger = logging.getLogger('boards.sync') |
|
67 | logger = logging.getLogger('boards.sync') | |
64 |
|
68 | |||
@@ -120,6 +124,9 class SyncManager: | |||||
120 | pub_time = et.SubElement(content_tag, TAG_PUB_TIME) |
|
124 | pub_time = et.SubElement(content_tag, TAG_PUB_TIME) | |
121 | pub_time.text = str(post.get_pub_time_str()) |
|
125 | pub_time.text = str(post.get_pub_time_str()) | |
122 |
|
126 | |||
|
127 | update_time = et.SubElement(content_tag, TAG_UPDATE_TIME) | |||
|
128 | update_time.text = str(post.last_edit_time) | |||
|
129 | ||||
123 | if post.tripcode: |
|
130 | if post.tripcode: | |
124 | tripcode = et.SubElement(content_tag, TAG_TRIPCODE) |
|
131 | tripcode = et.SubElement(content_tag, TAG_TRIPCODE) | |
125 | tripcode.text = post.tripcode |
|
132 | tripcode.text = post.tripcode | |
@@ -141,8 +148,6 class SyncManager: | |||||
141 | for file in attachments: |
|
148 | for file in attachments: | |
142 | SyncManager._attachment_to_xml( |
|
149 | SyncManager._attachment_to_xml( | |
143 | attachments_tag, attachment_refs, file) |
|
150 | attachments_tag, attachment_refs, file) | |
144 | version_tag = et.SubElement(content_tag, TAG_VERSION) |
|
|||
145 | version_tag.text = str(post.version) |
|
|||
146 |
|
151 | |||
147 | global_id.content = et.tostring(content_tag, ENCODING_UNICODE) |
|
152 | global_id.content = et.tostring(content_tag, ENCODING_UNICODE) | |
148 | global_id.save() |
|
153 | global_id.save() | |
@@ -191,8 +196,8 class SyncManager: | |||||
191 | global_id, exists = GlobalId.from_xml_element(tag_id) |
|
196 | global_id, exists = GlobalId.from_xml_element(tag_id) | |
192 | signatures = SyncManager._verify_model(global_id, content_str, tag_model) |
|
197 | signatures = SyncManager._verify_model(global_id, content_str, tag_model) | |
193 |
|
198 | |||
194 |
|
|
199 | update_time = tag_content.find(TAG_UPDATE_TIME).text | |
195 |
is_old = exists and global_id.post. |
|
200 | is_old = exists and global_id.post.last_edit_time < parse_datetime(update_time) | |
196 | if exists and not is_old: |
|
201 | if exists and not is_old: | |
197 | logger.debug('Post {} exists and is up to date.'.format(global_id)) |
|
202 | logger.debug('Post {} exists and is up to date.'.format(global_id)) | |
198 | else: |
|
203 | else: | |
@@ -204,13 +209,13 class SyncManager: | |||||
204 |
|
209 | |||
205 | title = tag_content.find(TAG_TITLE).text or '' |
|
210 | title = tag_content.find(TAG_TITLE).text or '' | |
206 | text = tag_content.find(TAG_TEXT).text or '' |
|
211 | text = tag_content.find(TAG_TEXT).text or '' | |
207 | pub_time = tag_content.find(TAG_PUB_TIME).text |
|
|||
208 | tripcode_tag = tag_content.find(TAG_TRIPCODE) |
|
212 | tripcode_tag = tag_content.find(TAG_TRIPCODE) | |
209 | if tripcode_tag is not None: |
|
213 | if tripcode_tag is not None: | |
210 | tripcode = tripcode_tag.text or '' |
|
214 | tripcode = tripcode_tag.text or '' | |
211 | else: |
|
215 | else: | |
212 | tripcode = '' |
|
216 | tripcode = '' | |
213 |
|
217 | |||
|
218 | pub_time = tag_content.find(TAG_PUB_TIME).text | |||
214 | thread = tag_content.find(TAG_THREAD) |
|
219 | thread = tag_content.find(TAG_THREAD) | |
215 | tags = [] |
|
220 | tags = [] | |
216 | if thread: |
|
221 | if thread: | |
@@ -257,15 +262,14 class SyncManager: | |||||
257 | Post.objects.update_post( |
|
262 | Post.objects.update_post( | |
258 | post, title=title, text=text, pub_time=pub_time, |
|
263 | post, title=title, text=text, pub_time=pub_time, | |
259 | tags=tags, files=files, file_urls=urls, |
|
264 | tags=tags, files=files, file_urls=urls, | |
260 | tripcode=tripcode, version=version) |
|
265 | tripcode=tripcode, version=version, last_edit_time=update_time) | |
261 | logger.debug('Parsed updated post {}'.format(global_id)) |
|
266 | logger.debug('Parsed updated post {}'.format(global_id)) | |
262 | else: |
|
267 | else: | |
263 | Post.objects.import_post( |
|
268 | Post.objects.import_post( | |
264 | title=title, text=text, pub_time=pub_time, |
|
269 | title=title, text=text, pub_time=pub_time, | |
265 | opening_post=opening_post, tags=tags, |
|
270 | opening_post=opening_post, tags=tags, | |
266 | global_id=global_id, files=files, |
|
271 | global_id=global_id, files=files, | |
267 | file_urls=urls, tripcode=tripcode, |
|
272 | file_urls=urls, tripcode=tripcode, last_edit_time=update_time) | |
268 | version=version) |
|
|||
269 | logger.debug('Parsed new post {}'.format(global_id)) |
|
273 | logger.debug('Parsed new post {}'.format(global_id)) | |
270 |
|
274 | |||
271 | @staticmethod |
|
275 | @staticmethod | |
@@ -285,8 +289,8 class SyncManager: | |||||
285 | tag_model = et.SubElement(models, TAG_MODEL) |
|
289 | tag_model = et.SubElement(models, TAG_MODEL) | |
286 | tag_id = et.SubElement(tag_model, TAG_ID) |
|
290 | tag_id = et.SubElement(tag_model, TAG_ID) | |
287 | post.global_id.to_xml_element(tag_id) |
|
291 | post.global_id.to_xml_element(tag_id) | |
288 |
|
|
292 | update_time = et.SubElement(tag_model, TAG_UPDATE_TIME) | |
289 |
|
|
293 | update_time.text = str(post.last_edit_time) | |
290 |
|
294 | |||
291 | return et.tostring(response, ENCODING_UNICODE) |
|
295 | return et.tostring(response, ENCODING_UNICODE) | |
292 |
|
296 | |||
@@ -372,7 +376,7 class SyncManager: | |||||
372 | request.set(ATTR_VERSION, '1.0') |
|
376 | request.set(ATTR_VERSION, '1.0') | |
373 |
|
377 | |||
374 | model = et.SubElement(request, TAG_MODEL) |
|
378 | model = et.SubElement(request, TAG_MODEL) | |
375 |
model.set(ATTR_VERSION, |
|
379 | model.set(ATTR_VERSION, CURRENT_MODEL_VERSION) | |
376 | model.set(ATTR_NAME, 'post') |
|
380 | model.set(ATTR_NAME, 'post') | |
377 |
|
381 | |||
378 | if opening_post: |
|
382 | if opening_post: |
@@ -186,7 +186,7 class Thread(models.Model): | |||||
186 | """ |
|
186 | """ | |
187 | Gets replies with only fields that are used for viewing. |
|
187 | Gets replies with only fields that are used for viewing. | |
188 | """ |
|
188 | """ | |
189 |
return self.get_replies().defer('text', 'last_edit_time' |
|
189 | return self.get_replies().defer('text', 'last_edit_time') | |
190 |
|
190 | |||
191 | def get_top_level_replies(self) -> QuerySet: |
|
191 | def get_top_level_replies(self) -> QuerySet: | |
192 | return self.get_replies().exclude(refposts__threads__in=[self]) |
|
192 | return self.get_replies().exclude(refposts__threads__in=[self]) |
@@ -64,6 +64,7 | |||||
64 | <button name="method" value="subscribe" class="not_fav">★ {% trans "Add to favorites" %}</button> |
|
64 | <button name="method" value="subscribe" class="not_fav">★ {% trans "Add to favorites" %}</button> | |
65 | {% endif %} |
|
65 | {% endif %} | |
66 | </form> |
|
66 | </form> | |
|
67 | • | |||
67 | <form action="{% url 'tag' tag.get_name %}" method="post" class="post-button-form"> |
|
68 | <form action="{% url 'tag' tag.get_name %}" method="post" class="post-button-form"> | |
68 | {% if is_hidden %} |
|
69 | {% if is_hidden %} | |
69 | <button name="method" value="unhide" class="fav">{% trans "Show" %}</button> |
|
70 | <button name="method" value="unhide" class="fav">{% trans "Show" %}</button> | |
@@ -71,6 +72,7 | |||||
71 | <button name="method" value="hide" class="not_fav">{% trans "Hide" %}</button> |
|
72 | <button name="method" value="hide" class="not_fav">{% trans "Hide" %}</button> | |
72 | {% endif %} |
|
73 | {% endif %} | |
73 | </form> |
|
74 | </form> | |
|
75 | • | |||
74 | <a href="{% url 'tag_gallery' tag.get_name %}">{% trans 'Gallery' %}</a> |
|
76 | <a href="{% url 'tag_gallery' tag.get_name %}">{% trans 'Gallery' %}</a> | |
75 | </p> |
|
77 | </p> | |
76 | {% if tag.get_description %} |
|
78 | {% if tag.get_description %} |
@@ -65,22 +65,22 class KeyTest(TestCase): | |||||
65 | '<models>' |
|
65 | '<models>' | |
66 | '<model name="post">' |
|
66 | '<model name="post">' | |
67 | '<content>' |
|
67 | '<content>' | |
68 |
'<id key=" |
|
68 | '<id key="{}" local-id="{}" type="{}" />' | |
69 | '<title>test_title</title>' |
|
69 | '<title>test_title</title>' | |
70 |
'<text>[post] |
|
70 | '<text>[post]{}[/post]</text>' | |
71 |
'<thread><id key=" |
|
71 | '<thread><id key="{}" local-id="{}" type="{}" /></thread>' | |
72 |
'<pub-time> |
|
72 | '<pub-time>{}</pub-time>' | |
73 |
'< |
|
73 | '<update-time>{}</update-time>' | |
74 |
'</content>' |
|
74 | '</content>'.format( | |
75 | key.public_key, |
|
75 | key.public_key, | |
76 | reply_post.id, |
|
76 | reply_post.id, | |
77 | key.key_type, |
|
77 | key.key_type, | |
78 |
|
|
78 | post.global_id, | |
79 | key.public_key, |
|
79 | key.public_key, | |
80 | post.id, |
|
80 | post.id, | |
81 | key.key_type, |
|
81 | key.key_type, | |
82 |
|
|
82 | reply_post.get_pub_time_str(), | |
83 |
post. |
|
83 | reply_post.last_edit_time, | |
84 | ) in response, |
|
84 | ) in response, | |
85 | 'Wrong XML generated for the GET response.') |
|
85 | 'Wrong XML generated for the GET response.') | |
86 |
|
86 |
@@ -37,13 +37,13 class SyncTest(TestCase): | |||||
37 | '<models>' |
|
37 | '<models>' | |
38 | '<model name="post">' |
|
38 | '<model name="post">' | |
39 | '<content>' |
|
39 | '<content>' | |
40 |
'<id key=" |
|
40 | '<id key="{}" local-id="{}" type="{}" />' | |
41 |
'<title> |
|
41 | '<title>{}</title>' | |
42 |
'<text> |
|
42 | '<text>{}</text>' | |
43 |
'<tags><tag> |
|
43 | '<tags><tag>{}</tag></tags>' | |
44 |
'<pub-time> |
|
44 | '<pub-time>{}</pub-time>' | |
45 | '<version>%s</version>' |
|
45 | '<update-time>{}</update-time>' | |
46 |
'</content>' |
|
46 | '</content>'.format( | |
47 | post.global_id.key, |
|
47 | post.global_id.key, | |
48 | post.global_id.local_id, |
|
48 | post.global_id.local_id, | |
49 | post.global_id.key_type, |
|
49 | post.global_id.key_type, | |
@@ -51,7 +51,7 class SyncTest(TestCase): | |||||
51 | post.get_sync_text(), |
|
51 | post.get_sync_text(), | |
52 | post.get_thread().get_tags().first().get_name(), |
|
52 | post.get_thread().get_tags().first().get_name(), | |
53 | post.get_pub_time_str(), |
|
53 | post.get_pub_time_str(), | |
54 |
post. |
|
54 | post.last_edit_time, | |
55 | ) in response, |
|
55 | ) in response, | |
56 | 'Wrong response generated for the GET request.') |
|
56 | 'Wrong response generated for the GET request.') | |
57 |
|
57 | |||
@@ -86,13 +86,13 class SyncTest(TestCase): | |||||
86 | '<models>' |
|
86 | '<models>' | |
87 | '<model name="post">' |
|
87 | '<model name="post">' | |
88 | '<content>' |
|
88 | '<content>' | |
89 |
'<id key=" |
|
89 | '<id key="{}" local-id="{}" type="{}" />' | |
90 |
'<title> |
|
90 | '<title>{}</title>' | |
91 |
'<text> |
|
91 | '<text>{}</text>' | |
92 |
'<tags><tag> |
|
92 | '<tags><tag>{}</tag></tags>' | |
93 |
'<pub-time> |
|
93 | '<pub-time>{}</pub-time>' | |
94 | '<version>%s</version>' |
|
94 | '<update-time>{}</update-time>' | |
95 |
'</content>' |
|
95 | '</content>'.format( | |
96 | post.global_id.key, |
|
96 | post.global_id.key, | |
97 | post.global_id.local_id, |
|
97 | post.global_id.local_id, | |
98 | post.global_id.key_type, |
|
98 | post.global_id.key_type, | |
@@ -100,7 +100,6 class SyncTest(TestCase): | |||||
100 | post.get_sync_text(), |
|
100 | post.get_sync_text(), | |
101 | post.get_thread().get_tags().first().get_name(), |
|
101 | post.get_thread().get_tags().first().get_name(), | |
102 | post.get_pub_time_str(), |
|
102 | post.get_pub_time_str(), | |
103 | post.version, |
|
|||
104 | ) in response, |
|
103 | ) in response, | |
105 | 'Wrong response generated for the GET request.') |
|
104 | 'Wrong response generated for the GET request.') | |
106 |
|
105 | |||
@@ -128,21 +127,21 class SyncTest(TestCase): | |||||
128 | '<models>' |
|
127 | '<models>' | |
129 | '<model>' |
|
128 | '<model>' | |
130 | '<id key="{}" local-id="{}" type="{}" />' |
|
129 | '<id key="{}" local-id="{}" type="{}" />' | |
131 |
'< |
|
130 | '<update-time>{}</update-time>' | |
132 | '</model>' |
|
131 | '</model>' | |
133 | '<model>' |
|
132 | '<model>' | |
134 | '<id key="{}" local-id="{}" type="{}" />' |
|
133 | '<id key="{}" local-id="{}" type="{}" />' | |
135 |
'< |
|
134 | '<update-time>{}</update-time>' | |
136 | '</model>' |
|
135 | '</model>' | |
137 | '</models>'.format( |
|
136 | '</models>'.format( | |
138 | post.global_id.key, |
|
137 | post.global_id.key, | |
139 | post.global_id.local_id, |
|
138 | post.global_id.local_id, | |
140 | post.global_id.key_type, |
|
139 | post.global_id.key_type, | |
141 |
post. |
|
140 | post.last_edit_time, | |
142 | post2.global_id.key, |
|
141 | post2.global_id.key, | |
143 | post2.global_id.local_id, |
|
142 | post2.global_id.local_id, | |
144 | post2.global_id.key_type, |
|
143 | post2.global_id.key_type, | |
145 |
post2. |
|
144 | post2.last_edit_time, | |
146 | ) in response_all, |
|
145 | ) in response_all, | |
147 | 'Wrong response generated for the LIST request for all posts.') |
|
146 | 'Wrong response generated for the LIST request for all posts.') | |
148 |
|
147 | |||
@@ -173,13 +172,13 class SyncTest(TestCase): | |||||
173 | '<models>' |
|
172 | '<models>' | |
174 | '<model>' |
|
173 | '<model>' | |
175 | '<id key="{}" local-id="{}" type="{}" />' |
|
174 | '<id key="{}" local-id="{}" type="{}" />' | |
176 |
'< |
|
175 | '<update-time>{}</update-time>' | |
177 | '</model>' |
|
176 | '</model>' | |
178 | '</models>'.format( |
|
177 | '</models>'.format( | |
179 | post.global_id.key, |
|
178 | post.global_id.key, | |
180 | post.global_id.local_id, |
|
179 | post.global_id.local_id, | |
181 | post.global_id.key_type, |
|
180 | post.global_id.key_type, | |
182 |
post. |
|
181 | post.last_edit_time, | |
183 | ) in response_thread, |
|
182 | ) in response_thread, | |
184 | 'Wrong response generated for the LIST request for posts of ' |
|
183 | 'Wrong response generated for the LIST request for posts of ' | |
185 | 'existing thread.') |
|
184 | 'existing thread.') | |
@@ -240,13 +239,13 class SyncTest(TestCase): | |||||
240 | '<models>' |
|
239 | '<models>' | |
241 | '<model>' |
|
240 | '<model>' | |
242 | '<id key="{}" local-id="{}" type="{}" />' |
|
241 | '<id key="{}" local-id="{}" type="{}" />' | |
243 |
'< |
|
242 | '<update-time>{}</update_time>' | |
244 | '</model>' |
|
243 | '</model>' | |
245 | '</models>'.format( |
|
244 | '</models>'.format( | |
246 | post2.global_id.key, |
|
245 | post2.global_id.key, | |
247 | post2.global_id.local_id, |
|
246 | post2.global_id.local_id, | |
248 | post2.global_id.key_type, |
|
247 | post2.global_id.key_type, | |
249 |
post2. |
|
248 | post2.last_edit_time, | |
250 | ) in response_thread, |
|
249 | ) in response_thread, | |
251 | 'Wrong response generated for the LIST request for posts of ' |
|
250 | 'Wrong response generated for the LIST request for posts of ' | |
252 | 'existing thread.') |
|
251 | 'existing thread.') |
@@ -97,7 +97,7 Sample response: | |||||
97 | <models> |
|
97 | <models> | |
98 | <model> |
|
98 | <model> | |
99 | <id key="id1" type="ecdsa" local-id="1"> |
|
99 | <id key="id1" type="ecdsa" local-id="1"> | |
100 | <version>1</version> |
|
100 | <update-time>2017-01-01 00:00:00</update-time> | |
101 | </model> |
|
101 | </model> | |
102 | <model> |
|
102 | <model> | |
103 | <id key="id1" type="ecdsa" local-id="2" /> |
|
103 | <id key="id1" type="ecdsa" local-id="2" /> |
@@ -1,6 +1,6 | |||||
1 | # 0 Title # |
|
1 | # 0 Title # | |
2 |
|
2 | |||
3 | "post" model reference |
|
3 | "post" model reference of version 1.1 | |
4 |
|
4 | |||
5 | # 1 Description # |
|
5 | # 1 Description # | |
6 |
|
6 | |||
@@ -13,7 +13,7 | |||||
13 | * title -- text field. |
|
13 | * title -- text field. | |
14 | * text -- text field. |
|
14 | * text -- text field. | |
15 | * pub-time -- timestamp (TBD: Define format). |
|
15 | * pub-time -- timestamp (TBD: Define format). | |
16 |
* |
|
16 | * update -- when post content changes, the update time should be incremented. | |
17 |
|
17 | |||
18 | # 2.2 Optional fields # |
|
18 | # 2.2 Optional fields # | |
19 |
|
19 |
General Comments 0
You need to be logged in to leave comments.
Login now