Compare commits
4 commits
be7c3eb749
...
863ab021a6
Author | SHA1 | Date | |
---|---|---|---|
863ab021a6 | |||
b9b5badec6 | |||
a08a2998a3 | |||
77c84fc083 |
4 changed files with 91 additions and 26 deletions
|
@ -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}",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
104
www/src/App.elm
104
www/src/App.elm
|
@ -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 )
|
||||||
|
|
Loading…
Reference in a new issue