Merge branch 'feature/47-django2.0' into 'develop'

Feature/47 django2.0

Closes #47

See merge request funkwhale/funkwhale!29
This commit is contained in:
Eliot Berriot 2017-12-16 15:13:52 +00:00
commit abb6b9eac4
47 changed files with 299 additions and 179 deletions

View File

@ -13,14 +13,22 @@ stages:
test_api: test_api:
stage: test stage: test
image: funkwhale/funkwhale:base image: funkwhale/funkwhale:base
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/pip-cache"
DATABASE_URL: "sqlite://"
before_script: before_script:
- python3 -m venv --copies virtualenv
- source virtualenv/bin/activate
- cd api - cd api
- pip install -r requirements/base.txt
- pip install -r requirements/local.txt
- pip install -r requirements/test.txt - pip install -r requirements/test.txt
script: script:
- pytest - pytest
variables: cache:
DATABASE_URL: "sqlite://" key: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
paths:
- "$CI_PROJECT_DIR/pip-cache"
tags: tags:
- docker - docker

View File

@ -16,6 +16,10 @@ Bugfixes:
- Player: better handling of errors when fetching the audio file (#46) - Player: better handling of errors when fetching the audio file (#46)
- Csrf: default CSRF_TRUSTED_ORIGINS to ALLOWED_HOSTS to avoid Csrf issues on admin (#49) - Csrf: default CSRF_TRUSTED_ORIGINS to ALLOWED_HOSTS to avoid Csrf issues on admin (#49)
Tech:
- Django 2 compatibility, lot of packages upgrades (#47)
0.2.4 (2017-12-14) 0.2.4 (2017-12-14)
------------------ ------------------

View File

@ -8,7 +8,9 @@ COPY ./requirements.apt /requirements.apt
RUN apt-get update -qq && grep "^[^#;]" requirements.apt | xargs apt-get install -y RUN apt-get update -qq && grep "^[^#;]" requirements.apt | xargs apt-get install -y
COPY ./requirements /requirements COPY ./requirements/base.txt /requirements
RUN pip install -r /requirements/base.txt
COPY ./requirements/production.txt /requirements
RUN pip install -r /requirements/production.txt RUN pip install -r /requirements/production.txt
COPY . /app COPY . /app

View File

@ -25,22 +25,32 @@ v1_patterns = router.urls
v1_patterns += [ v1_patterns += [
url(r'^providers/', url(r'^providers/',
include('funkwhale_api.providers.urls', namespace='providers')), include(
('funkwhale_api.providers.urls', 'providers'),
namespace='providers')),
url(r'^favorites/', url(r'^favorites/',
include('funkwhale_api.favorites.urls', namespace='favorites')), include(
('funkwhale_api.favorites.urls', 'favorites'),
namespace='favorites')),
url(r'^search$', url(r'^search$',
views.Search.as_view(), name='search'), views.Search.as_view(), name='search'),
url(r'^radios/', url(r'^radios/',
include('funkwhale_api.radios.urls', namespace='radios')), include(
('funkwhale_api.radios.urls', 'radios'),
namespace='radios')),
url(r'^history/', url(r'^history/',
include('funkwhale_api.history.urls', namespace='history')), include(
('funkwhale_api.history.urls', 'history'),
namespace='history')),
url(r'^users/', url(r'^users/',
include('funkwhale_api.users.api_urls', namespace='users')), include(
('funkwhale_api.users.api_urls', 'users'),
namespace='users')),
url(r'^token/', url(r'^token/',
jwt_views.obtain_jwt_token), jwt_views.obtain_jwt_token),
url(r'^token/refresh/', jwt_views.refresh_jwt_token), url(r'^token/refresh/', jwt_views.refresh_jwt_token),
] ]
urlpatterns = [ urlpatterns = [
url(r'^v1/', include(v1_patterns, namespace='v1')) url(r'^v1/', include((v1_patterns, 'v1'), namespace='v1'))
] ]

View File

@ -75,7 +75,7 @@ INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
# MIDDLEWARE CONFIGURATION # MIDDLEWARE CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
MIDDLEWARE_CLASSES = ( MIDDLEWARE = (
# Make sure djangosecure.middleware.SecurityMiddleware is listed first # Make sure djangosecure.middleware.SecurityMiddleware is listed first
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'funkwhale_api.users.middleware.AnonymousSessionMiddleware', 'funkwhale_api.users.middleware.AnonymousSessionMiddleware',

View File

@ -31,7 +31,7 @@ EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND',
# django-debug-toolbar # django-debug-toolbar
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',) MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
# INTERNAL_IPS = ('127.0.0.1', '10.0.2.2',) # INTERNAL_IPS = ('127.0.0.1', '10.0.2.2',)

View File

@ -36,7 +36,7 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# #
# #
# # Make sure djangosecure.middleware.SecurityMiddleware is listed first # # Make sure djangosecure.middleware.SecurityMiddleware is listed first
# MIDDLEWARE_CLASSES = SECURITY_MIDDLEWARE + MIDDLEWARE_CLASSES # MIDDLEWARE = SECURITY_MIDDLEWARE + MIDDLEWARE
# #
# # set this to 60 seconds and then to 518400 when you can prove it works # # set this to 60 seconds and then to 518400 when you can prove it works
# SECURE_HSTS_SECONDS = 60 # SECURE_HSTS_SECONDS = 60

View File

@ -22,8 +22,8 @@ CACHES = {
'LOCATION': '' 'LOCATION': ''
} }
} }
INSTALLED_APPS += ('kombu.transport.django',)
BROKER_URL = 'django://' BROKER_URL = 'memory://'
# TESTING # TESTING
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -10,9 +10,9 @@ from django.views import defaults as default_views
urlpatterns = [ urlpatterns = [
# Django Admin, use {% url 'admin:index' %} # Django Admin, use {% url 'admin:index' %}
url(settings.ADMIN_URL, include(admin.site.urls)), url(settings.ADMIN_URL, admin.site.urls),
url(r'^api/', include("config.api_urls", namespace="api")), url(r'^api/', include(("config.api_urls", 'api'), namespace="api")),
url(r'^api/auth/', include('rest_auth.urls')), url(r'^api/auth/', include('rest_auth.urls')),
url(r'^api/auth/registration/', include('funkwhale_api.users.rest_auth_urls')), url(r'^api/auth/registration/', include('funkwhale_api.users.rest_auth_urls')),
url(r'^accounts/', include('allauth.urls')), url(r'^accounts/', include('allauth.urls')),

View File

@ -7,5 +7,5 @@ class ConditionalAuthentication(BasePermission):
def has_permission(self, request, view): def has_permission(self, request, view):
if settings.API_AUTHENTICATION_REQUIRED: if settings.API_AUTHENTICATION_REQUIRED:
return request.user and request.user.is_authenticated() return request.user and request.user.is_authenticated
return True return True

View File

@ -25,7 +25,7 @@ class Migration(migrations.Migration):
'ordering': ('domain',), 'ordering': ('domain',),
}, },
managers=[ managers=[
(b'objects', django.contrib.sites.models.SiteManager()), ('objects', django.contrib.sites.models.SiteManager()),
], ],
), ),
] ]

View File

@ -19,8 +19,8 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)), ('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)),
('creation_date', models.DateTimeField(default=django.utils.timezone.now)), ('creation_date', models.DateTimeField(default=django.utils.timezone.now)),
('track', models.ForeignKey(related_name='track_favorites', to='music.Track')), ('track', models.ForeignKey(related_name='track_favorites', to='music.Track', on_delete=models.CASCADE)),
('user', models.ForeignKey(related_name='track_favorites', to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(related_name='track_favorites', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
], ],
options={ options={
'ordering': ('-creation_date',), 'ordering': ('-creation_date',),

View File

@ -5,8 +5,10 @@ from funkwhale_api.music.models import Track
class TrackFavorite(models.Model): class TrackFavorite(models.Model):
creation_date = models.DateTimeField(default=timezone.now) creation_date = models.DateTimeField(default=timezone.now)
user = models.ForeignKey('users.User', related_name='track_favorites') user = models.ForeignKey(
track = models.ForeignKey(Track, related_name='track_favorites') 'users.User', related_name='track_favorites', on_delete=models.CASCADE)
track = models.ForeignKey(
Track, related_name='track_favorites', on_delete=models.CASCADE)
class Meta: class Meta:
unique_together = ('track', 'user') unique_together = ('track', 'user')

View File

@ -1,6 +1,6 @@
import json import json
from test_plus.test import TestCase from test_plus.test import TestCase
from django.core.urlresolvers import reverse from django.urls import reverse
from funkwhale_api.music.models import Track, Artist from funkwhale_api.music.models import Track, Artist
from funkwhale_api.favorites.models import TrackFavorite from funkwhale_api.favorites.models import TrackFavorite

View File

@ -20,8 +20,8 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('end_date', models.DateTimeField(null=True, blank=True, default=django.utils.timezone.now)), ('end_date', models.DateTimeField(null=True, blank=True, default=django.utils.timezone.now)),
('session_key', models.CharField(null=True, blank=True, max_length=100)), ('session_key', models.CharField(null=True, blank=True, max_length=100)),
('track', models.ForeignKey(related_name='listenings', to='music.Track')), ('track', models.ForeignKey(related_name='listenings', to='music.Track', on_delete=models.CASCADE)),
('user', models.ForeignKey(blank=True, null=True, related_name='listenings', to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(blank=True, null=True, related_name='listenings', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
], ],
options={ options={
'ordering': ('-end_date',), 'ordering': ('-end_date',),

View File

@ -7,8 +7,14 @@ from funkwhale_api.music.models import Track
class Listening(models.Model): class Listening(models.Model):
end_date = models.DateTimeField(default=timezone.now, null=True, blank=True) end_date = models.DateTimeField(default=timezone.now, null=True, blank=True)
track = models.ForeignKey(Track, related_name="listenings") track = models.ForeignKey(
user = models.ForeignKey('users.User', related_name="listenings", null=True, blank=True) Track, related_name="listenings", on_delete=models.CASCADE)
user = models.ForeignKey(
'users.User',
related_name="listenings",
null=True,
blank=True,
on_delete=models.CASCADE)
session_key = models.CharField(max_length=100, null=True, blank=True) session_key = models.CharField(max_length=100, null=True, blank=True)
class Meta: class Meta:

View File

@ -0,0 +1,12 @@
import factory
from funkwhale_api.music.tests import factories
from funkwhale_api.users.tests.factories import UserFactory
class ListeningFactory(factory.django.DjangoModelFactory):
user = factory.SubFactory(UserFactory)
track = factory.SubFactory(factories.TrackFactory)
class Meta:
model = 'history.Listening'

View File

@ -1,15 +1,16 @@
import random import random
import json import json
from test_plus.test import TestCase from test_plus.test import TestCase
from django.core.urlresolvers import reverse from django.urls import reverse
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils import timezone from django.utils import timezone
from model_mommy import mommy from funkwhale_api.music.tests.factories import TrackFactory
from funkwhale_api.users.models import User from funkwhale_api.users.models import User
from funkwhale_api.history import models from funkwhale_api.history import models
class TestHistory(TestCase): class TestHistory(TestCase):
def setUp(self): def setUp(self):
@ -17,12 +18,12 @@ class TestHistory(TestCase):
self.user = User.objects.create_user(username='test', email='test@test.com', password='test') self.user = User.objects.create_user(username='test', email='test@test.com', password='test')
def test_can_create_listening(self): def test_can_create_listening(self):
track = mommy.make('music.Track') track = TrackFactory()
now = timezone.now() now = timezone.now()
l = models.Listening.objects.create(user=self.user, track=track) l = models.Listening.objects.create(user=self.user, track=track)
def test_anonymous_user_can_create_listening_via_api(self): def test_anonymous_user_can_create_listening_via_api(self):
track = mommy.make('music.Track') track = TrackFactory()
url = self.reverse('api:v1:history:listenings-list') url = self.reverse('api:v1:history:listenings-list')
response = self.client.post(url, { response = self.client.post(url, {
'track': track.pk, 'track': track.pk,
@ -34,7 +35,7 @@ class TestHistory(TestCase):
self.assertIsNotNone(listening.session_key) self.assertIsNotNone(listening.session_key)
def test_logged_in_user_can_create_listening_via_api(self): def test_logged_in_user_can_create_listening_via_api(self):
track = mommy.make('music.Track') track = TrackFactory()
self.client.login(username=self.user.username, password='test') self.client.login(username=self.user.username, password='test')

View File

@ -22,14 +22,14 @@ class ListeningViewSet(mixins.CreateModelMixin,
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
if self.request.user.is_authenticated(): if self.request.user.is_authenticated:
return queryset.filter(user=self.request.user) return queryset.filter(user=self.request.user)
else: else:
return queryset.filter(session_key=self.request.session.session_key) return queryset.filter(session_key=self.request.session.session_key)
def get_serializer_context(self): def get_serializer_context(self):
context = super().get_serializer_context() context = super().get_serializer_context()
if self.request.user.is_authenticated(): if self.request.user.is_authenticated:
context['user'] = self.request.user context['user'] = self.request.user
else: else:
context['session_key'] = self.request.session.session_key context['session_key'] = self.request.session.session_key

View File

@ -44,7 +44,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')),
('creation_date', models.DateTimeField(default=django.utils.timezone.now)), ('creation_date', models.DateTimeField(default=django.utils.timezone.now)),
('submitted_by', models.ForeignKey(related_name='imports', to=settings.AUTH_USER_MODEL)), ('submitted_by', models.ForeignKey(related_name='imports', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
@ -54,7 +54,7 @@ class Migration(migrations.Migration):
('source', models.URLField()), ('source', models.URLField()),
('mbid', models.UUIDField(editable=False)), ('mbid', models.UUIDField(editable=False)),
('status', models.CharField(default='pending', choices=[('pending', 'Pending'), ('finished', 'finished')], max_length=30)), ('status', models.CharField(default='pending', choices=[('pending', 'Pending'), ('finished', 'finished')], max_length=30)),
('batch', models.ForeignKey(related_name='jobs', to='music.ImportBatch')), ('batch', models.ForeignKey(related_name='jobs', to='music.ImportBatch', on_delete=models.CASCADE)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
@ -64,8 +64,8 @@ class Migration(migrations.Migration):
('mbid', models.UUIDField(editable=False, blank=True, null=True)), ('mbid', models.UUIDField(editable=False, blank=True, null=True)),
('creation_date', models.DateTimeField(default=django.utils.timezone.now)), ('creation_date', models.DateTimeField(default=django.utils.timezone.now)),
('title', models.CharField(max_length=255)), ('title', models.CharField(max_length=255)),
('album', models.ForeignKey(related_name='tracks', blank=True, null=True, to='music.Album')), ('album', models.ForeignKey(related_name='tracks', blank=True, null=True, to='music.Album', on_delete=models.CASCADE)),
('artist', models.ForeignKey(related_name='tracks', to='music.Artist')), ('artist', models.ForeignKey(related_name='tracks', to='music.Artist', on_delete=models.CASCADE)),
], ],
options={ options={
'abstract': False, 'abstract': False,
@ -78,12 +78,12 @@ class Migration(migrations.Migration):
('audio_file', models.FileField(upload_to='tracks')), ('audio_file', models.FileField(upload_to='tracks')),
('source', models.URLField(blank=True, null=True)), ('source', models.URLField(blank=True, null=True)),
('duration', models.IntegerField(blank=True, null=True)), ('duration', models.IntegerField(blank=True, null=True)),
('track', models.ForeignKey(related_name='files', to='music.Track')), ('track', models.ForeignKey(related_name='files', to='music.Track', on_delete=models.CASCADE)),
], ],
), ),
migrations.AddField( migrations.AddField(
model_name='album', model_name='album',
name='artist', name='artist',
field=models.ForeignKey(related_name='albums', to='music.Artist'), field=models.ForeignKey(related_name='albums', to='music.Artist', on_delete=models.CASCADE),
), ),
] ]

View File

@ -39,11 +39,11 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='lyrics', model_name='lyrics',
name='work', name='work',
field=models.ForeignKey(related_name='lyrics', to='music.Work', blank=True, null=True), field=models.ForeignKey(related_name='lyrics', to='music.Work', blank=True, null=True, on_delete=models.CASCADE),
), ),
migrations.AddField( migrations.AddField(
model_name='track', model_name='track',
name='work', name='work',
field=models.ForeignKey(related_name='tracks', to='music.Work', blank=True, null=True), field=models.ForeignKey(related_name='tracks', to='music.Work', blank=True, null=True, on_delete=models.CASCADE),
), ),
] ]

View File

@ -10,7 +10,7 @@ from django.conf import settings
from django.db import models from django.db import models
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.core.files import File from django.core.files import File
from django.core.urlresolvers import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from versatileimagefield.fields import VersatileImageField from versatileimagefield.fields import VersatileImageField
@ -108,7 +108,8 @@ def import_tracks(instance, cleaned_data, raw_data):
class Album(APIModelMixin): class Album(APIModelMixin):
title = models.CharField(max_length=255) title = models.CharField(max_length=255)
artist = models.ForeignKey(Artist, related_name='albums') artist = models.ForeignKey(
Artist, related_name='albums', on_delete=models.CASCADE)
release_date = models.DateField(null=True) release_date = models.DateField(null=True)
release_group_id = models.UUIDField(null=True, blank=True) release_group_id = models.UUIDField(null=True, blank=True)
cover = VersatileImageField(upload_to='albums/covers/%Y/%m/%d', null=True, blank=True) cover = VersatileImageField(upload_to='albums/covers/%Y/%m/%d', null=True, blank=True)
@ -245,7 +246,12 @@ class Work(APIModelMixin):
class Lyrics(models.Model): class Lyrics(models.Model):
work = models.ForeignKey(Work, related_name='lyrics', null=True, blank=True) work = models.ForeignKey(
Work,
related_name='lyrics',
null=True,
blank=True,
on_delete=models.CASCADE)
url = models.URLField(unique=True) url = models.URLField(unique=True)
content = models.TextField(null=True, blank=True) content = models.TextField(null=True, blank=True)
@ -268,10 +274,21 @@ class Lyrics(models.Model):
class Track(APIModelMixin): class Track(APIModelMixin):
title = models.CharField(max_length=255) title = models.CharField(max_length=255)
artist = models.ForeignKey(Artist, related_name='tracks') artist = models.ForeignKey(
Artist, related_name='tracks', on_delete=models.CASCADE)
position = models.PositiveIntegerField(null=True, blank=True) position = models.PositiveIntegerField(null=True, blank=True)
album = models.ForeignKey(Album, related_name='tracks', null=True, blank=True) album = models.ForeignKey(
work = models.ForeignKey(Work, related_name='tracks', null=True, blank=True) Album,
related_name='tracks',
null=True,
blank=True,
on_delete=models.CASCADE)
work = models.ForeignKey(
Work,
related_name='tracks',
null=True,
blank=True,
on_delete=models.CASCADE)
musicbrainz_model = 'recording' musicbrainz_model = 'recording'
api = musicbrainz.api.recordings api = musicbrainz.api.recordings
@ -340,7 +357,8 @@ class Track(APIModelMixin):
class TrackFile(models.Model): class TrackFile(models.Model):
track = models.ForeignKey(Track, related_name='files') track = models.ForeignKey(
Track, related_name='files', on_delete=models.CASCADE)
audio_file = models.FileField(upload_to='tracks/%Y/%m/%d', max_length=255) audio_file = models.FileField(upload_to='tracks/%Y/%m/%d', max_length=255)
source = models.URLField(null=True, blank=True) source = models.URLField(null=True, blank=True)
duration = models.IntegerField(null=True, blank=True) duration = models.IntegerField(null=True, blank=True)
@ -376,7 +394,8 @@ class TrackFile(models.Model):
class ImportBatch(models.Model): class ImportBatch(models.Model):
creation_date = models.DateTimeField(default=timezone.now) creation_date = models.DateTimeField(default=timezone.now)
submitted_by = models.ForeignKey('users.User', related_name='imports') submitted_by = models.ForeignKey(
'users.User', related_name='imports', on_delete=models.CASCADE)
class Meta: class Meta:
ordering = ['-creation_date'] ordering = ['-creation_date']
@ -392,9 +411,14 @@ class ImportBatch(models.Model):
return 'finished' return 'finished'
class ImportJob(models.Model): class ImportJob(models.Model):
batch = models.ForeignKey(ImportBatch, related_name='jobs') batch = models.ForeignKey(
ImportBatch, related_name='jobs', on_delete=models.CASCADE)
track_file = models.ForeignKey( track_file = models.ForeignKey(
TrackFile, related_name='jobs', null=True, blank=True) TrackFile,
related_name='jobs',
null=True,
blank=True,
on_delete=models.CASCADE)
source = models.URLField() source = models.URLField()
mbid = models.UUIDField(editable=False) mbid = models.UUIDField(editable=False)
STATUS_CHOICES = ( STATUS_CHOICES = (

View File

@ -59,3 +59,30 @@ class ImportJobFactory(factory.django.DjangoModelFactory):
class Meta: class Meta:
model = 'music.ImportJob' model = 'music.ImportJob'
class WorkFactory(factory.django.DjangoModelFactory):
mbid = factory.Faker('uuid4')
language = 'eng'
nature = 'song'
title = factory.Faker('sentence', nb_words=3)
class Meta:
model = 'music.Work'
class LyricsFactory(factory.django.DjangoModelFactory):
work = factory.SubFactory(WorkFactory)
url = factory.Faker('url')
content = factory.Faker('paragraphs', nb=4)
class Meta:
model = 'music.Lyrics'
class TagFactory(factory.django.DjangoModelFactory):
name = factory.SelfAttribute('slug')
slug = factory.Faker('slug')
class Meta:
model = 'taggit.Tag'

View File

@ -1,7 +1,7 @@
import json import json
import unittest import unittest
from test_plus.test import TestCase from test_plus.test import TestCase
from django.core.urlresolvers import reverse from django.urls import reverse
from funkwhale_api.music import models from funkwhale_api.music import models
from funkwhale_api.utils.tests import TMPDirTestCaseMixin from funkwhale_api.utils.tests import TMPDirTestCaseMixin

View File

@ -1,25 +1,26 @@
import json import json
import unittest import unittest
from test_plus.test import TestCase from test_plus.test import TestCase
from django.core.urlresolvers import reverse from django.urls import reverse
from model_mommy import mommy
from funkwhale_api.music import models from funkwhale_api.music import models
from funkwhale_api.musicbrainz import api from funkwhale_api.musicbrainz import api
from funkwhale_api.music import serializers from funkwhale_api.music import serializers
from funkwhale_api.users.models import User from funkwhale_api.users.models import User
from funkwhale_api.music import lyrics as lyrics_utils
from .mocking import lyricswiki from .mocking import lyricswiki
from . import factories
from . import data as api_data from . import data as api_data
from funkwhale_api.music import lyrics as lyrics_utils
class TestLyrics(TestCase): class TestLyrics(TestCase):
@unittest.mock.patch('funkwhale_api.music.lyrics._get_html', @unittest.mock.patch('funkwhale_api.music.lyrics._get_html',
return_value=lyricswiki.content) return_value=lyricswiki.content)
def test_works_import_lyrics_if_any(self, *mocks): def test_works_import_lyrics_if_any(self, *mocks):
lyrics = mommy.make( lyrics = factories.LyricsFactory(
models.Lyrics,
url='http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!') url='http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!')
lyrics.fetch_content() lyrics.fetch_content()
@ -42,7 +43,7 @@ Is it me you're looking for?
content = """Hello content = """Hello
Is it me you're looking for?""" Is it me you're looking for?"""
l = mommy.make(models.Lyrics, content=content) l = factories.LyricsFactory(content=content)
expected = "<p>Hello<br />Is it me you're looking for?</p>" expected = "<p>Hello<br />Is it me you're looking for?</p>"
self.assertHTMLEqual(expected, l.content_rendered) self.assertHTMLEqual(expected, l.content_rendered)
@ -54,8 +55,7 @@ Is it me you're looking for?"""
@unittest.mock.patch('funkwhale_api.music.lyrics._get_html', @unittest.mock.patch('funkwhale_api.music.lyrics._get_html',
return_value=lyricswiki.content) return_value=lyricswiki.content)
def test_works_import_lyrics_if_any(self, *mocks): def test_works_import_lyrics_if_any(self, *mocks):
track = mommy.make( track = factories.TrackFactory(
models.Track,
work=None, work=None,
mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448')

View File

@ -2,13 +2,11 @@ from test_plus.test import TestCase
import unittest.mock import unittest.mock
from funkwhale_api.music import models from funkwhale_api.music import models
import datetime import datetime
from model_mommy import mommy
from . import factories
from . import data as api_data from . import data as api_data
from .cover import binary_data from .cover import binary_data
def prettyprint(d):
import json
print(json.dumps(d, sort_keys=True, indent=4))
class TestMusic(TestCase): class TestMusic(TestCase):
@ -79,9 +77,9 @@ class TestMusic(TestCase):
self.assertEqual(track, track2) self.assertEqual(track, track2)
def test_album_tags_deduced_from_tracks_tags(self): def test_album_tags_deduced_from_tracks_tags(self):
tag = mommy.make('taggit.Tag') tag = factories.TagFactory()
album = mommy.make('music.Album') album = factories.AlbumFactory()
tracks = mommy.make('music.Track', album=album, _quantity=5) tracks = factories.TrackFactory.create_batch(album=album, size=5)
for track in tracks: for track in tracks:
track.tags.add(tag) track.tags.add(tag)
@ -92,10 +90,10 @@ class TestMusic(TestCase):
self.assertIn(tag, album.tags) self.assertIn(tag, album.tags)
def test_artist_tags_deduced_from_album_tags(self): def test_artist_tags_deduced_from_album_tags(self):
tag = mommy.make('taggit.Tag') tag = factories.TagFactory()
artist = mommy.make('music.Artist') artist = factories.ArtistFactory()
album = mommy.make('music.Album', artist=artist) album = factories.AlbumFactory(artist=artist)
tracks = mommy.make('music.Track', album=album, _quantity=5) tracks = factories.TrackFactory.create_batch(album=album, size=5)
for track in tracks: for track in tracks:
track.tags.add(tag) track.tags.add(tag)
@ -108,7 +106,7 @@ class TestMusic(TestCase):
@unittest.mock.patch('funkwhale_api.musicbrainz.api.images.get_front', return_value=binary_data) @unittest.mock.patch('funkwhale_api.musicbrainz.api.images.get_front', return_value=binary_data)
def test_can_download_image_file_for_album(self, *mocks): def test_can_download_image_file_for_album(self, *mocks):
# client._api.get_image_front('55ea4f82-b42b-423e-a0e5-290ccdf443ed') # client._api.get_image_front('55ea4f82-b42b-423e-a0e5-290ccdf443ed')
album = mommy.make('music.Album', mbid='55ea4f82-b42b-423e-a0e5-290ccdf443ed') album = factories.AlbumFactory(mbid='55ea4f82-b42b-423e-a0e5-290ccdf443ed')
album.get_image() album.get_image()
album.save() album.save()

View File

@ -1,23 +1,24 @@
import json import json
import unittest import unittest
from test_plus.test import TestCase from test_plus.test import TestCase
from django.core.urlresolvers import reverse from django.urls import reverse
from model_mommy import mommy
from funkwhale_api.music import models from funkwhale_api.music import models
from funkwhale_api.musicbrainz import api from funkwhale_api.musicbrainz import api
from funkwhale_api.music import serializers from funkwhale_api.music import serializers
from funkwhale_api.music.tests import factories
from funkwhale_api.users.models import User from funkwhale_api.users.models import User
from . import data as api_data from . import data as api_data
class TestWorks(TestCase): class TestWorks(TestCase):
@unittest.mock.patch('funkwhale_api.musicbrainz.api.works.get', @unittest.mock.patch('funkwhale_api.musicbrainz.api.works.get',
return_value=api_data.works['get']['chop_suey']) return_value=api_data.works['get']['chop_suey'])
def test_can_import_work(self, *mocks): def test_can_import_work(self, *mocks):
recording = mommy.make( recording = factories.TrackFactory(
models.Track, mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448')
mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5'
work = models.Work.create_from_api(id=mbid) work = models.Work.create_from_api(id=mbid)
@ -36,8 +37,7 @@ class TestWorks(TestCase):
@unittest.mock.patch('funkwhale_api.musicbrainz.api.recordings.get', @unittest.mock.patch('funkwhale_api.musicbrainz.api.recordings.get',
return_value=api_data.tracks['get']['chop_suey']) return_value=api_data.tracks['get']['chop_suey'])
def test_can_get_work_from_recording(self, *mocks): def test_can_get_work_from_recording(self, *mocks):
recording = mommy.make( recording = factories.TrackFactory(
models.Track,
work=None, work=None,
mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448')
mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5'

View File

@ -2,7 +2,7 @@ import os
import json import json
import unicodedata import unicodedata
import urllib import urllib
from django.core.urlresolvers import reverse from django.urls import reverse
from django.db import models, transaction from django.db import models, transaction
from django.db.models.functions import Length from django.db.models.functions import Length
from django.conf import settings from django.conf import settings
@ -102,7 +102,7 @@ class TrackViewSet(TagViewSetMixin, SearchMixin, viewsets.ReadOnlyModelViewSet):
queryset = super().get_queryset() queryset = super().get_queryset()
filter_favorites = self.request.GET.get('favorites', None) filter_favorites = self.request.GET.get('favorites', None)
user = self.request.user user = self.request.user
if user.is_authenticated() and filter_favorites == 'true': if user.is_authenticated and filter_favorites == 'true':
queryset = queryset.filter(track_favorites__user=user) queryset = queryset.filter(track_favorites__user=user)
return queryset return queryset

View File

@ -1,7 +1,7 @@
import json import json
import unittest import unittest
from test_plus.test import TestCase from test_plus.test import TestCase
from django.core.urlresolvers import reverse from django.urls import reverse
from funkwhale_api.musicbrainz import api from funkwhale_api.musicbrainz import api
from . import data as api_data from . import data as api_data

View File

@ -22,7 +22,7 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=50)), ('name', models.CharField(max_length=50)),
('is_public', models.BooleanField(default=False)), ('is_public', models.BooleanField(default=False)),
('creation_date', models.DateTimeField(default=django.utils.timezone.now)), ('creation_date', models.DateTimeField(default=django.utils.timezone.now)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='playlists')), ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='playlists', on_delete=models.CASCADE)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
@ -33,9 +33,9 @@ class Migration(migrations.Migration):
('rght', models.PositiveIntegerField(db_index=True, editable=False)), ('rght', models.PositiveIntegerField(db_index=True, editable=False)),
('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
('position', models.PositiveIntegerField(db_index=True, editable=False)), ('position', models.PositiveIntegerField(db_index=True, editable=False)),
('playlist', models.ForeignKey(to='playlists.Playlist', related_name='playlist_tracks')), ('playlist', models.ForeignKey(to='playlists.Playlist', related_name='playlist_tracks', on_delete=models.CASCADE)),
('previous', mptt.fields.TreeOneToOneField(null=True, to='playlists.PlaylistTrack', related_name='next', blank=True)), ('previous', mptt.fields.TreeOneToOneField(null=True, to='playlists.PlaylistTrack', related_name='next', blank=True, on_delete=models.CASCADE)),
('track', models.ForeignKey(to='music.Track', related_name='playlist_tracks')), ('track', models.ForeignKey(to='music.Track', related_name='playlist_tracks', on_delete=models.CASCADE)),
], ],
options={ options={
'ordering': ('-playlist', 'position'), 'ordering': ('-playlist', 'position'),

View File

@ -7,7 +7,8 @@ from mptt.models import MPTTModel, TreeOneToOneField
class Playlist(models.Model): class Playlist(models.Model):
name = models.CharField(max_length=50) name = models.CharField(max_length=50)
is_public = models.BooleanField(default=False) is_public = models.BooleanField(default=False)
user = models.ForeignKey('users.User', related_name="playlists") user = models.ForeignKey(
'users.User', related_name="playlists", on_delete=models.CASCADE)
creation_date = models.DateTimeField(default=timezone.now) creation_date = models.DateTimeField(default=timezone.now)
def __str__(self): def __str__(self):
@ -21,9 +22,18 @@ class Playlist(models.Model):
class PlaylistTrack(MPTTModel): class PlaylistTrack(MPTTModel):
track = models.ForeignKey('music.Track', related_name='playlist_tracks') track = models.ForeignKey(
previous = TreeOneToOneField('self', blank=True, null=True, related_name='next') 'music.Track',
playlist = models.ForeignKey(Playlist, related_name='playlist_tracks') related_name='playlist_tracks',
on_delete=models.CASCADE)
previous = TreeOneToOneField(
'self',
blank=True,
null=True,
related_name='next',
on_delete=models.CASCADE)
playlist = models.ForeignKey(
Playlist, related_name='playlist_tracks', on_delete=models.CASCADE)
class MPTTMeta: class MPTTMeta:
level_attr = 'position' level_attr = 'position'

View File

@ -1,11 +1,10 @@
import json import json
from test_plus.test import TestCase from test_plus.test import TestCase
from django.core.urlresolvers import reverse from django.urls import reverse
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils import timezone from django.utils import timezone
from model_mommy import mommy from funkwhale_api.music.tests import factories
from funkwhale_api.users.models import User from funkwhale_api.users.models import User
from funkwhale_api.playlists import models from funkwhale_api.playlists import models
from funkwhale_api.playlists.serializers import PlaylistSerializer from funkwhale_api.playlists.serializers import PlaylistSerializer
@ -18,7 +17,7 @@ class TestPlayLists(TestCase):
self.user = User.objects.create_user(username='test', email='test@test.com', password='test') self.user = User.objects.create_user(username='test', email='test@test.com', password='test')
def test_can_create_playlist(self): def test_can_create_playlist(self):
tracks = list(mommy.make('music.Track', _quantity=5)) tracks = factories.TrackFactory.create_batch(size=5)
playlist = models.Playlist.objects.create(user=self.user, name="test") playlist = models.Playlist.objects.create(user=self.user, name="test")
previous = None previous = None
@ -49,7 +48,7 @@ class TestPlayLists(TestCase):
self.assertEqual(playlist.name, 'test') self.assertEqual(playlist.name, 'test')
def test_can_add_playlist_track_via_api(self): def test_can_add_playlist_track_via_api(self):
tracks = list(mommy.make('music.Track', _quantity=5)) tracks = factories.TrackFactory.create_batch(size=5)
playlist = models.Playlist.objects.create(user=self.user, name="test") playlist = models.Playlist.objects.create(user=self.user, name="test")
self.client.login(username=self.user.username, password='test') self.client.login(username=self.user.username, password='test')

View File

@ -1,8 +1,11 @@
from django.conf.urls import include, url from django.conf.urls import include, url
from funkwhale_api.music import views from funkwhale_api.music import views
urlpatterns = [ urlpatterns = [
url(r'^youtube/', include('funkwhale_api.providers.youtube.urls', namespace='youtube')), url(r'^youtube/', include(
url(r'^musicbrainz/', include('funkwhale_api.musicbrainz.urls', namespace='musicbrainz')), ('funkwhale_api.providers.youtube.urls', 'youtube'),
namespace='youtube')),
url(r'^musicbrainz/', include(
('funkwhale_api.musicbrainz.urls', 'musicbrainz'),
namespace='musicbrainz')),
] ]

View File

@ -2,7 +2,7 @@ import json
from collections import OrderedDict from collections import OrderedDict
import unittest import unittest
from test_plus.test import TestCase from test_plus.test import TestCase
from django.core.urlresolvers import reverse from django.urls import reverse
from funkwhale_api.providers.youtube.client import client from funkwhale_api.providers.youtube.client import client
from . import data as api_data from . import data as api_data

View File

@ -20,7 +20,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('radio_type', models.CharField(max_length=50)), ('radio_type', models.CharField(max_length=50)),
('creation_date', models.DateTimeField(default=django.utils.timezone.now)), ('creation_date', models.DateTimeField(default=django.utils.timezone.now)),
('user', models.ForeignKey(related_name='radio_sessions', blank=True, to=settings.AUTH_USER_MODEL, null=True)), ('user', models.ForeignKey(related_name='radio_sessions', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
@ -28,8 +28,8 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('position', models.IntegerField(default=1)), ('position', models.IntegerField(default=1)),
('session', models.ForeignKey(to='radios.RadioSession', related_name='session_tracks')), ('session', models.ForeignKey(to='radios.RadioSession', related_name='session_tracks', on_delete=models.CASCADE)),
('track', models.ForeignKey(to='music.Track', related_name='radio_session_tracks')), ('track', models.ForeignKey(to='music.Track', related_name='radio_session_tracks', on_delete=models.CASCADE)),
], ],
options={ options={
'ordering': ('session', 'position'), 'ordering': ('session', 'position'),

View File

@ -15,7 +15,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='radiosession', model_name='radiosession',
name='related_object_content_type', name='related_object_content_type',
field=models.ForeignKey(null=True, to='contenttypes.ContentType', blank=True), field=models.ForeignKey(null=True, to='contenttypes.ContentType', blank=True, on_delete=models.CASCADE),
), ),
migrations.AddField( migrations.AddField(
model_name='radiosession', model_name='radiosession',

View File

@ -7,11 +7,20 @@ from django.contrib.contenttypes.models import ContentType
from funkwhale_api.music.models import Track from funkwhale_api.music.models import Track
class RadioSession(models.Model): class RadioSession(models.Model):
user = models.ForeignKey('users.User', related_name='radio_sessions', null=True, blank=True) user = models.ForeignKey(
'users.User',
related_name='radio_sessions',
null=True,
blank=True,
on_delete=models.CASCADE)
session_key = models.CharField(max_length=100, null=True, blank=True) session_key = models.CharField(max_length=100, null=True, blank=True)
radio_type = models.CharField(max_length=50) radio_type = models.CharField(max_length=50)
creation_date = models.DateTimeField(default=timezone.now) creation_date = models.DateTimeField(default=timezone.now)
related_object_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, blank=True, null=True) related_object_content_type = models.ForeignKey(
ContentType,
blank=True,
null=True,
on_delete=models.CASCADE)
related_object_id = models.PositiveIntegerField(blank=True, null=True) related_object_id = models.PositiveIntegerField(blank=True, null=True)
related_object = GenericForeignKey('related_object_content_type', 'related_object_id') related_object = GenericForeignKey('related_object_content_type', 'related_object_id')
@ -43,9 +52,11 @@ class RadioSession(models.Model):
return registry[self.radio_type](session=self) return registry[self.radio_type](session=self)
class RadioSessionTrack(models.Model): class RadioSessionTrack(models.Model):
session = models.ForeignKey(RadioSession, related_name='session_tracks') session = models.ForeignKey(
RadioSession, related_name='session_tracks', on_delete=models.CASCADE)
position = models.IntegerField(default=1) position = models.IntegerField(default=1)
track = models.ForeignKey(Track, related_name='radio_session_tracks') track = models.ForeignKey(
Track, related_name='radio_session_tracks', on_delete=models.CASCADE)
class Meta: class Meta:
ordering = ('session', 'position') ordering = ('session', 'position')

View File

@ -1,16 +1,18 @@
import random import random
import json import json
from test_plus.test import TestCase from test_plus.test import TestCase
from django.core.urlresolvers import reverse from django.urls import reverse
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from model_mommy import mommy
from funkwhale_api.radios import radios from funkwhale_api.radios import radios
from funkwhale_api.radios import models from funkwhale_api.radios import models
from funkwhale_api.favorites.models import TrackFavorite from funkwhale_api.favorites.models import TrackFavorite
from funkwhale_api.users.models import User from funkwhale_api.users.models import User
from funkwhale_api.music.models import Artist from funkwhale_api.music.models import Artist
from funkwhale_api.music.tests import factories
from funkwhale_api.history.tests.factories import ListeningFactory
class TestRadios(TestCase): class TestRadios(TestCase):
@ -55,7 +57,7 @@ class TestRadios(TestCase):
self.assertTrue(picks[2] > picks[1]) self.assertTrue(picks[2] > picks[1])
def test_can_get_choices_for_favorites_radio(self): def test_can_get_choices_for_favorites_radio(self):
tracks = mommy.make('music.Track', _quantity=100) tracks = factories.TrackFactory.create_batch(size=100)
for i in range(20): for i in range(20):
TrackFavorite.add(track=random.choice(tracks), user=self.user) TrackFavorite.add(track=random.choice(tracks), user=self.user)
@ -73,7 +75,7 @@ class TestRadios(TestCase):
self.assertIn(pick, choices) self.assertIn(pick, choices)
def test_can_use_radio_session_to_filter_choices(self): def test_can_use_radio_session_to_filter_choices(self):
tracks = mommy.make('music.Track', _quantity=30) tracks = factories.TrackFactory.create_batch(size=30)
radio = radios.RandomRadio() radio = radios.RandomRadio()
session = radio.start_session(self.user) session = radio.start_session(self.user)
@ -85,7 +87,7 @@ class TestRadios(TestCase):
self.assertEqual(len(set(tracks_id)), 30) self.assertEqual(len(set(tracks_id)), 30)
def test_can_restore_radio_from_previous_session(self): def test_can_restore_radio_from_previous_session(self):
tracks = mommy.make('music.Track', _quantity=30) tracks = factories.TrackFactory.create_batch(size=30)
radio = radios.RandomRadio() radio = radios.RandomRadio()
session = radio.start_session(self.user) session = radio.start_session(self.user)
@ -115,7 +117,7 @@ class TestRadios(TestCase):
self.assertIsNotNone(session.session_key) self.assertIsNotNone(session.session_key)
def test_can_get_track_for_session_from_api(self): def test_can_get_track_for_session_from_api(self):
tracks = mommy.make('music.Track', _quantity=1) tracks = factories.TrackFactory.create_batch(size=1)
self.client.login(username=self.user.username, password='test') self.client.login(username=self.user.username, password='test')
url = reverse('api:v1:radios:sessions-list') url = reverse('api:v1:radios:sessions-list')
@ -129,7 +131,7 @@ class TestRadios(TestCase):
self.assertEqual(data['track']['id'], tracks[0].id) self.assertEqual(data['track']['id'], tracks[0].id)
self.assertEqual(data['position'], 1) self.assertEqual(data['position'], 1)
next_track = mommy.make('music.Track') next_track = factories.TrackFactory()
response = self.client.post(url, {'session': session.pk}) response = self.client.post(url, {'session': session.pk})
data = json.loads(response.content.decode('utf-8')) data = json.loads(response.content.decode('utf-8'))
@ -148,9 +150,10 @@ class TestRadios(TestCase):
radio.start_session(self.user, related_object=self.user) radio.start_session(self.user, related_object=self.user)
def test_can_start_artist_radio(self): def test_can_start_artist_radio(self):
artist = mommy.make('music.Artist') artist = factories.ArtistFactory()
wrong_tracks = mommy.make('music.Track', _quantity=30) wrong_tracks = factories.TrackFactory.create_batch(size=30)
good_tracks = mommy.make('music.Track', artist=artist, _quantity=5) good_tracks = factories.TrackFactory.create_batch(
artist=artist, size=5)
radio = radios.ArtistRadio() radio = radios.ArtistRadio()
session = radio.start_session(self.user, related_object=artist) session = radio.start_session(self.user, related_object=artist)
@ -159,9 +162,9 @@ class TestRadios(TestCase):
self.assertIn(radio.pick(), good_tracks) self.assertIn(radio.pick(), good_tracks)
def test_can_start_tag_radio(self): def test_can_start_tag_radio(self):
tag = mommy.make('taggit.Tag') tag = factories.TagFactory()
wrong_tracks = mommy.make('music.Track', _quantity=30) wrong_tracks = factories.TrackFactory.create_batch(size=30)
good_tracks = mommy.make('music.Track', _quantity=5) good_tracks = factories.TrackFactory.create_batch(size=5)
for track in good_tracks: for track in good_tracks:
track.tags.add(tag) track.tags.add(tag)
@ -172,7 +175,7 @@ class TestRadios(TestCase):
self.assertIn(radio.pick(), good_tracks) self.assertIn(radio.pick(), good_tracks)
def test_can_start_artist_radio_from_api(self): def test_can_start_artist_radio_from_api(self):
artist = mommy.make('music.Artist') artist = factories.ArtistFactory()
url = reverse('api:v1:radios:sessions-list') url = reverse('api:v1:radios:sessions-list')
response = self.client.post(url, {'radio_type': 'artist', 'related_object_id': artist.id}) response = self.client.post(url, {'radio_type': 'artist', 'related_object_id': artist.id})
@ -181,10 +184,10 @@ class TestRadios(TestCase):
self.assertEqual(session.related_object, artist) self.assertEqual(session.related_object, artist)
def test_can_start_less_listened_radio(self): def test_can_start_less_listened_radio(self):
history = mommy.make('history.Listening', _quantity=5, user=self.user) history = ListeningFactory.create_batch(size=5, user=self.user)
wrong_tracks = [h.track for h in history] wrong_tracks = [h.track for h in history]
good_tracks = mommy.make('music.Track', _quantity=30) good_tracks = factories.TrackFactory.create_batch(size=30)
radio = radios.LessListenedRadio() radio = radios.LessListenedRadio()
session = radio.start_session(self.user) session = radio.start_session(self.user)

View File

@ -6,4 +6,5 @@ router = routers.SimpleRouter()
router.register(r'sessions', views.RadioSessionViewSet, 'sessions') router.register(r'sessions', views.RadioSessionViewSet, 'sessions')
router.register(r'tracks', views.RadioSessionTrackViewSet, 'tracks') router.register(r'tracks', views.RadioSessionTrackViewSet, 'tracks')
urlpatterns = router.urls urlpatterns = router.urls

View File

@ -19,14 +19,14 @@ class RadioSessionViewSet(mixins.CreateModelMixin,
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
if self.request.user.is_authenticated(): if self.request.user.is_authenticated:
return queryset.filter(user=self.request.user) return queryset.filter(user=self.request.user)
else: else:
return queryset.filter(session_key=self.request.session.session_key) return queryset.filter(session_key=self.request.session.session_key)
def get_serializer_context(self): def get_serializer_context(self):
context = super().get_serializer_context() context = super().get_serializer_context()
if self.request.user.is_authenticated(): if self.request.user.is_authenticated:
context['user'] = self.request.user context['user'] = self.request.user
else: else:
context['session_key'] = self.request.session.session_key context['session_key'] = self.request.session.session_key
@ -44,7 +44,7 @@ class RadioSessionTrackViewSet(mixins.CreateModelMixin,
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
session = serializer.validated_data['session'] session = serializer.validated_data['session']
try: try:
if request.user.is_authenticated(): if request.user.is_authenticated:
assert request.user == session.user assert request.user == session.user
else: else:
assert request.session.session_key == session.session_key assert request.session.session_key == session.session_key

View File

@ -1,6 +1,11 @@
class AnonymousSessionMiddleware(object): class AnonymousSessionMiddleware:
def process_request(self, request): def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if not request.session.session_key: if not request.session.session_key:
request.session.save() request.session.save()
response = self.get_response(request)
return response

View File

@ -38,7 +38,7 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'users', 'verbose_name_plural': 'users',
}, },
managers=[ managers=[
(b'objects', django.contrib.auth.models.UserManager()), ('objects', django.contrib.auth.models.UserManager()),
], ],
), ),
] ]

View File

@ -2,7 +2,7 @@
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.core.urlresolvers import reverse from django.urls import reverse
from django.db import models from django.db import models
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _

View File

@ -1,59 +1,59 @@
# Bleeding edge Django # Bleeding edge Django
django==1.11 django>=2.0,<2.1
# Configuration # Configuration
django-environ==0.4.0 django-environ>=0.4,<0.5
django-secure==1.0.1 whitenoise>=3.3,<3.4
whitenoise==2.0.6
# Models
django-model-utils==2.3.1
# Images # Images
Pillow==3.0.0 Pillow>=4.3,<4.4
# For user registration, either via email or social # For user registration, either via email or social
# Well-built with regular release cycles! # Well-built with regular release cycles!
django-allauth==0.24.1 django-allauth>=0.34,<0.35
# Python-PostgreSQL Database Adapter # Python-PostgreSQL Database Adapter
psycopg2==2.6.1 psycopg2>=2.7,<=2.8
# Time zones support # Time zones support
pytz==2015.7 pytz==2017.3
# Redis support # Redis support
django-redis==4.3.0 django-redis>=4.5,<4.6
redis>=2.10.0 redis>=2.10,<2.11
celery==3.1.19 celery>=3.1,<3.2
# Your custom requirements go here # Your custom requirements go here
django-cors-headers==2.1.0 django-cors-headers>=2.1,<2.2
musicbrainzngs==0.6 musicbrainzngs==0.6
youtube_dl>=2015.12.21 youtube_dl>=2017.12.14
djangorestframework==3.6.3 djangorestframework>=3.7,<3.8
djangorestframework-jwt==1.11.0 djangorestframework-jwt>=1.11,<1.12
django-celery==3.2.1 django-celery>=3.2,<3.3
django-mptt==0.8.7 django-mptt>=0.9,<0.10
google-api-python-client==1.6.2 google-api-python-client>=1.6,<1.7
arrow==0.10.0 arrow>=0.12,<0.13
django-taggit==0.22.1 persisting-theory>=0.2,<0.3
persisting-theory==0.2.1 django-versatileimagefield>=1.8,<1.9
django-versatileimagefield==1.7.1 django-filter>=1.1,<1.2
django-cachalot==1.5.0 django-rest-auth>=0.9,<0.10
django-filter==1.1 beautifulsoup4>=4.6,<4.7
django-rest-auth==0.9.1 Markdown>=2.6,<2.7
beautifulsoup4==4.6.0 ipython>=6,<7
Markdown==2.6.8 mutagen>=1.39,<1.40
ipython==6.1.0
mutagen==1.38
# Until this is merged
#django-taggit>=0.22,<0.23
git+https://github.com/jdufresne/django-taggit.git@e8f7f216f04c9781bebc84363ab24d575f948ede
# Until this is merged # Until this is merged
git+https://github.com/EliotBerriot/PyMemoize.git@django git+https://github.com/EliotBerriot/PyMemoize.git@django
# Until this is merged
#django-cachalot==1.5.0
git+https://github.com/EliotBerriot/django-cachalot.git@django-2
django-dynamic-preferences>=1.3,<1.4 django-dynamic-preferences>=1.5,<1.6

View File

@ -1,15 +1,15 @@
# Local development dependencies go here # Local development dependencies go here
-r base.txt
coverage==4.0.3 coverage>=4.4,<4.5
django_coverage_plugin==1.1 django_coverage_plugin>=1.5,<1.6
Sphinx==1.6.2 Sphinx>=1.6,<1.7
django-extensions==1.5.9 django-extensions>=1.9,<1.10
Werkzeug==0.11.2 Werkzeug>=0.13,<0.14
django-test-plus==1.0.11 django-test-plus>=1.0.20
factory_boy>=2.8.1 factory_boy>=2.8.1
# django-debug-toolbar that works with Django 1.5+ # django-debug-toolbar that works with Django 1.5+
django-debug-toolbar>=1.5,<1.6 django-debug-toolbar>=1.9,<1.10
# improved REPL # improved REPL
ipdb==0.8.1 ipdb==0.8.1

View File

@ -1,8 +1,5 @@
# Pro-tip: Try not to put anything here. There should be no dependency in # Pro-tip: Try not to put anything here. There should be no dependency in
# production that isn't in development. # production that isn't in development.
-r base.txt
# WSGI Handler # WSGI Handler
# ------------------------------------------------ # ------------------------------------------------

View File

@ -1,9 +1,6 @@
# Test dependencies go here. # Test dependencies go here.
-r local.txt
flake8
flake8==2.5.0
model-mommy==1.3.2
pytest pytest
pytest-django pytest-django
pytest-mock pytest-mock