[Migration] refactor client syncing
[mygpo.git] / mygpo / api / advanced / sync.py
blob78f20e90fa1c44daf9e767d9f0692910c026b8d9
2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 from django.http import HttpResponseBadRequest, HttpResponseNotFound
19 from django.views.decorators.csrf import csrf_exempt
20 from django.views.decorators.cache import never_cache
22 from mygpo.decorators import allowed_methods, cors_origin
23 from mygpo.core.json import JSONDecodeError
24 from mygpo.utils import parse_request_body
25 from mygpo.api.basic_auth import require_valid_user, check_username
26 from mygpo.api.httpresponse import JsonResponse
27 from mygpo.users.models import DeviceDoesNotExist, User
28 from mygpo.users.tasks import sync_user
29 from mygpo.users.sync import get_grouped_devices
32 @csrf_exempt
33 @require_valid_user
34 @check_username
35 @never_cache
36 @allowed_methods(['GET', 'POST'])
37 @cors_origin()
38 def main(request, username):
39 """ API Endpoint for Device Synchronisation """
41 if request.method == 'GET':
42 return JsonResponse(get_sync_status(request.user))
44 else:
45 try:
46 actions = parse_request_body(request)
47 except JSONDecodeError as e:
48 return HttpResponseBadRequest(str(e))
50 synclist = actions.get('synchronize', [])
51 stopsync = actions.get('stop-synchronize', [])
53 try:
54 update_sync_status(request.user, synclist, stopsync)
55 except ValueError as e:
56 return HttpResponseBadRequest(str(e))
57 except DeviceDoesNotExist as e:
58 return HttpResponseNotFound(str(e))
60 # reload user to get current sync status
61 user = User.get(request.user._id)
62 return JsonResponse(get_sync_status(user))
66 def get_sync_status(user):
67 """ Returns the current Device Sync status """
69 sync_groups = []
70 unsynced = []
72 for group in get_grouped_devices(user):
73 uids = [device.uid for device in group.devices]
75 if group.is_synced:
76 sync_groups.append(uids)
78 else:
79 unsynced = uids
81 return {
82 'synchronized': sync_groups,
83 'not-synchronized': unsynced
88 def update_sync_status(user, synclist, stopsync):
89 """ Updates the current Device Sync status
91 Synchronisation between devices can be set up and stopped. Devices are
92 identified by their UIDs. Unknown UIDs cause errors, no new devices are
93 created. """
95 for devlist in synclist:
97 if len(devlist) <= 1:
98 raise ValueError('at least two devices are needed to sync')
100 # Setup all devices to sync with the first in the list
101 uid = devlist[0]
102 dev = user.get_device_by_uid(uid)
104 for other_uid in devlist[1:]:
105 other = user.get_device_by_uid(other_uid)
106 dev.sync_with(other)
109 for uid in stopsync:
110 dev = user.get_device_by_uid(uid)
111 try:
112 dev.stop_sync()
113 except ValueError:
114 # if all devices of a sync-group are un-synced,
115 # the last one will raise a ValueError, because it is no longer
116 # being synced -- we just ignore it
117 pass
119 user.save()
121 sync_user.delay(user)