See #890: added moderation note model, serializers and views
This commit is contained in:
parent
80c8610632
commit
ab3bc96783
|
@ -363,3 +363,19 @@ class ManageReportFilterSet(filters.FilterSet):
|
|||
class Meta:
|
||||
model = moderation_models.Report
|
||||
fields = ["q", "is_handled", "type", "submitter_email"]
|
||||
|
||||
|
||||
class ManageNoteFilterSet(filters.FilterSet):
|
||||
q = fields.SmartSearchFilter(
|
||||
config=search.SearchConfig(
|
||||
search_fields={"summary": {"to": "summary"}},
|
||||
filter_fields={
|
||||
"uuid": {"to": "uuid"},
|
||||
"author": get_actor_filter("author"),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = moderation_models.Note
|
||||
fields = ["q"]
|
||||
|
|
|
@ -3,6 +3,7 @@ from django.db import transaction
|
|||
|
||||
from rest_framework import serializers
|
||||
|
||||
from funkwhale_api.common import fields as common_fields
|
||||
from funkwhale_api.common import serializers as common_serializers
|
||||
from funkwhale_api.common import utils as common_utils
|
||||
from funkwhale_api.federation import models as federation_models
|
||||
|
@ -676,3 +677,27 @@ class ManageReportSerializer(serializers.ModelSerializer):
|
|||
"target_owner",
|
||||
"summary",
|
||||
]
|
||||
|
||||
|
||||
class ManageNoteSerializer(serializers.ModelSerializer):
|
||||
author = ManageBaseActorSerializer(required=False)
|
||||
target = common_fields.GenericRelation(
|
||||
{
|
||||
"report": {
|
||||
"queryset": moderation_models.Report.objects.all(),
|
||||
"id_attr": "uuid",
|
||||
"id_field": serializers.UUIDField(),
|
||||
},
|
||||
"account": {
|
||||
"queryset": federation_models.Actor.objects.all(),
|
||||
"id_attr": "full_username",
|
||||
"id_field": serializers.EmailField(),
|
||||
"get_query": moderation_serializers.get_actor_query,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = moderation_models.Note
|
||||
fields = ["id", "uuid", "creation_date", "summary", "author", "target"]
|
||||
read_only_fields = ["uuid", "creation_date", "author"]
|
||||
|
|
|
@ -18,6 +18,7 @@ moderation_router.register(
|
|||
r"instance-policies", views.ManageInstancePolicyViewSet, "instance-policies"
|
||||
)
|
||||
moderation_router.register(r"reports", views.ManageReportViewSet, "reports")
|
||||
moderation_router.register(r"notes", views.ManageNoteViewSet, "notes")
|
||||
|
||||
users_router = routers.OptionalSlashRouter()
|
||||
users_router.register(r"users", views.ManageUserViewSet, "users")
|
||||
|
|
|
@ -478,6 +478,27 @@ class ManageReportViewSet(
|
|||
ordering_fields = ["id", "creation_date", "handled_date"]
|
||||
|
||||
|
||||
class ManageNoteViewSet(
|
||||
mixins.ListModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
mixins.CreateModelMixin,
|
||||
viewsets.GenericViewSet,
|
||||
):
|
||||
lookup_field = "uuid"
|
||||
queryset = (
|
||||
moderation_models.Note.objects.all().order_by("-creation_date").select_related()
|
||||
)
|
||||
serializer_class = serializers.ManageNoteSerializer
|
||||
filterset_class = filters.ManageNoteFilterSet
|
||||
required_scope = "instance:notes"
|
||||
ordering_fields = ["id", "creation_date"]
|
||||
|
||||
def perform_create(self, serializer):
|
||||
author = self.request.user.actor
|
||||
return serializer.save(author=author)
|
||||
|
||||
|
||||
class ManageTagViewSet(
|
||||
mixins.ListModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
|
|
|
@ -30,7 +30,6 @@ class InstancePolicyAdmin(admin.ModelAdmin):
|
|||
list_select_related = True
|
||||
|
||||
|
||||
|
||||
@admin.register(models.Report)
|
||||
class ReportAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
|
@ -42,13 +41,8 @@ class ReportAdmin(admin.ModelAdmin):
|
|||
"creation_date",
|
||||
"handled_date",
|
||||
]
|
||||
list_filter = [
|
||||
"type",
|
||||
"is_handled",
|
||||
]
|
||||
search_fields = [
|
||||
"summary",
|
||||
]
|
||||
list_filter = ["type", "is_handled"]
|
||||
search_fields = ["summary"]
|
||||
list_select_related = True
|
||||
|
||||
|
||||
|
|
|
@ -41,6 +41,16 @@ class UserFilterFactory(NoUpdateOnCreate, factory.DjangoModelFactory):
|
|||
)
|
||||
|
||||
|
||||
@registry.register
|
||||
class NoteFactory(NoUpdateOnCreate, factory.DjangoModelFactory):
|
||||
author = factory.SubFactory(federation_factories.ActorFactory)
|
||||
target = None
|
||||
summary = factory.Faker("paragraph")
|
||||
|
||||
class Meta:
|
||||
model = "moderation.Note"
|
||||
|
||||
|
||||
@registry.register
|
||||
class ReportFactory(NoUpdateOnCreate, factory.DjangoModelFactory):
|
||||
submitter = factory.SubFactory(federation_factories.ActorFactory)
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# Generated by Django 2.2.4 on 2019-08-29 09:08
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('federation', '0020_auto_20190730_0846'),
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('moderation', '0003_report'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Note',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, unique=True)),
|
||||
('creation_date', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('summary', models.TextField(max_length=50000)),
|
||||
('target_id', models.IntegerField(null=True)),
|
||||
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='moderation_notes', to='federation.Actor')),
|
||||
('target_content_type', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -164,6 +164,21 @@ class Report(federation_models.FederationMixin):
|
|||
return super().save(**kwargs)
|
||||
|
||||
|
||||
class Note(models.Model):
|
||||
uuid = models.UUIDField(default=uuid.uuid4, unique=True)
|
||||
creation_date = models.DateTimeField(default=timezone.now)
|
||||
summary = models.TextField(max_length=50000)
|
||||
author = models.ForeignKey(
|
||||
"federation.Actor", related_name="moderation_notes", on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
target_id = models.IntegerField(null=True)
|
||||
target_content_type = models.ForeignKey(
|
||||
ContentType, null=True, on_delete=models.CASCADE
|
||||
)
|
||||
target = GenericForeignKey("target_content_type", "target_id")
|
||||
|
||||
|
||||
@receiver(pre_save, sender=Report)
|
||||
def set_handled_date(sender, instance, **kwargs):
|
||||
if instance.is_handled is True and not instance.handled_date:
|
||||
|
|
|
@ -119,7 +119,15 @@ class TrackStateSerializer(serializers.ModelSerializer):
|
|||
class LibraryStateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = music_models.Library
|
||||
fields = ["id", "uuid", "fid", "name", "description", "creation_date", "privacy_level"]
|
||||
fields = [
|
||||
"id",
|
||||
"uuid",
|
||||
"fid",
|
||||
"name",
|
||||
"description",
|
||||
"creation_date",
|
||||
"privacy_level",
|
||||
]
|
||||
|
||||
|
||||
@state_serializers.register(name="playlists.Playlist")
|
||||
|
|
|
@ -48,6 +48,8 @@ PERMISSIONS_CONFIGURATION = {
|
|||
"write:instance:domains",
|
||||
"read:instance:reports",
|
||||
"write:instance:reports",
|
||||
"read:instance:notes",
|
||||
"write:instance:notes",
|
||||
},
|
||||
},
|
||||
"library": {
|
||||
|
|
|
@ -35,6 +35,7 @@ BASE_SCOPES = [
|
|||
Scope("instance:domains", "Access instance domains"),
|
||||
Scope("instance:policies", "Access instance moderation policies"),
|
||||
Scope("instance:reports", "Access instance moderation reports"),
|
||||
Scope("instance:notes", "Access instance moderation notes"),
|
||||
]
|
||||
SCOPES = [
|
||||
Scope("read", children=[s.copy("read") for s in BASE_SCOPES]),
|
||||
|
|
|
@ -550,3 +550,20 @@ def test_manage_report_serializer(factories, to_api_date):
|
|||
s = serializers.ManageReportSerializer(report)
|
||||
|
||||
assert s.data == expected
|
||||
|
||||
|
||||
def test_manage_note_serializer(factories, to_api_date):
|
||||
actor = factories["federation.Actor"]()
|
||||
note = factories["moderation.Note"](target=actor)
|
||||
|
||||
expected = {
|
||||
"id": note.id,
|
||||
"uuid": str(note.uuid),
|
||||
"summary": note.summary,
|
||||
"creation_date": to_api_date(note.creation_date),
|
||||
"author": serializers.ManageBaseActorSerializer(note.author).data,
|
||||
"target": {"type": "account", "full_username": actor.full_username},
|
||||
}
|
||||
s = serializers.ManageNoteSerializer(note)
|
||||
|
||||
assert s.data == expected
|
||||
|
|
|
@ -391,6 +391,50 @@ def test_upload_delete(factories, superuser_api_client):
|
|||
assert response.status_code == 204
|
||||
|
||||
|
||||
def test_note_create(factories, superuser_api_client):
|
||||
actor = superuser_api_client.user.create_actor()
|
||||
target = factories["federation.Actor"]()
|
||||
data = {
|
||||
"summary": "Hello",
|
||||
"target": {"type": "account", "full_username": target.full_username},
|
||||
}
|
||||
url = reverse("api:v1:manage:moderation:notes-list")
|
||||
response = superuser_api_client.post(url, data, format="json")
|
||||
assert response.status_code == 201
|
||||
|
||||
note = actor.moderation_notes.latest("id")
|
||||
assert note.target == target
|
||||
assert response.data == serializers.ManageNoteSerializer(note).data
|
||||
|
||||
|
||||
def test_note_list(factories, superuser_api_client, settings):
|
||||
note = factories["moderation.Note"]()
|
||||
url = reverse("api:v1:manage:moderation:notes-list")
|
||||
response = superuser_api_client.get(url)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
assert response.data["count"] == 1
|
||||
assert response.data["results"][0] == serializers.ManageNoteSerializer(note).data
|
||||
|
||||
|
||||
def test_note_delete(factories, superuser_api_client):
|
||||
note = factories["moderation.Note"]()
|
||||
url = reverse("api:v1:manage:moderation:notes-detail", kwargs={"uuid": note.uuid})
|
||||
response = superuser_api_client.delete(url)
|
||||
|
||||
assert response.status_code == 204
|
||||
|
||||
|
||||
def test_note_detail(factories, superuser_api_client):
|
||||
note = factories["moderation.Note"]()
|
||||
url = reverse("api:v1:manage:moderation:notes-detail", kwargs={"uuid": note.uuid})
|
||||
response = superuser_api_client.get(url)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.data == serializers.ManageNoteSerializer(note).data
|
||||
|
||||
|
||||
def test_tag_detail(factories, superuser_api_client):
|
||||
tag = factories["tags.Tag"]()
|
||||
url = reverse("api:v1:manage:tags-detail", kwargs={"name": tag.name})
|
||||
|
|
Loading…
Reference in New Issue