Improved queries & filtering #162

This commit is contained in:
Sven Heidemann 2023-01-16 15:13:45 +01:00
parent c75cc54d16
commit 807827e30f
12 changed files with 207 additions and 84 deletions

@ -1 +1 @@
Subproject commit 54b1b3860cb570d29c8ba2590dd082a1fa744265
Subproject commit b0ae87621bbe54fd9c5650071ec8c1c9ec32df48

View File

@ -3,7 +3,6 @@ from ariadne.constants import PLAYGROUND_HTML
from cpl_core.configuration import ConfigurationABC
from cpl_core.environment import ApplicationEnvironmentABC
from flask import request, jsonify
from graphql import MiddlewareManager
from bot_api.logging.api_logger import ApiLogger
from bot_api.route.route import Route

View File

@ -0,0 +1,20 @@
from datetime import datetime
from bot_graphql.abc.query_type_abc import QueryTypeABC
class DiscordQueryTypeABC(QueryTypeABC):
def __init__(
self,
id: str,
discord_id: str,
created_at: datetime = None,
modified_at: datetime = None,
):
QueryTypeABC.__init__(self, id, created_at, modified_at)
self._discord_id = discord_id
@property
def discord_id(self) -> str:
return self._discord_id

View File

@ -1,5 +1,5 @@
import functools
from abc import ABC
from abc import ABC, abstractmethod
from inspect import signature, Parameter
from typing import Optional
@ -40,6 +40,14 @@ class FilterABC(ABC):
return query
@abstractmethod
def from_dict(self, values: dict):
pass
@abstractmethod
def filter(self, query: List, *args) -> List:
pass
@staticmethod
@ServiceProviderABC.inject
def get_filter(f, values: dict, services: ServiceProviderABC):
@ -52,7 +60,14 @@ class FilterABC(ABC):
if issubclass(parameter.annotation, FilterABC):
filter = services.get_service(parameter.annotation)
filter.from_dict(values)
return filter
return filter @ staticmethod
@staticmethod
@ServiceProviderABC.inject
def get_collection_filter(filter_type: type, values: dict, services: ServiceProviderABC):
filter: FilterABC = services.get_service(filter_type)
filter.from_dict(values)
return filter
@classmethod
def resolve_filter_annotation(cls, f=None):

View File

@ -1,5 +1,30 @@
from typing import Callable
from ariadne import ObjectType
from cpl_query.extension import List
from bot_graphql.abc.filter_abc import FilterABC
class QueryABC(ObjectType):
__abstract__ = True
def __init__(self, name: str):
ObjectType.__init__(self, name)
def _add_collection(self, name: str, 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"])
else:
kwargs["filter"] = None
return self._resolve_collection(get_collection(*args), *args, **kwargs)
self.set_field(f"{name}s", wrapper)
self.set_field(f"{name}_count", lambda *args: get_collection(*args).count())
def _resolve_collection(self, collection: List, *_, filter: FilterABC = None):
if filter is not None:
return filter.filter(collection)
return collection

View File

@ -0,0 +1,29 @@
from abc import ABC
from datetime import datetime
from typing import Optional
class QueryTypeABC(ABC):
def __init__(
self,
id: str,
created_at: datetime = None,
modified_at: datetime = None,
):
ABC.__init__(self)
self._id = id
self._created_at = created_at
self._modified_at = modified_at
@property
def id(self) -> str:
return self._id
@property
def created_at(self) -> Optional[datetime]:
return self._created_at
@property
def modified_at(self) -> Optional[datetime]:
return self._modified_at

View File

@ -1,15 +1,25 @@
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_core.abc.client_utils_abc import ClientUtilsABC
from bot_data.model.user import User
from bot_graphql.abc.filter_abc import FilterABC
from modules.level.service.level_service import LevelService
class UserFilter(FilterABC):
def __init__(
self,
bot: DiscordBotServiceABC,
client_utils: ClientUtilsABC,
levels: LevelService,
):
FilterABC.__init__(self)
self._bot = bot
self._client_utils = client_utils
self._levels = levels
self._id = None
self._discord_id = None
self._name = None
@ -38,10 +48,19 @@ class UserFilter(FilterABC):
if self._discord_id is not None:
query = query.where(lambda x: x.discord_id == self._discord_id)
if self._name is not None:
query = query.where(
lambda x: self._bot.get_user(x.discord_id).name == self._name
or self._name in self._bot.get_user(x.discord_id).name
)
if self._xp is not None:
query = query.where(lambda x: x.xp == self._xp)
# if self._ontime is not None:
# query = query.where(lambda x: self._client_utils.get_ontime_for_user(x) == self._ontime)
if self._ontime is not None:
query = query.where(lambda x: self._client_utils.get_ontime_for_user(x) == self._ontime)
if self._level is not None:
query = query.where(lambda x: self._levels.get_level(x) == self._level)
return self.skip_and_take(query)

View File

@ -8,11 +8,32 @@ type Mutation {
}
type Query {
servers(filter: ServerFilter): [Server]
server_count: Int
auto_roles: [AutoRole]
auto_role_count: Int
auto_role_rules: [AutoRole]
auto_role_rule_count: Int
clients: [Client]
client_count: Int
known_users: [KnownUser]
known_user_count: Int
levels: [Level]
level_count: Int
servers(filter: ServerFilter): [Server]
server_count: Int
user_joined_servers: [User]
user_joined_server_count: Int
user_joined_voice_channels: [User]
user_joined_voice_channel_count: Int
users(filter: UserFilter): [User]
user_count: Int
}
type KnownUser implements TableQuery {
@ -38,10 +59,18 @@ type Server implements TableQuery {
id: ID
discord_id: String
name: String
auto_roles: [AutoRole]
auto_role_count: Int
clients: [Client]
members: [User]
client_count: Int
users(filter: UserFilter): [User]
user_count: Int
levels: [Level]
level_count: Int
created_at: String
modified_at: String
@ -69,7 +98,9 @@ type AutoRole implements TableQuery {
message_id: String
server: Server
rules: [AutoRoleRule]
auto_role_rules: [AutoRoleRule]
auto_role_rule_count: Int
created_at: String
modified_at: String
@ -109,7 +140,10 @@ type User implements TableQuery {
level: Level
joined_servers: [UserJoinedServer]
joined_server_count: Int
joined_voice_channels: [UserJoinedVoiceChannel]
joined_voice_channel_count: Int
server: Server

View File

@ -2,7 +2,6 @@ from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.model.auto_role import AutoRole
from bot_graphql.abc.data_query_abc import DataQueryABC
from bot_graphql.filter.auto_role_rule_filter import AutoRoleRuleFilter
from bot_graphql.filter.server_filter import ServerFilter
@ -21,7 +20,9 @@ class AutoRoleQuery(DataQueryABC):
self.set_field("channel_id", self.resolve_channel_id)
self.set_field("message_id", self.resolve_message_id)
self.set_field("server", self.resolve_server)
self.set_field("rules", self.resolve_rules)
self._add_collection(
"auto_role_rule", lambda x, *_: self._auto_role_rules.get_auto_role_rules_by_auto_role_id(x.auto_role_id)
)
@staticmethod
def resolve_id(x: AutoRole, *_):
@ -40,9 +41,3 @@ class AutoRoleQuery(DataQueryABC):
return filter.filter(self._servers.get_server_by_id(x.server_id))
return self._servers.get_server_by_id(x.server_id)
def resolve_rules(self, x: AutoRole, *_, filter: AutoRoleRuleFilter = None):
if filter is not None:
return filter.filter(self._auto_role_rules.get_auto_role_rules_by_auto_role_id(x.auto_role_id))
return self._auto_role_rules.get_auto_role_rules_by_auto_role_id(x.auto_role_id)

View File

@ -6,9 +6,6 @@ from bot_data.abc.level_repository_abc import LevelRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.model.server import Server
from bot_graphql.abc.data_query_abc import DataQueryABC
from bot_graphql.abc.filter_abc import FilterABC
from bot_graphql.filter.auto_role_filter import AutoRoleFilter
from bot_graphql.filter.level_filter import LevelFilter
from bot_graphql.filter.user_filter import UserFilter
@ -33,10 +30,15 @@ class ServerQuery(DataQueryABC):
self.set_field("id", self.resolve_id)
self.set_field("discord_id", self.resolve_discord_id)
self.set_field("name", self.resolve_name)
self.set_field("auto_roles", self.resolve_auto_roles)
self.set_field("clients", self.resolve_clients)
self.set_field("members", self.resolve_members)
self.set_field("levels", self.resolve_levels)
self._add_collection(
"auto_role", lambda server, *_: self._auto_roles.get_auto_roles_by_server_id(server.server_id)
)
self._add_collection("client", lambda server, *_: self._clients.get_clients_by_server_id(server.server_id))
self._add_collection(
"user", lambda server, *_: self._users.get_users_by_server_id(server.server_id), UserFilter
)
self._add_collection("level", lambda server, *_: self._levels.get_levels_by_server_id(server.server_id))
@staticmethod
def resolve_id(server: Server, *_):
@ -49,26 +51,3 @@ class ServerQuery(DataQueryABC):
def resolve_name(self, server: Server, *_):
guild = self._bot.get_guild(server.discord_server_id)
return None if guild is None else guild.name
def resolve_auto_roles(self, server: Server, *_, filter: AutoRoleFilter = None):
if filter is not None:
return filter.filter(self._auto_roles.get_auto_roles_by_server_id(server.server_id))
return self._auto_roles.get_auto_roles_by_server_id(server.server_id)
def resolve_clients(self, server: Server, *_):
return self._clients.get_clients_by_server_id(server.server_id)
@FilterABC.resolve_filter_annotation
def resolve_members(self, server: Server, *_, filter: UserFilter = None):
if filter is not None:
return filter.filter(self._users.get_users_by_server_id(server.server_id))
return self._users.get_users_by_server_id(server.server_id)
@FilterABC.resolve_filter_annotation
def resolve_levels(self, server: Server, *_, filter: LevelFilter = None):
if filter is not None:
return filter.filter(self._levels.get_levels_by_server_id(server.server_id))
return self._levels.get_levels_by_server_id(server.server_id)

View File

@ -31,8 +31,12 @@ class UserQuery(DataQueryABC):
self.set_field("xp", self.resolve_xp)
self.set_field("ontime", self.resolve_ontime)
self.set_field("level", self.resolve_level)
self.set_field("joined_servers", self.resolve_joined_servers)
self.set_field("joined_voice_channels", self.resolve_joined_voice_channel)
self._add_collection(
"joined_server", lambda user, *_: self._ujs.get_user_joined_servers_by_user_id(user.user_id)
)
self._add_collection(
"joined_voice_channel", lambda user, *_: self._ujvs.get_user_joined_voice_channels_by_user_id(user.user_id)
)
self.set_field("server", self.resolve_server)
@staticmethod
@ -58,12 +62,6 @@ class UserQuery(DataQueryABC):
def resolve_level(self, user: User, *_):
return self._levels.get_level(user)
def resolve_joined_servers(self, user: User, *_):
return self._ujs.get_user_joined_servers_by_user_id(user.user_id)
def resolve_joined_voice_channel(self, user: User, *_):
return self._ujvs.get_user_joined_voice_channels_by_user_id(user.user_id)
@staticmethod
def resolve_server(user: User, *_):
return user.server

View File

@ -1,39 +1,49 @@
from ariadne import QueryType
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_graphql.abc.filter_abc import FilterABC
from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC
from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_graphql.abc.query_abc import QueryABC
from bot_graphql.filter.auto_role_filter import AutoRoleFilter
from bot_graphql.filter.auto_role_rule_filter import AutoRoleRuleFilter
from bot_graphql.filter.level_filter import LevelFilter
from bot_graphql.filter.server_filter import ServerFilter
from bot_graphql.filter.user_filter import UserFilter
class Query(QueryType):
class Query(QueryABC):
def __init__(
self,
servers: ServerRepositoryABC,
auto_roles: AutoRoleRepositoryABC,
clients: ClientRepositoryABC,
known_users: KnownUserRepositoryABC,
levels: LevelRepositoryABC,
servers: ServerRepositoryABC,
user_joined_servers: UserJoinedServerRepositoryABC,
user_joined_voice_channel: UserJoinedVoiceChannelRepositoryABC,
users: UserRepositoryABC,
):
QueryType.__init__(self)
self._servers = servers
QueryABC.__init__(self, "Query")
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_channels = user_joined_voice_channel
self._users = users
self.set_field("servers", self.resolve_servers)
self.set_field("server_count", self.resolve_server_count)
self.set_field("known_users", self.resolve_known_users)
self.set_field("known_user_count", self.resolve_known_users_count)
@FilterABC.resolve_filter_annotation
def resolve_servers(self, *_, filter: ServerFilter = None):
if filter is not None:
return filter.filter(self._servers.get_servers())
else:
return self._servers.get_servers()
def resolve_server_count(self, *_):
return self._servers.get_servers().count()
def resolve_known_users(self, *_):
return self._known_users.get_users()
def resolve_known_users_count(self, *_):
return self._known_users.get_users().count()
self._add_collection("auto_role", lambda *_: self._auto_roles.get_auto_roles(), AutoRoleFilter)
self._add_collection("auto_role_rule", lambda *_: self._auto_roles.get_auto_role_rules(), AutoRoleRuleFilter)
self._add_collection("client", lambda *_: self._clients.get_clients())
self._add_collection("known_user", lambda *_: self._known_users.get_users())
self._add_collection("level", lambda *_: self._levels.get_levels(), LevelFilter)
self._add_collection("server", lambda *_: self._servers.get_servers(), ServerFilter)
self._add_collection("user_joined_server", lambda *_: self._user_joined_servers.get_user_joined_servers())
self._add_collection(
"user_joined_voice_channel", lambda *_: self._user_joined_voice_channels.get_user_joined_voice_channels()
)
self._add_collection("user", lambda *_: self._users.get_users(), UserFilter)