Compare commits
No commits in common. "f52b9631546f7b1c2c7f124a5b61b54f6a53e85b" and "dc3cc6427b6dbf9158c2592c90eded86194d5c32" have entirely different histories.
f52b963154
...
dc3cc6427b
4 changed files with 49 additions and 151 deletions
58
README.org
58
README.org
|
@ -1,58 +0,0 @@
|
||||||
#+TITLE: Tutor
|
|
||||||
|
|
||||||
A collection manager for Magic: The Gathering playing cards.
|
|
||||||
|
|
||||||
* Searching
|
|
||||||
Text in the search bar will be used to filter cards having that text in their
|
|
||||||
name. Additionally, the keyword expressions below can be used to search for
|
|
||||||
cards with certain properties.
|
|
||||||
|
|
||||||
** Examples
|
|
||||||
|
|
||||||
- ~bolt~ :: Find all cards with "bolt" in the name
|
|
||||||
- ~"God of"~ :: Find all cards with "God of" in the name
|
|
||||||
- ~t:legendary t:creature c:jund~ :: Find all legendary creatures with a color
|
|
||||||
identity of red/blue/green
|
|
||||||
- ~color<=ubg~ :: Find all spells that are blue, black, green, or any
|
|
||||||
combination thereof.
|
|
||||||
- ~color:red set:stx rarity>=rare~ :: Find all red cards in Strixhaven that are
|
|
||||||
rare or mythic
|
|
||||||
- ~t:enchantment o:"enters the battlefield"~ :: Find all enchantments with ETB
|
|
||||||
effects
|
|
||||||
|
|
||||||
** Keywords
|
|
||||||
*** Colors
|
|
||||||
- Keywords :: =c=, =color=
|
|
||||||
- Operators :: ~:~ (matches), ~>=~ (greater than or equal to), ~<=~ (less than
|
|
||||||
or equal to)
|
|
||||||
|
|
||||||
Matches cards of the chosen color or colors.
|
|
||||||
|
|
||||||
- Single colors :: =w= or =white=, =u= or =blue=, =b= or =black, =g= or =green=, =r= or =red=
|
|
||||||
- Any combination of abbreviated single colors :: e.g.: =rg=, =uw=, or =wubgr=
|
|
||||||
- Ravnican guilds :: =boros= (white/red), =golgari= (green/black), =selesnya=
|
|
||||||
(green/white), =dimir= (blue/black), =orzhov= (white/black), =izzet=
|
|
||||||
(blue/red), =gruul= (red/green), =azorius= (white/blue), =rakdos= (black/red),
|
|
||||||
=simic= (green/blue)
|
|
||||||
- Alaran shards :: =bant= (white/green/blue), =esper= (blue/white/black),
|
|
||||||
=grixis= (black/blue/red), =jund= (red/blue/green), =naya= (green/red/white)
|
|
||||||
- Tarkirian wedges :: =abzan= (white/black/green), =jeskai= (white/blue/red),
|
|
||||||
=sultai= (blue/black/green), =mardu= (white/black/red), =temur=
|
|
||||||
(blue/red/green)
|
|
||||||
|
|
||||||
*** Sets
|
|
||||||
- Keywords :: =s=, =set=, =e=, =expansion=
|
|
||||||
- Operators :: ~:~ (matches)
|
|
||||||
|
|
||||||
*** Rarity
|
|
||||||
- Keywords :: =r=, =rarity=
|
|
||||||
- Operators :: ~:~ (matches), ~>=~ (greater than or equal to), ~<=~ (less than
|
|
||||||
or equal to)
|
|
||||||
|
|
||||||
*** Type
|
|
||||||
- Keywords :: =t=, =type=
|
|
||||||
- Operators :: ~:~ (matches)
|
|
||||||
|
|
||||||
*** Oracle Text
|
|
||||||
- Keywords :: =o=, =oracle=
|
|
||||||
- Operators :: ~:~ (matches)
|
|
0
README.rst
Normal file
0
README.rst
Normal file
|
@ -102,56 +102,38 @@ async def advanced_search(
|
||||||
params = {}
|
params = {}
|
||||||
sets = []
|
sets = []
|
||||||
|
|
||||||
logger.debug("Performing search for: %s", search)
|
logging.debug("Performing search for: %s", search)
|
||||||
for i, criterion in enumerate(search.criteria):
|
for i, criterion in enumerate(search.criteria):
|
||||||
param = f"param_{i}"
|
param = f"param_{i}"
|
||||||
if isinstance(criterion, tutor.search.Name):
|
if isinstance(criterion, tutor.search.Name):
|
||||||
constraints.append(f"cards.name LIKE :{param}")
|
constraints.append(f"cards.name LIKE :{param}")
|
||||||
params[param] = f"%{criterion.text}%"
|
params[param] = f"%{criterion.name}%"
|
||||||
if isinstance(criterion, tutor.search.Type):
|
if isinstance(criterion, tutor.search.Type):
|
||||||
constraints.append(f"cards.type_line LIKE :{param}")
|
constraints.append(f"cards.type_line LIKE :{param}")
|
||||||
params[param] = f"%{criterion.text}%"
|
params[param] = f"%{criterion.name}%"
|
||||||
if isinstance(criterion, tutor.search.Expansion):
|
if isinstance(criterion, tutor.search.IsExpansion):
|
||||||
constraints.append(f"cards.set_code LIKE :{param}")
|
constraints.append(f"cards.set_code LIKE :{param}")
|
||||||
params[param] = criterion.set_code
|
params[param] = criterion.set_code
|
||||||
if isinstance(criterion, tutor.search.Color):
|
if isinstance(criterion, tutor.search.InExpansion):
|
||||||
if criterion.operator == tutor.search.Operator.matches:
|
sets.append(criterion.set_code)
|
||||||
constraints.append(f"cards.color_identity LIKE :{param}")
|
if isinstance(criterion, tutor.search.IsColor):
|
||||||
params[param] = tutor.models.Color.to_string(criterion.colors)
|
constraints.append(f"cards.color_identity LIKE :{param}")
|
||||||
if criterion.operator == tutor.search.Operator.lte:
|
params[param] = tutor.models.Color.to_string(criterion.colors)
|
||||||
constraints.append(
|
if isinstance(criterion, tutor.search.IsRarity):
|
||||||
"({})".format(
|
constraints.append(f"cards.rarity LIKE :{param}")
|
||||||
" OR ".join(
|
params[param] = str(criterion.rarity)
|
||||||
[
|
|
||||||
f"cards.color_identity LIKE :{param}_{color}"
|
|
||||||
for color in criterion.colors
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
params.update(
|
|
||||||
{f"{param}_{color}": str(color) for color in criterion.colors}
|
|
||||||
)
|
|
||||||
params[param] = tutor.models.Color.to_string(criterion.colors)
|
|
||||||
if criterion.operator == tutor.search.Operator.gte:
|
|
||||||
constraints.append(f"cards.color_identity LIKE :{param}")
|
|
||||||
params[param] = "%{}%".format(
|
|
||||||
"%".join(tutor.models.Color.to_string(criterion.colors))
|
|
||||||
)
|
|
||||||
if isinstance(criterion, tutor.search.Rarity):
|
|
||||||
if criterion.operator == tutor.search.Operator.matches:
|
|
||||||
constraints.append(f"cards.rarity LIKE :{param}")
|
|
||||||
params[param] = str(criterion.rarity)
|
|
||||||
if criterion.operator == tutor.search.Operator.lte:
|
|
||||||
constraints.append(f"rarities.rarity_ord <= :{param}")
|
|
||||||
params[param] = criterion.rarity.value
|
|
||||||
if criterion.operator == tutor.search.Operator.gte:
|
|
||||||
constraints.append(f"rarities.rarity_ord >= :{param}")
|
|
||||||
params[param] = criterion.rarity.value
|
|
||||||
if isinstance(criterion, tutor.search.Oracle):
|
if isinstance(criterion, tutor.search.Oracle):
|
||||||
constraints.append(f"cards.oracle_text LIKE :{param}")
|
constraints.append(f"cards.oracle_text LIKE :{param}")
|
||||||
params[param] = f"%{criterion.text}%"
|
params[param] = f"%{criterion.text}%"
|
||||||
|
|
||||||
|
if sets:
|
||||||
|
set_params = {f"set_{i}": set_code for i, set_code in enumerate(sets)}
|
||||||
|
constraints.append(
|
||||||
|
"cards.set_code IN ({})".format(
|
||||||
|
", ".join([f":{key}" for key in set_params.keys()])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
params.update(set_params)
|
||||||
if in_collection is not None:
|
if in_collection is not None:
|
||||||
if in_collection:
|
if in_collection:
|
||||||
joins.append("JOIN copies ON (cards.scryfall_id = copies.scryfall_id)")
|
joins.append("JOIN copies ON (cards.scryfall_id = copies.scryfall_id)")
|
||||||
|
@ -175,7 +157,6 @@ async def advanced_search(
|
||||||
f"LIMIT {offset},{limit}",
|
f"LIMIT {offset},{limit}",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
logger.debug("Query: %s", (query, params))
|
|
||||||
cursor = await db.execute(query, params)
|
cursor = await db.execute(query, params)
|
||||||
rows = await cursor.fetchall()
|
rows = await cursor.fetchall()
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import enum
|
|
||||||
import functools
|
import functools
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
@ -8,35 +7,38 @@ import parsy
|
||||||
import tutor.models
|
import tutor.models
|
||||||
|
|
||||||
|
|
||||||
class Operator(enum.Enum):
|
|
||||||
matches = ":"
|
|
||||||
gte = ">="
|
|
||||||
lte = "<="
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
|
||||||
class Criterion:
|
class Criterion:
|
||||||
operator: Operator
|
...
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Expansion(Criterion):
|
class IsExpansion(Criterion):
|
||||||
set_code: parsy.string
|
set_code: parsy.string
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Color(Criterion):
|
class InExpansion(Criterion):
|
||||||
|
set_code: parsy.string
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class IsColor(Criterion):
|
||||||
colors: typing.Set[tutor.models.Color]
|
colors: typing.Set[tutor.models.Color]
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Rarity(Criterion):
|
class IsRarity(Criterion):
|
||||||
rarity: tutor.models.Rarity
|
rarity: tutor.models.Rarity
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Name(Criterion):
|
||||||
|
name: parsy.string
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Type(Criterion):
|
class Type(Criterion):
|
||||||
text: parsy.string
|
name: parsy.string
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
|
@ -44,12 +46,6 @@ class Oracle(Criterion):
|
||||||
text: parsy.string
|
text: parsy.string
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
|
||||||
class Name(Criterion):
|
|
||||||
operator: Operator = Operator.matches
|
|
||||||
text: parsy.string = ""
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Search:
|
class Search:
|
||||||
criteria: typing.List[Criterion]
|
criteria: typing.List[Criterion]
|
||||||
|
@ -67,10 +63,7 @@ W, U, B, G, R = (
|
||||||
tutor.models.Color.Green,
|
tutor.models.Color.Green,
|
||||||
tutor.models.Color.Red,
|
tutor.models.Color.Red,
|
||||||
)
|
)
|
||||||
|
matches = parsy.string(":")
|
||||||
matches = parsy.string(":").map(Operator)
|
|
||||||
gte = parsy.string(">=").map(Operator)
|
|
||||||
lte = parsy.string("<=").map(Operator)
|
|
||||||
|
|
||||||
color = (
|
color = (
|
||||||
ustring("w").result(W)
|
ustring("w").result(W)
|
||||||
|
@ -119,56 +112,38 @@ wedge = (
|
||||||
| lstring("temur").result({U, R, G})
|
| lstring("temur").result({U, R, G})
|
||||||
)
|
)
|
||||||
|
|
||||||
any_color = single_color | guild | shard | wedge | multicolor
|
colors = single_color | guild | shard | wedge | multicolor
|
||||||
|
|
||||||
colors = parsy.seq(
|
|
||||||
_keyword=lstring_from("c", "color", "colors"),
|
|
||||||
operator=matches | gte | lte,
|
|
||||||
colors=any_color,
|
|
||||||
).combine_dict(Color)
|
|
||||||
|
|
||||||
expansion_string = (
|
expansion_string = (
|
||||||
parsy.regex(r"[a-zA-Z0-9]+").map(lambda s: s.upper()).desc("expansion set code")
|
parsy.regex(r"[a-zA-Z0-9]+").map(lambda s: s.upper()).desc("expansion set code")
|
||||||
)
|
)
|
||||||
|
|
||||||
expansion = parsy.seq(
|
is_expansion = lstring_from("e", "expansion", "s", "set") >> matches >> expansion_string.map(IsExpansion)
|
||||||
_keyword=lstring_from("e", "expansion", "s", "set"),
|
|
||||||
operator=matches,
|
|
||||||
set_code=expansion_string,
|
|
||||||
).combine_dict(Expansion)
|
|
||||||
|
|
||||||
# in_expansion = lstring("in") >> matches >> expansion_string.map(InExpansion)
|
in_expansion = lstring("in") >> matches >> expansion_string.map(InExpansion)
|
||||||
|
|
||||||
any_rarity = (
|
rarity = (
|
||||||
lstring_from("c", "common").result(tutor.models.Rarity.Common)
|
lstring_from("c", "common").result(tutor.models.Rarity.Common)
|
||||||
| lstring_from("u", "uncommon").result(tutor.models.Rarity.Uncommon)
|
| lstring_from("u", "uncommon").result(tutor.models.Rarity.Uncommon)
|
||||||
| lstring_from("r", "rare").result(tutor.models.Rarity.Rare)
|
| lstring_from("r", "rare").result(tutor.models.Rarity.Rare)
|
||||||
| lstring_from("m", "mythic").result(tutor.models.Rarity.Mythic)
|
| lstring_from("m", "mythic").result(tutor.models.Rarity.Mythic)
|
||||||
)
|
)
|
||||||
|
|
||||||
rarity = parsy.seq(
|
is_rarity = lstring_from("r", "rarity") >> matches >> rarity.map(IsRarity)
|
||||||
_keyword=lstring_from("r", "rarity"),
|
|
||||||
operator=matches | gte | lte,
|
is_color = lstring_from("c", "color", "colors") >> matches >> colors.map(IsColor)
|
||||||
rarity=any_rarity,
|
|
||||||
).combine_dict(Rarity)
|
|
||||||
|
|
||||||
string_literal = parsy.regex(r'"[^"]*"').map(lambda s: s[1:-1]) | parsy.regex(r"[^\s]+")
|
string_literal = parsy.regex(r'"[^"]*"').map(lambda s: s[1:-1]) | parsy.regex(r"[^\s]+")
|
||||||
|
|
||||||
type_line = parsy.seq(
|
has_type = lstring_from("t", "type") >> matches >> string_literal.map(Type)
|
||||||
_keyword=lstring_from("t", "type"),
|
|
||||||
operator=matches,
|
|
||||||
text=string_literal,
|
|
||||||
).combine_dict(Type)
|
|
||||||
|
|
||||||
oracle = parsy.seq(
|
has_oracle = lstring_from("o", "oracle") >> matches >> string_literal.map(Oracle)
|
||||||
_keyword=lstring_from("o", "oracle"),
|
|
||||||
operator=matches,
|
|
||||||
text=string_literal,
|
|
||||||
).combine_dict(Oracle)
|
|
||||||
|
|
||||||
name = parsy.seq(text=string_literal).combine_dict(Name)
|
name = string_literal.map(Name)
|
||||||
|
|
||||||
criterion = colors | expansion | rarity | type_line | oracle | name
|
criterion = (
|
||||||
|
is_expansion | in_expansion | is_rarity | is_color | has_type | has_oracle | name
|
||||||
|
)
|
||||||
|
|
||||||
padding = parsy.regex(r"\s*")
|
padding = parsy.regex(r"\s*")
|
||||||
search = padding >> (criterion << padding).many().map(Search)
|
search = padding >> (criterion << padding).many().map(Search)
|
||||||
|
|
Loading…
Reference in a new issue