summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2020-11-24 13:31:48 +0100
committerYorhel <git@yorhel.nl>2020-11-24 13:31:48 +0100
commit3b4d475316b1f2bb014e35efd033bac2fa586f67 (patch)
tree0c16ef8efb89bc8ad20b5a04c34a28f5f0b53aef
parent3cfbd795f0cd8756edc1bb69e9248fed72fe59f7 (diff)
AdvSearch: Add a whole bunch of range filtersHEADmaster
-rw-r--r--elm/AdvSearch/Fields.elm79
-rw-r--r--elm/AdvSearch/Range.elm187
-rw-r--r--lib/VNWeb/AdvSearch.pm28
3 files changed, 276 insertions, 18 deletions
diff --git a/elm/AdvSearch/Fields.elm b/elm/AdvSearch/Fields.elm
index a18fdf76..0ad40a5d 100644
--- a/elm/AdvSearch/Fields.elm
+++ b/elm/AdvSearch/Fields.elm
@@ -10,7 +10,8 @@ import Lib.Api as Api
import AdvSearch.Set as AS
import AdvSearch.Producers as AP
import AdvSearch.Tags as AG
-import AdvSearch.RDate as AR
+import AdvSearch.RDate as AD
+import AdvSearch.Range as AR
import AdvSearch.Query exposing (..)
@@ -201,8 +202,18 @@ type FieldModel
| FMBlood (AS.Model String)
| FMSexChar (AS.Model String)
| FMSexSpoil (AS.Model String)
+ | FMHeight (AR.Model Int)
+ | FMWeight (AR.Model Int)
+ | FMBust (AR.Model Int)
+ | FMWaist (AR.Model Int)
+ | FMHips (AR.Model Int)
+ | FMCup (AR.Model String)
+ | FMAge (AR.Model Int)
+ | FMPopularity (AR.Model Int)
+ | FMRating (AR.Model Int)
+ | FMVotecount (AR.Model Int)
| FMDeveloper AP.Model
- | FMRDate AR.Model
+ | FMRDate AD.Model
| FMTag AG.Model
type FieldMsg
@@ -216,8 +227,18 @@ type FieldMsg
| FSBlood (AS.Msg String)
| FSSexChar (AS.Msg String)
| FSSexSpoil (AS.Msg String)
+ | FSHeight AR.Msg
+ | FSWeight AR.Msg
+ | FSBust AR.Msg
+ | FSWaist AR.Msg
+ | FSHips AR.Msg
+ | FSCup AR.Msg
+ | FSAge AR.Msg
+ | FSPopularity AR.Msg
+ | FSRating AR.Msg
+ | FSVotecount AR.Msg
| FSDeveloper AP.Msg
- | FSRDate AR.Msg
+ | FSRDate AD.Msg
| FSTag AG.Msg
| FToggle Bool
| FDel -- intercepted in nestUpdate
@@ -265,19 +286,29 @@ fields =
, f V "" Nothing FMTag AG.init (AG.fromQuery 2)
, f V "Length" Nothing FMLength AS.init AS.lengthFromQuery
, f V "Developer" Nothing FMDeveloper AP.init AP.devFromQuery
- , f V "Release date" Nothing FMRDate AR.init AR.fromQuery
+ , f V "Release date" Nothing FMRDate AD.init AD.fromQuery
+ , f V "Popularity" Nothing FMPopularity AR.popularityInit AR.popularityFromQuery
+ , f V "Rating" Nothing FMRating AR.ratingInit AR.ratingFromQuery
+ , f V "Number of votes" Nothing FMVotecount AR.votecountInit AR.votecountFromQuery
, f V "Release" Nothing FMNest (nestInit NRel R []) (nestFromQuery NRel V)
, f V "Character" Nothing FMNest (nestInit NChar C []) (nestFromQuery NChar V)
, f R "Language" (Just 1) FMLang AS.init AS.langFromQuery
, f R "Platform" (Just 2) FMPlatform AS.init AS.platformFromQuery
, f R "Developer" Nothing FMDeveloper AP.init AP.devFromQuery
- , f R "Release date" Nothing FMRDate AR.init AR.fromQuery
+ , f R "Release date" Nothing FMRDate AD.init AD.fromQuery
, f C "Role" (Just 1) FMRole AS.init AS.roleFromQuery
- , f C "Blood type" Nothing FMBlood AS.init AS.bloodFromQuery
+ , f C "Age" Nothing FMAge AR.ageInit AR.ageFromQuery
, f C "Sex" (Just 2) FMSexChar AS.init (AS.sexFromQuery AS.SexChar)
, f C "Sex (spoiler)" Nothing FMSexSpoil AS.init (AS.sexFromQuery AS.SexSpoil)
+ , f C "Blood type" Nothing FMBlood AS.init AS.bloodFromQuery
+ , f C "Height" Nothing FMHeight AR.heightInit AR.heightFromQuery
+ , f C "Weight" Nothing FMWeight AR.weightInit AR.weightFromQuery
+ , f C "Bust" Nothing FMBust AR.bustInit AR.bustFromQuery
+ , f C "Waist" Nothing FMWaist AR.waistInit AR.waistFromQuery
+ , f C "Hips" Nothing FMHips AR.hipsInit AR.hipsFromQuery
+ , f C "Cup size" Nothing FMCup AR.cupInit AR.cupFromQuery
]
@@ -314,8 +345,18 @@ fieldUpdate dat msg_ (num, dd, model) =
(FSBlood msg, FMBlood m) -> maps FMBlood (AS.update msg m)
(FSSexChar msg, FMSexChar m) -> maps FMSexChar (AS.update msg m)
(FSSexSpoil msg, FMSexSpoil m) -> maps FMSexSpoil (AS.update msg m)
+ (FSHeight msg, FMHeight m) -> maps FMHeight (AR.update msg m)
+ (FSWeight msg, FMWeight m) -> maps FMWeight (AR.update msg m)
+ (FSBust msg, FMBust m) -> maps FMBust (AR.update msg m)
+ (FSWaist msg, FMWaist m) -> maps FMWaist (AR.update msg m)
+ (FSHips msg, FMHips m) -> maps FMHips (AR.update msg m)
+ (FSCup msg, FMCup m) -> maps FMCup (AR.update msg m)
+ (FSAge msg, FMAge m) -> maps FMAge (AR.update msg m)
+ (FSPopularity msg,FMPopularity m)->maps FMPopularity (AR.update msg m)
+ (FSRating msg, FMRating m) -> maps FMRating (AR.update msg m)
+ (FSVotecount msg,FMVotecount m)-> maps FMVotecount (AR.update msg m)
(FSDeveloper msg,FMDeveloper m)-> mapf FMDeveloper FSDeveloper (AP.update dat msg m)
- (FSRDate msg, FMRDate m) -> maps FMRDate (AR.update msg m)
+ (FSRDate msg, FMRDate m) -> maps FMRDate (AD.update msg m)
(FSTag msg, FMTag m) -> mapf FMTag FSTag (AG.update dat msg m)
(FToggle b, _) -> (dat, (num, DD.toggle dd b, model), Cmd.none)
_ -> noop
@@ -350,8 +391,18 @@ fieldView dat (_, dd, model) =
FMBlood m -> vs FSBlood (AS.bloodView m)
FMSexChar m -> vs FSSexChar (AS.sexView AS.SexChar m)
FMSexSpoil m -> vs FSSexSpoil (AS.sexView AS.SexSpoil m)
+ FMHeight m -> vs FSHeight (AR.heightView m)
+ FMWeight m -> vs FSWeight (AR.weightView m)
+ FMBust m -> vs FSBust (AR.bustView m)
+ FMWaist m -> vs FSWaist (AR.waistView m)
+ FMHips m -> vs FSHips (AR.hipsView m)
+ FMCup m -> vs FSCup (AR.cupView m)
+ FMAge m -> vs FSAge (AR.ageView m)
+ FMPopularity m->vs FSPopularity(AR.popularityView m)
+ FMRating m -> vs FSRating (AR.ratingView m)
+ FMVotecount m-> vs FSVotecount(AR.votecountView m)
FMDeveloper m-> vs FSDeveloper(AP.devView dat m)
- FMRDate m -> vs FSRDate (AR.view m)
+ FMRDate m -> vs FSRDate (AD.view m)
FMTag m -> vs FSTag (AG.view dat m)
@@ -368,8 +419,18 @@ fieldToQuery (_, _, model) =
FMBlood m -> AS.toQuery (QStr 3) m
FMSexChar m -> AS.toQuery (QStr 4) m
FMSexSpoil m -> AS.toQuery (QStr 5) m
+ FMHeight m -> AR.toQuery (QInt 6) m
+ FMWeight m -> AR.toQuery (QInt 7) m
+ FMBust m -> AR.toQuery (QInt 8) m
+ FMWaist m -> AR.toQuery (QInt 9) m
+ FMHips m -> AR.toQuery (QInt 10) m
+ FMCup m -> AR.toQuery (QStr 11) m
+ FMAge m -> AR.toQuery (QInt 12) m
+ FMPopularity m->AR.toQuery (QInt 9) m
+ FMRating m -> AR.toQuery (QInt 10) m
+ FMVotecount m-> AR.toQuery (QInt 11) m
FMDeveloper m-> AP.toQuery (QInt 6) m
- FMRDate m -> AR.toQuery m
+ FMRDate m -> AD.toQuery m
FMTag m -> AG.toQuery m
diff --git a/elm/AdvSearch/Range.elm b/elm/AdvSearch/Range.elm
new file mode 100644
index 00000000..d7aa1a7f
--- /dev/null
+++ b/elm/AdvSearch/Range.elm
@@ -0,0 +1,187 @@
+module AdvSearch.Range exposing (..)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Lib.Html exposing (..)
+import Lib.Util exposing (..)
+import Array
+import Lib.Ffi as Ffi
+import Gen.Types as GT
+import AdvSearch.Query exposing (..)
+
+
+type alias Model a =
+ { op : Op
+ , val : Int
+ , lst : Array.Array a
+ }
+
+
+type Msg
+ = MOp Op Bool
+ | Val String
+
+
+update : Msg -> Model a -> Model a
+update msg model =
+ case msg of
+ MOp o _ -> { model | op = o }
+ Val n -> { model | val = Maybe.withDefault 0 (String.toInt n) }
+
+fromQuery : (Data, Model comparable) -> Op -> comparable -> Maybe (Data, Model comparable)
+fromQuery (dat,m) op v = Array.foldl (\v2 (i,r) -> (i+1, if v2 == v then Just i else r)) (0,Nothing) m.lst |> Tuple.second |> Maybe.map (\i -> (dat,{ m | val = i, op = op }))
+
+toQuery : (Op -> a -> Query) -> Model a -> Maybe Query
+toQuery f m = Array.get m.val m.lst |> Maybe.map (\v -> f m.op v)
+
+view : String -> (a -> String) -> Model a -> (Html Msg, () -> List (Html Msg))
+view lbl fmt model =
+ let val n = Array.get n model.lst |> Maybe.map fmt |> Maybe.withDefault ""
+ in
+ ( text <| lbl ++ " " ++ showOp model.op ++ " " ++ val model.val
+ , \() ->
+ [ div [ class "advheader", style "width" "290px" ]
+ [ h3 [] [ text lbl ]
+ , div [ class "opts" ]
+ [ div [ class "opselect" ] <|
+ List.map (\op ->
+ if model.op == op then b [] [ text (showOp op) ] else a [ href "#", onClickD (MOp op True) ] [ text (showOp op) ]
+ ) [Eq, Ne, Ge, Gt, Le, Lt]
+ ]
+ ]
+ , div [ style "display" "flex", style "justify-content" "space-between", style "margin-top" "5px" ]
+ [ b [ class "grayedout" ] [ text (val 0) ]
+ , b [] [ text (val model.val) ]
+ , b [ class "grayedout" ] [ text (val (Array.length model.lst - 1)) ]
+ ]
+ , input
+ [ type_ "range"
+ , Html.Attributes.min "0"
+ , Html.Attributes.max (String.fromInt (Array.length model.lst - 1))
+ , value (String.fromInt model.val)
+ , onInput Val
+ , style "width" "290px"
+ ] []
+ ]
+ )
+
+
+
+
+heightInit dat = (dat, { op = Ge, val = 150, lst = Array.initialize 300 (\n -> n+1) })
+
+heightFromQuery d q =
+ case q of
+ QInt 6 op v -> fromQuery (heightInit d) op v
+ _ -> Nothing
+
+heightView = view "Height" (\v -> String.fromInt v ++ "cm")
+
+
+
+
+weightInit dat = (dat, { op= Ge, val = 60, lst = Array.initialize 401 identity })
+
+weightFromQuery d q =
+ case q of
+ QInt 7 op v -> fromQuery (weightInit d) op v
+ _ -> Nothing
+
+weightView = view "Weight" (\v -> String.fromInt v ++ "kg")
+
+
+
+
+bustInit dat = (dat, { op = Ge, val = 40, lst = Array.initialize 101 (\n -> n+20) })
+
+bustFromQuery d q =
+ case q of
+ QInt 8 op v -> fromQuery (bustInit d) op v
+ _ -> Nothing
+
+bustView = view "Bust" (\v -> String.fromInt v ++ "cm")
+
+
+
+
+waistInit dat = (dat, { op = Ge, val = 40, lst = Array.initialize 101 (\n -> n+20) })
+
+waistFromQuery d q =
+ case q of
+ QInt 9 op v -> fromQuery (waistInit d) op v
+ _ -> Nothing
+
+waistView = view "Waist" (\v -> String.fromInt v ++ "cm")
+
+
+
+
+hipsInit dat = (dat, { op = Ge, val = 40, lst = Array.initialize 101 (\n -> n+20) })
+
+hipsFromQuery d q =
+ case q of
+ QInt 10 op v -> fromQuery (hipsInit d) op v
+ _ -> Nothing
+
+hipsView = view "Hips" (\v -> String.fromInt v ++ "cm")
+
+
+
+
+cupInit dat = (dat, { op = Ge, val = 3, lst = Array.fromList (List.map Tuple.first (List.drop 1 GT.cupSizes)) })
+
+cupFromQuery d q =
+ case q of
+ QStr 11 op v -> fromQuery (cupInit d) op v
+ _ -> Nothing
+
+cupView = view "Cup size" identity
+
+
+
+
+ageInit dat = (dat, { op = Ge, val = 17, lst = Array.initialize 120 (\n -> n+1) })
+
+ageFromQuery d q =
+ case q of
+ QInt 12 op v -> fromQuery (ageInit d) op v
+ _ -> Nothing
+
+ageView = view "Age" (\v -> if v == 1 then "1 year" else String.fromInt v ++ " years")
+
+
+
+
+popularityInit dat = (dat, { op = Ge, val = 10, lst = Array.initialize 101 identity })
+
+popularityFromQuery d q =
+ case q of
+ QInt 9 op v -> fromQuery (popularityInit d) op v
+ _ -> Nothing
+
+popularityView = view "Popularity" String.fromInt
+
+
+
+
+ratingInit dat = (dat, { op = Ge, val = 40, lst = Array.initialize 91 (\v -> v+10) })
+
+ratingFromQuery d q =
+ case q of
+ QInt 10 op v -> fromQuery (ratingInit d) op v
+ _ -> Nothing
+
+ratingView = view "Rating" (\v -> Ffi.fmtFloat (toFloat v / 10) 1)
+
+
+
+
+votecountInit dat = (dat, { op = Ge, val = 10, lst = Array.fromList [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 2000, 3000, 4000, 5000 ] })
+
+votecountFromQuery d q =
+ case q of
+ QInt 11 op v -> fromQuery (votecountInit d) op v
+ _ -> Nothing
+
+votecountView = view "# Votes" String.fromInt
diff --git a/lib/VNWeb/AdvSearch.pm b/lib/VNWeb/AdvSearch.pm
index f88fbb60..7e89f7e6 100644
--- a/lib/VNWeb/AdvSearch.pm
+++ b/lib/VNWeb/AdvSearch.pm
@@ -289,11 +289,14 @@ sub f {
-f v => 2 => 'lang', { enum => \%LANGUAGE }, '=' => sub { sql 'v.c_languages && ARRAY', \$_, '::language[]' };
-f v => 3 => 'olang', { enum => \%LANGUAGE }, '=' => sub { sql 'v.c_olang && ARRAY', \$_, '::language[]' };
-f v => 4 => 'platform', { enum => \%PLATFORM }, '=' => sub { sql 'v.c_platforms && ARRAY', \$_, '::platform[]' };
-f v => 5 => 'length', { uint => 1, enum => \%VN_LENGTH }, '=' => sub { sql 'v.length =', \$_ };
-f v => 7 => 'released', { fuzzyrdate => 1 }, sql => sub { sql 'v.c_released', $_[0], \($_ == 1 ? strftime('%Y%m%d', gmtime) : $_) };
+f v => 2 => 'lang', { enum => \%LANGUAGE }, '=' => sub { sql 'v.c_languages && ARRAY', \$_, '::language[]' };
+f v => 3 => 'olang', { enum => \%LANGUAGE }, '=' => sub { sql 'v.c_olang && ARRAY', \$_, '::language[]' };
+f v => 4 => 'platform', { enum => \%PLATFORM }, '=' => sub { sql 'v.c_platforms && ARRAY', \$_, '::platform[]' };
+f v => 5 => 'length', { uint => 1, enum => \%VN_LENGTH }, '=' => sub { sql 'v.length =', \$_ };
+f v => 7 => 'released', { fuzzyrdate => 1 }, sql => sub { sql 'v.c_released', $_[0], \($_ == 1 ? strftime('%Y%m%d', gmtime) : $_) };
+f v => 9 => 'popularity',{ uint => 1, range => [ 0, 100] }, sql => sub { sql 'v.c_popularity', $_[0], \($_/100) };
+f v => 10 => 'rating', { uint => 1, range => [10, 100] }, sql => sub { sql 'v.c_rating <> 0 AND v.c_rating', $_[0], \$_ };
+f v => 11 => 'vote-count',{ uint => 1, range => [ 0,1<<30] }, sql => sub { sql 'v.c_votecount', $_[0], \$_ };
f v => 6 => 'developer',{ vndbid => 'p' },
'=' => sub { sql 'v.id IN(SELECT rv.vid FROM releases r JOIN releases_vn rv ON rv.id = r.id JOIN releases_producers rp ON rp.id = r.id
@@ -315,10 +318,17 @@ f r => 7 => 'released', { fuzzyrdate => 1 }, sql => sub { sql 'r.released', $_[
-f c => 2 => 'role', { enum => \%CHAR_ROLE }, '=', sub { sql 'cv.role =', \$_ }; # TODO: SQL is different when not used as a subquery in VN search
-f c => 3 => 'blood-type', { enum => \%BLOOD_TYPE }, '=', sub { sql 'c.bloodt =', \$_ };
-f c => 4 => 'sex', { enum => \%GENDER }, '=', sub { sql 'c.gender =', \$_ };
-f c => 5 => 'sex-spoil', { enum => \%GENDER }, '=', sub { sql 'c.spoil_gender =', \$_ }; # TODO: Some way to search for NULL (separate "has-sex-spoil" filter?)
+f c => 2 => 'role', { enum => \%CHAR_ROLE }, '=' => sub { sql 'cv.role =', \$_ }; # TODO: SQL is different when not used as a subquery in VN search
+f c => 3 => 'blood-type', { enum => \%BLOOD_TYPE }, '=' => sub { sql 'c.bloodt =', \$_ };
+f c => 4 => 'sex', { enum => \%GENDER }, '=' => sub { sql 'c.gender =', \$_ };
+f c => 5 => 'sex-spoil', { enum => \%GENDER }, '=' => sub { sql 'c.spoil_gender =', \$_ }; # TODO: Some way to search for NULL (separate "has-sex-spoil" filter?)
+f c => 6 => 'height', { uint => 1, max => 32767 }, sql => sub { sql 'c.height <> 0 AND c.height', $_[0], \$_ };
+f c => 7 => 'weight', { uint => 1, max => 32767 }, sql => sub { sql 'c.weight', $_[0], \$_ };
+f c => 8 => 'bust', { uint => 1, max => 32767 }, sql => sub { sql 'c.s_bust <> 0 AND c.s_bust', $_[0], \$_ };
+f c => 9 => 'waist', { uint => 1, max => 32767 }, sql => sub { sql 'c.s_waist <> 0 AND c.s_waist', $_[0], \$_ };
+f c => 10 => 'hips', { uint => 1, max => 32767 }, sql => sub { sql 'c.s_hip <> 0 AND c.s_hip', $_[0], \$_ };
+f c => 11 => 'cup', { enum => \%CUP_SIZE }, sql => sub { sql 'c.cup_size <> \'\' AND c.cup_size', $_[0], \$_ };
+f c => 12 => 'age', { uint => 1, max => 32767 }, sql => sub { sql 'c.age <> 0 AND c.age', $_[0], \$_ };