Source code for gd.utils.filters

from enums import Enum

from gd.typing import AbstractUser, Any, Dict, Filters, Level, Optional, Sequence, Union

from gd.utils.enums import LevelDifficulty, DemonDifficulty, SearchStrategy, LevelLength
from gd.utils.text_tools import make_repr


[docs]class Filters: """Class that adds ability to create custom filters to apply when searching for levels. Attributes ---------- strategy: Optional[Union[:class:`int`, :class:`str`, :class:`SearchStrategy`]] A strategy to apply. difficulty: Optional[Sequence[Union[:class:`int`, :class:`str`, :class:`LevelDifficulty`]]] Level difficulties to apply. Either sequence of many or just one difficulty. demon_difficulty: Optional[Union[:class:`int`, :class:`str`, :class:`DemonDifficulty`]] **One** Demon difficulty to apply as a filter. length: Sequence[Union[:class:`int`, :class:`str`, :class:`LevelLength`]] Same as ``difficulty``, but for lengths. uncompleted: Optional[:class:`bool`] Whether to search for uncompleted levels only. If true, ``completed_levels`` is required. only_completed: Optional[:class:`bool`] Opposite of ``uncompleted``. completed_levels: Optional[Sequence[Union[:class:`int`, :class:`.Level`]]] A sequence of completed levels. require_coins: Optional[:class:`bool`] Whether levels must have coins in them. featured: Optional[:class:`bool`] Whether levels **must** be featured. epic: Optional[:class:`bool`] Whether levels ought to be epic. rated: Optional[:class:`bool`] This defaults to ``None``. If true, will search for only rated levels, if false, will search for only not rated levels. If ``None``, makes no changes. require_two_player: Optional[:class:`bool`] Whether a level must be for two players. song_id: Optional[:class:`int`] An ID of the song that should be used. use_custom_song: Optional[:class:`int`] Whether a custom song should be used. Requires ``song_id`` to be defined. require_original: Optional[:class:`bool`] Whether levels must be original. followed: Optional[Sequence[Union[:class:`int`, :class:`.AbstractUser`, :class:`.User`]]] Followed users, basically this will work as ``BY_USER`` ``strategy``, but for many users. """ def __init__( self, strategy: Union[int, str, SearchStrategy] = 0, difficulty: Optional[Sequence[Union[int, str, LevelDifficulty]]] = None, demon_difficulty: Optional[Union[int, str, DemonDifficulty]] = None, length: Optional[Union[int, str, LevelLength]] = None, uncompleted: bool = False, only_completed: bool = False, completed_levels: Optional[Sequence[Union[int, Level]]] = None, require_coins: bool = False, featured: bool = False, epic: bool = False, rated: Optional[bool] = None, require_two_player: bool = False, song_id: Optional[int] = None, use_custom_song: bool = False, require_original: bool = False, followed: Optional[Sequence[Union[int, AbstractUser]]] = None, ) -> None: if isinstance(difficulty, (int, str, Enum)): difficulty = [difficulty] if isinstance(length, (int, str, Enum)): length = [length] self.strategy = SearchStrategy.from_value(strategy) not_accepting = self.strategy.value in (5, 6, 16) self.difficulty = ( None if ((difficulty is None) or not_accepting) else tuple(map(LevelDifficulty.from_value, difficulty)) ) self.length = ( None if ((length is None) or not_accepting) else tuple(map(LevelLength.from_value, length)) ) self.demon_difficulty = ( None if ((demon_difficulty is None) or not_accepting) else DemonDifficulty.from_value(demon_difficulty) ) if self.demon_difficulty is not None: self.difficulty = (LevelDifficulty.DEMON,) self.uncompleted = uncompleted if completed_levels else False self.only_completed = only_completed if completed_levels else False self.completed_levels = list(completed_levels or []) self.require_coins = require_coins self.require_two_player = require_two_player self.rated = rated self.featured = featured self.epic = epic self.song_id = song_id self.use_custom_song = use_custom_song self.followed = list(followed or []) self.require_original = require_original self.set = ( "strategy", "difficulty", "demon_difficulty", "length", "uncompleted", "only_completed", "completed_levels", "require_coins", "require_two_player", "rated", "featured", "epic", "song_id", "use_custom_song", "followed", "require_original", ) def __repr__(self) -> str: info = {k: repr(getattr(self, k)) for k in self.set} return make_repr(self, info) @classmethod def setup_empty(cls, *args, **kwargs) -> Filters: return cls(*args, **kwargs) @classmethod def setup_simple(cls, *args, **kwargs) -> Filters: return cls(strategy=SearchStrategy.MOST_LIKED, *args, **kwargs) @classmethod def setup_by_user(cls, *args, **kwargs) -> Filters: return cls(strategy=SearchStrategy.BY_USER, *args, **kwargs) @classmethod def setup_with_song(cls, song_id: int, is_custom: bool = True, *args, **kwargs) -> Filters: return cls( strategy=SearchStrategy.MOST_LIKED, song_id=song_id, use_custom_song=is_custom, *args, **kwargs, ) @classmethod def setup_search_many(cls) -> Filters: return cls(strategy=SearchStrategy.SEARCH_MANY) @classmethod def setup_with_followed(cls, followed: Sequence[int], *args, **kwargs) -> Filters: return cls(strategy=SearchStrategy.FOLLOWED, followed=followed, *args, **kwargs) @classmethod def setup_by_friends(cls, *args, **kwargs) -> Filters: return cls(strategy=SearchStrategy.FRIENDS, *args, **kwargs) @classmethod def setup_client_followed(cls, client, *args, **kwargs) -> Filters: return cls(strategy=SearchStrategy.FOLLOWED, followed=client.db.followed, *args, **kwargs) @classmethod def setup_client_completed(cls, client, *args, **kwargs) -> Filters: return cls( strategy=SearchStrategy.MOST_LIKED, completed_levels=client.db.values.normal.completed, *args, **kwargs, ) def to_parameters(self) -> Dict[str, str]: main = { "type": self.strategy.value, "diff": "-" if self.difficulty is None else _join(self.difficulty), "len": "-" if self.length is None else _join(self.length), "uncompleted": int(self.uncompleted), "onlyCompleted": int(self.only_completed), "featured": int(self.featured), "original": int(self.require_original), "twoPlayer": int(self.require_two_player), "coins": int(self.require_coins), "epic": int(self.epic), } if self.demon_difficulty is not None: main.update(demonFilter=self.demon_difficulty.value) if self.uncompleted or self.only_completed: main.update(completedLevels=_join(self.completed_levels, wrap_with="({})")) if self.rated is not None: main.update({("star" if self.rated else "noStar"): 1}) if self.strategy == SearchStrategy.FOLLOWED: main.update(followed=_join(self.followed)) if self.song_id is not None: main.update(song=int(self.song_id), isCustom=int(self.use_custom_song)) filters = {k: str(v) for k, v in main.items()} return filters
def _join(elements: Sequence[Any], *, string: str = ",", wrap_with: str = "{}") -> str: def func(element: Any) -> str: to_str = element if isinstance(element, Enum): # Enum to_str = element.value elif hasattr(element, "account_id"): # User to_str = element.account_id elif hasattr(element, "id"): # Level to_str = element.id return str(to_str) return wrap_with.format(string.join(map(func, elements)))