From a9d0c7d7bc93a32c7ce0e9d1a6c0c1243b38a72c Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Wed, 7 Oct 2020 10:05:07 +0200 Subject: [PATCH] Expose more statistics * differenciate local & all events/comments/groups * add instance follows/followings Signed-off-by: Thomas Citharel --- js/src/graphql/statistics.ts | 5 +++ js/src/types/statistics.model.ts | 5 +++ js/src/views/About/AboutInstance.vue | 11 ++++-- lib/graphql/resolvers/statistics.ex | 11 ++++-- lib/graphql/schema/statistics.ex | 17 ++++++-- lib/mobilizon/actors/actors.ex | 22 +++++++++++ lib/service/statistics/statistics.ex | 11 ++++++ test/graphql/resolvers/statistics_test.exs | 46 ++++++++++++++++++++++ 8 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 test/graphql/resolvers/statistics_test.exs diff --git a/js/src/graphql/statistics.ts b/js/src/graphql/statistics.ts index 4fd8ff843..e229eafce 100644 --- a/js/src/graphql/statistics.ts +++ b/js/src/graphql/statistics.ts @@ -5,8 +5,13 @@ export const STATISTICS = gql` statistics { numberOfUsers numberOfEvents + numberOfLocalEvents numberOfComments + numberOfLocalComments numberOfGroups + numberOfLocalGroups + numberOfInstanceFollowings + numberOfInstanceFollowers } } `; diff --git a/js/src/types/statistics.model.ts b/js/src/types/statistics.model.ts index 3d8629341..e4a92996f 100644 --- a/js/src/types/statistics.model.ts +++ b/js/src/types/statistics.model.ts @@ -1,6 +1,11 @@ export interface IStatistics { numberOfUsers: number; numberOfEvents: number; + numberOfLocalEvents: number; numberOfComments: number; + numberOfLocalComments: number; numberOfGroups: number; + numberOfLocalGroups: number; + numberOfInstanceFollowings: number; + numberOfInstanceFollowers: number; } diff --git a/js/src/views/About/AboutInstance.vue b/js/src/views/About/AboutInstance.vue index ad30b08cf..175bca407 100644 --- a/js/src/views/About/AboutInstance.vue +++ b/js/src/views/About/AboutInstance.vue @@ -14,13 +14,13 @@ {{ statistics.numberOfUsers }} - {{ statistics.numberOfGroups }} + {{ statistics.numberOfLocalGroups }} - {{ statistics.numberOfEvents }} + {{ statistics.numberOfLocalEvents }} - {{ statistics.numberOfComments }} + {{ statistics.numberOfLocalComments }}
@@ -140,7 +140,7 @@ section { .statistics { display: grid; grid-template-columns: repeat(auto-fit, 150px); - grid-template-rows: repeat(2, 1fr); + gap: 2rem 0; p { text-align: right; padding: 0 15px; @@ -157,6 +157,9 @@ section { } } .contact { + h4 { + font-weight: bold; + } p { width: 200px; white-space: nowrap; diff --git a/lib/graphql/resolvers/statistics.ex b/lib/graphql/resolvers/statistics.ex index 3fdafeafc..c451cde40 100644 --- a/lib/graphql/resolvers/statistics.ex +++ b/lib/graphql/resolvers/statistics.ex @@ -12,9 +12,14 @@ defmodule Mobilizon.GraphQL.Resolvers.Statistics do {:ok, %{ number_of_users: StatisticsModule.get_cached_value(:local_users), - number_of_events: StatisticsModule.get_cached_value(:local_events), - number_of_comments: StatisticsModule.get_cached_value(:local_comments), - number_of_groups: StatisticsModule.get_cached_value(:local_groups) + number_of_events: StatisticsModule.get_cached_value(:federation_events), + number_of_local_events: StatisticsModule.get_cached_value(:local_events), + number_of_comments: StatisticsModule.get_cached_value(:federation_comments), + number_of_local_comments: StatisticsModule.get_cached_value(:local_comments), + number_of_groups: StatisticsModule.get_cached_value(:federation_groups), + number_of_local_groups: StatisticsModule.get_cached_value(:local_groups), + number_of_instance_followings: StatisticsModule.get_cached_value(:instance_followings), + number_of_instance_followers: StatisticsModule.get_cached_value(:instance_followers) }} end end diff --git a/lib/graphql/schema/statistics.ex b/lib/graphql/schema/statistics.ex index f3c19810b..58d3b27bf 100644 --- a/lib/graphql/schema/statistics.ex +++ b/lib/graphql/schema/statistics.ex @@ -10,9 +10,20 @@ defmodule Mobilizon.GraphQL.Schema.StatisticsType do object :statistics do # Instance name field(:number_of_users, :integer, description: "The number of local users") - field(:number_of_events, :integer, description: "The number of local events") - field(:number_of_comments, :integer, description: "The number of local comments") - field(:number_of_groups, :integer, description: "The number of local groups") + field(:number_of_events, :integer, description: "The total number of events") + field(:number_of_local_events, :integer, description: "The number of local events") + field(:number_of_comments, :integer, description: "The total number of comments") + field(:number_of_local_comments, :integer, description: "The number of local events") + field(:number_of_groups, :integer, description: "The total number of groups") + field(:number_of_local_groups, :integer, description: "The number of local groups") + + field(:number_of_instance_followers, :integer, + description: "The number of this instance's followers" + ) + + field(:number_of_instance_followings, :integer, + description: "The number of instances this instance follows" + ) end object :statistics_queries do diff --git a/lib/mobilizon/actors/actors.ex b/lib/mobilizon/actors/actors.ex index 75b55882b..c78f4c582 100644 --- a/lib/mobilizon/actors/actors.ex +++ b/lib/mobilizon/actors/actors.ex @@ -1076,6 +1076,17 @@ defmodule Mobilizon.Actors do |> Page.build_page(page, limit) end + @doc """ + Returns the number of followers for an actor + """ + @spec count_followers_for_actor(Actor.t()) :: integer() + def count_followers_for_actor(%Actor{id: actor_id}) do + actor_id + |> follower_for_actor_query() + |> where(approved: true) + |> Repo.aggregate(:count) + end + @doc """ Returns the list of followings for an actor. If actor A follows actor B and C, actor A's followings are B and C. @@ -1087,6 +1098,17 @@ defmodule Mobilizon.Actors do |> Repo.all() end + @doc """ + Returns the number of followings for an actor + """ + @spec count_followings_for_actor(Actor.t()) :: integer() + def count_followings_for_actor(%Actor{id: actor_id}) do + actor_id + |> followings_for_actor_query() + |> where(approved: true) + |> Repo.aggregate(:count) + end + @doc """ Returns the list of external followings for an actor. """ diff --git a/lib/service/statistics/statistics.ex b/lib/service/statistics/statistics.ex index c3a0efaf1..42998b968 100644 --- a/lib/service/statistics/statistics.ex +++ b/lib/service/statistics/statistics.ex @@ -4,6 +4,7 @@ defmodule Mobilizon.Service.Statistics do """ alias Mobilizon.{Actors, Discussions, Events, Users} + alias Mobilizon.Federation.ActivityPub.Relay def get_cached_value(key) do case Cachex.fetch(:statistics, key, fn key -> @@ -44,4 +45,14 @@ defmodule Mobilizon.Service.Statistics do defp create_cache(:federation_groups) do Actors.count_groups() end + + defp create_cache(:instance_followers) do + relay_actor = Relay.get_actor() + Actors.count_followers_for_actor(relay_actor) + end + + defp create_cache(:instance_followings) do + relay_actor = Relay.get_actor() + Actors.count_followings_for_actor(relay_actor) + end end diff --git a/test/graphql/resolvers/statistics_test.exs b/test/graphql/resolvers/statistics_test.exs new file mode 100644 index 000000000..3b6e155da --- /dev/null +++ b/test/graphql/resolvers/statistics_test.exs @@ -0,0 +1,46 @@ +defmodule Mobilizon.GraphQL.Resolvers.StatisticsTest do + use Mobilizon.Web.ConnCase + + import Mobilizon.Factory + + alias Mobilizon.GraphQL.AbsintheHelpers + + describe "statistics resolver" do + @statistics_query """ + query { + statistics { + numberOfUsers + numberOfEvents + numberOfLocalEvents + numberOfComments + numberOfLocalComments + numberOfGroups + numberOfLocalGroups + numberOfInstanceFollowings + numberOfInstanceFollowers + } + } + """ + + test "get statistics", %{conn: conn} do + Cachex.clear(:statistics) + insert(:event) + insert(:comment) + insert(:group) + actor = insert(:actor, user: nil, domain: "toto.tld") + insert(:event, organizer_actor: actor, local: false) + + res = AbsintheHelpers.graphql_query(conn, query: @statistics_query) + + assert res["data"]["statistics"]["numberOfUsers"] == 6 + assert res["data"]["statistics"]["numberOfLocalEvents"] == 2 + assert res["data"]["statistics"]["numberOfEvents"] == 3 + assert res["data"]["statistics"]["numberOfLocalComments"] == 1 + assert res["data"]["statistics"]["numberOfLocalGroups"] == 1 + + insert(:event) + # We keep the value in cache + assert res["data"]["statistics"]["numberOfLocalEvents"] == 2 + end + end +end