From 032197da3f114d10ae24a12512d75971461194c6 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Thu, 27 Dec 2018 19:58:34 +0100
Subject: [PATCH] Added stats endpoint for domain

---
 api/funkwhale_api/federation/models.py | 34 +++++++++++++++++++++++++-
 api/funkwhale_api/manage/views.py      |  5 ++++
 api/funkwhale_api/music/models.py      |  2 ++
 api/tests/federation/test_models.py    | 20 +++++++++++++++
 api/tests/manage/test_views.py         | 11 +++++++++
 5 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/api/funkwhale_api/federation/models.py b/api/funkwhale_api/federation/models.py
index ad4c0c7b..4b773040 100644
--- a/api/funkwhale_api/federation/models.py
+++ b/api/funkwhale_api/federation/models.py
@@ -88,7 +88,7 @@ class Domain(models.Model):
     name = models.CharField(primary_key=True, max_length=255)
     creation_date = models.DateTimeField(default=timezone.now)
     nodeinfo_fetch_date = models.DateTimeField(default=None, null=True, blank=True)
-    nodeinfo = JSONField(default=empty_dict, max_length=50000)
+    nodeinfo = JSONField(default=empty_dict, max_length=50000, blank=True)
 
     objects = DomainQuerySet.as_manager()
 
@@ -104,6 +104,38 @@ class Domain(models.Model):
 
         super().save(**kwargs)
 
+    def get_stats(self):
+        from funkwhale_api.music import models as music_models
+
+        data = Domain.objects.filter(pk=self.pk).aggregate(
+            actors=models.Count("actors", distinct=True),
+            outbox_activities=models.Count("actors__outbox_activities", distinct=True),
+            libraries=models.Count("actors__libraries", distinct=True),
+            uploads=models.Count("actors__libraries__uploads", distinct=True),
+            received_library_follows=models.Count(
+                "actors__libraries__received_follows", distinct=True
+            ),
+            emitted_library_follows=models.Count(
+                "actors__library_follows", distinct=True
+            ),
+        )
+        data["artists"] = music_models.Artist.objects.filter(
+            from_activity__actor__domain_id=self.pk
+        ).count()
+        data["albums"] = music_models.Album.objects.filter(
+            from_activity__actor__domain_id=self.pk
+        ).count()
+        data["tracks"] = music_models.Track.objects.filter(
+            from_activity__actor__domain_id=self.pk
+        ).count()
+
+        uploads = music_models.Upload.objects.filter(library__actor__domain_id=self.pk)
+        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 Actor(models.Model):
     ap_type = "Actor"
diff --git a/api/funkwhale_api/manage/views.py b/api/funkwhale_api/manage/views.py
index 99b3b41c..cee9d537 100644
--- a/api/funkwhale_api/manage/views.py
+++ b/api/funkwhale_api/manage/views.py
@@ -125,3 +125,8 @@ class ManageDomainViewSet(
         federation_tasks.update_domain_nodeinfo(domain_name=domain.name)
         domain.refresh_from_db()
         return response.Response(domain.nodeinfo, status=200)
+
+    @detail_route(methods=["get"])
+    def stats(self, request, *args, **kwargs):
+        domain = self.get_object()
+        return response.Response(domain.get_stats(), status=200)
diff --git a/api/funkwhale_api/music/models.py b/api/funkwhale_api/music/models.py
index 21ecb994..1bc2a143 100644
--- a/api/funkwhale_api/music/models.py
+++ b/api/funkwhale_api/music/models.py
@@ -617,6 +617,8 @@ class UploadQuerySet(models.QuerySet):
     def for_federation(self):
         return self.filter(import_status="finished", mimetype__startswith="audio/")
 
+    def with_file(self):
+        return self.exclude(audio_file=None).exclude(audio_file='')
 
 TRACK_FILE_IMPORT_STATUS_CHOICES = (
     ("pending", "Pending"),
diff --git a/api/tests/federation/test_models.py b/api/tests/federation/test_models.py
index c3032817..29367504 100644
--- a/api/tests/federation/test_models.py
+++ b/api/tests/federation/test_models.py
@@ -77,3 +77,23 @@ def test_external_domains(factories, settings):
     settings.FEDERATION_HOSTNAME = d1.pk
 
     assert list(models.Domain.objects.external()) == [d2]
+
+
+def test_domain_stats(factories):
+    expected = {
+        "actors": 0,
+        "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,
+    }
+
+    domain = factories["federation.Domain"]()
+
+    assert domain.get_stats() == expected
diff --git a/api/tests/manage/test_views.py b/api/tests/manage/test_views.py
index 31e1075e..95ffea46 100644
--- a/api/tests/manage/test_views.py
+++ b/api/tests/manage/test_views.py
@@ -103,3 +103,14 @@ def test_domain_nodeinfo(factories, superuser_api_client, mocker):
     assert response.data == {"status": "ok", "payload": {"hello": "world"}}
 
     update_domain_nodeinfo.assert_called_once_with(domain_name=domain.name)
+
+
+def test_domain_stats(factories, superuser_api_client, mocker):
+    domain = factories["federation.Domain"]()
+    get_stats = mocker.patch.object(
+        domain.__class__, "get_stats", return_value={"hello": "world"}
+    )
+    url = reverse("api:v1:manage:federation:domains-stats", kwargs={"pk": domain.name})
+    response = superuser_api_client.get(url)
+    assert response.status_code == 200
+    assert response.data == {"hello": "world"}
-- 
GitLab