Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 502 lines (418 sloc) 17.416 kB
04bbedb @alexkay Copyright header
authored
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright © 2009-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
a72c4ca @alexkay Email activation
authored
18 import random
c5912e9 @alexkay daemon: Send email notifications
authored
19 from smtplib import SMTPException
fac2b61 @alexkay api: POST user
authored
20 import string
7c2341c @alexkay Add UserArtists
authored
21 from time import sleep
a72c4ca @alexkay Email activation
authored
22
b14d6ea @alexkay Sign up / sign in /sign out
authored
23 from django.contrib.auth.models import User
a72c4ca @alexkay Email activation
authored
24 from django.core.mail import EmailMultiAlternatives
cbc53df @alexkay Delete account
authored
25 from django.db import connection, IntegrityError, models, transaction
6bbd228 @alexkay PRAGMA foreign_keys=1
authored
26 from django.db.backends.signals import connection_created
778944b @alexkay Initialise and sort by is_starred
authored
27 from django.db.models import Count, Q
47c91b7 @alexkay Add UserProfile
authored
28 from django.db.models.signals import post_save
6bbd228 @alexkay PRAGMA foreign_keys=1
authored
29 from django.dispatch import receiver
a72c4ca @alexkay Email activation
authored
30 from django.template.loader import render_to_string
6bbd228 @alexkay PRAGMA foreign_keys=1
authored
31
7c2341c @alexkay Add UserArtists
authored
32 import app.musicbrainz as mb
994a74d @alexkay Add the atom feed for new releases
authored
33 from app.tools import date_to_iso8601, date_to_str, str_to_date
7c2341c @alexkay Add UserArtists
authored
34
97bdbd5 @alexkay Star release groups
authored
35
13e32ee @alexkay Add Artist and ReleaseGroup models
authored
36 class Artist(models.Model):
c0ebc69 @alexkay Add an empty app
authored
37
13e32ee @alexkay Add Artist and ReleaseGroup models
authored
38 mbid = models.CharField(max_length=36, unique=True)
39 name = models.CharField(max_length=512)
40 sort_name = models.CharField(max_length=512)
41 disambiguation = models.CharField(max_length=512)
be118e8 @alexkay Make UserArtist a ManyToManyField
authored
42 users = models.ManyToManyField(User, through='UserArtist')
a72c4ca @alexkay Email activation
authored
43
449d6cc @alexkay Blacklist special-purpose artists
authored
44 blacklisted = [
45 '89ad4ac3-39f7-470e-963a-56509c546377', # Various Artists
aa0f948 @alexkay Block more special-purpose artists
authored
46 'fe5b7087-438f-4e7e-afaf-6d93c8c888b2', # Various Artists
3dd4347 @alexkay Blacklist another Various Artists alias
authored
47 '0677ef60-6be5-4e36-9d1e-8bb2bf85b981', # Various Artists
9e4bf7b @alexkay Blacklist another VA alias
authored
48 'b7c7dfd9-d735-4733-9b10-f060ac75bd6a', # Various Artists
449d6cc @alexkay Blacklist special-purpose artists
authored
49 'f731ccc4-e22a-43af-a747-64213329e088', # [anonymous]
50 '33cf029c-63b0-41a0-9855-be2a3665fb3b', # [data]
51 '314e1c25-dde7-4e4d-b2f4-0a7b9f7c56dc', # [dialogue]
52 'eec63d3c-3b81-4ad4-b1e4-7c147d4d2b61', # [no artist]
53 '9be7f096-97ec-4615-8957-8d40b5dcbc41', # [traditional]
54 '125ec42a-7229-4250-afc5-e057484327fe', # [unknown]
aa0f948 @alexkay Block more special-purpose artists
authored
55 '203b6058-2401-4bf0-89e3-8dc3d37c3f12', # [unknown]
56 '5e760f5a-ea55-4b53-a18f-021c0d9779a6', # [unknown]
49aa02f @alexkay Block even more special-purpose artists
authored
57 '1d8bc797-ec8a-40d2-8d80-b1346b56a65f', # [unknown]
58 '7734d67f-44d9-4ba2-91e3-9b067263210e', # [unknown]
e056496 @alexkay Black list [soundtrack]
authored
59 'd6bd72bc-b1e2-4525-92aa-0f853cbb41bf', # [soundtrack]
449d6cc @alexkay Blacklist special-purpose artists
authored
60 ]
61 class Blacklisted(Exception): pass
87c4ecb @alexkay Handle unknown artists
authored
62 class Unknown(Exception): pass
449d6cc @alexkay Blacklist special-purpose artists
authored
63
57ddf35 @alexkay Show added artists
authored
64 @classmethod
4c642cc @alexkay Show artist's release groups
authored
65 def get_by_mbid(cls, mbid):
7c2341c @alexkay Add UserArtists
authored
66 """ Fetches the artist and releases from MB if not in the database. """
449d6cc @alexkay Blacklist special-purpose artists
authored
67 if mbid in cls.blacklisted:
68 raise cls.Blacklisted()
69
4c642cc @alexkay Show artist's release groups
authored
70 try:
71 return cls.objects.get(mbid=mbid)
72 except cls.DoesNotExist:
7c2341c @alexkay Add UserArtists
authored
73 pass
74
75 artist_data = mb.get_artist(mbid)
87c4ecb @alexkay Handle unknown artists
authored
76 if artist_data is None:
4c642cc @alexkay Show artist's release groups
authored
77 return None
87c4ecb @alexkay Handle unknown artists
authored
78 if not artist_data:
79 raise cls.Unknown
4c642cc @alexkay Show artist's release groups
authored
80
7c2341c @alexkay Add UserArtists
authored
81 artist = Artist(
82 mbid=mbid, name=artist_data['name'], sort_name=artist_data['sort-name'],
10a8992 @alexkay daemon: Update artist info
authored
83 disambiguation=artist_data.get('disambiguation', ''))
2401aac @alexkay Fix occasional integrity errors
authored
84 try:
85 artist.save()
86 except IntegrityError:
87 # The artist was added while we were querying MB.
88 return cls.objects.get(mbid=mbid)
7c2341c @alexkay Add UserArtists
authored
89
90 # Add a few release groups immediately.
2401aac @alexkay Fix occasional integrity errors
authored
91 # Sleep 1s to comply with the MB web service.
92 sleep(1)
529eca9 @alexkay Add all release groups for new artists
authored
93 LIMIT = 100
94 release_groups = mb.get_release_groups(mbid, limit=LIMIT, offset=0)
7c2341c @alexkay Add UserArtists
authored
95 if release_groups:
96 with transaction.commit_on_success():
97 for rg_data in release_groups:
e14b44e @alexkay daemon: Implement ADD_ARTIST and ADD_RELEASE_GROUP jobs
authored
98 # Ignoring releases without a release date or a type.
99 if rg_data.get('first-release-date') and rg_data.get('type'):
7c2341c @alexkay Add UserArtists
authored
100 release_group = ReleaseGroup(
101 artist=artist,
102 mbid=rg_data['id'],
103 name=rg_data['title'],
104 type=rg_data['type'],
c05a3b0 @alexkay Calendar
authored
105 date=str_to_date(rg_data['first-release-date']),
7c2341c @alexkay Add UserArtists
authored
106 is_deleted=False)
9af209c @alexkay Multiple artists per release group
authored
107 release_group.save()
529eca9 @alexkay Add all release groups for new artists
authored
108
109 if release_groups is None or len(release_groups) == LIMIT:
110 # Add the remaining release groups
111 Job.add_release_groups(artist)
112
7c2341c @alexkay Add UserArtists
authored
113 return artist
114
4c642cc @alexkay Show artist's release groups
authored
115 @classmethod
57ddf35 @alexkay Show added artists
authored
116 def get_by_user(cls, user):
117 # TODO: paging
be118e8 @alexkay Make UserArtist a ManyToManyField
authored
118 return cls.objects.filter(users=user).order_by('sort_name')[:1000]
57ddf35 @alexkay Show added artists
authored
119
97bdbd5 @alexkay Star release groups
authored
120
3b047a0 @alexkay Job model
authored
121 class Job(models.Model):
122
b1876ee @alexkay Add multiple artists
authored
123 ADD_ARTIST = 1
529eca9 @alexkay Add all release groups for new artists
authored
124 ADD_RELEASE_GROUPS = 2
f41cab4 @alexkay Show empty album cover
authored
125 GET_COVER = 3
126 IMPORT_LASTFM = 4
3b047a0 @alexkay Job model
authored
127
529eca9 @alexkay Add all release groups for new artists
authored
128 user = models.ForeignKey(User, null=True)
3b047a0 @alexkay Job model
authored
129 type = models.IntegerField()
130 data = models.TextField()
131
b1876ee @alexkay Add multiple artists
authored
132 @classmethod
133 def add_artists(cls, user, names):
134 with transaction.commit_on_success():
135 for name in names:
136 cls(user=user, type=cls.ADD_ARTIST, data=name).save()
137
529eca9 @alexkay Add all release groups for new artists
authored
138 @classmethod
139 def add_release_groups(cls, artist):
140 cls(user=None, type=cls.ADD_RELEASE_GROUPS, data=artist.mbid).save()
b1876ee @alexkay Add multiple artists
authored
141
f41cab4 @alexkay Show empty album cover
authored
142 @classmethod
143 def get_cover(cls, mbid):
144 cls(user=None, type=cls.GET_COVER, data=mbid).save()
145
2db316b @alexkay Import artists from Last.fm
authored
146 @classmethod
6b5a789 @alexkay Allow to specify the period when importing from Last.fm
authored
147 def import_lastfm(cls, user, username, count, period):
148 data = str(count) + ',' + period + ',' + username
149 cls(user=user, type=cls.IMPORT_LASTFM, data=data).save()
2db316b @alexkay Import artists from Last.fm
authored
150
151 @classmethod
152 def importing_artists(cls, user):
153 """Returns a comma-separated list of all artists yet to be imported."""
154 q = cls.objects.filter(user=user)
155 q = q.filter(type=cls.ADD_ARTIST)
156 return [r.data for r in q]
157
158 @classmethod
159 def has_import_lastfm(cls, user):
160 return cls.objects.filter(user=user).filter(type=cls.IMPORT_LASTFM).exists()
161
f41cab4 @alexkay Show empty album cover
authored
162
861ddc0 @alexkay Add the Notification model
authored
163 class Notification(models.Model):
164
165 class Meta:
166 db_table = 'app_notification'
167 unique_together = ('user', 'release_group')
168
169 user = models.ForeignKey(User)
170 release_group = models.ForeignKey('ReleaseGroup')
171
14bc2b2 @alexkay daemon: Add Notifications
authored
172
13e32ee @alexkay Add Artist and ReleaseGroup models
authored
173 class ReleaseGroup(models.Model):
9af209c @alexkay Multiple artists per release group
authored
174 """De-normalised release groups
175
176 A release group can have different artists. Instead of adding a
177 many-to-many relationship between them, keep everything in one
178 table and group by mbid as needed.
179
180 """
181 class Meta:
182 unique_together = ('artist', 'mbid')
13e32ee @alexkay Add Artist and ReleaseGroup models
authored
183
184 artist = models.ForeignKey(Artist)
9af209c @alexkay Multiple artists per release group
authored
185 mbid = models.CharField(max_length=36)
13e32ee @alexkay Add Artist and ReleaseGroup models
authored
186 name = models.CharField(max_length=512)
187 type = models.CharField(max_length=16)
188 date = models.IntegerField() # 20080101 OR 20080100 OR 20080000
189 is_deleted = models.BooleanField()
861ddc0 @alexkay Add the Notification model
authored
190
191 users_who_starred = models.ManyToManyField(
192 User, through='Star', related_name='starred_release_groups')
193 users_to_notify = models.ManyToManyField(
194 User, through='Notification', related_name='new_release_groups')
47c91b7 @alexkay Add UserProfile
authored
195
4c642cc @alexkay Show artist's release groups
authored
196 def date_str(self):
c05a3b0 @alexkay Calendar
authored
197 return date_to_str(self.date)
4c642cc @alexkay Show artist's release groups
authored
198
994a74d @alexkay Add the atom feed for new releases
authored
199 def date_iso8601(self):
200 return date_to_iso8601(self.date)
201
4c642cc @alexkay Show artist's release groups
authored
202 @classmethod
f026ba0 @alexkay Fixes
authored
203 def get(cls, artist=None, user=None, limit=0, offset=0, feed=False):
994a74d @alexkay Add the atom feed for new releases
authored
204 if not artist and not user:
205 assert 'Both artist and user are None'
206 return None
cab32cf @alexkay Fix ReleaseGroup.get()
authored
207
208 # Unfortunately I don't see how to use ORM for these queries.
209 sql = """
210 SELECT
211 "app_releasegroup"."id",
212 "app_releasegroup"."artist_id",
213 "app_releasegroup"."mbid",
214 "app_releasegroup"."name",
215 "app_releasegroup"."type",
216 "app_releasegroup"."date",
217 "app_releasegroup"."is_deleted",
218 "app_artist"."mbid" AS "artist_mbid",
ca5d2b1 @alexkay api: GET releases
authored
219 "app_artist"."name" AS "artist_name",
220 "app_artist"."sort_name" AS "artist_sort_name",
221 "app_artist"."disambiguation" AS "artist_disambiguation"
cab32cf @alexkay Fix ReleaseGroup.get()
authored
222 {select}
223 FROM "app_releasegroup"
224 JOIN "app_artist" ON "app_artist"."id" = "app_releasegroup"."artist_id"
225 {join}
226 WHERE "app_releasegroup"."is_deleted" = 0
227 {where}
228 ORDER BY {order}
30f54c9 @alexkay Optimise ReleaseGroup.get()
authored
229 LIMIT %s OFFSET %s
cab32cf @alexkay Fix ReleaseGroup.get()
authored
230 """
231 select = join = where = ''
232 order = '"app_releasegroup"."date" DESC'
233 params = []
994a74d @alexkay Add the atom feed for new releases
authored
234 if artist:
cab32cf @alexkay Fix ReleaseGroup.get()
authored
235 where += '\nAND "app_releasegroup"."artist_id" = %s'
236 params.append(artist.id)
994a74d @alexkay Add the atom feed for new releases
authored
237 if user:
cab32cf @alexkay Fix ReleaseGroup.get()
authored
238 # Stars.
239 select += ',\n"app_star"."id" as "is_starred"'
240 join += '\nJOIN "app_userartist" ON "app_userartist"."artist_id" = "app_artist"."id"'
241 join += '\nLEFT JOIN "app_star" ON "app_star"."user_id" = "app_userartist"."user_id" AND "app_star"."release_group_id" = "app_releasegroup"."id"'
242 where += '\nAND "app_userartist"."user_id" = %s'
243 params.append(user.id)
64cb5b2 @alexkay Fix stars sort order
authored
244 order = '"app_star"."user_id" DESC, ' + order
cab32cf @alexkay Fix ReleaseGroup.get()
authored
245 # Release types.
ca9679d @alexkay Don't show duplicate releases for imported users
authored
246 profile = user.get_profile()
cab32cf @alexkay Fix ReleaseGroup.get()
authored
247 types = profile.get_types()
248 ss = ','.join('%s' for i in xrange(len(types)))
249 where += '\nAND "app_releasegroup"."type" IN (' + ss + ')'
250 params.extend(types)
251
f026ba0 @alexkay Fixes
authored
252 if feed and profile.legacy_id:
ca9679d @alexkay Don't show duplicate releases for imported users
authored
253 # Don't include release groups added during the import
cab32cf @alexkay Fix ReleaseGroup.get()
authored
254 # TODO: Feel free to remove this check some time in 2013.
255 where += '\nAND "app_releasegroup"."id" > 261202'
256
257 sql = sql.format(select=select, join=join, where=where, order=order)
30f54c9 @alexkay Optimise ReleaseGroup.get()
authored
258 params.extend([limit, offset])
259 return cls.objects.raw(sql, params)
4c642cc @alexkay Show artist's release groups
authored
260
261 @classmethod
c05a3b0 @alexkay Calendar
authored
262 def get_calendar(cls, date, limit, offset):
263 """Returns the list of release groups for the date."""
264 q = cls.objects.filter(date__lte=date)
cab32cf @alexkay Fix ReleaseGroup.get()
authored
265 q = q.select_related('artist')
266 # Calendar uses the same template as releases, adapt to conform.
267 q = q.extra(select={
268 'artist_mbid': '"app_artist"."mbid"',
ca5d2b1 @alexkay api: GET releases
authored
269 'artist_name': '"app_artist"."name"',
270 'artist_sort_name': '"app_artist"."sort_name"',
271 'artist_disambiguation': '"app_artist"."disambiguation"',
272 })
c05a3b0 @alexkay Calendar
authored
273 q = q.filter(is_deleted=False)
496db6a @alexkay Optimise ReleaseGroup.get_calendar()
authored
274 q = q.order_by('-date')
c05a3b0 @alexkay Calendar
authored
275 return q[offset:offset+limit]
4c642cc @alexkay Show artist's release groups
authored
276
97bdbd5 @alexkay Star release groups
authored
277
278 class Star(models.Model):
279
280 class Meta:
778944b @alexkay Initialise and sort by is_starred
authored
281 db_table = 'app_star'
97bdbd5 @alexkay Star release groups
authored
282 unique_together = ('user', 'release_group')
283
284 user = models.ForeignKey(User)
285 release_group = models.ForeignKey(ReleaseGroup)
286
287 @classmethod
4930247 @alexkay Fix starring
authored
288 def set(cls, user, id, value):
97bdbd5 @alexkay Star release groups
authored
289 try:
4930247 @alexkay Fix starring
authored
290 release_group = ReleaseGroup.objects.get(id=id)
97bdbd5 @alexkay Star release groups
authored
291 except ReleaseGroup.DoesNotExist:
292 return
293 if value:
294 cls.objects.get_or_create(user=user, release_group=release_group)
295 else:
296 cls.objects.filter(user=user, release_group=release_group).delete()
297
be118e8 @alexkay Make UserArtist a ManyToManyField
authored
298
0915072 @alexkay Add UserArtist model
authored
299 class UserArtist(models.Model):
300
301 class Meta:
302 unique_together = ('user', 'artist')
303
304 user = models.ForeignKey(User)
305 artist = models.ForeignKey(Artist)
7c2341c @alexkay Add UserArtists
authored
306 date = models.DateTimeField(auto_now_add=True)
307
308 @classmethod
3ae58f8 @alexkay Filter releases by user when authenticated
authored
309 def get(cls, user, artist):
310 try:
311 return cls.objects.get(user=user, artist=artist)
312 except cls.DoesNotExist:
313 return None
314
315 @classmethod
7c2341c @alexkay Add UserArtists
authored
316 def add(cls, user, artist):
317 user_artist = cls(user=user, artist=artist)
318 try:
319 user_artist.save()
320 except IntegrityError:
321 pass
0915072 @alexkay Add UserArtist model
authored
322
3ec7885 @alexkay Remove UserArtists
authored
323 @classmethod
324 def remove(cls, user, mbids):
325 with transaction.commit_on_success():
326 for mbid in mbids:
4930247 @alexkay Fix starring
authored
327 q = cls.objects.filter(user=user)
3ec7885 @alexkay Remove UserArtists
authored
328 q = q.filter(artist__mbid=mbid)
329 q.delete()
330
97bdbd5 @alexkay Star release groups
authored
331
47c91b7 @alexkay Add UserProfile
authored
332 class UserProfile(models.Model):
333
a72c4ca @alexkay Email activation
authored
334 code_length = 16
335
47c91b7 @alexkay Add UserProfile
authored
336 user = models.OneToOneField(User)
337
338 notify = models.BooleanField(default=True)
339 notify_album = models.BooleanField(default=True)
340 notify_single = models.BooleanField(default=True)
341 notify_ep = models.BooleanField(default=True)
342 notify_live = models.BooleanField(default=True)
343 notify_compilation = models.BooleanField(default=True)
344 notify_remix = models.BooleanField(default=True)
345 notify_other = models.BooleanField(default=True)
346 email_activated = models.BooleanField(default=False)
a72c4ca @alexkay Email activation
authored
347 activation_code = models.CharField(max_length=code_length)
348 reset_code = models.CharField(max_length=code_length)
b296691 @alexkay Add UserProfile.legacy_id, tidy
authored
349 legacy_id = models.IntegerField(null=True)
47c91b7 @alexkay Add UserProfile
authored
350
34a19ef @alexkay Filter user releases by type
authored
351 def get_types(self):
352 """Return the list of release types the user wants to follow."""
353 types = []
354 if self.notify_album: types.append('Album')
355 if self.notify_single: types.append('Single')
356 if self.notify_ep: types.append('EP')
357 if self.notify_live: types.append('Live')
358 if self.notify_compilation: types.append('Compilation')
359 if self.notify_remix: types.append('Remix')
360 if self.notify_other:
361 types.extend(['Soundtrack', 'Spokenword', 'Interview', 'Audiobook', 'Other'])
362 return types
363
a72c4ca @alexkay Email activation
authored
364 def generate_code(self):
365 code_chars = '23456789abcdefghijkmnpqrstuvwxyz'
366 return ''.join(random.choice(code_chars) for i in xrange(UserProfile.code_length))
367
cbc53df @alexkay Delete account
authored
368 def purge(self):
369 user = self.user
370 with transaction.commit_on_success():
371 Job.objects.filter(user=user).delete()
372 Notification.objects.filter(user=user).delete()
373 Star.objects.filter(user=user).delete()
374 UserArtist.objects.filter(user=user).delete()
375 UserSearch.objects.filter(user=user).delete()
376 self.delete()
377 # Cannot call user.delete() because it references deprecated auth_message.
378 cursor = connection.cursor()
379 cursor.execute('DELETE FROM auth_user WHERE id=%s', [user.id])
380
a72c4ca @alexkay Email activation
authored
381 def send_email(self, subject, text_template, html_template, **kwds):
382 text = render_to_string(text_template, kwds)
3b4ccca @alexkay Tidy
authored
383 msg = EmailMultiAlternatives(
384 subject,
385 text,
5e0df8e @alexkay Change the sender to muspy.com
authored
386 'muspy.com <info@muspy.com>',
3b4ccca @alexkay Tidy
authored
387 [self.user.email])
a72c4ca @alexkay Email activation
authored
388 if html_template:
389 html = render_to_string(html_template, kwds)
c5912e9 @alexkay daemon: Send email notifications
authored
390 msg.attach_alternative(html, "text/html")
391 try:
392 msg.send()
393 except SMTPException:
394 return False
395 return True
a72c4ca @alexkay Email activation
authored
396
397 def send_activation_email(self):
398 code = self.generate_code()
399 self.activation_code = code
400 self.save()
c5912e9 @alexkay daemon: Send email notifications
authored
401 self.send_email(
402 subject='Email Activation',
403 text_template='email/activate.txt',
404 html_template=None,
405 code=code)
a72c4ca @alexkay Email activation
authored
406
407 def send_reset_email(self):
408 code = self.generate_code()
409 self.reset_code = code
410 self.save()
c5912e9 @alexkay daemon: Send email notifications
authored
411 self.send_email(
412 subject='Password Reset Confirmation',
413 text_template='email/reset.txt',
414 html_template=None,
415 code=code)
a72c4ca @alexkay Email activation
authored
416
62a01e3 @alexkay One-click unsubscribe
authored
417 def unsubscribe(self):
418 self.notify = False
419 self.save()
420
a72c4ca @alexkay Email activation
authored
421 @classmethod
422 def activate(cls, code):
b296691 @alexkay Add UserProfile.legacy_id, tidy
authored
423 profiles = UserProfile.objects.filter(activation_code=code)
a72c4ca @alexkay Email activation
authored
424 if not profiles:
425 return False
426 profile = profiles[0]
427 profile.activation_code = ''
428 profile.email_activated = True
429 profile.save()
430 return True
431
432 @classmethod
433 def reset(cls, code):
b296691 @alexkay Add UserProfile.legacy_id, tidy
authored
434 profiles = UserProfile.objects.filter(reset_code=code)
e4e6368 @alexkay Password reset fixes
authored
435 if not profiles:
a72c4ca @alexkay Email activation
authored
436 return None, None
e4e6368 @alexkay Password reset fixes
authored
437 profile = profiles[0]
438 password = User.objects.make_random_password(length=16)
a72c4ca @alexkay Email activation
authored
439 profile.reset_code = ''
440 profile.user.set_password(password)
6521508 @alexkay Save the user and the profile within a transaction
authored
441 with transaction.commit_on_success():
442 profile.user.save()
443 profile.save()
a72c4ca @alexkay Email activation
authored
444 return profile.user.email, password
445
446 @classmethod
b296691 @alexkay Add UserProfile.legacy_id, tidy
authored
447 def get_by_email(cls, email):
448 users = User.objects.filter(email=email.lower())
449 return users[0].get_profile() if users else None
450
451 @classmethod
452 def get_by_legacy_id(cls, legacy_id):
453 profiles = cls.objects.filter(legacy_id=legacy_id)
454 return profiles[0] if profiles else None
455
456 @classmethod
457 def get_by_username(cls, username):
458 users = User.objects.filter(username=username)
a72c4ca @alexkay Email activation
authored
459 return users[0].get_profile() if users else None
13e32ee @alexkay Add Artist and ReleaseGroup models
authored
460
fac2b61 @alexkay api: POST user
authored
461 @classmethod
462 def create_user(cls, email, password):
463 chars = string.ascii_lowercase + string.digits
464 username = ''.join(random.choice(chars) for i in xrange(30))
465 return User.objects.create_user(username, email, password)
466
97bdbd5 @alexkay Star release groups
authored
467
b1876ee @alexkay Add multiple artists
authored
468 class UserSearch(models.Model):
469
470 user = models.ForeignKey(User)
471 search = models.CharField(max_length=512)
472
473 @classmethod
474 def get(cls, user):
475 return cls.objects.filter(user=user)
476
2f500a9 @alexkay Remove user searches
authored
477 @classmethod
478 def remove(cls, user, searches):
479 with transaction.commit_on_success():
480 for search in searches:
481 cls.objects.filter(user=user, search=search).delete()
482
b1876ee @alexkay Add multiple artists
authored
483
13e32ee @alexkay Add Artist and ReleaseGroup models
authored
484 # Activate foreign keys for sqlite.
485 @receiver(connection_created)
486 def activate_foreign_keys(sender, connection, **kwargs):
487 if connection.vendor == 'sqlite':
488 cursor = connection.cursor()
489 cursor.execute('PRAGMA foreign_keys=1;')
490
97bdbd5 @alexkay Star release groups
authored
491
13e32ee @alexkay Add Artist and ReleaseGroup models
authored
492 # Create a profile for each user.
493 @receiver(post_save, sender=User)
494 def user_post_save(sender, instance, created, **kwargs):
495 if created:
496 p = UserProfile()
497 p.user = instance
498 p.save()
499
97bdbd5 @alexkay Star release groups
authored
500
13e32ee @alexkay Add Artist and ReleaseGroup models
authored
501 User.__unicode__ = lambda x: x.email
Something went wrong with that request. Please try again.