Improved sorting #130

This commit is contained in:
Sven Heidemann 2023-02-17 19:15:03 +01:00
parent fea259fa3b
commit dfe4f28e24
20 changed files with 146 additions and 48 deletions

View File

@ -5,7 +5,6 @@ from typing import Callable
from cpl_query.extension import List from cpl_query.extension import List
from discord.ext.commands import Context from discord.ext.commands import Context
from bot_data.model.user import User
from modules.base.configuration.base_server_settings import BaseServerSettings from modules.base.configuration.base_server_settings import BaseServerSettings
@ -50,12 +49,12 @@ class ClientUtilsABC(ABC):
def is_message_xp_count_by_hour_higher_that_max_message_count_per_hour( def is_message_xp_count_by_hour_higher_that_max_message_count_per_hour(
self, self,
created_at: datetime, created_at: datetime,
user: User, user: "User",
settings: BaseServerSettings, settings: BaseServerSettings,
is_reaction: bool = False, is_reaction: bool = False,
) -> bool: ) -> bool:
pass pass
@abstractmethod @abstractmethod
def get_ontime_for_user(self, user: User) -> float: def get_ontime_for_user(self, user: "User") -> float:
pass pass

View File

@ -2,6 +2,8 @@ from datetime import datetime
from typing import Optional from typing import Optional
from cpl_core.database import TableABC from cpl_core.database import TableABC
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from bot_data.model.server import Server from bot_data.model.server import Server
@ -10,7 +12,7 @@ class AutoRole(TableABC):
def __init__( def __init__(
self, self,
server: Optional[Server], server: Optional[Server],
dc_channel_id: int, channel_id: int,
dc_message_id: int, dc_message_id: int,
created_at: datetime = None, created_at: datetime = None,
modified_at: datetime = None, modified_at: datetime = None,
@ -18,7 +20,7 @@ class AutoRole(TableABC):
): ):
self._auto_role_id = id self._auto_role_id = id
self._server = server self._server = server
self._discord_channel_id = dc_channel_id self._discord_channel_id = channel_id
self._discord_message_id = dc_message_id self._discord_message_id = dc_message_id
TableABC.__init__(self) TableABC.__init__(self)
@ -41,6 +43,12 @@ class AutoRole(TableABC):
def discord_channel_id(self, value: int): def discord_channel_id(self, value: int):
self._discord_channel_id = value self._discord_channel_id = value
@property
@ServiceProviderABC.inject
def discord_channel_name(self, bot: DiscordBotServiceABC) -> str:
channel = bot.get_channel(self.discord_channel_id)
return None if channel is None else channel.name
@property @property
def discord_message_id(self) -> int: def discord_message_id(self) -> int:
return self._discord_message_id return self._discord_message_id

View File

@ -1,6 +1,8 @@
from datetime import datetime from datetime import datetime
from cpl_core.database import TableABC from cpl_core.database import TableABC
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from bot_data.model.auto_role import AutoRole from bot_data.model.auto_role import AutoRole
@ -44,6 +46,12 @@ class AutoRoleRule(TableABC):
def role_id(self) -> int: def role_id(self) -> int:
return self._discord_role_id return self._discord_role_id
@property
@ServiceProviderABC.inject
def role_name(self, bot: DiscordBotServiceABC) -> str:
guild = bot.get_guild(self.auto_role.server.discord_id)
return guild.get_role(self.role_id).name
@role_id.setter @role_id.setter
def role_id(self, value: int): def role_id(self, value: int):
self._discord_role_id = value self._discord_role_id = value

View File

@ -1,6 +1,8 @@
from datetime import datetime from datetime import datetime
from cpl_core.database import TableABC from cpl_core.database import TableABC
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from bot_data.model.server import Server from bot_data.model.server import Server
@ -40,6 +42,11 @@ class Client(TableABC):
def discord_id(self) -> int: def discord_id(self) -> int:
return self._discord_client_id return self._discord_client_id
@property
@ServiceProviderABC.inject
def name(self, bot: DiscordBotServiceABC) -> str:
return bot.user.name
@property @property
def sent_message_count(self) -> int: def sent_message_count(self) -> int:
return self._sent_message_count return self._sent_message_count

View File

@ -1,6 +1,8 @@
from datetime import datetime from datetime import datetime
from cpl_core.database import TableABC from cpl_core.database import TableABC
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
class Server(TableABC): class Server(TableABC):
@ -26,6 +28,18 @@ class Server(TableABC):
def discord_id(self) -> int: def discord_id(self) -> int:
return self._discord_server_id return self._discord_server_id
@property
@ServiceProviderABC.inject
def name(self, bot: DiscordBotServiceABC) -> str:
guild = bot.get_guild(self.discord_id)
return None if guild is None else guild.name
@property
@ServiceProviderABC.inject
def icon_url(self, bot: DiscordBotServiceABC) -> str:
guild = bot.get_guild(self.discord_id)
return None if guild is None else guild.icon.url
@staticmethod @staticmethod
def get_select_all_string() -> str: def get_select_all_string() -> str:
return str( return str(

View File

@ -2,7 +2,10 @@ from datetime import datetime
from typing import Optional from typing import Optional
from cpl_core.database import TableABC from cpl_core.database import TableABC
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from bot_core.abc.client_utils_abc import ClientUtilsABC
from bot_data.model.server import Server from bot_data.model.server import Server
@ -35,6 +38,20 @@ class User(TableABC):
def discord_id(self) -> int: def discord_id(self) -> int:
return self._discord_id return self._discord_id
@property
@ServiceProviderABC.inject
def name(self, bot: DiscordBotServiceABC) -> str:
guild = bot.get_guild(self.server.discord_id)
user = guild.get_member(self.discord_id)
return None if user is None else user.name
@property
@ServiceProviderABC.inject
def icon_url(self, bot: DiscordBotServiceABC) -> str:
guild = bot.get_guild(self.server.discord_id)
user = guild.get_member(self.discord_id)
return None if user is None else user.display_icon
@property @property
def xp(self) -> int: def xp(self) -> int:
return self._xp return self._xp
@ -44,6 +61,19 @@ class User(TableABC):
self._modified_at = datetime.now().isoformat() self._modified_at = datetime.now().isoformat()
self._xp = value self._xp = value
@property
@ServiceProviderABC.inject
def ontime(self, client_utils: ClientUtilsABC) -> float:
return client_utils.get_ontime_for_user(self)
@property
@ServiceProviderABC.inject
def level(self, services: ServiceProviderABC) -> "Level":
from modules.level.service.level_service import LevelService
levels: LevelService = services.get_service(LevelService)
return levels.get_level(self)
@property @property
def minecraft_id(self) -> Optional[str]: def minecraft_id(self) -> Optional[str]:
return self._minecraft_id return self._minecraft_id

View File

@ -1,6 +1,8 @@
from datetime import datetime from datetime import datetime
from cpl_core.database import TableABC from cpl_core.database import TableABC
from cpl_core.dependency_injection import ServiceProviderABC
from cpl_discord.service import DiscordBotServiceABC
from bot_data.model.user import User from bot_data.model.user import User
@ -9,7 +11,7 @@ class UserJoinedVoiceChannel(TableABC):
def __init__( def __init__(
self, self,
user: User, user: User,
dc_channel_id: int, channel_id: int,
joined_on: datetime, joined_on: datetime,
leaved_on: datetime = None, leaved_on: datetime = None,
created_at: datetime = None, created_at: datetime = None,
@ -17,7 +19,7 @@ class UserJoinedVoiceChannel(TableABC):
id=0, id=0,
): ):
self._join_id = id self._join_id = id
self._dc_channel_id = dc_channel_id self._channel_id = channel_id
self._user = user self._user = user
self._joined_on = joined_on self._joined_on = joined_on
self._leaved_on = leaved_on self._leaved_on = leaved_on
@ -31,8 +33,13 @@ class UserJoinedVoiceChannel(TableABC):
return self._join_id return self._join_id
@property @property
def dc_channel_id(self) -> int: def channel_id(self) -> int:
return self._dc_channel_id return self._channel_id
@property
@ServiceProviderABC.inject
def channel_name(self, bot: DiscordBotServiceABC) -> str:
return bot.get_channel(self.channel_id).name
@property @property
def user(self) -> User: def user(self) -> User:
@ -101,7 +108,7 @@ class UserJoinedVoiceChannel(TableABC):
`UserId`, `DiscordChannelId`, `JoinedOn`, `LeavedOn`, `CreatedAt`, `LastModifiedAt` `UserId`, `DiscordChannelId`, `JoinedOn`, `LeavedOn`, `CreatedAt`, `LastModifiedAt`
) VALUES ( ) VALUES (
{self._user.id}, {self._user.id},
{self._dc_channel_id}, {self._channel_id},
'{self._joined_on}', '{self._joined_on}',
'{self._leaved_on}', '{self._leaved_on}',
'{self._created_at}', '{self._created_at}',
@ -116,7 +123,7 @@ class UserJoinedVoiceChannel(TableABC):
`UserId`, `DiscordChannelId`, `JoinedOn`, `CreatedAt`, `LastModifiedAt` `UserId`, `DiscordChannelId`, `JoinedOn`, `CreatedAt`, `LastModifiedAt`
) VALUES ( ) VALUES (
{self._user.id}, {self._user.id},
{self._dc_channel_id}, {self._channel_id},
'{self._joined_on}', '{self._joined_on}',
'{self._created_at}', '{self._created_at}',
'{self._modified_at}' '{self._modified_at}'

View File

@ -36,7 +36,7 @@ class ServerFilter(FilterABC):
if self._name is not None: if self._name is not None:
def where_guild(x: Guild): def where_guild(x: Guild):
guild = bot.get_guild(x.discord_server_id) guild = bot.get_guild(x.discord_id)
return guild is not None and ( return guild is not None and (
self._name.lower() == guild.name.lower() or self._name.lower() in guild.name.lower() self._name.lower() == guild.name.lower() or self._name.lower() in guild.name.lower()
) )

View File

@ -1,11 +1,17 @@
import functools
from cpl_core.utils import String
from cpl_query.extension import List from cpl_query.extension import List
from bot_api.exception.service_error_code_enum import ServiceErrorCode
from bot_api.exception.service_exception import ServiceException
from bot_graphql.abc.filter_abc import FilterABC from bot_graphql.abc.filter_abc import FilterABC
class Sort(FilterABC): class Sort(FilterABC):
def __init__(self): def __init__(self):
FilterABC.__init__(self) FilterABC.__init__(self)
self._sort_direction = None self._sort_direction = None
self._sort_column = None self._sort_column = None
@ -16,8 +22,19 @@ class Sort(FilterABC):
if "sortColumn" in values: if "sortColumn" in values:
self._sort_column = values["sortColumn"] self._sort_column = values["sortColumn"]
@staticmethod
def _rgetattr(obj, attr, *args):
def _getattr(obj, attr):
return getattr(obj, attr, *args)
return functools.reduce(_getattr, [obj] + attr.split("."))
def _by_column(self, x): def _by_column(self, x):
atr = getattr(x, self._sort_column, None) atr = self._rgetattr(x, String.convert_to_snake_case(self._sort_column), None)
if atr is None:
raise ServiceException(
ServiceErrorCode.InvalidData, f"Attribute {self._sort_column} in object of {type(x)} not found"
)
return atr return atr
def filter(self, query: List, *args) -> List: def filter(self, query: List, *args) -> List:

View File

@ -51,12 +51,12 @@ class UserJoinedVoiceChannelFilter(FilterABC):
query = query.where(lambda x: x.id == self._id) query = query.where(lambda x: x.id == self._id)
if self._channel_id is not None: if self._channel_id is not None:
query = query.where(lambda x: x.dc_channel_id == self._channel_id) query = query.where(lambda x: x.channel_id == self._channel_id)
if self._channel_name is not None and self._channel_id is not None: if self._channel_name is not None and self._channel_id is not None:
def get_channel_name(x: UserJoinedVoiceChannel): def get_channel_name(x: UserJoinedVoiceChannel):
name = self._bot.get_channel(x.dc_channel_id).name name = self._bot.get_channel(x.channel_id).name
return name == self._channel_name or self._channel_name in name return name == self._channel_name or self._channel_name in name
query = query.where(get_channel_name) query = query.where(get_channel_name)

View File

@ -40,9 +40,9 @@ class AutoRoleQuery(DataQueryABC):
def resolve_channel_id(x: AutoRole, *_): def resolve_channel_id(x: AutoRole, *_):
return x.discord_channel_id return x.discord_channel_id
def resolve_channel_name(self, x: AutoRole, *_): @staticmethod
channel = self._bot.get_channel(x.discord_channel_id) def resolve_channel_name(x: AutoRole, *_):
return None if channel is None else channel.name return x.discord_channel_name
@staticmethod @staticmethod
def resolve_message_id(x: AutoRole, *_): def resolve_message_id(x: AutoRole, *_):

View File

@ -34,9 +34,9 @@ class AutoRoleRuleQuery(DataQueryABC):
def resolve_role_id(x: AutoRoleRule, *_): def resolve_role_id(x: AutoRoleRule, *_):
return x.role_id return x.role_id
def resolve_role_name(self, x: AutoRoleRule, *_): @staticmethod
guild = self._bot.get_guild(x.auto_role.server.discord_id) def resolve_role_name(x: AutoRoleRule, *_):
return guild.get_role(x.role_id).name return x.role_name
def resolve_auto_role(self, x: AutoRoleRule, *_): def resolve_auto_role(self, x: AutoRoleRule, *_):
return self._auto_roles.get_auto_role_by_id(x.auto_role.id) return self._auto_roles.get_auto_role_by_id(x.auto_role.id)

View File

@ -31,8 +31,9 @@ class ClientQuery(DataQueryABC):
def resolve_discord_id(client: Client, *_): def resolve_discord_id(client: Client, *_):
return client.discord_id return client.discord_id
def resolve_name(self, client: Client, *_): @staticmethod
return self._bot.user.name def resolve_name(client: Client, *_):
return client.name
@staticmethod @staticmethod
def resolve_sent_message_count(client: Client, *_): def resolve_sent_message_count(client: Client, *_):

View File

@ -59,10 +59,10 @@ class ServerQuery(DataQueryABC):
def resolve_discord_id(server: Server, *_): def resolve_discord_id(server: Server, *_):
return server.discord_id return server.discord_id
def resolve_name(self, server: Server, *_): @staticmethod
guild = self._bot.get_guild(server.discord_id) def resolve_name(server: Server, *_):
return None if guild is None else guild.name return server.name
def resolve_icon_url(self, server: Server, *_): @staticmethod
guild = self._bot.get_guild(server.discord_id) def resolve_icon_url(server: Server, *_):
return None if guild is None else guild.icon.url return server.icon_url

View File

@ -23,10 +23,11 @@ class UserJoinedVoiceChannelQuery(DataQueryABC):
@staticmethod @staticmethod
def resolve_channel_id(x: UserJoinedVoiceChannel, *_): def resolve_channel_id(x: UserJoinedVoiceChannel, *_):
return x.dc_channel_id return x.channel_id
def resolve_channel_name(self, x: UserJoinedVoiceChannel, *_): @staticmethod
return self._bot.get_channel(x.dc_channel_id).name def resolve_channel_name(x: UserJoinedVoiceChannel, *_):
return x.channel_name
@staticmethod @staticmethod
def resolve_user(x: UserJoinedVoiceChannel, *_): def resolve_user(x: UserJoinedVoiceChannel, *_):

View File

@ -67,10 +67,9 @@ class UserQuery(DataQueryABC):
def resolve_discord_id(user: User, *_): def resolve_discord_id(user: User, *_):
return user.discord_id return user.discord_id
def resolve_name(self, user: User, *_): @staticmethod
guild = self._bot.get_guild(user.server.discord_id) def resolve_name(user: User, *_):
user = guild.get_member(user.discord_id) return user.name
return None if user is None else user.name
@staticmethod @staticmethod
def resolve_xp(user: User, *_): def resolve_xp(user: User, *_):
@ -80,11 +79,13 @@ class UserQuery(DataQueryABC):
def resolve_minecraft_id(user: User, *_): def resolve_minecraft_id(user: User, *_):
return user.minecraft_id return user.minecraft_id
def resolve_ontime(self, user: User, *_): @staticmethod
return self._client_utils.get_ontime_for_user(user) def resolve_ontime(user: User, *_):
return user.ontime
def resolve_level(self, user: User, *_): @staticmethod
return self._levels.get_level(user) def resolve_level(user: User, *_):
return user.level
@staticmethod @staticmethod
def resolve_server(user: User, *_): def resolve_server(user: User, *_):

View File

@ -48,7 +48,7 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC):
self._logger.info(__name__, f"Module {type(self)} loaded") self._logger.info(__name__, f"Module {type(self)} loaded")
def _update_voice_state(self, joined: bool, dc_user_id: int, dc_channel_id: int, server: Server): def _update_voice_state(self, joined: bool, dc_user_id: int, channel_id: int, server: Server):
user: Optional[User] = None user: Optional[User] = None
try: try:
user = self._users.get_user_by_discord_id_and_server_id(dc_user_id, server.id) user = self._users.get_user_by_discord_id_and_server_id(dc_user_id, server.id)
@ -62,7 +62,7 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC):
try: try:
if joined: if joined:
join = UserJoinedVoiceChannel(user, dc_channel_id, datetime.now()) join = UserJoinedVoiceChannel(user, channel_id, datetime.now())
self._user_joins_vc.add_user_joined_voice_channel(join) self._user_joins_vc.add_user_joined_voice_channel(join)
self._db.save_changes() self._db.save_changes()
return return

View File

@ -1,4 +1,9 @@
export interface Sort { export interface Sort {
sortColumn?: string; sortColumn?: string;
sortDirection?: string; sortDirection?: SortDirection;
}
export enum SortDirection {
ASC = "ASC",
DESC = "DESC",
} }

View File

@ -12,7 +12,7 @@ import { Server } from "../../../../../models/data/server.model";
import { catchError } from "rxjs/operators"; import { catchError } from "rxjs/operators";
import { Queries } from "../../../../../models/graphql/queries.model"; import { Queries } from "../../../../../models/graphql/queries.model";
import { Page } from "../../../../../models/graphql/filter/page.model"; import { Page } from "../../../../../models/graphql/filter/page.model";
import { Sort } from "../../../../../models/graphql/filter/sort.model"; import { Sort, SortDirection } from "../../../../../models/graphql/filter/sort.model";
import { Query } from "../../../../../models/graphql/query.model"; import { Query } from "../../../../../models/graphql/query.model";
import { SidebarService } from "../../../../../services/sidebar/sidebar.service"; import { SidebarService } from "../../../../../services/sidebar/sidebar.service";
@ -106,7 +106,7 @@ export class DashboardComponent implements OnInit {
this.sort = { this.sort = {
sortColumn: event.sortField ?? "", sortColumn: event.sortField ?? "",
sortDirection: event.sortOrder === 1 ? "asc" : event.sortOrder === -1 ? "desc" : "asc" sortDirection: event.sortOrder === 1 ? SortDirection.ASC : event.sortOrder === -1 ? SortDirection.DESC: SortDirection.ASC
}; };
if (event.filters) { if (event.filters) {

View File

@ -26,7 +26,7 @@
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th pSortableColumn="id"> <th class="table-header-small" pSortableColumn="id">
<div class="table-header-label"> <div class="table-header-label">
<div class="table-header-text">{{'view.server.members.headers.id' | translate}}</div> <div class="table-header-text">{{'view.server.members.headers.id' | translate}}</div>
<p-sortIcon field="id" class="table-header-icon"></p-sortIcon> <p-sortIcon field="id" class="table-header-icon"></p-sortIcon>
@ -61,10 +61,10 @@
</div> </div>
</th> </th>
<th class="table-header-small-dropdown" pSortableColumn="level"> <th class="table-header-small-dropdown" pSortableColumn="level.name">
<div class="table-header-label"> <div class="table-header-label">
<div class="table-header-text">{{'view.server.members.headers.level' | translate}}</div> <div class="table-header-text">{{'view.server.members.headers.level' | translate}}</div>
<p-sortIcon field="level" class="table-header-icon"></p-sortIcon> <p-sortIcon field="level.name" class="table-header-icon"></p-sortIcon>
</div> </div>
</th> </th>