Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
Julien Veyssier
funkwhale
Commits
62ca3bd7
Verified
Commit
62ca3bd7
authored
Jun 09, 2018
by
Eliot Berriot
Browse files
Blacked the code
parent
b6fc0051
Changes
279
Expand all
Hide whitespace changes
Inline
Side-by-side
api/config/api_urls.py
View file @
62ca3bd7
...
...
@@ -12,70 +12,70 @@ from dynamic_preferences.api.viewsets import GlobalPreferencesViewSet
from
dynamic_preferences.users.viewsets
import
UserPreferencesViewSet
router
=
routers
.
SimpleRouter
()
router
.
register
(
r
'
settings
'
,
GlobalPreferencesViewSet
,
base_name
=
'
settings
'
)
router
.
register
(
r
'
activity
'
,
activity_views
.
ActivityViewSet
,
'
activity
'
)
router
.
register
(
r
'
tags
'
,
views
.
TagViewSet
,
'
tags
'
)
router
.
register
(
r
'
tracks
'
,
views
.
TrackViewSet
,
'
tracks
'
)
router
.
register
(
r
'
trackfiles
'
,
views
.
TrackFileViewSet
,
'
trackfiles
'
)
router
.
register
(
r
'
artists
'
,
views
.
ArtistViewSet
,
'
artists
'
)
router
.
register
(
r
'
albums
'
,
views
.
AlbumViewSet
,
'
albums
'
)
router
.
register
(
r
'
import-batches
'
,
views
.
ImportBatchViewSet
,
'
import-batches
'
)
router
.
register
(
r
'
import-jobs
'
,
views
.
ImportJobViewSet
,
'
import-jobs
'
)
router
.
register
(
r
'
submit
'
,
views
.
SubmitViewSet
,
'
submit
'
)
router
.
register
(
r
'
playlists
'
,
playlists_views
.
PlaylistViewSet
,
'
playlists
'
)
router
.
register
(
r
"
settings
"
,
GlobalPreferencesViewSet
,
base_name
=
"
settings
"
)
router
.
register
(
r
"
activity
"
,
activity_views
.
ActivityViewSet
,
"
activity
"
)
router
.
register
(
r
"
tags
"
,
views
.
TagViewSet
,
"
tags
"
)
router
.
register
(
r
"
tracks
"
,
views
.
TrackViewSet
,
"
tracks
"
)
router
.
register
(
r
"
trackfiles
"
,
views
.
TrackFileViewSet
,
"
trackfiles
"
)
router
.
register
(
r
"
artists
"
,
views
.
ArtistViewSet
,
"
artists
"
)
router
.
register
(
r
"
albums
"
,
views
.
AlbumViewSet
,
"
albums
"
)
router
.
register
(
r
"
import-batches
"
,
views
.
ImportBatchViewSet
,
"
import-batches
"
)
router
.
register
(
r
"
import-jobs
"
,
views
.
ImportJobViewSet
,
"
import-jobs
"
)
router
.
register
(
r
"
submit
"
,
views
.
SubmitViewSet
,
"
submit
"
)
router
.
register
(
r
"
playlists
"
,
playlists_views
.
PlaylistViewSet
,
"
playlists
"
)
router
.
register
(
r
'playlist-tracks'
,
playlists_views
.
PlaylistTrackViewSet
,
'playlist-tracks'
)
r
"playlist-tracks"
,
playlists_views
.
PlaylistTrackViewSet
,
"playlist-tracks"
)
v1_patterns
=
router
.
urls
subsonic_router
=
routers
.
SimpleRouter
(
trailing_slash
=
False
)
subsonic_router
.
register
(
r
'
subsonic/rest
'
,
SubsonicViewSet
,
base_name
=
'
subsonic
'
)
subsonic_router
.
register
(
r
"
subsonic/rest
"
,
SubsonicViewSet
,
base_name
=
"
subsonic
"
)
v1_patterns
+=
[
url
(
r
'^instance/'
,
url
(
r
"^instance/"
,
include
((
"funkwhale_api.instance.urls"
,
"instance"
),
namespace
=
"instance"
),
),
url
(
r
"^manage/"
,
include
((
"funkwhale_api.manage.urls"
,
"manage"
),
namespace
=
"manage"
),
),
url
(
r
"^federation/"
,
include
(
(
'funkwhale_api.instance.urls'
,
'instance'
),
namespace
=
'instance'
)),
url
(
r
'^manage/'
,
include
(
(
'funkwhale_api.manage.urls'
,
'manage'
),
namespace
=
'manage'
)),
url
(
r
'^federation/'
,
include
(
(
'funkwhale_api.federation.api_urls'
,
'federation'
),
namespace
=
'federation'
)),
url
(
r
'^providers/'
,
include
(
(
'funkwhale_api.providers.urls'
,
'providers'
),
namespace
=
'providers'
)),
url
(
r
'^favorites/'
,
include
(
(
'funkwhale_api.favorites.urls'
,
'favorites'
),
namespace
=
'favorites'
)),
url
(
r
'^search$'
,
views
.
Search
.
as_view
(),
name
=
'search'
),
url
(
r
'^radios/'
,
include
(
(
'funkwhale_api.radios.urls'
,
'radios'
),
namespace
=
'radios'
)),
url
(
r
'^history/'
,
include
(
(
'funkwhale_api.history.urls'
,
'history'
),
namespace
=
'history'
)),
url
(
r
'^users/'
,
include
(
(
'funkwhale_api.users.api_urls'
,
'users'
),
namespace
=
'users'
)),
url
(
r
'^requests/'
,
include
(
(
'funkwhale_api.requests.api_urls'
,
'requests'
),
namespace
=
'requests'
)),
url
(
r
'^token/$'
,
jwt_views
.
obtain_jwt_token
,
name
=
'token'
),
url
(
r
'^token/refresh/$'
,
jwt_views
.
refresh_jwt_token
,
name
=
'token_refresh'
),
(
"funkwhale_api.federation.api_urls"
,
"federation"
),
namespace
=
"federation"
),
),
url
(
r
"^providers/"
,
include
((
"funkwhale_api.providers.urls"
,
"providers"
),
namespace
=
"providers"
),
),
url
(
r
"^favorites/"
,
include
((
"funkwhale_api.favorites.urls"
,
"favorites"
),
namespace
=
"favorites"
),
),
url
(
r
"^search$"
,
views
.
Search
.
as_view
(),
name
=
"search"
),
url
(
r
"^radios/"
,
include
((
"funkwhale_api.radios.urls"
,
"radios"
),
namespace
=
"radios"
),
),
url
(
r
"^history/"
,
include
((
"funkwhale_api.history.urls"
,
"history"
),
namespace
=
"history"
),
),
url
(
r
"^users/"
,
include
((
"funkwhale_api.users.api_urls"
,
"users"
),
namespace
=
"users"
),
),
url
(
r
"^requests/"
,
include
((
"funkwhale_api.requests.api_urls"
,
"requests"
),
namespace
=
"requests"
),
),
url
(
r
"^token/$"
,
jwt_views
.
obtain_jwt_token
,
name
=
"token"
),
url
(
r
"^token/refresh/$"
,
jwt_views
.
refresh_jwt_token
,
name
=
"token_refresh"
),
]
urlpatterns
=
[
url
(
r
'
^v1/
'
,
include
((
v1_patterns
,
'
v1
'
),
namespace
=
'
v1
'
))
]
+
format_suffix_patterns
(
subsonic_router
.
urls
,
allowed
=
[
'
view
'
])
url
(
r
"
^v1/
"
,
include
((
v1_patterns
,
"
v1
"
),
namespace
=
"
v1
"
))
]
+
format_suffix_patterns
(
subsonic_router
.
urls
,
allowed
=
[
"
view
"
])
api/config/routing.py
View file @
62ca3bd7
...
...
@@ -7,12 +7,13 @@ from funkwhale_api.common.auth import TokenAuthMiddleware
from
funkwhale_api.instance
import
consumers
application
=
ProtocolTypeRouter
({
# Empty for now (http->django views is added by default)
"websocket"
:
TokenAuthMiddleware
(
URLRouter
([
url
(
"^api/v1/instance/activity$"
,
consumers
.
InstanceActivityConsumer
),
])
),
})
application
=
ProtocolTypeRouter
(
{
# Empty for now (http->django views is added by default)
"websocket"
:
TokenAuthMiddleware
(
URLRouter
(
[
url
(
"^api/v1/instance/activity$"
,
consumers
.
InstanceActivityConsumer
)]
)
)
}
)
api/config/settings/common.py
View file @
62ca3bd7
This diff is collapsed.
Click to expand it.
api/config/settings/local.py
View file @
62ca3bd7
# -*- coding: utf-8 -*-
'''
"""
Local settings
- Run in Debug mode
- Use console backend for emails
- Add Django Debug Toolbar
- Add django-extensions as app
'''
"""
from
.common
import
*
# noqa
# DEBUG
# ------------------------------------------------------------------------------
DEBUG
=
env
.
bool
(
'
DJANGO_DEBUG
'
,
default
=
True
)
TEMPLATES
[
0
][
'
OPTIONS
'
][
'
debug
'
]
=
DEBUG
DEBUG
=
env
.
bool
(
"
DJANGO_DEBUG
"
,
default
=
True
)
TEMPLATES
[
0
][
"
OPTIONS
"
][
"
debug
"
]
=
DEBUG
# SECRET CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
# Note: This key only used for development and testing.
SECRET_KEY
=
env
(
"DJANGO_SECRET_KEY"
,
default
=
'mc$&b=5j#6^bv7tld1gyjp2&+^-qrdy=0sw@r5sua*1zp4fmxc'
)
SECRET_KEY
=
env
(
"DJANGO_SECRET_KEY"
,
default
=
"mc$&b=5j#6^bv7tld1gyjp2&+^-qrdy=0sw@r5sua*1zp4fmxc"
)
# Mail settings
# ------------------------------------------------------------------------------
EMAIL_HOST
=
'
localhost
'
EMAIL_HOST
=
"
localhost
"
EMAIL_PORT
=
1025
# django-debug-toolbar
# ------------------------------------------------------------------------------
MIDDLEWARE
+=
(
'
debug_toolbar.middleware.DebugToolbarMiddleware
'
,)
MIDDLEWARE
+=
(
"
debug_toolbar.middleware.DebugToolbarMiddleware
"
,)
# INTERNAL_IPS = ('127.0.0.1', '10.0.2.2',)
DEBUG_TOOLBAR_CONFIG
=
{
'DISABLE_PANELS'
:
[
'debug_toolbar.panels.redirects.RedirectsPanel'
,
],
'SHOW_TEMPLATE_CONTEXT'
:
True
,
'SHOW_TOOLBAR_CALLBACK'
:
lambda
request
:
True
,
"DISABLE_PANELS"
:
[
"debug_toolbar.panels.redirects.RedirectsPanel"
],
"SHOW_TEMPLATE_CONTEXT"
:
True
,
"SHOW_TOOLBAR_CALLBACK"
:
lambda
request
:
True
,
}
# django-extensions
# ------------------------------------------------------------------------------
# INSTALLED_APPS += ('django_extensions', )
INSTALLED_APPS
+=
(
'
debug_toolbar
'
,
)
INSTALLED_APPS
+=
(
"
debug_toolbar
"
,
)
# TESTING
# ------------------------------------------------------------------------------
TEST_RUNNER
=
'
django.test.runner.DiscoverRunner
'
TEST_RUNNER
=
"
django.test.runner.DiscoverRunner
"
########## CELERY
# In development, all tasks will be executed locally by blocking until the task returns
...
...
@@ -57,23 +57,15 @@ CELERY_TASK_ALWAYS_EAGER = False
# Your local stuff: Below this line define 3rd party library settings
LOGGING
=
{
'version'
:
1
,
'handlers'
:
{
'console'
:{
'level'
:
'DEBUG'
,
'class'
:
'logging.StreamHandler'
,
},
},
'loggers'
:
{
'django.request'
:
{
'handlers'
:[
'console'
],
'propagate'
:
True
,
'level'
:
'DEBUG'
,
},
''
:
{
'level'
:
'DEBUG'
,
'handlers'
:
[
'console'
],
"version"
:
1
,
"handlers"
:
{
"console"
:
{
"level"
:
"DEBUG"
,
"class"
:
"logging.StreamHandler"
}},
"loggers"
:
{
"django.request"
:
{
"handlers"
:
[
"console"
],
"propagate"
:
True
,
"level"
:
"DEBUG"
,
},
""
:
{
"level"
:
"DEBUG"
,
"handlers"
:
[
"console"
]},
},
}
CSRF_TRUSTED_ORIGINS
=
[
o
for
o
in
ALLOWED_HOSTS
]
api/config/settings/production.py
View file @
62ca3bd7
# -*- coding: utf-8 -*-
'''
"""
Production Configurations
- Use djangosecure
...
...
@@ -8,7 +8,7 @@ Production Configurations
- Use Redis on Heroku
'''
"""
from
__future__
import
absolute_import
,
unicode_literals
from
django.utils
import
six
...
...
@@ -58,19 +58,24 @@ CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS
# ------------------------------------------------------------------------------
# Uploaded Media Files
# ------------------------
DEFAULT_FILE_STORAGE
=
'
django.core.files.storage.FileSystemStorage
'
DEFAULT_FILE_STORAGE
=
"
django.core.files.storage.FileSystemStorage
"
# Static Assets
# ------------------------
STATICFILES_STORAGE
=
'
django.contrib.staticfiles.storage.StaticFilesStorage
'
STATICFILES_STORAGE
=
"
django.contrib.staticfiles.storage.StaticFilesStorage
"
# TEMPLATE CONFIGURATION
# ------------------------------------------------------------------------------
# See:
# https://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.loaders.cached.Loader
TEMPLATES
[
0
][
'OPTIONS'
][
'loaders'
]
=
[
(
'django.template.loaders.cached.Loader'
,
[
'django.template.loaders.filesystem.Loader'
,
'django.template.loaders.app_directories.Loader'
,
]),
TEMPLATES
[
0
][
"OPTIONS"
][
"loaders"
]
=
[
(
"django.template.loaders.cached.Loader"
,
[
"django.template.loaders.filesystem.Loader"
,
"django.template.loaders.app_directories.Loader"
,
],
)
]
# CACHING
...
...
@@ -78,7 +83,6 @@ TEMPLATES[0]['OPTIONS']['loaders'] = [
# Heroku URL does not pass the DB number, so we parse it in
# LOGGING CONFIGURATION
# ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
...
...
@@ -88,43 +92,39 @@ TEMPLATES[0]['OPTIONS']['loaders'] = [
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING
=
{
'version'
:
1
,
'disable_existing_loggers'
:
False
,
'filters'
:
{
'require_debug_false'
:
{
'()'
:
'django.utils.log.RequireDebugFalse'
"version"
:
1
,
"disable_existing_loggers"
:
False
,
"filters"
:
{
"require_debug_false"
:
{
"()"
:
"django.utils.log.RequireDebugFalse"
}},
"formatters"
:
{
"verbose"
:
{
"format"
:
"%(levelname)s %(asctime)s %(module)s "
"%(process)d %(thread)d %(message)s"
}
},
'formatters'
:
{
'verbose'
:
{
'format'
:
'%(levelname)s %(asctime)s %(module)s '
'%(process)d %(thread)d %(message)s'
"handlers"
:
{
"mail_admins"
:
{
"level"
:
"ERROR"
,
"filters"
:
[
"require_debug_false"
],
"class"
:
"django.utils.log.AdminEmailHandler"
,
},
"console"
:
{
"level"
:
"DEBUG"
,
"class"
:
"logging.StreamHandler"
,
"formatter"
:
"verbose"
,
},
},
'handl
ers
'
:
{
'mail_admins'
:
{
'level'
:
'ERROR'
,
'filters'
:
[
'require_debug_false'
]
,
'class'
:
'django.utils.log.AdminEmailHandler'
"logg
ers
"
:
{
"django.request"
:
{
"handlers"
:
[
"mail_admins"
]
,
"level"
:
"ERROR"
,
"propagate"
:
True
,
},
'console'
:
{
'
level
'
:
'DEBUG'
,
'class'
:
'logging.StreamHandler'
,
'formatter'
:
'verbose'
,
"django.security.DisallowedHost"
:
{
"
level
"
:
"ERROR"
,
"handlers"
:
[
"console"
,
"mail_admins"
]
,
"propagate"
:
True
,
},
},
'loggers'
:
{
'django.request'
:
{
'handlers'
:
[
'mail_admins'
],
'level'
:
'ERROR'
,
'propagate'
:
True
},
'django.security.DisallowedHost'
:
{
'level'
:
'ERROR'
,
'handlers'
:
[
'console'
,
'mail_admins'
],
'propagate'
:
True
}
}
}
...
...
api/config/urls.py
View file @
62ca3bd7
...
...
@@ -11,32 +11,30 @@ from django.views import defaults as default_views
urlpatterns
=
[
# Django Admin, use {% url 'admin:index' %}
url
(
settings
.
ADMIN_URL
,
admin
.
site
.
urls
),
url
(
r
'^api/'
,
include
((
"config.api_urls"
,
'api'
),
namespace
=
"api"
)),
url
(
r
'^'
,
include
(
(
'funkwhale_api.federation.urls'
,
'federation'
),
namespace
=
"federation"
)),
url
(
r
'^api/v1/auth/'
,
include
(
'rest_auth.urls'
)),
url
(
r
'^api/v1/auth/registration/'
,
include
(
'funkwhale_api.users.rest_auth_urls'
)),
url
(
r
'^accounts/'
,
include
(
'allauth.urls'
)),
url
(
r
"^api/"
,
include
((
"config.api_urls"
,
"api"
),
namespace
=
"api"
)),
url
(
r
"^"
,
include
(
(
"funkwhale_api.federation.urls"
,
"federation"
),
namespace
=
"federation"
),
),
url
(
r
"^api/v1/auth/"
,
include
(
"rest_auth.urls"
)),
url
(
r
"^api/v1/auth/registration/"
,
include
(
"funkwhale_api.users.rest_auth_urls"
)),
url
(
r
"^accounts/"
,
include
(
"allauth.urls"
)),
# Your stuff: custom urls includes go here
]
if
settings
.
DEBUG
:
# This allows the error pages to be debugged during development, just visit
# these url in browser to see how these error pages look like.
urlpatterns
+=
[
url
(
r
'
^400/$
'
,
default_views
.
bad_request
),
url
(
r
'
^403/$
'
,
default_views
.
permission_denied
),
url
(
r
'
^404/$
'
,
default_views
.
page_not_found
),
url
(
r
'
^500/$
'
,
default_views
.
server_error
),
url
(
r
"
^400/$
"
,
default_views
.
bad_request
),
url
(
r
"
^403/$
"
,
default_views
.
permission_denied
),
url
(
r
"
^404/$
"
,
default_views
.
page_not_found
),
url
(
r
"
^500/$
"
,
default_views
.
server_error
),
]
+
static
(
settings
.
MEDIA_URL
,
document_root
=
settings
.
MEDIA_ROOT
)
if
'
debug_toolbar
'
in
settings
.
INSTALLED_APPS
:
if
"
debug_toolbar
"
in
settings
.
INSTALLED_APPS
:
import
debug_toolbar
urlpatterns
+=
[
url
(
r
'^__debug__/'
,
include
(
debug_toolbar
.
urls
)),
]
urlpatterns
+=
[
url
(
r
"^__debug__/"
,
include
(
debug_toolbar
.
urls
))]
api/demo/demo-user.py
View file @
62ca3bd7
from
funkwhale_api.users.models
import
User
u
=
User
.
objects
.
create
(
email
=
'
demo@demo.com
'
,
username
=
'
demo
'
,
is_staff
=
True
)
u
.
set_password
(
'
demo
'
)
u
.
subsonic_api_token
=
'
demo
'
u
=
User
.
objects
.
create
(
email
=
"
demo@demo.com
"
,
username
=
"
demo
"
,
is_staff
=
True
)
u
.
set_password
(
"
demo
"
)
u
.
subsonic_api_token
=
"
demo
"
u
.
save
()
api/funkwhale_api/__init__.py
View file @
62ca3bd7
# -*- coding: utf-8 -*-
__version__
=
'0.14.1'
__version_info__
=
tuple
([
int
(
num
)
if
num
.
isdigit
()
else
num
for
num
in
__version__
.
replace
(
'-'
,
'.'
,
1
).
split
(
'.'
)])
__version__
=
"0.14.1"
__version_info__
=
tuple
(
[
int
(
num
)
if
num
.
isdigit
()
else
num
for
num
in
__version__
.
replace
(
"-"
,
"."
,
1
).
split
(
"."
)
]
)
api/funkwhale_api/activity/apps.py
View file @
62ca3bd7
...
...
@@ -2,8 +2,9 @@ from django.apps import AppConfig, apps
from
.
import
record
class
ActivityConfig
(
AppConfig
):
name
=
'
funkwhale_api.activity
'
name
=
"
funkwhale_api.activity
"
def
ready
(
self
):
super
(
ActivityConfig
,
self
).
ready
()
...
...
api/funkwhale_api/activity/record.py
View file @
62ca3bd7
...
...
@@ -2,37 +2,36 @@ import persisting_theory
class
ActivityRegistry
(
persisting_theory
.
Registry
):
look_into
=
'
activities
'
look_into
=
"
activities
"
def
_register_for_model
(
self
,
model
,
attr
,
value
):
key
=
model
.
_meta
.
label
d
=
self
.
setdefault
(
key
,
{
'
consumers
'
:
[]})
d
=
self
.
setdefault
(
key
,
{
"
consumers
"
:
[]})
d
[
attr
]
=
value
def
register_serializer
(
self
,
serializer_class
):
model
=
serializer_class
.
Meta
.
model
self
.
_register_for_model
(
model
,
'
serializer
'
,
serializer_class
)
self
.
_register_for_model
(
model
,
"
serializer
"
,
serializer_class
)
return
serializer_class
def
register_consumer
(
self
,
label
):
def
decorator
(
func
):
consumers
=
self
[
label
][
'
consumers
'
]
consumers
=
self
[
label
][
"
consumers
"
]
if
func
not
in
consumers
:
consumers
.
append
(
func
)
return
func
return
decorator
registry
=
ActivityRegistry
()
def
send
(
obj
):
conf
=
registry
[
obj
.
__class__
.
_meta
.
label
]
consumers
=
conf
[
'
consumers
'
]
consumers
=
conf
[
"
consumers
"
]
if
not
consumers
:
return
serializer
=
conf
[
'
serializer
'
](
obj
)
serializer
=
conf
[
"
serializer
"
](
obj
)
for
consumer
in
consumers
:
consumer
(
data
=
serializer
.
data
,
obj
=
obj
)
api/funkwhale_api/activity/serializers.py
View file @
62ca3bd7
...
...
@@ -4,8 +4,8 @@ from funkwhale_api.activity import record
class
ModelSerializer
(
serializers
.
ModelSerializer
):
id
=
serializers
.
CharField
(
source
=
'
get_activity_url
'
)
local_id
=
serializers
.
IntegerField
(
source
=
'
id
'
)
id
=
serializers
.
CharField
(
source
=
"
get_activity_url
"
)
local_id
=
serializers
.
IntegerField
(
source
=
"
id
"
)
# url = serializers.SerializerMethodField()
def
get_url
(
self
,
obj
):
...
...
@@ -17,8 +17,7 @@ class AutoSerializer(serializers.Serializer):
A serializer that will automatically use registered activity serializers
to serialize an henerogeneous list of objects (favorites, listenings, etc.)
"""
def
to_representation
(
self
,
instance
):
serializer
=
record
.
registry
[
instance
.
_meta
.
label
][
'serializer'
](
instance
)
serializer
=
record
.
registry
[
instance
.
_meta
.
label
][
"serializer"
](
instance
)
return
serializer
.
data
api/funkwhale_api/activity/utils.py
View file @
62ca3bd7
...
...
@@ -6,31 +6,25 @@ from funkwhale_api.history.models import Listening
def
combined_recent
(
limit
,
**
kwargs
):
datetime_field
=
kwargs
.
pop
(
'datetime_field'
,
'creation_date'
)
source_querysets
=
{
qs
.
model
.
_meta
.
label
:
qs
for
qs
in
kwargs
.
pop
(
'querysets'
)
}
datetime_field
=
kwargs
.
pop
(
"datetime_field"
,
"creation_date"
)
source_querysets
=
{
qs
.
model
.
_meta
.
label
:
qs
for
qs
in
kwargs
.
pop
(
"querysets"
)}
querysets
=
{
k
:
qs
.
annotate
(
__type
=
models
.
Value
(
qs
.
model
.
_meta
.
label
,
output_field
=
models
.
CharField
()
)
).
values
(
'pk'
,
datetime_field
,
'__type'
)
__type
=
models
.
Value
(
qs
.
model
.
_meta
.
label
,
output_field
=
models
.
CharField
())
).
values
(
"pk"
,
datetime_field
,
"__type"
)
for
k
,
qs
in
source_querysets
.
items
()
}
_qs_list
=
list
(
querysets
.
values
())
union_qs
=
_qs_list
[
0
].
union
(
*
_qs_list
[
1
:])
records
=
[]
for
row
in
union_qs
.
order_by
(
'-{}'
.
format
(
datetime_field
))[:
limit
]:
records
.
append
({
'type'
:
row
[
'__type'
],
'when'
:
row
[
datetime_field
],
'pk'
:
row
[
'pk'
]
})
for
row
in
union_qs
.
order_by
(
"-{}"
.
format
(
datetime_field
))[:
limit
]:
records
.
append
(
{
"type"
:
row
[
"__type"
],
"when"
:
row
[
datetime_field
],
"pk"
:
row
[
"pk"
]}
)
# Now we bulk-load each object type in turn
to_load
=
{}
for
record
in
records
:
to_load
.
setdefault
(
record
[
'
type
'
],
[]).
append
(
record
[
'
pk
'
])
to_load
.
setdefault
(
record
[
"
type
"
],
[]).
append
(
record
[
"
pk
"
])
fetched
=
{}
for
key
,
pks
in
to_load
.
items
():
...
...
@@ -39,26 +33,19 @@ def combined_recent(limit, **kwargs):
# Annotate 'records' with loaded objects
for
record
in
records
:
record
[
'
object
'
]
=
fetched
[(
record
[
'
type
'
],
record
[
'
pk
'
])]
record
[
"
object
"
]
=
fetched
[(
record
[
"
type
"
],
record
[
"
pk
"
])]
return
records
def
get_activity
(
user
,
limit
=
20
):
query
=
fields
.
privacy_level_query
(
user
,
lookup_field
=
'user__privacy_level'
)
query
=
fields
.
privacy_level_query
(
user
,
lookup_field
=
"user__privacy_level"
)
querysets
=
[
Listening
.
objects
.
filter
(
query
).
select_related
(
'track'
,
'user'
,
'track__artist'
,
'track__album__artist'
,
"track"
,
"user"
,
"track__artist"
,
"track__album__artist"
),
TrackFavorite
.
objects
.
filter
(
query
).
select_related
(
'track'
,
'user'
,
'track__artist'
,
'track__album__artist'
,
"track"
,
"user"
,
"track__artist"
,
"track__album__artist"
),
]
records
=
combined_recent
(
limit
=
limit
,
querysets
=
querysets
)
return
[
r
[
'
object
'
]
for
r
in
records
]