feat: Assign upload groups to existing and news v1 uploads
This commit is contained in:
parent
c0282b850d
commit
d6418ff016
|
@ -57,6 +57,7 @@ class UploadAdmin(admin.ModelAdmin):
|
||||||
"size",
|
"size",
|
||||||
"bitrate",
|
"bitrate",
|
||||||
"import_status",
|
"import_status",
|
||||||
|
"upload_group",
|
||||||
]
|
]
|
||||||
list_select_related = ["track"]
|
list_select_related = ["track"]
|
||||||
search_fields = [
|
search_fields = [
|
||||||
|
@ -69,6 +70,11 @@ class UploadAdmin(admin.ModelAdmin):
|
||||||
list_filter = ["mimetype", "import_status", "library__privacy_level"]
|
list_filter = ["mimetype", "import_status", "library__privacy_level"]
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(models.UploadGroup)
|
||||||
|
class UploadGroupAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.UploadVersion)
|
@admin.register(models.UploadVersion)
|
||||||
class UploadVersionAdmin(admin.ModelAdmin):
|
class UploadVersionAdmin(admin.ModelAdmin):
|
||||||
list_display = [
|
list_display = [
|
||||||
|
|
|
@ -164,6 +164,14 @@ class TrackFactory(
|
||||||
license = factory.PostGeneration(_license_post_generation)
|
license = factory.PostGeneration(_license_post_generation)
|
||||||
|
|
||||||
|
|
||||||
|
@registry.register
|
||||||
|
class UploadGroupFactory(factory.django.DjangoModelFactory):
|
||||||
|
name = factory.Faker("name")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = "music.UploadGroup"
|
||||||
|
|
||||||
|
|
||||||
@registry.register
|
@registry.register
|
||||||
class UploadFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
|
class UploadFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
|
||||||
fid = factory.Faker("federation_url")
|
fid = factory.Faker("federation_url")
|
||||||
|
@ -172,6 +180,7 @@ class UploadFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
|
||||||
audio_file = factory.django.FileField(
|
audio_file = factory.django.FileField(
|
||||||
from_path=os.path.join(SAMPLES_PATH, "test.ogg")
|
from_path=os.path.join(SAMPLES_PATH, "test.ogg")
|
||||||
)
|
)
|
||||||
|
upload_group = factory.SubFactory(UploadGroupFactory)
|
||||||
|
|
||||||
bitrate = None
|
bitrate = None
|
||||||
size = None
|
size = None
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 3.2.23 on 2024-01-15 10:55
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("music", "0058_uploadgroup"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="upload",
|
||||||
|
name="upload_group",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="uploads",
|
||||||
|
to="music.uploadgroup",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Generated by Django 3.2.23 on 2024-01-15 10:55
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def populate_upload_groups(apps, schema_editor):
|
||||||
|
upload = apps.get_model("music", "Upload")
|
||||||
|
upload_group = apps.get_model("music", "UploadGroup")
|
||||||
|
for upload in upload.objects.all():
|
||||||
|
group, _ = upload_group.objects.get_or_create(name=upload.import_reference)
|
||||||
|
upload.upload_group = group
|
||||||
|
upload.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("music", "0059_upload_upload_group"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [migrations.RunPython(populate_upload_groups)]
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 3.2.23 on 2024-01-15 11:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("music", "0060_auto_20240115_1055"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="upload",
|
||||||
|
name="upload_group",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="uploads",
|
||||||
|
to="music.uploadgroup",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -738,6 +738,9 @@ class Upload(models.Model):
|
||||||
related_name="uploads",
|
related_name="uploads",
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
)
|
)
|
||||||
|
upload_group = models.ForeignKey(
|
||||||
|
"UploadGroup", related_name="uploads", on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
# metadata from federation
|
# metadata from federation
|
||||||
metadata = JSONField(
|
metadata = JSONField(
|
||||||
|
@ -754,6 +757,7 @@ class Upload(models.Model):
|
||||||
)
|
)
|
||||||
# a short reference provided by the client to group multiple files
|
# a short reference provided by the client to group multiple files
|
||||||
# in the same import
|
# in the same import
|
||||||
|
# TODO DEPRECATED This can be removed when APIv1 gets removed or fully replace by import_group.name
|
||||||
import_reference = models.CharField(max_length=50, default=get_import_reference)
|
import_reference = models.CharField(max_length=50, default=get_import_reference)
|
||||||
|
|
||||||
# optional metadata about import results (error messages, etc.)
|
# optional metadata about import results (error messages, etc.)
|
||||||
|
@ -1434,5 +1438,17 @@ def update_request_status(sender, instance, created, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
class UploadGroup(models.Model):
|
class UploadGroup(models.Model):
|
||||||
|
"""
|
||||||
|
Upload groups are supposed to bundle uploads in order to make it easier to keep an overview
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
name A name that can be selected by the user
|
||||||
|
guid A globally unique identifier to reference the group
|
||||||
|
"""
|
||||||
|
|
||||||
name = models.CharField(max_length=255, default=datetime.datetime.now)
|
name = models.CharField(max_length=255, default=datetime.datetime.now)
|
||||||
guid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
guid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
|
@ -790,7 +790,11 @@ class UploadViewSet(
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
upload = serializer.save()
|
group_name = serializer.validated_data.get("import_reference") or str(
|
||||||
|
datetime.datetime.date(datetime.datetime.now())
|
||||||
|
)
|
||||||
|
upload_group, _ = models.UploadGroup.objects.get_or_create(name=group_name)
|
||||||
|
upload = serializer.save(upload_group=upload_group)
|
||||||
if upload.import_status == "pending":
|
if upload.import_status == "pending":
|
||||||
common_utils.on_commit(tasks.process_upload.delay, upload_id=upload.pk)
|
common_utils.on_commit(tasks.process_upload.delay, upload_id=upload.pk)
|
||||||
|
|
||||||
|
|
|
@ -810,6 +810,56 @@ def test_user_can_create_upload(logged_in_api_client, factories, mocker, audio_f
|
||||||
m.assert_called_once_with(tasks.process_upload.delay, upload_id=upload.pk)
|
m.assert_called_once_with(tasks.process_upload.delay, upload_id=upload.pk)
|
||||||
|
|
||||||
|
|
||||||
|
def test_upload_creates_implicit_upload_group(
|
||||||
|
logged_in_api_client, factories, mocker, audio_file
|
||||||
|
):
|
||||||
|
library = factories["music.Library"](actor__user=logged_in_api_client.user)
|
||||||
|
url = reverse("api:v1:uploads-list")
|
||||||
|
upload_group_count = models.UploadGroup.objects.count()
|
||||||
|
|
||||||
|
response = logged_in_api_client.post(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
"audio_file": audio_file,
|
||||||
|
"source": "upload://test",
|
||||||
|
"library": library.uuid,
|
||||||
|
"import_metadata": '{"title": "foo"}',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 201
|
||||||
|
assert upload_group_count + 1 == models.UploadGroup.objects.count()
|
||||||
|
assert (
|
||||||
|
models.UploadGroup.objects.filter(
|
||||||
|
name=str(datetime.datetime.date(datetime.datetime.now()))
|
||||||
|
).count()
|
||||||
|
== 1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_upload_creates_named_upload_group(
|
||||||
|
logged_in_api_client, factories, mocker, audio_file
|
||||||
|
):
|
||||||
|
library = factories["music.Library"](actor__user=logged_in_api_client.user)
|
||||||
|
url = reverse("api:v1:uploads-list")
|
||||||
|
upload_group_count = models.UploadGroup.objects.count()
|
||||||
|
|
||||||
|
response = logged_in_api_client.post(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
"audio_file": audio_file,
|
||||||
|
"source": "upload://test",
|
||||||
|
"import_reference": "test",
|
||||||
|
"library": library.uuid,
|
||||||
|
"import_metadata": '{"title": "foo"}',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 201
|
||||||
|
assert upload_group_count + 1 == models.UploadGroup.objects.count()
|
||||||
|
assert models.UploadGroup.objects.filter(name="test").count() == 1
|
||||||
|
|
||||||
|
|
||||||
def test_user_can_create_upload_in_channel(
|
def test_user_can_create_upload_in_channel(
|
||||||
logged_in_api_client, factories, mocker, audio_file
|
logged_in_api_client, factories, mocker, audio_file
|
||||||
):
|
):
|
||||||
|
|
Loading…
Reference in New Issue