Compare commits

...

4 commits

4 changed files with 91 additions and 26 deletions

View file

@ -141,12 +141,19 @@ async def advanced_search(
joins.append("LEFT JOIN copies ON (cards.scryfall_id = copies.scryfall_id)") joins.append("LEFT JOIN copies ON (cards.scryfall_id = copies.scryfall_id)")
constraints.append("copies.id IS NULL") constraints.append("copies.id IS NULL")
joins.append("JOIN sets ON (cards.set_code = sets.set_code)") joins.append("JOIN sets ON (cards.set_code = sets.set_code)")
joins.append("JOIN rarities ON (cards.rarity = rarities.rarity)")
query = " ".join( query = " ".join(
[ [
"SELECT cards.* FROM cards", "SELECT cards.* FROM cards",
" ".join(joins), " ".join(joins),
"WHERE" if constraints else "", "WHERE" if constraints else "",
" AND ".join(constraints), " AND ".join(constraints),
"ORDER BY rarities.rarity_ord DESC",
", length(cards.color_identity) DESC",
", CASE ",
" WHEN length(cards.color_identity) > 0 THEN '0'"
" ELSE cards.color_identity END ASC",
", cards.name ASC",
f"LIMIT {offset},{limit}", f"LIMIT {offset},{limit}",
] ]
) )

View file

@ -118,7 +118,7 @@ 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")
) )
is_expansion = lstring_from("e", "s") >> matches >> expansion_string.map(IsExpansion) is_expansion = lstring_from("e", "expansion", "s", "set") >> matches >> expansion_string.map(IsExpansion)
in_expansion = lstring("in") >> matches >> expansion_string.map(InExpansion) in_expansion = lstring("in") >> matches >> expansion_string.map(InExpansion)

View file

@ -65,7 +65,9 @@ class SearchHandler(tornado.web.RequestHandler):
search, search,
limit=limit + 1, limit=limit + 1,
offset=limit * (page - 1), offset=limit * (page - 1),
in_collection=in_collection, in_collection=in_collection in ("yes", "true")
if in_collection
else None,
) )
has_more = cards and len(cards) > limit has_more = cards and len(cards) > limit
self.set_header("Content-Type", "application/json") self.set_header("Content-Type", "application/json")

View file

@ -3,6 +3,7 @@ module App exposing (main)
import Browser import Browser
import Browser.Dom import Browser.Dom
import Browser.Events import Browser.Events
import Browser.Navigation
import Dict import Dict
import Element as E import Element as E
import Element.Background as Background import Element.Background as Background
@ -18,6 +19,8 @@ import Regex
import Task import Task
import Url import Url
import Url.Builder import Url.Builder
import Url.Parser exposing ((</>), (<?>))
import Url.Parser.Query
type alias Window = type alias Window =
@ -41,7 +44,8 @@ type alias Card =
type alias Model = type alias Model =
{ viewport : Window { navigationKey : Browser.Navigation.Key
, viewport : Window
, criteria : Criteria , criteria : Criteria
, cardPage : Maybe (ResultPage Card) , cardPage : Maybe (ResultPage Card)
} }
@ -130,22 +134,26 @@ expectPaginatedJson toMsg decoder =
Err (Http.BadBody (Json.Decode.errorToString err)) Err (Http.BadBody (Json.Decode.errorToString err))
searchQuery : Criteria -> List Url.Builder.QueryParameter
searchQuery criteria =
[ Url.Builder.string "q" criteria.query
, Url.Builder.string "in_collection"
(if criteria.ownedOnly then
"yes"
else
""
)
]
search : Criteria -> Cmd Msg search : Criteria -> Cmd Msg
search criteria = search criteria =
Http.get Http.get
{ url = { url =
Url.Builder.absolute Url.Builder.absolute
[ "search" ] [ "search" ]
[ Url.Builder.string "q" criteria.query (Url.Builder.int "limit" 18 :: searchQuery criteria)
, Url.Builder.string "in_collection"
(if criteria.ownedOnly then
"yes"
else
""
)
, Url.Builder.int "limit" 18
]
, expect = expectPaginatedJson FoundCards decodeCard , expect = expectPaginatedJson FoundCards decodeCard
} }
@ -167,13 +175,45 @@ decodeCard =
|> JDP.required "rarity" Json.Decode.string |> JDP.required "rarity" Json.Decode.string
init : Json.Decode.Value -> url -> key -> ( Model, Cmd Msg ) parseUrl : Url.Parser.Parser (Criteria -> a) a
init _ _ _ = parseUrl =
let let
criteria = query =
Url.Parser.Query.string "q"
|> Url.Parser.Query.map (Maybe.withDefault "")
inCollection =
Url.Parser.Query.enum "in_collection"
(Dict.fromList
[ ( "true", True )
, ( "false", False )
, ( "yes", True )
, ( "no", False )
]
)
|> Url.Parser.Query.map (Maybe.withDefault True)
in
Url.Parser.top <?> Url.Parser.Query.map2 Criteria query inCollection
criteriaFromUrl : Url.Url -> Criteria
criteriaFromUrl url =
let
emptyCriteria =
{ query = "", ownedOnly = True } { query = "", ownedOnly = True }
in in
( { viewport = { width = 1280, height = 720 } Url.Parser.parse parseUrl url
|> Maybe.withDefault emptyCriteria
init : Json.Decode.Value -> Url.Url -> Browser.Navigation.Key -> ( Model, Cmd Msg )
init _ url key =
let
criteria =
criteriaFromUrl url
in
( { navigationKey = key
, viewport = { width = 1280, height = 720 }
, criteria = criteria , criteria = criteria
, cardPage = Nothing , cardPage = Nothing
} }
@ -191,14 +231,18 @@ init _ _ _ =
) )
updateCriteria : CriteriaMsg -> Criteria -> Criteria updateCriteria : CriteriaMsg -> Criteria -> ( Criteria, Cmd Msg )
updateCriteria msg model = updateCriteria msg model =
case msg of case msg of
UpdateName text -> UpdateName text ->
{ model | query = text } ( { model | query = text }, Cmd.none )
UpdateOwnedOnly value -> UpdateOwnedOnly value ->
{ model | ownedOnly = value } let
newCriteria =
{ model | ownedOnly = value }
in
( newCriteria, search newCriteria )
updateCardPage cardpage newCards = updateCardPage cardpage newCards =
@ -208,8 +252,12 @@ updateCardPage cardpage newCards =
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Cmd Msg )
update msg model = update msg model =
case msg of case msg of
UrlChanged _ -> UrlChanged url ->
( model, Cmd.none ) let
criteria =
criteriaFromUrl url
in
( { model | criteria = criteria }, search criteria )
LinkClicked _ -> LinkClicked _ ->
( model, Cmd.none ) ( model, Cmd.none )
@ -218,12 +266,20 @@ update msg model =
( { model | viewport = viewport }, Cmd.none ) ( { model | viewport = viewport }, Cmd.none )
UpdateCriteria criteriaMsg -> UpdateCriteria criteriaMsg ->
( { model | criteria = updateCriteria criteriaMsg model.criteria } let
, Cmd.none ( newCriteria, cmd ) =
) updateCriteria criteriaMsg model.criteria
in
( { model | criteria = newCriteria }, cmd )
Search -> Search ->
( model, search model.criteria ) ( model
, Cmd.batch
[ search model.criteria
, Browser.Navigation.pushUrl model.navigationKey <|
Url.Builder.relative [] (searchQuery model.criteria)
]
)
GetPage url -> GetPage url ->
( model, loadPage url ) ( model, loadPage url )