##// END OF EJS Templates
Some progress in the sync
neko259 -
r1144:a82e4e4d decentral
parent child Browse files
Show More
@@ -1,46 +1,61 b''
1 1 import re
2 2 import urllib.parse
3 3 import httplib2
4 import xml.etree.ElementTree as ET
5
4 6 from django.core.management import BaseCommand
5 7 from boards.models import GlobalId
6 8
7 9 __author__ = 'neko259'
8 10
9 11
10 12 REGEX_GLOBAL_ID = re.compile(r'(\w+)::([\w\+/]+)::(\d+)')
11 13
12 14
13 15 class Command(BaseCommand):
14 16 help = 'Send a sync or get request to the server.' + \
15 17 'sync_with_server <server_url> [post_global_id]'
16 18
17 19 def add_arguments(self, parser):
18 20 parser.add_argument('url', type=str)
19 parser.add_argument('global_id', type=str)
21 #parser.add_argument('global_id', type=str) # TODO Implement this
20 22
21 23 def handle(self, *args, **options):
22 24 url = options.get('url')
23 25 global_id_str = options.get('global_id')
24 26 if global_id_str:
25 27 match = REGEX_GLOBAL_ID.match(global_id_str)
26 28 if match:
27 29 key_type = match.group(1)
28 30 key = match.group(2)
29 31 local_id = match.group(3)
30 32
31 33 global_id = GlobalId(key_type=key_type, key=key,
32 34 local_id=local_id)
33 35
34 36 xml = GlobalId.objects.generate_request_get([global_id])
35 37 data = {'xml': xml}
36 38 body = urllib.parse.urlencode(data)
37 39 h = httplib2.Http()
38 40 response, content = h.request(url, method="POST", body=body)
39 41
40 42 # TODO Parse content and get the model list
41 43
42 44 print(content)
43 45 else:
44 46 raise Exception('Invalid global ID')
45 47 else:
46 raise Exception('Full sync is not supported yet.')
48 h = httplib2.Http()
49 response, content = h.request(url, method="POST")
50
51 print(content)
52
53 root = ET.fromstring(content)
54 status = root.findall('status')[0].text
55 if status == 'success':
56 models = root.findall('models')[0]
57 for model in models:
58 model_content = model[0]
59 print(model_content.findall('text')[0].text)
60 else:
61 raise Exception('Invalid response status')
@@ -1,85 +1,89 b''
1 1 from django.conf.urls import patterns, url
2 2 from django.views.i18n import javascript_catalog
3 3
4 4 from boards import views
5 5 from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed
6 6 from boards.views import api, tag_threads, all_threads, \
7 7 settings, all_tags
8 8 from boards.views.authors import AuthorsView
9 9 from boards.views.notifications import NotificationView
10 10 from boards.views.search import BoardSearchView
11 11 from boards.views.static import StaticPageView
12 12 from boards.views.preview import PostPreviewView
13 13 from boards.views.sync import get_post_sync_data
14 14
15 15
16 16 js_info_dict = {
17 17 'packages': ('boards',),
18 18 }
19 19
20 20 urlpatterns = patterns('',
21 21 # /boards/
22 22 url(r'^$', all_threads.AllThreadsView.as_view(), name='index'),
23 23 # /boards/page/
24 24 url(r'^page/(?P<page>\w+)/$', all_threads.AllThreadsView.as_view(),
25 25 name='index'),
26 26
27 27 # /boards/tag/tag_name/
28 28 url(r'^tag/(?P<tag_name>\w+)/$', tag_threads.TagView.as_view(),
29 29 name='tag'),
30 30 # /boards/tag/tag_id/page/
31 31 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/$',
32 32 tag_threads.TagView.as_view(), name='tag'),
33 33
34 34 # /boards/thread/
35 35 url(r'^thread/(?P<post_id>\d+)/$', views.thread.normal.NormalThreadView.as_view(),
36 36 name='thread'),
37 37 url(r'^thread/(?P<post_id>\d+)/mode/gallery/$', views.thread.gallery.GalleryThreadView.as_view(),
38 38 name='thread_gallery'),
39 39
40 40 url(r'^settings/$', settings.SettingsView.as_view(), name='settings'),
41 41 url(r'^tags/(?P<query>\w+)?/?$', all_tags.AllTagsView.as_view(), name='tags'),
42 42 url(r'^authors/$', AuthorsView.as_view(), name='authors'),
43 43
44 44 url(r'^banned/$', views.banned.BannedView.as_view(), name='banned'),
45 45 url(r'^staticpage/(?P<name>\w+)/$', StaticPageView.as_view(),
46 46 name='staticpage'),
47 47
48 48 # RSS feeds
49 49 url(r'^rss/$', AllThreadsFeed()),
50 50 url(r'^page/(?P<page>\d+)/rss/$', AllThreadsFeed()),
51 51 url(r'^tag/(?P<tag_name>\w+)/rss/$', TagThreadsFeed()),
52 52 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/rss/$', TagThreadsFeed()),
53 53 url(r'^thread/(?P<post_id>\d+)/rss/$', ThreadPostsFeed()),
54 54
55 55 # i18n
56 56 url(r'^jsi18n/$', javascript_catalog, js_info_dict,
57 57 name='js_info_dict'),
58 58
59 59 # API
60 60 url(r'^api/post/(?P<post_id>\d+)/$', api.get_post, name="get_post"),
61 61 url(r'^api/diff_thread$',
62 62 api.api_get_threaddiff, name="get_thread_diff"),
63 63 url(r'^api/threads/(?P<count>\w+)/$', api.api_get_threads,
64 64 name='get_threads'),
65 65 url(r'^api/tags/$', api.api_get_tags, name='get_tags'),
66 66 url(r'^api/thread/(?P<opening_post_id>\w+)/$', api.api_get_thread_posts,
67 67 name='get_thread'),
68 68 url(r'^api/add_post/(?P<opening_post_id>\w+)/$', api.api_add_post,
69 69 name='add_post'),
70 70 url(r'^api/notifications/(?P<username>\w+)/$', api.api_get_notifications,
71 71 name='api_notifications'),
72 72
73 # Sync protocol API
74 url(r'^api/sync/pull/$', api.sync_pull, name='api_sync_pull'),
75 # TODO 'get' request
76
73 77 # Search
74 78 url(r'^search/$', BoardSearchView.as_view(), name='search'),
75 79
76 80 # Notifications
77 81 url(r'^notifications/(?P<username>\w+)$', NotificationView.as_view(), name='notifications'),
78 82
79 83 # Post preview
80 84 url(r'^preview/$', PostPreviewView.as_view(), name='preview'),
81 85
82 86 url(r'^post_xml/(?P<post_id>\d+)$', get_post_sync_data,
83 87 name='post_sync_data'),
84 88
85 89 )
@@ -1,231 +1,246 b''
1 1 import json
2 2 import logging
3 3
4 4 from django.db import transaction
5 5 from django.http import HttpResponse
6 6 from django.shortcuts import get_object_or_404
7 7 from django.core import serializers
8 8
9 9 from boards.forms import PostForm, PlainErrorList
10 10 from boards.models import Post, Thread, Tag
11 11 from boards.utils import datetime_to_epoch
12 12 from boards.views.thread import ThreadView
13 13 from boards.models.user import Notification
14 14
15 15
16 16 __author__ = 'neko259'
17 17
18 18 PARAMETER_TRUNCATED = 'truncated'
19 19 PARAMETER_TAG = 'tag'
20 20 PARAMETER_OFFSET = 'offset'
21 21 PARAMETER_DIFF_TYPE = 'type'
22 22 PARAMETER_POST = 'post'
23 23 PARAMETER_UPDATED = 'updated'
24 24 PARAMETER_LAST_UPDATE = 'last_update'
25 25 PARAMETER_THREAD = 'thread'
26 26 PARAMETER_UIDS = 'uids'
27 27
28 28 DIFF_TYPE_HTML = 'html'
29 29 DIFF_TYPE_JSON = 'json'
30 30
31 31 STATUS_OK = 'ok'
32 32 STATUS_ERROR = 'error'
33 33
34 34 logger = logging.getLogger(__name__)
35 35
36 36
37 37 @transaction.atomic
38 38 def api_get_threaddiff(request):
39 39 """
40 40 Gets posts that were changed or added since time
41 41 """
42 42
43 43 thread_id = request.GET.get(PARAMETER_THREAD)
44 44 uids_str = request.POST.get(PARAMETER_UIDS).strip()
45 45 uids = uids_str.split(' ')
46 46
47 47 thread = get_object_or_404(Post, id=thread_id).get_thread()
48 48
49 49 json_data = {
50 50 PARAMETER_UPDATED: [],
51 51 PARAMETER_LAST_UPDATE: None, # TODO Maybe this can be removed already?
52 52 }
53 53 posts = Post.objects.filter(threads__in=[thread]).exclude(uid__in=uids)
54 54
55 55 diff_type = request.GET.get(PARAMETER_DIFF_TYPE, DIFF_TYPE_HTML)
56 56
57 57 for post in posts:
58 58 json_data[PARAMETER_UPDATED].append(get_post_data(post.id, diff_type, request))
59 59 json_data[PARAMETER_LAST_UPDATE] = str(thread.last_edit_time)
60 60
61 61 return HttpResponse(content=json.dumps(json_data))
62 62
63 63
64 64 def api_add_post(request, opening_post_id):
65 65 """
66 66 Adds a post and return the JSON response for it
67 67 """
68 68
69 69 opening_post = get_object_or_404(Post, id=opening_post_id)
70 70
71 71 logger.info('Adding post via api...')
72 72
73 73 status = STATUS_OK
74 74 errors = []
75 75
76 76 if request.method == 'POST':
77 77 form = PostForm(request.POST, request.FILES, error_class=PlainErrorList)
78 78 form.session = request.session
79 79
80 80 if form.need_to_ban:
81 81 # Ban user because he is suspected to be a bot
82 82 # _ban_current_user(request)
83 83 status = STATUS_ERROR
84 84 if form.is_valid():
85 85 post = ThreadView().new_post(request, form, opening_post,
86 86 html_response=False)
87 87 if not post:
88 88 status = STATUS_ERROR
89 89 else:
90 90 logger.info('Added post #%d via api.' % post.id)
91 91 else:
92 92 status = STATUS_ERROR
93 93 errors = form.as_json_errors()
94 94
95 95 response = {
96 96 'status': status,
97 97 'errors': errors,
98 98 }
99 99
100 100 return HttpResponse(content=json.dumps(response))
101 101
102 102
103 103 def get_post(request, post_id):
104 104 """
105 105 Gets the html of a post. Used for popups. Post can be truncated if used
106 106 in threads list with 'truncated' get parameter.
107 107 """
108 108
109 109 post = get_object_or_404(Post, id=post_id)
110 110 truncated = PARAMETER_TRUNCATED in request.GET
111 111
112 112 return HttpResponse(content=post.get_view(truncated=truncated))
113 113
114 114
115 115 def api_get_threads(request, count):
116 116 """
117 117 Gets the JSON thread opening posts list.
118 118 Parameters that can be used for filtering:
119 119 tag, offset (from which thread to get results)
120 120 """
121 121
122 122 if PARAMETER_TAG in request.GET:
123 123 tag_name = request.GET[PARAMETER_TAG]
124 124 if tag_name is not None:
125 125 tag = get_object_or_404(Tag, name=tag_name)
126 126 threads = tag.get_threads().filter(archived=False)
127 127 else:
128 128 threads = Thread.objects.filter(archived=False)
129 129
130 130 if PARAMETER_OFFSET in request.GET:
131 131 offset = request.GET[PARAMETER_OFFSET]
132 132 offset = int(offset) if offset is not None else 0
133 133 else:
134 134 offset = 0
135 135
136 136 threads = threads.order_by('-bump_time')
137 137 threads = threads[offset:offset + int(count)]
138 138
139 139 opening_posts = []
140 140 for thread in threads:
141 141 opening_post = thread.get_opening_post()
142 142
143 143 # TODO Add tags, replies and images count
144 144 post_data = get_post_data(opening_post.id, include_last_update=True)
145 145 post_data['bumpable'] = thread.can_bump()
146 146 post_data['archived'] = thread.archived
147 147
148 148 opening_posts.append(post_data)
149 149
150 150 return HttpResponse(content=json.dumps(opening_posts))
151 151
152 152
153 153 # TODO Test this
154 154 def api_get_tags(request):
155 155 """
156 156 Gets all tags or user tags.
157 157 """
158 158
159 159 # TODO Get favorite tags for the given user ID
160 160
161 161 tags = Tag.objects.get_not_empty_tags()
162 162
163 163 term = request.GET.get('term')
164 164 if term is not None:
165 165 tags = tags.filter(name__contains=term)
166 166
167 167 tag_names = [tag.name for tag in tags]
168 168
169 169 return HttpResponse(content=json.dumps(tag_names))
170 170
171 171
172 172 # TODO The result can be cached by the thread last update time
173 173 # TODO Test this
174 174 def api_get_thread_posts(request, opening_post_id):
175 175 """
176 176 Gets the JSON array of thread posts
177 177 """
178 178
179 179 opening_post = get_object_or_404(Post, id=opening_post_id)
180 180 thread = opening_post.get_thread()
181 181 posts = thread.get_replies()
182 182
183 183 json_data = {
184 184 'posts': [],
185 185 'last_update': None,
186 186 }
187 187 json_post_list = []
188 188
189 189 for post in posts:
190 190 json_post_list.append(get_post_data(post.id))
191 191 json_data['last_update'] = datetime_to_epoch(thread.last_edit_time)
192 192 json_data['posts'] = json_post_list
193 193
194 194 return HttpResponse(content=json.dumps(json_data))
195 195
196 196
197 197 def api_get_notifications(request, username):
198 198 last_notification_id_str = request.GET.get('last', None)
199 199 last_id = int(last_notification_id_str) if last_notification_id_str is not None else None
200 200
201 201 posts = Notification.objects.get_notification_posts(username=username,
202 202 last=last_id)
203 203
204 204 json_post_list = []
205 205 for post in posts:
206 206 json_post_list.append(get_post_data(post.id))
207 207 return HttpResponse(content=json.dumps(json_post_list))
208 208
209 209
210 210 def api_get_post(request, post_id):
211 211 """
212 212 Gets the JSON of a post. This can be
213 213 used as and API for external clients.
214 214 """
215 215
216 216 post = get_object_or_404(Post, id=post_id)
217 217
218 218 json = serializers.serialize("json", [post], fields=(
219 219 "pub_time", "_text_rendered", "title", "text", "image",
220 220 "image_width", "image_height", "replies", "tags"
221 221 ))
222 222
223 223 return HttpResponse(content=json)
224 224
225 225
226 226 # TODO Remove this method and use post method directly
227 227 def get_post_data(post_id, format_type=DIFF_TYPE_JSON, request=None,
228 228 include_last_update=False):
229 229 post = get_object_or_404(Post, id=post_id)
230 230 return post.get_post_data(format_type=format_type, request=request,
231 231 include_last_update=include_last_update)
232
233
234 # TODO Make a separate module for sync API methods
235 def sync_pull(request):
236 """
237 Return 'get' request response for all posts.
238 """
239 request_xml = request.get('xml')
240 if request_xml is None:
241 posts = Post.objects.all()
242 else:
243 pass # TODO Parse the XML and get filters from it
244
245 xml = Post.objects.generate_response_get(posts)
246 return HttpResponse(content=xml)
General Comments 0
You need to be logged in to leave comments. Login now