diff --git a/api/funkwhale_api/instance/nodeinfo.py b/api/funkwhale_api/instance/nodeinfo.py index 0b8f4b3cecc5d456637b20d051550e48a160834f..286cb0284f9d922c74b10d10b4cd5ee4f3a70d1f 100644 --- a/api/funkwhale_api/instance/nodeinfo.py +++ b/api/funkwhale_api/instance/nodeinfo.py @@ -17,7 +17,7 @@ def get(): "protocols": ["activitypub"], "services": {"inbound": [], "outbound": []}, "openRegistrations": preferences.get("users__registration_enabled"), - "usage": {"users": {"total": 0}}, + "usage": {"users": {"total": 0, "activeHalfyear": 0, "activeMonth": 0}}, "metadata": { "private": preferences.get("instance__nodeinfo_private"), "shortDescription": preferences.get("instance__short_description"), @@ -28,7 +28,7 @@ def get(): "federationNeedsApproval": preferences.get( "federation__music_needs_approval" ), - "anonymousCanListen": preferences.get( + "anonymousCanListen": not preferences.get( "common__api_authentication_required" ), }, @@ -37,7 +37,11 @@ def get(): if share_stats: getter = memo(lambda: stats.get(), max_age=600) statistics = getter() - data["usage"]["users"]["total"] = statistics["users"] + data["usage"]["users"]["total"] = statistics["users"]["total"] + data["usage"]["users"]["activeHalfyear"] = statistics["users"][ + "active_halfyear" + ] + data["usage"]["users"]["activeMonth"] = statistics["users"]["active_month"] data["metadata"]["library"]["tracks"] = {"total": statistics["tracks"]} data["metadata"]["library"]["artists"] = {"total": statistics["artists"]} data["metadata"]["library"]["albums"] = {"total": statistics["albums"]} diff --git a/api/funkwhale_api/instance/stats.py b/api/funkwhale_api/instance/stats.py index 0cb1b9796f5bbd837e40dc8e4c9c74155559fd28..50f69da84dc7807bf106805447cd91343aea72c5 100644 --- a/api/funkwhale_api/instance/stats.py +++ b/api/funkwhale_api/instance/stats.py @@ -1,4 +1,7 @@ +import datetime + from django.db.models import Sum +from django.utils import timezone from funkwhale_api.favorites.models import TrackFavorite from funkwhale_api.history.models import Listening @@ -19,6 +22,15 @@ def get(): def get_users(): + qs = User.objects.filter(is_active=True) + now = timezone.now() + active_month = now - datetime.timedelta(days=30) + active_halfyear = now - datetime.timedelta(days=30 * 6) + return { + "total": qs.count(), + "active_month": qs.filter(last_activity__gte=active_month).count(), + "active_halfyear": qs.filter(last_activity__gte=active_halfyear).count(), + } return User.objects.count() diff --git a/api/tests/instance/test_nodeinfo.py b/api/tests/instance/test_nodeinfo.py index 0fa1b4fc3a5bf24122bb9aaf0c413fbbfbb4e60f..a5bdc70933c696faae1297540532b594453d110b 100644 --- a/api/tests/instance/test_nodeinfo.py +++ b/api/tests/instance/test_nodeinfo.py @@ -5,7 +5,7 @@ from funkwhale_api.instance import nodeinfo def test_nodeinfo_dump(preferences, mocker): preferences["instance__nodeinfo_stats_enabled"] = True stats = { - "users": 1, + "users": {"total": 1, "active_halfyear": 12, "active_month": 13}, "tracks": 2, "albums": 3, "artists": 4, @@ -21,7 +21,7 @@ def test_nodeinfo_dump(preferences, mocker): "protocols": ["activitypub"], "services": {"inbound": [], "outbound": []}, "openRegistrations": preferences["users__registration_enabled"], - "usage": {"users": {"total": stats["users"]}}, + "usage": {"users": {"total": 1, "activeHalfyear": 12, "activeMonth": 13}}, "metadata": { "private": preferences["instance__nodeinfo_private"], "shortDescription": preferences["instance__short_description"], @@ -32,7 +32,7 @@ def test_nodeinfo_dump(preferences, mocker): "federationNeedsApproval": preferences[ "federation__music_needs_approval" ], - "anonymousCanListen": preferences[ + "anonymousCanListen": not preferences[ "common__api_authentication_required" ], "tracks": {"total": stats["tracks"]}, @@ -58,7 +58,7 @@ def test_nodeinfo_dump_stats_disabled(preferences, mocker): "protocols": ["activitypub"], "services": {"inbound": [], "outbound": []}, "openRegistrations": preferences["users__registration_enabled"], - "usage": {"users": {"total": 0}}, + "usage": {"users": {"total": 0, "activeHalfyear": 0, "activeMonth": 0}}, "metadata": { "private": preferences["instance__nodeinfo_private"], "shortDescription": preferences["instance__short_description"], @@ -69,7 +69,7 @@ def test_nodeinfo_dump_stats_disabled(preferences, mocker): "federationNeedsApproval": preferences[ "federation__music_needs_approval" ], - "anonymousCanListen": preferences[ + "anonymousCanListen": not preferences[ "common__api_authentication_required" ], }, diff --git a/api/tests/instance/test_stats.py b/api/tests/instance/test_stats.py index 4820735d5d4b15841369eb8d5e564f892914fbf9..255e60d130f5a37330d2182e906b4e469d395bba 100644 --- a/api/tests/instance/test_stats.py +++ b/api/tests/instance/test_stats.py @@ -1,10 +1,15 @@ -from funkwhale_api.instance import stats +import datetime +from funkwhale_api.instance import stats -def test_get_users(mocker): - mocker.patch("funkwhale_api.users.models.User.objects.count", return_value=42) - assert stats.get_users() == 42 +def test_get_users(factories, now): + factories["users.User"](last_activity=now) + factories["users.User"](last_activity=now - datetime.timedelta(days=29)) + factories["users.User"](last_activity=now - datetime.timedelta(days=31)) + factories["users.User"](last_activity=now - datetime.timedelta(days=190)) + factories["users.User"](is_active=False) + assert stats.get_users() == {"total": 4, "active_month": 2, "active_halfyear": 3} def test_get_music_duration(factories): diff --git a/front/tests/unit/specs/store/instance.spec.js b/front/tests/unit/specs/store/instance.spec.js index a6488dc7f4af45fabc1a17126fb9a0a5e75d7a0a..5ae771c75f3b2e6efdb189f46fb3ff67c0a9de23 100644 --- a/front/tests/unit/specs/store/instance.spec.js +++ b/front/tests/unit/specs/store/instance.spec.js @@ -1,5 +1,6 @@ import {expect} from 'chai' var sinon = require('sinon') +import axios from 'axios' import moxios from 'moxios' import store from '@/store/instance' import { testAction } from '../../utils' @@ -14,6 +15,7 @@ describe('store/instance', () => { afterEach(function () { sandbox.restore() moxios.uninstall() + axios.defaults.baseURL = null }) describe('mutations', () => {