diff --git a/src/domdiv/main.py b/src/domdiv/main.py index e40a5bc..8c168da 100644 --- a/src/domdiv/main.py +++ b/src/domdiv/main.py @@ -395,6 +395,25 @@ def parse_opts(cmdline_args=None): ", ".join("%s" % x for x in FAN_CHOICES) ), ) + group_select.add_argument( + "--exclude-expansions", + "--exclude-expansion", + nargs="*", + action="append", + metavar="EXCLUDED", + dest="exclude_expansions", + help="Limit dividers to not include the specified expansions. " + "Useful if you want all the expansions, except for one or two. " + "If an expansion is explicitly specified with both '--expansion' and " + "'--exclude-expansion', then '--exclude-expansion' wins, and the " + "expansion is NOT included. Expansion names can also be given in the " + "language specified by the --language parameter. Any expansion with a " + "space in the name must be enclosed in double quotes. This may be " + "called multiple times. Values are not case sensitive. Wildcards may " + "be used. See the help for '--expansion' for details on wildcards. May " + "be the name of an official expansion or fan expansion - see the help " + "for --expansion and --fan for a list of possible names.", + ) group_select.add_argument( "--edition", choices=EDITION_CHOICES, @@ -780,6 +799,12 @@ def clean_opts(options): # keyword to indicate no options. Same as --expansions without any expansions given. options.expansions = [] + if options.exclude_expansions: + # options.exclude_expansions is a list of lists. Reduce to single lowercase list + options.exclude_expansions = [ + item.lower() for sublist in options.exclude_expansions for item in sublist + ] + if options.fan is None: # No instance given, so default to no Fan expansions options.fan = [] @@ -1470,17 +1495,19 @@ def filter_sort_cards(cards, options): Fan_sets = set() # Will hold fan sets Fan_search = [] # Will hold fan sets for searching, both set key and set_name wantedSets = set() # Will hold all the sets requested for printing + + All_search = [] # Will hold all sets for searching, both set key and set_name for s in Card.sets: + search_items = [s.lower(), Card.sets[s].get("set_name", None).lower()] + All_search.extend(search_items) if Card.sets[s].get("fan", False): # Fan Expansion Fan_sets.add(s) - Fan_search.extend([s.lower(), Card.sets[s].get("set_name", None).lower()]) + Fan_search.extend(search_items) else: # Official Expansion Official_sets.add(s) - Official_search.extend( - [s.lower(), Card.sets[s].get("set_name", None).lower()] - ) + Official_search.extend(search_items) # If expansion names given, then find out which expansions are requested # Expansion names can be the names from the language or the cardset_tag @@ -1539,6 +1566,34 @@ def filter_sort_cards(cards, options): if unknownExpansions: print("Error - unknown fan expansion(s): %s" % ", ".join(unknownExpansions)) + if options.exclude_expansions: + # Expand out any wildcards, matching set key or set name in the given language + expanded_expansions = [] + for e in options.exclude_expansions: + matches = fnmatch.filter(All_search, e) + if matches: + expanded_expansions.extend(matches) + else: + expanded_expansions.append(e) + + # Now get the actual sets that are matched above + options.exclude_expansions = set( + [e for e in expanded_expansions] + ) # Remove duplicates + knownExpansions = set() + for e in options.exclude_expansions: + for s in Card.sets: + if s.lower() == e or Card.sets[s].get("set_name", "").lower() == e: + wantedSets.discard(s) + knownExpansions.add(e) + # Give indication if an imput did not match anything + unknownExpansions = options.exclude_expansions - knownExpansions + if unknownExpansions: + print( + "Error - unknown exclude expansion(s): %s" + % ", ".join(unknownExpansions) + ) + # Now keep only the cards that are in the sets that have been requested keep_cards = [] for c in cards: diff --git a/tests/carddb_tests.py b/tests/carddb_tests.py index a1b457c..93ddbff 100644 --- a/tests/carddb_tests.py +++ b/tests/carddb_tests.py @@ -140,3 +140,63 @@ def test_only_type(): # Curse: +1 from base # Action Attack: +2 from Alchemy assert len(cards) == 8 + + +def test_expansion(): + # test that we can use --expansion or + # --expansions, that we can have multiple + # items with a single flag, that * syntax + # works, that we can use either the + # cardset tag or name, and that capitalziation + # doesn't matter + options = main.parse_opts( + [ + "--expansion", + "advEntUres", + "dominion 2nd*", + "--expansions=intrigue1stEdition", + ] + ) + options = main.clean_opts(options) + options.data_path = "." + cards = main.read_card_data(options) + cards = main.filter_sort_cards(cards, options) + card_sets = set(x.cardset.lower() for x in cards) + assert card_sets == { + "adventures", + "dominion 2nd edition", + "dominion 2nd edition upgrade", + "intrigue 1st edition", + } + + +def test_exclude_expansion(): + # test that we can use --exclude-expansion or + # --exclude-expansions, that we can have multiple + # items with a single flag, that * syntax + # works, that we can use either the + # cardset tag or name, and that capitalziation + # doesn't matter + options = main.parse_opts( + [ + "--expansions", + "adventures", + "dominion*", + "intrigue*", + "--exclude-expansions", + "dominiOn1stEditIon", + "intrigue 2nd*", + "--exclude-expansion", + "dominion 2nd edition", + ] + ) + options = main.clean_opts(options) + options.data_path = "." + cards = main.read_card_data(options) + cards = main.filter_sort_cards(cards, options) + card_sets = set(x.cardset.lower() for x in cards) + assert card_sets == { + "adventures", + "dominion 2nd edition upgrade", + "intrigue 1st edition", + }