Skip to content
Newer
Older
100644 323 lines (265 sloc) 11 KB
b273e48 @alexkay Set up API URLs
authored
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright © 2011 Alexander Kojevnikov <alexander@kojevnikov.com>
4 #
5 # muspy is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # muspy is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Affero General Public License for more details.
14 #
15 # You should have received a copy of the GNU Affero General Public License
16 # along with muspy. If not, see <http://www.gnu.org/licenses/>.
17
ca5d2b1 @alexkay api: GET releases
authored
18 from datetime import date
19
20 from django.contrib.auth.models import User
bf5a23d @alexkay api: artist and release handlers
authored
21 from django.core.exceptions import ObjectDoesNotExist
22
b273e48 @alexkay Set up API URLs
authored
23 from piston.handler import AnonymousBaseHandler, BaseHandler
4eff413 @alexkay api: DELETE artists
authored
24 from piston.resource import Resource
bf5a23d @alexkay api: artist and release handlers
authored
25 from piston.utils import rc
b273e48 @alexkay Set up API URLs
authored
26
8e94aec @alexkay api: Import from Last.fm
authored
27 from app import lastfm
b273e48 @alexkay Set up API URLs
authored
28 from app.models import *
29
30
4eff413 @alexkay api: DELETE artists
authored
31 class ApiResource(Resource):
32 """TODO: Remove after upgrading to django-piston >= 0.2.3"""
33 def __init__(self, handler, authentication=None):
34 super(ApiResource, self).__init__(handler, authentication)
35 self.csrf_exempt = getattr(self.handler, 'csrf_exempt', True)
36
37
b273e48 @alexkay Set up API URLs
authored
38 class ArtistHandler(AnonymousBaseHandler):
bf5a23d @alexkay api: artist and release handlers
authored
39 allowed_methods = ('GET',)
40
41 def read(self, request, mbid):
42 try:
43 artist = Artist.objects.get(mbid=mbid)
44 except Artist.DoesNotExist:
45 return rc.NOT_HERE
46
47 return {
48 'mbid': artist.mbid,
49 'name': artist.name,
50 'sort_name': artist.sort_name,
51 'disambiguation': artist.disambiguation,
52 }
53
4eff413 @alexkay api: DELETE artists
authored
54
e16077b @alexkay api: GET artists
authored
55 class ArtistsHandler(BaseHandler):
54545fb @alexkay api: PUT artists
authored
56 allowed_methods = ('GET', 'PUT', 'DELETE')
e16077b @alexkay api: GET artists
authored
57
4eff413 @alexkay api: DELETE artists
authored
58 def read(self, request, userid, mbid):
e16077b @alexkay api: GET artists
authored
59 if request.user.username != userid:
60 return rc.FORBIDDEN
61
62 artists = Artist.get_by_user(user=request.user)
63 return [{
64 'mbid': artist.mbid,
65 'name': artist.name,
66 'sort_name': artist.sort_name,
67 'disambiguation': artist.disambiguation,
68 } for artist in artists]
69
54545fb @alexkay api: PUT artists
authored
70 def update(self, request, userid, mbid):
71 if request.user.username != userid:
72 return rc.FORBIDDEN
73
8e94aec @alexkay api: Import from Last.fm
authored
74 if mbid:
75 try:
76 artist = Artist.get_by_mbid(mbid)
77 except (Artist.Blacklisted, Artist.Unknown):
78 return rc.BAD_REQUEST
79 if not artist:
80 return rc.NOT_FOUND
81
82 UserArtist.add(request.user, artist)
83 response = rc.ALL_OK
84 response.content = {
85 'mbid': artist.mbid,
86 'name': artist.name,
87 'sort_name': artist.sort_name,
88 'disambiguation': artist.disambiguation,
89 }
90 return response
54545fb @alexkay api: PUT artists
authored
91
8e94aec @alexkay api: Import from Last.fm
authored
92 import_from = request.POST.get('import', '')
93 username = request.POST.get('username', '')
94 count = min(500, max(0, int(request.POST.get('count', 0))))
95 period = request.POST.get('period', '')
96
97 if (import_from != 'last.fm' or not username or not count or
98 period not in ['overall', '12month', '6month', '3month', '7day']):
54545fb @alexkay api: PUT artists
authored
99 return rc.BAD_REQUEST
100
8e94aec @alexkay api: Import from Last.fm
authored
101 if Job.has_import_lastfm(request.user) or Job.importing_artists(request.user):
102 response = rc.THROTTLED
103 response.write(
104 ': The user already has a pending import. '
105 'Please wait until the import finishes before importing again.')
106 return response
107
108 if not lastfm.has_user(username):
109 response = rc.BAD_REQUEST
110 response.write(': Unknown user: %s' % username)
111 return response
112
113 Job.import_lastfm(request.user, username, count, period)
114 return rc.ALL_OK
54545fb @alexkay api: PUT artists
authored
115
4eff413 @alexkay api: DELETE artists
authored
116 def delete(self, request, userid, mbid):
117 if request.user.username != userid:
118 return rc.FORBIDDEN
119
120 if not mbid:
121 return rc.BAD_REQUEST
122
123 UserArtist.remove(user=request.user, mbids=[mbid])
124 return rc.DELETED
125
126
bf5a23d @alexkay api: artist and release handlers
authored
127 class ReleaseHandler(AnonymousBaseHandler):
128 allowed_methods = ('GET',)
129
130 def read(self, request, mbid):
47f2017 @alexkay api: Multiple artists per release
authored
131 q = ReleaseGroup.objects.select_related('artist')
132 q = q.filter(mbid=mbid)
133 q = q.filter(is_deleted=False)
134 releases = list(q)
135 if not releases:
bf5a23d @alexkay api: artist and release handlers
authored
136 return rc.NOT_HERE
b273e48 @alexkay Set up API URLs
authored
137
47f2017 @alexkay api: Multiple artists per release
authored
138 release = releases[0]
139 artists = [release.artist for release in releases]
bf5a23d @alexkay api: artist and release handlers
authored
140 return {
141 'mbid': release.mbid,
142 'name': release.name,
143 'type': release.type,
144 'date': release.date_str(),
47f2017 @alexkay api: Multiple artists per release
authored
145 'artists': [{
146 'mbid': artist.mbid,
147 'name': artist.name,
148 'sort_name': artist.sort_name,
149 'disambiguation': artist.disambiguation,
150 } for artist in artists]
bf5a23d @alexkay api: artist and release handlers
authored
151 }
ca5d2b1 @alexkay api: GET releases
authored
152
153
154 class ReleasesHandler(AnonymousBaseHandler):
155 allowed_methods = ('GET',)
156
157 def read(self, request, userid):
158 artist = user = None
159 if userid:
160 try:
161 user = User.objects.get(username=userid)
162 except User.DoesNotExist:
163 return rc.NOT_HERE
164
165 limit = min(100, max(0, int(request.GET.get('limit', 40))))
166 offset = max(0, int(request.GET.get('offset', 0)))
59bf7d6 @alexkay api: Add a 'since' parameter to /releases
authored
167 mbid = request.GET.get('mbid', '')
168 since = request.GET.get('since', '')
ca5d2b1 @alexkay api: GET releases
authored
169
170 if mbid:
171 try:
172 artist = Artist.get_by_mbid(mbid)
173 except (Artist.Blacklisted, Artist.Unknown):
174 return rc.BAD_REQUEST
175 if not artist:
176 return rc.NOT_HERE
177
59bf7d6 @alexkay api: Add a 'since' parameter to /releases
authored
178 if since:
179 try:
180 release = ReleaseGroup.objects.get(mbid=since)
181 except ReleaseGroup.DoesNotExist:
182 return rc.NOT_HERE
183 q = ReleaseGroup.objects.filter(id__gt=release.id)
184 if user:
185 q = q.filter(artist__users=user)
186 q = q.filter(is_deleted=False)
187 q = q.order_by('id')
188 q = q.select_related('artist')
189 q = q.extra(select={
190 'artist_mbid': '"app_artist"."mbid"',
191 'artist_name': '"app_artist"."name"',
192 'artist_sort_name': '"app_artist"."sort_name"',
193 'artist_disambiguation': '"app_artist"."disambiguation"',
194 })
195 releases = q[:limit]
196 elif artist or user:
ca5d2b1 @alexkay api: GET releases
authored
197 releases = ReleaseGroup.get(artist=artist, user=user, limit=limit, offset=offset)
198 else:
199 today = int(date.today().strftime('%Y%m%d'))
200 releases = ReleaseGroup.get_calendar(date=today, limit=limit, offset=offset)
201
202 return [{
203 'mbid': release.mbid,
204 'name': release.name,
205 'type': release.type,
206 'date': release.date_str(),
207 'artist': {
208 'mbid': release.artist_mbid,
209 'name': release.artist_name,
210 'sort_name': release.artist_sort_name,
211 'disambiguation': release.artist_disambiguation,
212 }
213 } for release in releases]
d371b7f @alexkay api: GET user
authored
214
215
fac2b61 @alexkay api: POST user
authored
216 class AnonymousUserHandler(AnonymousBaseHandler):
217 allowed_methods = ('POST')
218
219 def create(self, request, userid):
220 email = request.POST.get('email', '').lower().strip()
221 password = request.POST.get('password', '')
222 activate = int(request.POST.get('activate', '0'))
223
224 if not email:
225 response = rc.BAD_REQUEST
226 response.write(': empty email address')
227 return response
228
229 if not password:
230 response = rc.BAD_REQUEST
231 response.write(': empty password')
232 return response
233
234 if UserProfile.get_by_email(email):
235 response = rc.BAD_REQUEST
236 response.write(': email already in use');
237 return response
238
239 user = UserProfile.create_user(email, password)
240
241 if activate:
242 user.get_profile().send_activation_email()
243
244 return rc.CREATED
245
246
d371b7f @alexkay api: GET user
authored
247 class UserHandler(BaseHandler):
fac2b61 @alexkay api: POST user
authored
248 allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
249 anonymous = AnonymousUserHandler
d371b7f @alexkay api: GET user
authored
250
251 def read(self, request, userid):
252 if userid and request.user.username != userid:
253 return rc.BAD_REQUEST
254
255 user = request.user
256 profile = user.get_profile()
257
258 return {
259 'userid': user.username,
008527f @alexkay api: PUT user
authored
260 'email': user.email,
261 'notify': profile.notify,
262 'notify_album': profile.notify_album,
263 'notify_single': profile.notify_single,
264 'notify_ep': profile.notify_ep,
265 'notify_live': profile.notify_live,
266 'notify_compilation': profile.notify_compilation,
267 'notify_remix': profile.notify_remix,
268 'notify_other': profile.notify_other,
269 }
270
271 def update(self, request, userid):
272 if request.user.username != userid:
273 return rc.FORBIDDEN
274
275 user = request.user
276 profile = user.get_profile()
277
278 if 'email' in request.POST:
279 user.email = request.POST['email'].lower().strip()
280 profile.email_activated = False
281 if 'notify' in request.POST:
f23ebb9 @alexkay api: Fix a crash when using incorrect boolean literals
authored
282 profile.notify = request.POST['notify'] in ['1', 'true']
008527f @alexkay api: PUT user
authored
283 if 'notify_album' in request.POST:
f23ebb9 @alexkay api: Fix a crash when using incorrect boolean literals
authored
284 profile.notify_album = request.POST['notify_album'] in ['1', 'true']
008527f @alexkay api: PUT user
authored
285 if 'notify_single' in request.POST:
f23ebb9 @alexkay api: Fix a crash when using incorrect boolean literals
authored
286 profile.notify_single = request.POST['notify_single'] in ['1', 'true']
008527f @alexkay api: PUT user
authored
287 if 'notify_ep' in request.POST:
f23ebb9 @alexkay api: Fix a crash when using incorrect boolean literals
authored
288 profile.notify_ep = request.POST['notify_ep'] in ['1', 'true']
008527f @alexkay api: PUT user
authored
289 if 'notify_live' in request.POST:
f23ebb9 @alexkay api: Fix a crash when using incorrect boolean literals
authored
290 profile.notify_live = request.POST['notify_live'] in ['1', 'true']
008527f @alexkay api: PUT user
authored
291 if 'notify_compilation' in request.POST:
f23ebb9 @alexkay api: Fix a crash when using incorrect boolean literals
authored
292 profile.notify_compilation = request.POST['notify_compilation'] in ['1', 'true']
008527f @alexkay api: PUT user
authored
293 if 'notify_remix' in request.POST:
f23ebb9 @alexkay api: Fix a crash when using incorrect boolean literals
authored
294 profile.notify_remix = request.POST['notify_remix'] in ['1', 'true']
008527f @alexkay api: PUT user
authored
295 if 'notify_other' in request.POST:
f23ebb9 @alexkay api: Fix a crash when using incorrect boolean literals
authored
296 profile.notify_other = request.POST['notify_other'] in ['1', 'true']
008527f @alexkay api: PUT user
authored
297
298 with transaction.commit_on_success():
299 user.save()
300 profile.save()
301
302 response = rc.ALL_OK
303 response.content = {
304 'userid': user.username,
305 'email': user.email,
d371b7f @alexkay api: GET user
authored
306 'notify': profile.notify,
307 'notify_album': profile.notify_album,
308 'notify_single': profile.notify_single,
309 'notify_ep': profile.notify_ep,
310 'notify_live': profile.notify_live,
311 'notify_compilation': profile.notify_compilation,
312 'notify_remix': profile.notify_remix,
313 'notify_other': profile.notify_other,
314 }
008527f @alexkay api: PUT user
authored
315 return response
caf7270 @alexkay api: DELETE user
authored
316
317 def delete(self, request, userid):
318 if request.user.username != userid:
319 return rc.FORBIDDEN
320
321 request.user.get_profile().purge()
322 return rc.DELETED
Something went wrong with that request. Please try again.