From f7f25e9428dd065a7ca5f8b1213dcf6ee88af447 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 5 Nov 2023 13:50:47 +0100 Subject: [PATCH] Added base for server statistics #420 --- bot/src/bot_graphql/abc/query_abc.py | 12 ++- bot/src/bot_graphql/graphql/server.gql | 10 +++ bot/src/bot_graphql/graphql_module.py | 2 + bot/src/bot_graphql/queries/server_query.py | 4 + .../queries/server_statistic_query.py | 77 +++++++++++++++++++ 5 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 bot/src/bot_graphql/queries/server_statistic_query.py diff --git a/bot/src/bot_graphql/abc/query_abc.py b/bot/src/bot_graphql/abc/query_abc.py index 850fb22b..b141d89b 100644 --- a/bot/src/bot_graphql/abc/query_abc.py +++ b/bot/src/bot_graphql/abc/query_abc.py @@ -38,7 +38,7 @@ class QueryABC(ObjectType): def __init__(self, name: str): ObjectType.__init__(self, name) - def add_collection(self, name: str, get_collection: Callable, filter_type: type = None): + def _get_wrapper(self, get_collection: Callable, filter_type: type = None): def wrapper(*args, **kwargs): if filter_type is not None and "filter" in kwargs: kwargs["filter"] = FilterABC.get_collection_filter(filter_type, kwargs["filter"]) @@ -67,8 +67,14 @@ class QueryABC(ObjectType): return self._resolve_collection(collection, *args, **kwargs) - self.set_field(f"{name}s", wrapper) - self.set_field(f"{name}Count", lambda *args: wrapper(*args).count()) + return wrapper + + def add_field(self, name: str, get_collection: Callable, filter_type: type = None): + self.set_field(name, self._get_wrapper(get_collection, filter_type)) + + def add_collection(self, name: str, get_collection: Callable, filter_type: type = None): + self.add_field(f"{name}s", get_collection, filter_type) + self.set_field(f"{name}Count", lambda *args: self._get_wrapper(get_collection, filter_type)(*args).count()) def _can_user_see_element(self, user: AuthUser, element: T) -> bool: @ServiceProviderABC.inject diff --git a/bot/src/bot_graphql/graphql/server.gql b/bot/src/bot_graphql/graphql/server.gql index 1e80be20..38ee1dc8 100644 --- a/bot/src/bot_graphql/graphql/server.gql +++ b/bot/src/bot_graphql/graphql/server.gql @@ -38,6 +38,8 @@ type Server implements TableWithHistoryQuery { config: ServerConfig hasFeatureFlag(flag: String): FeatureFlag + statistic(date: String): ServerStatistic + createdAt: String modifiedAt: String @@ -59,4 +61,12 @@ input ServerFilter { id: ID discordId: String name: String +} + +type ServerStatistic { + userJoinedVoiceChannelCount: Int + userJoinedVoiceChannelOntime: Int + userJoinedGameServerCount: Int + userJoinedGameServerOntime: Int + messageCount: Int } \ No newline at end of file diff --git a/bot/src/bot_graphql/graphql_module.py b/bot/src/bot_graphql/graphql_module.py index e15e0c35..95d73f7b 100644 --- a/bot/src/bot_graphql/graphql_module.py +++ b/bot/src/bot_graphql/graphql_module.py @@ -57,6 +57,7 @@ from bot_graphql.queries.level_query import LevelQuery from bot_graphql.queries.server_config_query import ServerConfigQuery from bot_graphql.queries.server_history_query import ServerHistoryQuery from bot_graphql.queries.server_query import ServerQuery +from bot_graphql.queries.server_statistic_query import ServerStatisticQuery from bot_graphql.queries.short_role_name_history_query import ShortRoleNameHistoryQuery from bot_graphql.queries.short_role_name_query import ShortRoleNameQuery from bot_graphql.queries.technician_config_history_query import ( @@ -138,6 +139,7 @@ class GraphQLModule(ModuleABC): services.add_transient(QueryABC, ShortRoleNameQuery) services.add_transient(QueryABC, UserWarningHistoryQuery) services.add_transient(QueryABC, UserWarningQuery) + services.add_transient(QueryABC, ServerStatisticQuery) services.add_transient(QueryABC, DiscordQuery) services.add_transient(QueryABC, GuildQuery) diff --git a/bot/src/bot_graphql/queries/server_query.py b/bot/src/bot_graphql/queries/server_query.py index 1398fa08..b5203724 100644 --- a/bot/src/bot_graphql/queries/server_query.py +++ b/bot/src/bot_graphql/queries/server_query.py @@ -107,6 +107,10 @@ class ServerQuery(DataQueryWithHistoryABC): "hasFeatureFlag", lambda server, *_, **kwargs: self._resolve_has_feature_flag(server, *_, **kwargs), ) + self.set_field( + "statistic", + lambda server, *_, **kwargs: server, + ) @staticmethod def resolve_id(server: Server, *_): diff --git a/bot/src/bot_graphql/queries/server_statistic_query.py b/bot/src/bot_graphql/queries/server_statistic_query.py new file mode 100644 index 00000000..7f01de89 --- /dev/null +++ b/bot/src/bot_graphql/queries/server_statistic_query.py @@ -0,0 +1,77 @@ +import datetime + +from cpl_core.configuration import ConfigurationABC + +from bot_data.abc.user_joined_game_server_repository_abc import UserJoinedGameServerRepositoryABC +from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC +from bot_data.abc.user_message_count_per_hour_repository_abc import UserMessageCountPerHourRepositoryABC +from bot_data.model.server_config import ServerConfig +from bot_graphql.abc.query_abc import QueryABC + + +class ServerStatisticQuery(QueryABC): + def __init__( + self, + config: ConfigurationABC, + user_joined_voice_channels: UserJoinedVoiceChannelRepositoryABC, + user_joined_game_servers: UserJoinedGameServerRepositoryABC, + user_messages: UserMessageCountPerHourRepositoryABC, + ): + QueryABC.__init__(self, "ServerStatistic") + + self._config = config + + self._user_joined_voice_channels = user_joined_voice_channels + self._user_joined_game_servers = user_joined_game_servers + self._user_messages = user_messages + + self.add_field("userJoinedVoiceChannelCount", self._resolve_voice_channel_count) + self.add_field("userJoinedVoiceChannelOntime", self._resolve_voice_channel_ontime) + self.add_field("userJoinedGameServerCount", self._resolve_game_server_count) + self.add_field("userJoinedGameServerOntime", self._resolve_game_server_ontime) + self.add_field( + "messageCount", + self._resolve_message_count, + ) + + def _resolve_voice_channel_count(self, server, *_, **kwargs): + return ( + self._user_joined_voice_channels.get_user_joined_voice_channels() + .where(lambda x: x.user.server.id == server.id and x.created_at.date() >= self._get_date(**kwargs)) + .count() + ) + + def _resolve_voice_channel_ontime(self, server, *_, **kwargs): + return ( + self._user_joined_voice_channels.get_user_joined_voice_channels() + .where(lambda x: x.user.server.id == server.id and x.created_at.date() >= self._get_date(**kwargs)) + .sum(lambda x: x.time) + ) + + def _resolve_game_server_count(self, server, *_, **kwargs): + return ( + self._user_joined_game_servers.get_user_joined_game_servers() + .where(lambda x: x.user.server.id == server.id and x.created_at.date() >= self._get_date(**kwargs)) + .count() + ) + + def _resolve_game_server_ontime(self, server, *_, **kwargs): + return ( + self._user_joined_game_servers.get_user_joined_game_servers() + .where(lambda x: x.user.server.id == server.id and x.created_at.date() >= self._get_date(**kwargs)) + .sum(lambda x: x.time) + ) + + def _resolve_message_count(self, server, *_, **kwargs): + settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{server.discord_id}") + return ( + self._user_messages.get_user_message_count_per_hours() + .where(lambda x: x.user.server.id == server.id and x.created_at.date() >= self._get_date(**kwargs)) + .sum(lambda x: x.xp_count / settings.xp_per_message) + ) + + def _get_date(self, **kwargs) -> datetime.date: + if "date" not in kwargs: + return datetime.date.today() - datetime.timedelta(days=7) + + return datetime.datetime.strptime(kwargs["date"], "%d.%m.%Y").date()