Skip to content

Commit

Permalink
Support produces: in card search
Browse files Browse the repository at this point in the history
Fixes #11736.
Fixes #8618.
  • Loading branch information
bakert committed May 3, 2024
1 parent c4ce1d2 commit 0099d99
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 17 deletions.
20 changes: 9 additions & 11 deletions find/find_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,15 @@ def test_mana_costs() -> None:
# s = 'devotion:{u/b}{u/b}{u/b}'
# do_functional_test(s, ['Ashemnoor Gouger', 'Niv-Mizzet Parun', 'Omniscience', 'Phrexian Obliterator', 'Progenitus'], ['Cunning Nightbonger', 'Watery Grave'])

# s = 'produces=wu'
# do_functional_test(s, ['Azorius Signet', 'Celestial Colonnade'], ['Birds of Paradise', 'Teferi, Time Raveler'])

# s = 'produces:rc'
# do_functional_test(s, ['Aether Hub', 'Ramunap Ruins', 'The Seedcore', 'Talisman of Creativity', 'Grinning Ignus', 'Lithoform Blight'], ["Arcum's Astrolabe", 'Sulfur Falls', 'Birds of Paradise', 'Simian Spirit Guide', 'Pentad Prism'])
# s = 'produces:c'
# do_functional_test(s, ['Aether Hub', 'Channel', 'Talisman of Creativity', 'Thaumatic Compass', 'Worn Powerstone', 'Boreal Druid', 'Catacomb Sifter', 'Grand Architect'], ['Thran Portal', "Arcum's Astrolabe", 'Burst Lightning', 'Chromatic Star'])
# s = 'produces>g'
# do_functional_test(s, ['Aether Hub', "Arcum's Astrolabe", 'Birds of Paradise', "Llanowar Wastes", 'Lotus Bloom', 'Pentad Prism', 'Opulent Palace', 'Vivid Marsh', 'Servant of the Conduit', 'Song of Freyalise', "Mirari's Wake", 'Gruul Signet'], ['Thran Portal', 'Ramunap Ruins', 'Rofellos, Llanowar Emissary'])
# s = 'produces>'
# do_functional_test(s, [], ['Forest'])
s = 'produces=wu'
do_functional_test(s, ['Azorius Signet', 'Celestial Colonnade'], ['Birds of Paradise', 'Teferi, Time Raveler'])

s = 'produces:rc'
do_functional_test(s, ['Aether Hub', 'Ramunap Ruins', 'The Seedcore', 'Talisman of Creativity', 'Grinning Ignus', 'Lithoform Blight'], ["Arcum's Astrolabe", 'Sulfur Falls', 'Birds of Paradise', 'Simian Spirit Guide', 'Pentad Prism'])
s = 'produces:c'
do_functional_test(s, ['Aether Hub', 'Channel', 'Talisman of Creativity', 'Thaumatic Compass', 'Worn Powerstone', 'Boreal Druid', 'Catacomb Sifter', 'Grand Architect'], ['Thran Portal', "Arcum's Astrolabe", 'Burst Lightning', 'Chromatic Star'])
s = 'produces>g'
do_functional_test(s, ['Aether Hub', "Arcum's Astrolabe", 'Birds of Paradise', "Llanowar Wastes", 'Lotus Bloom', 'Pentad Prism', 'Opulent Palace', 'Vivid Marsh', 'Servant of the Conduit', 'Song of Freyalise', "Mirari's Wake", 'Gruul Signet'], ['Thran Portal', 'Ramunap Ruins', 'Rofellos, Llanowar Emissary'])

Check warning on line 91 in find/find_test.py

View workflow job for this annotation

GitHub Actions / lint

Double quotes found but single quotes preferred

Check warning on line 91 in find/find_test.py

View workflow job for this annotation

GitHub Actions / lint

Double quotes found but single quotes preferred

@pytest.mark.functional
def test_power_toughness_and_loyalty() -> None:
Expand Down
11 changes: 7 additions & 4 deletions find/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ def parse_criterion(key: Token, operator: Token, term: Token) -> str:
return color_where('color', operator.value(), term.value())
if key.value() in ['coloridentity', 'commander', 'identity', 'ci', 'id', 'cid']:
return color_where('color_identity', operator.value(), term.value())
if key.value() == 'produces':
return color_where('produced_mana', operator.value(), term.value())
if key.value() in ['oracle', 'o']:
return text_where('text', term, exclude_parenthetical=True)
if key.value() in ['fulloracle', 'fo']:
Expand Down Expand Up @@ -221,12 +223,12 @@ def text_where(column: str, term: Token, exclude_parenthetical: bool = False) ->
return f'({column} {operator} {escaped})'

def subtable_where(subtable: str, value: str, operator: str | None = None) -> str:
# Specialcase colorless because it has no entry in the color table.
# Specialcase colorless for color and coloridentity because they have no entries (but not true for produced_mana).
if (subtable in ['color', 'color_identity']) and value == 'c':
return f'(c.id NOT IN (SELECT card_id FROM card_{subtable}))'
v = value_lookup(subtable, value)
if str(v).isdigit():
column = f'{subtable}_id'.replace('color_identity_id', 'color_id')
column = f'{subtable}_id'.replace('color_identity_id', 'color_id').replace('produced_mana_id', 'color_id')
operator = '=' if not operator else operator
else:
column = subtable
Expand All @@ -252,7 +254,7 @@ def color_where(subtable: str, operator: str, term: str) -> str:
colors = set(COLOR_COMBINATIONS_LOWER[term])
else:
colors = set(term)
if 'c' in colors and len(colors) > 1:
if 'c' in colors and len(colors) > 1 and subtable != 'produced_mana':
raise InvalidValueException('A card cannot be colorless and colored')
if 'm' in colors and len(colors) > 1:
raise InvalidValueException(f"Using 'm' with other colors is not supported, use '{subtable}>{term.replace('m', '')}' instead")
Expand All @@ -264,7 +266,7 @@ def color_where(subtable: str, operator: str, term: str) -> str:
if 'm' in colors:
min_colors = 2
colors.remove('m')
if 'c' in colors:
if 'c' in colors and subtable != 'produced_mana':
max_colors = 0
colors.remove('c')
if operator in ['=', '!']:
Expand Down Expand Up @@ -400,6 +402,7 @@ def init_value_lookup() -> None:
VALUE_LOOKUP[table] = d
if table == 'color':
VALUE_LOOKUP['color_identity'] = d
VALUE_LOOKUP['produced_mana'] = d

def is_subquery(subquery_name: str) -> str:
if subquery_name == 'dfc':
Expand Down
1 change: 1 addition & 0 deletions magic/abc/card_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class CardDescription(TypedDict, total=False):
oracle_id: str
oracle_text: str
power: str
produced_mana: list[str]
name: str
rarity: str
reserved: bool
Expand Down
7 changes: 5 additions & 2 deletions magic/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from shared.pd_exception import DatabaseException

# Bump this if you modify the schema.
SCHEMA_VERSION = 109
SCHEMA_VERSION = 110
DATABASE = Container()

def db() -> Database:
Expand Down Expand Up @@ -52,6 +52,8 @@ def setup() -> None:
db().execute(sql)
sql = create_table_def('card_color_identity', card.card_color_properties())
db().execute(sql)
sql = create_table_def('card_produced_mana', card.card_color_properties())
db().execute(sql)
sql = create_table_def('card_supertype', card.card_type_properties('supertype'))
db().execute(sql)
sql = create_table_def('card_subtype', card.card_type_properties('subtype'))
Expand All @@ -73,7 +75,8 @@ def setup() -> None:
('Blue', 'U'),
('Black', 'B'),
('Red', 'R'),
('Green', 'G')
('Green', 'G'),
('Colorless', 'C') -- This is a little funky as we only use it for produced_mana, not color or color_identity.
""")
db().execute("""INSERT INTO rarity (name) VALUES
('Basic Land'),
Expand Down
7 changes: 7 additions & 0 deletions magic/multiverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ async def insert_cards(new_date: datetime.datetime, sets: list[dict[str, Any]],
db().execute('SET FOREIGN_KEY_CHECKS=0') # Avoid needing to drop _cache_card (which has an FK relationship with card) so that the database continues to function while we perform the update.
db().execute('DELETE FROM card_color')
db().execute('DELETE FROM card_color_identity')
db().execute('DELETE FROM card_produced_mana')
db().execute('DELETE FROM card_legality')
db().execute('DELETE FROM card_bug')
db().execute('DELETE FROM face')
Expand All @@ -185,6 +186,7 @@ async def insert_cards_async(printings: list[CardDescription]) -> list[int]:
if values['card_color']: # We should not issue this query if we are only inserting colorless cards as they don't have an entry in this table.
insert_many('card_color', card.card_color_properties(), values['card_color'])
insert_many('card_color_identity', card.card_color_properties(), values['card_color_identity'])
insert_many('card_produced_mana', card.card_color_properties(), values['card_produced_mana'])
insert_many('printing', card.printing_properties(), values['printing'])
insert_many('card_flavor_name', card.card_flavor_name_properties(), values['flavor_name'])
insert_many('face', card.face_properties(), values['face'], ['position'])
Expand All @@ -203,6 +205,7 @@ async def determine_values_async(printings: list[CardDescription], next_card_id:
meld_result_printings: list[CardDescription] = []
card_color_values: list[dict[str, Any]] = []
card_color_identity_values: list[dict[str, Any]] = []
card_produced_mana_values: list[dict[str, Any]] = []
printing_values: list[dict[str, Any]] = []
card_legality_values: list[dict[str, Any]] = []
rarity_ids = {x['name']: x['id'] for x in db().select('SELECT id, name FROM rarity')}
Expand Down Expand Up @@ -269,6 +272,9 @@ async def determine_values_async(printings: list[CardDescription], next_card_id:
for color in p.get('color_identity', []):
color_id = colors[color]
card_color_identity_values.append({'card_id': card_id, 'color_id': color_id})
for color in [c for c in p.get('produced_mana', []) if c not in ["T"]]: # Ignore Sole Performer and its production of "T". Scryfall itself doesn't allow searching for that.

Check warning on line 275 in magic/multiverse.py

View workflow job for this annotation

GitHub Actions / lint

Double quotes found but single quotes preferred

Check warning on line 275 in magic/multiverse.py

View workflow job for this annotation

GitHub Actions / lint

Double quotes found but single quotes preferred
color_id = colors[color]
card_produced_mana_values.append({'card_id': card_id, 'color_id': color_id})
# DFCs store their colors in their faces, not at the top level. See #9022.
for color in face_colors(p):
color_id = colors[color]
Expand Down Expand Up @@ -297,6 +303,7 @@ async def determine_values_async(printings: list[CardDescription], next_card_id:
'card': card_values,
'card_color': card_color_values,
'card_color_identity': card_color_identity_values,
'card_produced_mana': card_produced_mana_values,
'face': face_values,
'printing': printing_values,
'card_legality': card_legality_values,
Expand Down

0 comments on commit 0099d99

Please sign in to comment.