See #170: store and compute modification date on artists

This commit is contained in:
Eliot Berriot 2020-03-19 14:41:15 +01:00
parent b5297150f0
commit 1654044a9f
No known key found for this signature in database
GPG Key ID: 6B501DFD73514E14
12 changed files with 85 additions and 7 deletions

View File

@ -28,10 +28,17 @@ class ChannelFilter(moderation_filters.HiddenContentFilterSet):
subscribed = django_filters.BooleanFilter( subscribed = django_filters.BooleanFilter(
field_name="_", method="filter_subscribed" field_name="_", method="filter_subscribed"
) )
ordering = django_filters.OrderingFilter(
# tuple-mapping retains order
fields=(
("creation_date", "creation_date"),
("artist__modification_date", "modification_date"),
)
)
class Meta: class Meta:
model = models.Channel model = models.Channel
fields = ["q", "scope", "tag", "subscribed"] fields = ["q", "scope", "tag", "subscribed", "ordering"]
hidden_content_fields_mapping = moderation_filters.USER_FILTER_CONFIG["CHANNEL"] hidden_content_fields_mapping = moderation_filters.USER_FILTER_CONFIG["CHANNEL"]
def filter_subscribed(self, queryset, name, value): def filter_subscribed(self, queryset, name, value):

View File

@ -384,7 +384,12 @@ def get_channel_from_rss_url(url, raise_exception=False):
library=channel.library, library=channel.library,
delete_existing=True, delete_existing=True,
) )
latest_upload_date = max([upload.creation_date for upload in uploads])
if (
not channel.artist.modification_date
or channel.artist.modification_date < latest_upload_date
):
common_utils.update_modification_date(channel.artist)
return channel, uploads return channel, uploads

View File

@ -77,6 +77,7 @@ class RelatedField(serializers.RelatedField):
self.display_value(item), self.display_value(item),
) )
for item in queryset for item in queryset
if self.serializer
] ]
) )

View File

@ -1,3 +1,5 @@
import datetime
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.utils.deconstruct import deconstructible from django.utils.deconstruct import deconstructible
@ -14,6 +16,7 @@ from urllib.parse import parse_qs, urlencode, urlsplit, urlunsplit
from django.conf import settings from django.conf import settings
from django import urls from django import urls
from django.db import models, transaction from django.db import models, transaction
from django.utils import timezone
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -405,3 +408,17 @@ def get_mimetype_from_ext(path):
def get_audio_mimetype(mt): def get_audio_mimetype(mt):
aliases = {"audio/x-mp3": "audio/mpeg", "audio/mpeg3": "audio/mpeg"} aliases = {"audio/x-mp3": "audio/mpeg", "audio/mpeg3": "audio/mpeg"}
return aliases.get(mt, mt) return aliases.get(mt, mt)
def update_modification_date(obj, field="modification_date"):
IGNORE_DELAY = 60
current_value = getattr(obj, field)
now = timezone.now()
ignore = current_value is not None and current_value < now - datetime.timedelta(
seconds=IGNORE_DELAY
)
if ignore:
setattr(obj, field, now)
obj.__class__.objects.filter(pk=obj.pk).update(**{field: now})
return now

View File

@ -5,7 +5,7 @@ from . import models
@admin.register(models.Artist) @admin.register(models.Artist)
class ArtistAdmin(admin.ModelAdmin): class ArtistAdmin(admin.ModelAdmin):
list_display = ["name", "mbid", "creation_date"] list_display = ["name", "mbid", "creation_date", "modification_date"]
search_fields = ["name", "mbid"] search_fields = ["name", "mbid"]

View File

@ -0,0 +1,33 @@
# Generated by Django 3.0.4 on 2020-03-19 12:49
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('music', '0050_auto_20200129_1344'),
]
operations = [
migrations.RemoveField(
model_name='album',
name='cover',
),
migrations.AddField(
model_name='artist',
name='modification_date',
field=models.DateTimeField(db_index=True, default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='upload',
name='import_status',
field=models.CharField(choices=[('draft', 'Draft'), ('pending', 'Pending'), ('finished', 'Finished'), ('errored', 'Errored'), ('skipped', 'Skipped')], default='pending', max_length=25),
),
migrations.AlterField(
model_name='uploadversion',
name='mimetype',
field=models.CharField(choices=[('audio/mpeg3', 'mp3'), ('audio/x-mp3', 'mp3'), ('audio/mpeg', 'mp3'), ('video/ogg', 'ogg'), ('audio/ogg', 'ogg'), ('audio/opus', 'opus'), ('audio/x-m4a', 'aac'), ('audio/x-m4a', 'm4a'), ('audio/x-flac', 'flac'), ('audio/flac', 'flac')], max_length=50),
),
]

View File

@ -251,7 +251,7 @@ class Artist(APIModelMixin):
choices=ARTIST_CONTENT_CATEGORY_CHOICES, choices=ARTIST_CONTENT_CATEGORY_CHOICES,
null=True, null=True,
) )
modification_date = models.DateTimeField(default=timezone.now, db_index=True)
api = musicbrainz.api.artists api = musicbrainz.api.artists
objects = ArtistQuerySet.as_manager() objects = ArtistQuerySet.as_manager()

View File

@ -160,6 +160,7 @@ def serialize_artist_simple(artist):
"mbid": str(artist.mbid), "mbid": str(artist.mbid),
"name": artist.name, "name": artist.name,
"creation_date": DATETIME_FIELD.to_representation(artist.creation_date), "creation_date": DATETIME_FIELD.to_representation(artist.creation_date),
"modification_date": DATETIME_FIELD.to_representation(artist.modification_date),
"is_local": artist.is_local, "is_local": artist.is_local,
"content_category": artist.content_category, "content_category": artist.content_category,
} }

View File

@ -289,6 +289,8 @@ def process_upload(upload, update_denormalization=True):
"bitrate", "bitrate",
] ]
) )
if channel:
common_utils.update_modification_date(channel.artist)
if update_denormalization: if update_denormalization:
models.TrackActor.create_entries( models.TrackActor.create_entries(

View File

@ -129,7 +129,7 @@ class ArtistViewSet(
required_scope = "libraries" required_scope = "libraries"
anonymous_policy = "setting" anonymous_policy = "setting"
filterset_class = filters.ArtistFilter filterset_class = filters.ArtistFilter
ordering_fields = ("id", "name", "creation_date") ordering_fields = ("id", "name", "creation_date", "modification_date")
fetches = federation_decorators.fetches_route() fetches = federation_decorators.fetches_route()
mutations = common_decorators.mutations_route(types=["update"]) mutations = common_decorators.mutations_route(types=["update"])
@ -186,7 +186,12 @@ class AlbumViewSet(
permission_classes = [oauth_permissions.ScopePermission] permission_classes = [oauth_permissions.ScopePermission]
required_scope = "libraries" required_scope = "libraries"
anonymous_policy = "setting" anonymous_policy = "setting"
ordering_fields = ("creation_date", "release_date", "title") ordering_fields = (
"creation_date",
"release_date",
"title",
"artist__modification_date",
)
filterset_class = filters.AlbumFilter filterset_class = filters.AlbumFilter
fetches = federation_decorators.fetches_route() fetches = federation_decorators.fetches_route()
@ -335,6 +340,7 @@ class TrackViewSet(
"position", "position",
"disc_number", "disc_number",
"artist__name", "artist__name",
"artist__modification_date",
) )
fetches = federation_decorators.fetches_route() fetches = federation_decorators.fetches_route()
mutations = common_decorators.mutations_route(types=["update"]) mutations = common_decorators.mutations_route(types=["update"])

View File

@ -834,9 +834,9 @@ def test_get_channel_from_rss_url(db, r_mock, mocker):
</rss> </rss>
""" """
parsed_feed = feedparser.parse(xml_payload) parsed_feed = feedparser.parse(xml_payload)
r_mock.get(rss_url, text=xml_payload) r_mock.get(rss_url, text=xml_payload)
update_modification_date = mocker.spy(common_utils, "update_modification_date")
feed_init = mocker.spy(serializers.RssFeedSerializer, "__init__") feed_init = mocker.spy(serializers.RssFeedSerializer, "__init__")
feed_save = mocker.spy(serializers.RssFeedSerializer, "save") feed_save = mocker.spy(serializers.RssFeedSerializer, "save")
item_init = mocker.spy(serializers.RssFeedItemSerializer, "__init__") item_init = mocker.spy(serializers.RssFeedItemSerializer, "__init__")
@ -865,6 +865,7 @@ def test_get_channel_from_rss_url(db, r_mock, mocker):
library=channel.library, library=channel.library,
delete_existing=True, delete_existing=True,
) )
update_modification_date.assert_called_once_with(channel.artist)
def test_get_channel_from_rss_honor_mrf_inbox_before_http( def test_get_channel_from_rss_honor_mrf_inbox_before_http(

View File

@ -6,6 +6,7 @@ import uuid
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.utils import timezone from django.utils import timezone
from funkwhale_api.common import utils as common_utils
from funkwhale_api.federation import serializers as federation_serializers from funkwhale_api.federation import serializers as federation_serializers
from funkwhale_api.federation import jsonld from funkwhale_api.federation import jsonld
from funkwhale_api.federation import utils as federation_utils from funkwhale_api.federation import utils as federation_utils
@ -1040,6 +1041,8 @@ def test_process_channel_upload_forces_artist_and_attributed_to(
factories, mocker, faker factories, mocker, faker
): ):
channel = factories["audio.Channel"](attributed_to__local=True) channel = factories["audio.Channel"](attributed_to__local=True)
update_modification_date = mocker.spy(common_utils, "update_modification_date")
attachment = factories["common.Attachment"](actor=channel.attributed_to) attachment = factories["common.Attachment"](actor=channel.attributed_to)
import_metadata = { import_metadata = {
"title": "Real title", "title": "Real title",
@ -1081,6 +1084,8 @@ def test_process_channel_upload_forces_artist_and_attributed_to(
assert upload.track.attributed_to == channel.attributed_to assert upload.track.attributed_to == channel.attributed_to
assert upload.track.attachment_cover == attachment assert upload.track.attachment_cover == attachment
update_modification_date.assert_called_once_with(channel.artist)
def test_process_upload_uses_import_metadata_if_valid(factories, mocker): def test_process_upload_uses_import_metadata_if_valid(factories, mocker):
track = factories["music.Track"]() track = factories["music.Track"]()