Improve price lookup and display

This commit is contained in:
Correl Roush 2023-01-11 23:56:20 -05:00
parent 20e8c024ce
commit 10e239d65e
8 changed files with 89 additions and 42 deletions

View file

@ -149,8 +149,17 @@ SELECT "oracle"."oracle_id"
, "scryfall"."collector_number"
, "scryfall"."release_date"
, "scryfall"."rarity"
, "sets"."name" AS "set_name"
, "card_prices"."usd" AS "price_usd"
, "card_prices"."usd_foil" AS "price_usd_foil"
, "card_prices"."eur" AS "price_eur"
, "card_prices"."eur_foil" AS "price_eur_foil"
, "card_prices"."tix" AS "price_tix"
FROM "oracle"
JOIN "scryfall" USING ("oracle_id");
JOIN "scryfall" USING ("oracle_id")
JOIN "card_prices" ON ("scryfall"."scryfall_id" = "card_prices"."scryfall_id"
AND "card_prices"."date" = (SELECT VALUE FROM "vars" WHERE "key" = 'last_update'))
JOIN "sets" USING ("set_code");
DROP MATERIALIZED VIEW IF EXISTS "oracle_latest";
CREATE MATERIALIZED VIEW "oracle_latest" AS
@ -167,9 +176,18 @@ SELECT "oracle"."oracle_id"
, "scryfall"."collector_number"
, "scryfall"."release_date"
, "scryfall"."rarity"
, "sets"."name" AS "set_name"
, "card_prices"."usd" AS "price_usd"
, "card_prices"."usd_foil" AS "price_usd_foil"
, "card_prices"."eur" AS "price_eur"
, "card_prices"."eur_foil" AS "price_eur_foil"
, "card_prices"."tix" AS "price_tix"
FROM "oracle"
JOIN (
SELECT DISTINCT ON ("oracle_id") *
FROM "scryfall"
ORDER BY "oracle_id", "release_date" DESC
) "scryfall" USING ("oracle_id");
) "scryfall" USING ("oracle_id")
JOIN "card_prices" ON ("scryfall"."scryfall_id" = "card_prices"."scryfall_id"
AND "card_prices"."date" = (SELECT VALUE FROM "vars" WHERE "key" = 'last_update'))
JOIN "sets" USING ("set_code");

View file

@ -16,6 +16,13 @@ import tutor.search
logger = logging.getLogger(__name__)
def convert_price(price: typing.Optional[str]) -> typing.Optional[decimal.Decimal]:
if price:
return decimal.Decimal(price)
else:
return None
async def search(
db: psycopg.Cursor,
name: typing.Optional[str] = None,
@ -171,10 +178,6 @@ async def advanced_search(
else:
joins.append("LEFT JOIN copies ON (cards.scryfall_id = copies.scryfall_id)")
joins.append("JOIN sets ON (cards.set_code = sets.set_code)")
joins.append(
"JOIN card_prices ON (cards.scryfall_id = card_prices.scryfall_id "
"AND card_prices.date = (select value from vars where key = %(last_update_key)s))"
)
orderings = [
"cards.rarity DESC",
"length(cards.color_identity) DESC",
@ -185,15 +188,15 @@ async def advanced_search(
]
if sort_by == "price":
orderings = [
'CAST(COALESCE(CASE WHEN "copies"."isFoil" THEN card_prices.usd_foil ELSE card_prices.usd END, 0) as decimal) DESC',
'CAST(COALESCE(CASE WHEN "copies"."isFoil" THEN cards.price_usd_foil ELSE cards.price_usd END, 0) as decimal) DESC',
*orderings,
]
params["last_update_key"] = "last_update"
query = " ".join(
[
"SELECT cards.*, card_prices.*, copies.*",
', CASE WHEN "copies"."isFoil" THEN card_prices.usd_foil',
" ELSE card_prices.usd END AS usd",
"SELECT cards.*, copies.*",
', CASE WHEN "copies"."isFoil" THEN cards.price_usd_foil',
" ELSE cards.price_usd END AS usd",
"FROM cards",
" ".join(joins),
"WHERE" if constraints else "",
@ -207,12 +210,6 @@ async def advanced_search(
await db.execute(query, params)
rows = await db.fetchall()
def convert_price(price: typing.Optional[str]) -> typing.Optional[decimal.Decimal]:
if price:
return decimal.Decimal(price)
else:
return None
return [
tutor.models.CardCopy(
card=tutor.models.Card(
@ -220,6 +217,7 @@ async def advanced_search(
oracle_id=row["oracle_id"],
name=row["name"],
set_code=row["set_code"],
set_name=row["set_name"],
collector_number=row["collector_number"],
rarity=tutor.models.Rarity.from_string(row["rarity"]),
color_identity=tutor.models.Color.from_string(row["color_identity"]),
@ -231,11 +229,11 @@ async def advanced_search(
legalities={},
edhrec_rank=row["edhrec_rank"],
oracle_text=row["oracle_text"],
price_usd=convert_price(row["usd"]),
price_usd_foil=convert_price(row["usd_foil"]),
price_eur=convert_price(row["eur"]),
price_eur_foil=convert_price(row["eur_foil"]),
price_tix=convert_price(row["tix"]),
price_usd=convert_price(row["price_usd"]),
price_usd_foil=convert_price(row["price_usd_foil"]),
price_eur=convert_price(row["price_eur"]),
price_eur_foil=convert_price(row["price_eur_foil"]),
price_tix=convert_price(row["price_tix"]),
),
foil=row["isFoil"] if row["isFoil"] is not None else False,
collection=row["collection"] or "Default",
@ -380,9 +378,15 @@ async def get_decks(
'oracle_text', "oracle_latest"."oracle_text",
'scryfall_id', "oracle_latest"."scryfall_id",
'set_code', "oracle_latest"."set_code",
'set_name', "oracle_latest"."set_name",
'collector_number', "oracle_latest"."collector_number",
'rarity', "oracle_latest"."rarity",
'release_date', "oracle_latest"."release_date",
'price_usd', "oracle_latest"."price_usd",
'price_usd_foil', "oracle_latest"."price_usd_foil",
'price_eur', "oracle_latest"."price_eur",
'price_eur_foil', "oracle_latest"."price_eur_foil",
'price_tix', "oracle_latest"."price_tix",
'quantity', "deck_list"."quantity"
))) AS "cards"
FROM "decks"
@ -417,9 +421,15 @@ async def get_decks(
oracle_text=card.get("oracle_text"),
scryfall_id=card["scryfall_id"],
set_code=card["set_code"],
set_name=card["set_name"],
collector_number=card["collector_number"],
rarity=card["rarity"],
release_date=card["release_date"],
price_usd=convert_price(card.get("price_usd")),
price_usd_foil=convert_price(card.get("price_usd_foil")),
price_eur=convert_price(card.get("price_eur")),
price_eur_foil=convert_price(card.get("price_eur_foil")),
price_tix=convert_price(card.get("price_tix")),
),
quantity=card["quantity"],
)
@ -450,9 +460,15 @@ async def get_deck(
'oracle_text', "oracle_latest"."oracle_text",
'scryfall_id', "oracle_latest"."scryfall_id",
'set_code', "oracle_latest"."set_code",
'set_name', "oracle_latest"."set_name",
'collector_number', "oracle_latest"."collector_number",
'rarity', "oracle_latest"."rarity",
'release_date', "oracle_latest"."release_date",
'price_usd', "oracle_latest"."price_usd",
'price_usd_foil', "oracle_latest"."price_usd_foil",
'price_eur', "oracle_latest"."price_eur",
'price_eur_foil', "oracle_latest"."price_eur_foil",
'price_tix', "oracle_latest"."price_tix",
'quantity', "deck_list"."quantity"
))) AS "cards"
FROM "decks"
@ -486,9 +502,15 @@ async def get_deck(
oracle_text=card.get("oracle_text"),
scryfall_id=card["scryfall_id"],
set_code=card["set_code"],
set_name=card["set_name"],
collector_number=card["collector_number"],
rarity=card["rarity"],
release_date=card["release_date"],
price_usd=convert_price(card.get("price_usd")),
price_usd_foil=convert_price(card.get("price_usd_foil")),
price_eur=convert_price(card.get("price_eur")),
price_eur_foil=convert_price(card.get("price_eur_foil")),
price_tix=convert_price(card.get("price_tix")),
),
quantity=card["quantity"],
)
@ -517,16 +539,13 @@ async def collection_stats(db: psycopg.Cursor) -> dict:
SELECT COUNT("copies"."id") AS cards
, SUM(
CASE WHEN "copies"."isFoil"
THEN "card_prices"."usd_foil"
ELSE "card_prices"."usd"
THEN "cards"."price_usd_foil"
ELSE "cards"."price_usd"
END
) AS value
, COUNT(DISTINCT cards.set_code) AS sets
FROM "copies"
JOIN "cards" USING ("scryfall_id")
LEFT JOIN "card_prices" USING ("scryfall_id")
WHERE "card_prices"."date" =
(SELECT "value" FROM "vars" where "key" = 'last_update')
"""
)
return await db.fetchone()

View file

@ -82,6 +82,7 @@ class Card:
oracle_id: uuid.UUID
name: str
set_code: str
set_name: str
collector_number: str
rarity: str
color_identity: typing.List[Color]

View file

@ -65,9 +65,9 @@ class JSONEncoder(json.JSONEncoder):
}
def _card(self, card: tutor.models.Card) -> dict:
def price(amount: typing.Optional[decimal.Decimal]) -> typing.Optional[str]:
def price(amount: typing.Optional[decimal.Decimal]) -> typing.Optional[float]:
if amount is not None:
return str(amount)
return float(amount)
else:
return None
@ -75,6 +75,7 @@ class JSONEncoder(json.JSONEncoder):
"scryfall_id": str(card.scryfall_id),
"name": card.name,
"set_code": card.set_code,
"set_name": card.set_name,
"collector_number": card.collector_number,
"rarity": str(card.rarity),
"color_identity": tutor.models.Color.to_string(card.color_identity),

View file

@ -18,7 +18,8 @@
"elm/regex": "1.0.0",
"elm/url": "1.0.0",
"elm-community/maybe-extra": "5.2.0",
"mdgriffith/elm-ui": "1.1.8"
"mdgriffith/elm-ui": "1.1.8",
"myrho/elm-round": "1.0.5"
},
"indirect": {
"elm/bytes": "1.0.8",

View file

@ -5,11 +5,11 @@ import Json.Decode.Pipeline as JDP
type alias Prices =
{ usd : Maybe String
, usd_foil : Maybe String
, eur : Maybe String
, eur_foil : Maybe String
, tix : Maybe String
{ usd : Maybe Float
, usd_foil : Maybe Float
, eur : Maybe Float
, eur_foil : Maybe Float
, tix : Maybe Float
}
@ -91,8 +91,8 @@ decodeCopy =
decodePrices : Json.Decode.Decoder Prices
decodePrices =
Json.Decode.succeed Prices
|> JDP.required "usd" (Json.Decode.nullable Json.Decode.string)
|> JDP.required "usd_foil" (Json.Decode.nullable Json.Decode.string)
|> JDP.required "eur" (Json.Decode.nullable Json.Decode.string)
|> JDP.required "eur_foil" (Json.Decode.nullable Json.Decode.string)
|> JDP.required "tix" (Json.Decode.nullable Json.Decode.string)
|> JDP.required "usd" (Json.Decode.nullable Json.Decode.float)
|> JDP.required "usd_foil" (Json.Decode.nullable Json.Decode.float)
|> JDP.required "eur" (Json.Decode.nullable Json.Decode.float)
|> JDP.required "eur_foil" (Json.Decode.nullable Json.Decode.float)
|> JDP.required "tix" (Json.Decode.nullable Json.Decode.float)

View file

@ -495,7 +495,13 @@ viewCardBrowser model =
[]
)
in
UI.cardRow { foil = copy.foil, subtitle = copy.collection } cardAttrs model.symbols copy.card
UI.cardRow
{ foil = copy.foil
, subtitle = String.join " " [ copy.collection ]
}
cardAttrs
model.symbols
copy.card
in
E.column attrs
[ E.row

View file

@ -20,6 +20,7 @@ import Element.Background as Background
import Element.Border as Border
import Element.Font as Font
import Maybe.Extra
import Round
import Spinner
import Symbol
import Task
@ -183,7 +184,7 @@ subtitle string =
E.el [ Font.size 16, Font.italic, Font.color colors.subtitle ] <| E.text string
priceBadge : { currency : String, amount : String } -> E.Element msg
priceBadge : { currency : String, amount : Float } -> E.Element msg
priceBadge { currency, amount } =
E.el
[ Border.rounded 5
@ -196,7 +197,7 @@ priceBadge { currency, amount } =
<|
E.row [ E.width E.fill ]
[ E.el [ E.width <| E.fillPortion 1 ] <| E.text <| String.toUpper currency
, E.el [ E.width <| E.fillPortion 2, Font.alignRight ] <| E.text amount
, E.el [ E.width <| E.fillPortion 2, Font.alignRight ] <| E.text <| Round.round 2 amount
]