From c6a5d49942747ca77992d872c84661b03782e04b Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 20:28:38 +0100 Subject: [PATCH] Added logic to call statistics #46 --- kdb-bot/src/bot/translation/de.json | 12 +- kdb-bot/src/modules/stats/__init__.py | 1 + kdb-bot/src/modules/stats/command/__init__.py | 1 + .../src/modules/stats/command/stats_group.py | 133 ++++++++++++++++++ kdb-bot/src/modules/stats/model/__init__.py | 1 + kdb-bot/src/modules/stats/model/statistic.py | 21 +++ .../modules/stats/model/statistic_result.py | 24 ++++ kdb-bot/src/modules/stats/service/__init__.py | 1 + .../stats/service/statistic_service.py | 69 +++++++++ kdb-bot/src/modules/stats/stats.json | 46 ++++++ kdb-bot/src/modules/stats/test/user_xp_asc.py | 37 +++++ .../src/modules/stats/test/user_xp_desc.py | 37 +++++ 12 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 kdb-bot/src/modules/stats/__init__.py create mode 100644 kdb-bot/src/modules/stats/command/__init__.py create mode 100644 kdb-bot/src/modules/stats/command/stats_group.py create mode 100644 kdb-bot/src/modules/stats/model/__init__.py create mode 100644 kdb-bot/src/modules/stats/model/statistic.py create mode 100644 kdb-bot/src/modules/stats/model/statistic_result.py create mode 100644 kdb-bot/src/modules/stats/service/__init__.py create mode 100644 kdb-bot/src/modules/stats/service/statistic_service.py create mode 100644 kdb-bot/src/modules/stats/stats.json create mode 100644 kdb-bot/src/modules/stats/test/user_xp_asc.py create mode 100644 kdb-bot/src/modules/stats/test/user_xp_desc.py diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 45281393..edb2e87c 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -217,7 +217,17 @@ } }, "database": {}, - "permission": { + "permission": {}, + "stats": { + "list": { + "statistic": "Statistik", + "description": "Beschreibung" + }, + "view": { + "statistic": "Statistik", + "description": "Beschreibung", + "failed": "Statistik kann nicht gezeigt werden :(" + } } }, "api": { diff --git a/kdb-bot/src/modules/stats/__init__.py b/kdb-bot/src/modules/stats/__init__.py new file mode 100644 index 00000000..ad5eca30 --- /dev/null +++ b/kdb-bot/src/modules/stats/__init__.py @@ -0,0 +1 @@ +# imports: diff --git a/kdb-bot/src/modules/stats/command/__init__.py b/kdb-bot/src/modules/stats/command/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/command/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py new file mode 100644 index 00000000..32b32f61 --- /dev/null +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -0,0 +1,133 @@ +from typing import List as TList + +import discord +from cpl_discord.command import DiscordCommandABC +from cpl_query.extension import List +from cpl_translation import TranslatePipe +from discord import app_commands +from discord.ext import commands +from discord.ext.commands import Context + +from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.logging.command_logger import CommandLogger +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.permission.abc.permission_service_abc import PermissionServiceABC +from modules.stats.model.statistic import Statistic +from modules.stats.service.statistic_service import StatisticService +from modules.stats.test.user_xp_asc import user_xp_asc +from modules.stats.test.user_xp_desc import user_xp_desc + + +class StatsGroup(DiscordCommandABC): + + def __init__( + self, + logger: CommandLogger, + message_service: MessageServiceABC, + client_utils: ClientUtilsServiceABC, + translate: TranslatePipe, + permission_service: PermissionServiceABC, + statistic: StatisticService, + servers: ServerRepositoryABC, + ): + DiscordCommandABC.__init__(self) + + self._logger = logger + self._client_utils = client_utils + self._message_service = message_service + self._t = translate + self._permissions = permission_service + self._statistic = statistic + self._servers = servers + + self._stats = List(Statistic, [ + Statistic('Benutzer XP Aufsteigend', 'Zeigt XP von jedem Benutzer, aufsteigend nach XP', user_xp_asc), + Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', user_xp_desc) + ]) + + @commands.hybrid_group() + @commands.guild_only() + async def stats(self, ctx: Context): + pass + + @stats.command(alias='rules') + @commands.guild_only() + async def list(self, ctx: Context, wait: int = None): + self._logger.debug(__name__, f'Received command stats list {ctx}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_moderator(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command stats list') + return + + embed = discord.Embed( + title=self._t.transform('modules.auto_role.list.title'), + description=self._t.transform('modules.auto_role.list.description'), + color=int('ef9d0d', 16) + ) + + statistics = '' + descriptions = '' + for statistic in self._stats: + statistics += f'\n{statistic["Name"]}' + descriptions += f'\n{statistic["Description"]}' + + embed.add_field(name=self._t.transform('modules.stats.list.statistic'), value=statistics, inline=True) + embed.add_field(name=self._t.transform('modules.stats.list.description'), value=statistics, inline=True) + await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) + self._logger.trace(__name__, f'Finished command stats list') + + @stats.command() + @commands.guild_only() + async def view(self, ctx: Context, name: str, wait: int = None): + self._logger.debug(__name__, f'Received command stats {ctx}:{name}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if ctx.guild is None: + return + + try: + server = self._servers.get_server_by_discord_id(ctx.guild.id) + statistic = self._stats.where(lambda s: s.name == name).single() + result = await self._statistic.execute(statistic.func, server) + + # headers = '' + # rows = '' + # for header in result.header: + # headers += f'\n{header}' + # + # for row in result.values: + # row_str = '' + # for column in row: + # row_str += f'\n{column}' + # rows += f'\n{row_str}' + + embed = discord.Embed( + title=statistic.name, + description=statistic.description, + color=int('ef9d0d', 16) + ) + + for i in range(result.header.count()): + header = result.header[i] + value = '' + for row in result.values: + value += f'\n{row[i]}' + embed.add_field(name=header, value=value, inline=True) + + # embed.add_field(name=self._t.transform('modules.auto_role.list.message_id'), value=rows, inline=True) + await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) + # await self._message_service.send_ctx_msg(ctx, name, wait_before_delete=wait) + except Exception as e: + self._logger.error(__name__, f'Cannot view statistic {name}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.view.failed')) + + self._logger.trace(__name__, f'Finished stats command') + + @view.autocomplete('name') + async def view_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in self._stats] diff --git a/kdb-bot/src/modules/stats/model/__init__.py b/kdb-bot/src/modules/stats/model/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/model/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/model/statistic.py b/kdb-bot/src/modules/stats/model/statistic.py new file mode 100644 index 00000000..2db1b75d --- /dev/null +++ b/kdb-bot/src/modules/stats/model/statistic.py @@ -0,0 +1,21 @@ +from typing import Callable + + +class Statistic: + + def __init__(self, name: str, description: str, func: Callable): + self._name = name + self._description = description + self._func = func + + @property + def name(self) -> str: + return self._name + + @property + def description(self) -> str: + return self._description + + @property + def func(self) -> Callable: + return self._func diff --git a/kdb-bot/src/modules/stats/model/statistic_result.py b/kdb-bot/src/modules/stats/model/statistic_result.py new file mode 100644 index 00000000..c2c53742 --- /dev/null +++ b/kdb-bot/src/modules/stats/model/statistic_result.py @@ -0,0 +1,24 @@ +from cpl_query.extension import List + + +class StatisticResult: + + def __init__(self): + self._header = List(str) + self._values = List(List) + + @property + def header(self) -> List[str]: + return self._header + + @header.setter + def header(self, value: List[str]): + self._header = value + + @property + def values(self) -> List[List]: + return self._values + + @values.setter + def values(self, value: List[List]): + self._values = value diff --git a/kdb-bot/src/modules/stats/service/__init__.py b/kdb-bot/src/modules/stats/service/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/service/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/service/statistic_service.py b/kdb-bot/src/modules/stats/service/statistic_service.py new file mode 100644 index 00000000..b0f59d62 --- /dev/null +++ b/kdb-bot/src/modules/stats/service/statistic_service.py @@ -0,0 +1,69 @@ +from abc import abstractmethod +from typing import Callable + +from cpl_discord.service import DiscordBotServiceABC + +from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC +from bot_data.abc.client_repository_abc import ClientRepositoryABC +from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC +from bot_data.abc.level_repository_abc import LevelRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC +from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server import Server +from modules.stats.model.statistic_result import StatisticResult + + +class StatisticService: + + def __init__( + self, + auto_roles: AutoRoleRepositoryABC, + clients: ClientRepositoryABC, + known_users: KnownUserRepositoryABC, + levels: LevelRepositoryABC, + servers: ServerRepositoryABC, + user_joined_servers: UserJoinedServerRepositoryABC, + user_joined_voice_channel: UserJoinedVoiceChannelRepositoryABC, + users: UserRepositoryABC, + bot: DiscordBotServiceABC, + ): + self._auto_roles = auto_roles + self._clients = clients + self._known_users = known_users + self._levels = levels + self._servers = servers + self._user_joined_servers = user_joined_servers + self._user_joined_voice_channel = user_joined_voice_channel + self._users = users + self._bot = bot + + async def execute(self, _f: Callable, server: Server) -> StatisticResult: + guild = self._bot.guilds.where(lambda g: g.id == server.discord_server_id).single() + + return await _f( + self._auto_roles + .get_auto_roles() + .where(lambda x: x.server.server_id == server.server_id), + self._clients + .get_clients() + .where(lambda x: x.server.server_id == server.server_id), + self._known_users.get_users(), + self._levels + .get_levels() + .where(lambda x: x.server.server_id == server.server_id), + self._servers + .get_servers() + .where(lambda x: x.server_id == server.server_id), + self._user_joined_servers + .get_user_joined_servers() + .where(lambda x: x.user.server.server_id == server.server_id), + self._user_joined_voice_channel + .get_user_joined_voice_channels() + .where(lambda x: x.user.server.server_id == server.server_id), + self._users + .get_users() + .where(lambda x: x.server.server_id == server.server_id), + guild + ) diff --git a/kdb-bot/src/modules/stats/stats.json b/kdb-bot/src/modules/stats/stats.json new file mode 100644 index 00000000..c4fec974 --- /dev/null +++ b/kdb-bot/src/modules/stats/stats.json @@ -0,0 +1,46 @@ +{ + "ProjectSettings": { + "Name": "stats", + "Version": { + "Major": "0", + "Minor": "0", + "Micro": "0" + }, + "Author": "", + "AuthorEmail": "", + "Description": "", + "LongDescription": "", + "URL": "", + "CopyrightDate": "", + "CopyrightName": "", + "LicenseName": "", + "LicenseDescription": "", + "Dependencies": [ + "cpl-core>=2022.10.0.post7" + ], + "DevDependencies": [ + "cpl-cli>=2022.10.1" + ], + "PythonVersion": ">=3.10.4", + "PythonPath": { + "linux": "" + }, + "Classifiers": [] + }, + "BuildSettings": { + "ProjectType": "library", + "SourcePath": "", + "OutputPath": "../../dist", + "Main": "stats.main", + "EntryPoint": "stats", + "IncludePackageData": false, + "Included": [], + "Excluded": [ + "*/__pycache__", + "*/logs", + "*/tests" + ], + "PackageData": {}, + "ProjectReferences": [] + } +} \ No newline at end of file diff --git a/kdb-bot/src/modules/stats/test/user_xp_asc.py b/kdb-bot/src/modules/stats/test/user_xp_asc.py new file mode 100644 index 00000000..9ee1219e --- /dev/null +++ b/kdb-bot/src/modules/stats/test/user_xp_asc.py @@ -0,0 +1,37 @@ +from cpl_discord.container import Guild +from cpl_query.extension import List + +from bot_data.model.auto_role import AutoRole +from bot_data.model.client import Client +from bot_data.model.known_user import KnownUser +from bot_data.model.level import Level +from bot_data.model.server import Server +from bot_data.model.user import User +from bot_data.model.user_joined_server import UserJoinedServer +from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel +from modules.stats.model.statistic_result import StatisticResult + + +async def user_xp_asc( + auto_roles: List[AutoRole], + clients: List[Client], + known_users: List[KnownUser], + levels: List[Level], + servers: List[Server], + user_joined_servers: List[UserJoinedServer], + user_joined_voice_channel: List[UserJoinedVoiceChannel], + users: List[User], + guild: Guild +) -> StatisticResult: + result = StatisticResult() + result.header.append('Name') + result.header.append('XP') + + for user in users.order_by(lambda u: u.xp): + row = List(str) + member = guild.get_member(user.discord_id) + row.append(member.name) + row.append(str(user.xp)) + result.values.append(row) + + return result diff --git a/kdb-bot/src/modules/stats/test/user_xp_desc.py b/kdb-bot/src/modules/stats/test/user_xp_desc.py new file mode 100644 index 00000000..32cd0f3e --- /dev/null +++ b/kdb-bot/src/modules/stats/test/user_xp_desc.py @@ -0,0 +1,37 @@ +from cpl_discord.container import Guild +from cpl_query.extension import List + +from bot_data.model.auto_role import AutoRole +from bot_data.model.client import Client +from bot_data.model.known_user import KnownUser +from bot_data.model.level import Level +from bot_data.model.server import Server +from bot_data.model.user import User +from bot_data.model.user_joined_server import UserJoinedServer +from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel +from modules.stats.model.statistic_result import StatisticResult + + +async def user_xp_desc( + auto_roles: List[AutoRole], + clients: List[Client], + known_users: List[KnownUser], + levels: List[Level], + servers: List[Server], + user_joined_servers: List[UserJoinedServer], + user_joined_voice_channel: List[UserJoinedVoiceChannel], + users: List[User], + guild: Guild +) -> StatisticResult: + result = StatisticResult() + result.header.append('Name') + result.header.append('XP') + + for user in users.order_by_descending(lambda u: u.xp): + row = List(str) + member = guild.get_member(user.discord_id) + row.append(member.name) + row.append(str(user.xp)) + result.values.append(row) + + return result