def test_name_match_invert(self): """ Tests that """ root_param = AndParam(True) card = create_test_card({'name': 'foo'}) param = CardNameParam('bar') root_param.add_parameter(param) self.assertIn(card, Card.objects.filter(root_param.query()))
def create_colour_param(colour_params: List[Bit], param_class: type, match_colours: bool, exclude_colours: bool) -> AndParam: """ Creates a series of colour parameters based on the given colours :param colour_params: A list of bits flags that should be searched for :param param_class: The colour parameter class (either Colour of ColourIdentity) :param match_colours: Whether the colours should match exactly or not :param exclude_colours: Whether unselected colours should be excluded or not :return: A root AndParam with a series of colour parameters underneath """ root_param = AndParam() if match_colours: colour_root = AndParam() else: colour_root = OrParam() root_param.add_parameter(colour_root) for colour in colour_params: colour_root.add_parameter(param_class(colour)) if exclude_colours: exclude_param = OrParam(inverse=True) root_param.add_parameter(exclude_param) for colour in [c for c in Card.colour_flags.values() if c not in colour_params]: param = param_class(colour) exclude_param.add_parameter(param) return root_param
def simple_word_group_parameter(self) -> CardSearchParam: """ Attempts to parse a parameter that has a list of words inside parentheses For example "oracle:(foo bar)" :return: An AndParam containing the parameters. All the parameters will be of the type specified before the colon at the start of the string """ parameter_type = self.match("param_type") operator = self.match("operator") param_values = self.match("simple_word_group") and_param = AndParam() for value in param_values: param = self.parse_param(parameter_type, operator, value) and_param.add_parameter(param) return and_param
class CardSearch: def __init__(self): self.root_parameter = AndParam() self.sort_params = list() def add_sort_param(self, sort_param: CardSortParam): self.sort_params.append(sort_param) def result_search(self): result = self.root_parameter.get_result() self.add_sort_param(CardNameSortParam()) result = result.order_by(*[ order for sort_param in self.sort_params for order in sort_param.get_sort_list() ]) return result
def test_name_match_invert(self) -> None: """ Tests that """ root_param = AndParam() root_param.negated = True card = create_test_card({"name": "foo"}) set_obj = create_test_set("Setty", "SET", {}) printing = create_test_card_printing(card, set_obj, {}) param = CardNameParam("bar") root_param.add_parameter(param) self.assertIn(printing, CardPrinting.objects.filter(root_param.query()))
def and_group(self) -> CardSearchParam: """ Attempts to parse a list of parameters separated by "and"s :return: The AndParam group, or the single parameter if there is only one """ result = self.match("parameter_group") and_group = None while True: self.maybe_keyword("and") param_group = self.maybe_match("parameter_group") if not param_group: break if and_group is None: and_group = AndParam() and_group.add_parameter(result) and_group.add_parameter(param_group) return and_group or result
def create_colour_param( colour_params: List[int], param_class: type, match_colours: bool, exclude_colours: bool, ) -> AndParam: """ Creates a series of colour parameters based on the given colours :param colour_params: A list of bits flags that should be searched for :param param_class: The colour parameter class (either Colour of ColourIdentity) :param match_colours: Whether the colours should match exactly or not :param exclude_colours: Whether unselected colours should be excluded or not :return: A root AndParam with a series of colour parameters underneath """ root_param = AndParam() if match_colours: colour_root = AndParam() else: colour_root = OrParam() root_param.add_parameter(colour_root) for colour in colour_params: colour_root.add_parameter(param_class(colour)) if exclude_colours: exclude_param = OrParam() exclude_param.negated = True for colour in [ c for c in Card.colour_flags.values() if c not in colour_params ]: param = param_class(colour) exclude_param.add_parameter(param) if exclude_param.child_parameters: root_param.add_parameter(exclude_param) return root_param
def __init__(self) -> None: self.root_parameter: BranchParam = AndParam() self.sort_params: List[CardSortParam] = [] self.paginator: Optional[Paginator] = None self.results: List[SearchResult] = [] self.page: Optional[int] = None
class BaseSearch: """ The core searching object. This can be extended to have different fields, but they should all use a single root node with parameters haning off of it """ def __init__(self): self.root_parameter = AndParam() self.sort_params = list() self.paginator = None self.results = [] self.page = None # pylint: disable=no-self-use def build_parameters(self): """ Build the parameters tree for this search object :return: """ return def search(self, page_number: int = 1, page_size: int = 25): """ Runs the search for this search and constructs :param page_number: The result page :param page_size: The number of items per page """ queryset = Card.objects.filter(self.root_parameter.query()).distinct() self.add_sort_param(CardNameSortParam()) self.add_sort_param(CardColourSortParam()) self.add_sort_param(CardPowerSortParam()) queryset = queryset.order_by( *[order for sort_param in self.sort_params for order in sort_param.get_sort_list()]) self.paginator = Paginator(queryset, page_size) try: self.page = self.paginator.page(page_number) except EmptyPage: return cards = list(self.page) prefetch_related_objects(cards, 'printings__printed_languages__physical_cards__ownerships') prefetch_related_objects(cards, 'printings__printed_languages__language') prefetch_related_objects(cards, 'printings__set') prefetch_related_objects(cards, 'printings__rarity') preferred_set = self.get_preferred_set() self.results = [SearchResult(card, selected_set=preferred_set) for card in cards] def get_preferred_set(self) -> Optional[Set]: """ Gets the set that would be preferred for each card result (this should be overridden) :return: """ raise NotImplementedError() def get_page_buttons(self, current_page: int, page_span: int) -> List[PageButton]: """ Gets the page buttons that should appear for this search based on the number of pages in the results and hoa many pages the buttons should span :param current_page: The current page :param page_span: The number of pages to the left and right of the current page :return: A list of page buttons. Some of them can disabled padding buttons, and there will be a next and previous button at the start and end too """ page_buttons = [PageButton(page_number, True, is_active=page_number == current_page) for page_number in self.paginator.page_range if abs(page_number - current_page) <= page_span] # if the current page is great enough # put a link to the first page at the start followed by a spacer if current_page - page_span > 1: page_buttons.insert(0, PageButton(None, False, is_spacer=True)) page_buttons.insert(0, PageButton(1, True)) if current_page + page_span <= self.paginator.num_pages - 2: page_buttons.append(PageButton(None, False, is_spacer=True)) if current_page + page_span <= self.paginator.num_pages - 1: page_buttons.append(PageButton(self.paginator.num_pages, True)) page_buttons.insert(0, PageButton(max(current_page - 1, 1), current_page != 1, is_previous=True)) page_buttons.append(PageButton(current_page + 1, current_page != self.paginator.num_pages, is_next=True)) return page_buttons def add_sort_param(self, sort_param: CardSortParam) -> None: """ Adds a sort parameter :param sort_param: :return: """ self.sort_params.append(sort_param)
def __init__(self): self.root_parameter = AndParam() self.sort_params = list() self.paginator = None self.results = [] self.page = None
def build_parameters(self) -> None: """ Constructs the parameter list for this searching object """ root_param = self.root_parameter if self.card_name: root_param.add_parameter(CardNameParam(self.card_name)) if self.rules_text: root_param.add_parameter(CardRulesTextParam(self.rules_text)) if self.flavour_text: root_param.add_parameter(CardFlavourTextParam(self.flavour_text)) if self.type_text: root_param.add_parameter(CardTypeParam(self.type_text)) if self.subtype_text: root_param.add_parameter(CardSubtypeParam(self.subtype_text)) if self.min_cmc is not None: root_param.add_parameter(CardCmcParam(self.min_cmc, "GTE")) if self.max_cmc is not None: root_param.add_parameter(CardCmcParam(self.max_cmc, "LTE")) if self.min_power is not None: root_param.add_parameter(CardNumPowerParam(self.min_power, "GTE")) if self.max_power is not None: root_param.add_parameter(CardNumPowerParam(self.max_power, "LTE")) if self.min_toughness is not None: root_param.add_parameter( CardNumToughnessParam(self.min_toughness, "GTE")) if self.max_toughness is not None: root_param.add_parameter( CardNumToughnessParam(self.max_toughness, "LTE")) if self.mana_cost is not None and self.mana_cost != "": root_param.add_parameter(CardManaCostParam(self.mana_cost, False)) if self.colours: self.root_parameter.add_parameter( create_colour_param( self.colours, CardColourParam, match_colours=self.match_colours_exactly, exclude_colours=self.exclude_unselected_colours, )) if self.colour_identities: self.root_parameter.add_parameter( create_colour_param( self.colour_identities, CardColourIdentityParam, match_colours=self.match_colour_identities_exactly, exclude_colours=self.exclude_unselected_colour_identities, )) if self.rarities: rarity_node: BranchParam = AndParam( ) if self.match_rarities_exactly else OrParam() self.root_parameter.add_parameter(rarity_node) for rarity in self.rarities: rarity_node.add_parameter(CardRarityParam(rarity)) if self.sets: set_node: BranchParam = AndParam( ) if self.match_sets_exactly else OrParam() self.root_parameter.add_parameter(set_node) for set_obj in self.sets: set_node.add_parameter(CardSetParam(set_obj))
def __init__(self): self.root_parameter = AndParam() self.sort_params = list()