diff --git a/api/funkwhale_api/federation/models.py b/api/funkwhale_api/federation/models.py index ad88422e4..35b15b667 100644 --- a/api/funkwhale_api/federation/models.py +++ b/api/funkwhale_api/federation/models.py @@ -61,16 +61,6 @@ class ActorQuerySet(models.QuerySet): return qs - def with_outbox_activities_count(self): - return self.annotate( - outbox_activities_count=models.Count("outbox_activities", distinct=True) - ) - - def with_followers_count(self): - return self.annotate( - followers_count=models.Count("received_follows", distinct=True) - ) - def with_uploads_count(self): return self.annotate( uploads_count=models.Count("libraries__uploads", distinct=True) @@ -86,7 +76,9 @@ class DomainQuerySet(models.QuerySet): def with_outbox_activities_count(self): return self.annotate( - outbox_activities_count=models.Count("actors__outbox_activities", distinct=True) + outbox_activities_count=models.Count( + "actors__outbox_activities", distinct=True + ) ) @@ -186,10 +178,10 @@ class Actor(models.Model): @property def full_username(self): - return "{}@{}".format(self.preferred_username, self.domain) + return "{}@{}".format(self.preferred_username, self.domain_id) def __str__(self): - return "{}@{}".format(self.preferred_username, self.domain) + return "{}@{}".format(self.preferred_username, self.domain_id) @property def is_local(self): @@ -217,6 +209,35 @@ class Actor(models.Model): data["total"] = sum(data.values()) return data + def get_stats(self): + from funkwhale_api.music import models as music_models + + data = Actor.objects.filter(pk=self.pk).aggregate( + outbox_activities=models.Count("outbox_activities", distinct=True), + libraries=models.Count("libraries", distinct=True), + received_library_follows=models.Count( + "libraries__received_follows", distinct=True + ), + emitted_library_follows=models.Count("library_follows", distinct=True), + ) + data["artists"] = music_models.Artist.objects.filter( + from_activity__actor=self.pk + ).count() + data["albums"] = music_models.Album.objects.filter( + from_activity__actor=self.pk + ).count() + data["tracks"] = music_models.Track.objects.filter( + from_activity__actor=self.pk + ).count() + + uploads = music_models.Upload.objects.filter(library__actor=self.pk) + data["uploads"] = uploads.count() + data["media_total_size"] = uploads.aggregate(v=models.Sum("size"))["v"] or 0 + data["media_downloaded_size"] = ( + uploads.with_file().aggregate(v=models.Sum("size"))["v"] or 0 + ) + return data + class InboxItem(models.Model): """ diff --git a/api/funkwhale_api/manage/filters.py b/api/funkwhale_api/manage/filters.py index dfb901924..51648298a 100644 --- a/api/funkwhale_api/manage/filters.py +++ b/api/funkwhale_api/manage/filters.py @@ -37,10 +37,15 @@ class ManageActorFilterSet(filters.FilterSet): search_fields={ "name": {"to": "name"}, "username": {"to": "preferred_username"}, + "email": {"to": "user__email"}, "bio": {"to": "summary"}, "type": {"to": "type"}, }, - filter_fields={"domain": {"to": "domain_id__iexact"}}, + filter_fields={ + "domain": {"to": "domain__name__iexact"}, + "username": {"to": "preferred_username__iexact"}, + "email": {"to": "user__email__iexact"}, + }, ) ) local = filters.BooleanFilter(name="_", method="filter_local") diff --git a/api/funkwhale_api/manage/serializers.py b/api/funkwhale_api/manage/serializers.py index 3b06fb848..76d0cf05f 100644 --- a/api/funkwhale_api/manage/serializers.py +++ b/api/funkwhale_api/manage/serializers.py @@ -116,6 +116,7 @@ class ManageUserSerializer(serializers.ModelSerializer): "permissions", "privacy_level", "upload_quota", + "full_username", ) read_only_fields = [ "id", @@ -194,9 +195,8 @@ class ManageDomainSerializer(serializers.ModelSerializer): class ManageActorSerializer(serializers.ModelSerializer): - outbox_activities_count = serializers.SerializerMethodField() uploads_count = serializers.SerializerMethodField() - followers_count = serializers.SerializerMethodField() + user = ManageUserSerializer() class Meta: model = federation_models.Actor @@ -205,6 +205,7 @@ class ManageActorSerializer(serializers.ModelSerializer): "url", "fid", "preferred_username", + "full_username", "domain", "name", "summary", @@ -215,16 +216,9 @@ class ManageActorSerializer(serializers.ModelSerializer): "outbox_url", "shared_inbox_url", "manually_approves_followers", - "outbox_activities_count", "uploads_count", - "followers_count", + "user", ] def get_uploads_count(self, o): return getattr(o, "uploads_count", 0) - - def get_followers_count(self, o): - return getattr(o, "followers_count", 0) - - def get_outbox_activities_count(self, o): - return getattr(o, "outbox_activities_count", 0) diff --git a/api/funkwhale_api/manage/views.py b/api/funkwhale_api/manage/views.py index ddd4fe571..0697c6c14 100644 --- a/api/funkwhale_api/manage/views.py +++ b/api/funkwhale_api/manage/views.py @@ -138,10 +138,9 @@ class ManageActorViewSet( lookup_value_regex = r"([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)" queryset = ( federation_models.Actor.objects.all() - .with_outbox_activities_count() - .with_followers_count() .with_uploads_count() .order_by("-creation_date") + .select_related("user") ) serializer_class = serializers.ManageActorSerializer filter_class = filters.ManageActorFilterSet @@ -155,7 +154,6 @@ class ManageActorViewSet( "creation_date", "last_fetch_date", "uploads_count", - "followers_count", "outbox_activities_count", ] diff --git a/api/funkwhale_api/users/models.py b/api/funkwhale_api/users/models.py index 07bb4bae4..79650301e 100644 --- a/api/funkwhale_api/users/models.py +++ b/api/funkwhale_api/users/models.py @@ -204,6 +204,9 @@ class User(AbstractUser): return ["user.{}.{}".format(self.pk, g) for g in groups] + def full_username(self): + return "{}@{}".format(self.username, settings.FEDERATION_HOSTNAME) + def generate_code(length=10): return "".join( diff --git a/api/tests/federation/test_models.py b/api/tests/federation/test_models.py index 293675048..f59293b67 100644 --- a/api/tests/federation/test_models.py +++ b/api/tests/federation/test_models.py @@ -97,3 +97,22 @@ def test_domain_stats(factories): domain = factories["federation.Domain"]() assert domain.get_stats() == expected + + +def test_actor_stats(factories): + expected = { + "libraries": 0, + "tracks": 0, + "albums": 0, + "uploads": 0, + "artists": 0, + "outbox_activities": 0, + "received_library_follows": 0, + "emitted_library_follows": 0, + "media_total_size": 0, + "media_downloaded_size": 0, + } + + actor = factories["federation.Actor"]() + + assert actor.get_stats() == expected diff --git a/api/tests/manage/test_serializers.py b/api/tests/manage/test_serializers.py index 83d49cd66..803820b48 100644 --- a/api/tests/manage/test_serializers.py +++ b/api/tests/manage/test_serializers.py @@ -55,16 +55,12 @@ def test_manage_domain_serializer(factories, now): def test_manage_actor_serializer(factories, now): actor = factories["federation.Actor"]() - setattr(actor, "outbox_activities_count", 23) - setattr(actor, "followers_count", 42) setattr(actor, "uploads_count", 66) expected = { "id": actor.id, "name": actor.name, "creation_date": actor.creation_date.isoformat().split("+")[0] + "Z", "last_fetch_date": actor.last_fetch_date.isoformat().split("+")[0] + "Z", - "outbox_activities_count": 23, - "followers_count": 42, "uploads_count": 66, "fid": actor.fid, "url": actor.url, @@ -76,6 +72,8 @@ def test_manage_actor_serializer(factories, now): "summary": actor.summary, "preferred_username": actor.preferred_username, "manually_approves_followers": actor.manually_approves_followers, + "full_username": actor.full_username, + "user": None, } s = serializers.ManageActorSerializer(actor) diff --git a/front/src/Embed.vue b/front/src/Embed.vue index fdd3406fa..7987b054a 100644 --- a/front/src/Embed.vue +++ b/front/src/Embed.vue @@ -247,7 +247,6 @@ export default { self.isLoading = false; }).catch(error => { if (error.response) { - console.log(error.response) if (error.response.status === 404) { self.error = 'server_not_found' } @@ -274,7 +273,6 @@ export default { self.isLoading = false; }).catch(error => { if (error.response) { - console.log(error.response) if (error.response.status === 404) { self.error = 'server_not_found' } diff --git a/front/src/components/manage/moderation/AccountsTable.vue b/front/src/components/manage/moderation/AccountsTable.vue new file mode 100644 index 000000000..8750b4ec9 --- /dev/null +++ b/front/src/components/manage/moderation/AccountsTable.vue @@ -0,0 +1,205 @@ + + + diff --git a/front/src/components/manage/users/UsersTable.vue b/front/src/components/manage/users/UsersTable.vue index 33b2433cb..974ca392d 100644 --- a/front/src/components/manage/users/UsersTable.vue +++ b/front/src/components/manage/users/UsersTable.vue @@ -45,7 +45,7 @@