2 次代碼提交 b0357858c8 ... 6073ce4c02

作者 SHA1 備註 提交日期
  Cadel Watson 6073ce4c02 Encode data ready for indexeddb 1 年之前
  Cadel Watson 6a97056901 Fetch and save set data from server 1 年之前
共有 6 個文件被更改,包括 270 次插入3 次删除
  1. 5 0
      js/app.js
  2. 2 2
      server/main.py
  3. 1 0
      src/Card.elm
  4. 132 1
      src/Database.elm
  5. 8 0
      src/Main.elm
  6. 122 0
      tests/TestDatabase.elm

+ 5 - 0
js/app.js

@@ -53,5 +53,10 @@ openDBRequest.onsuccess = (event) => {
             app.ports.receiveDoesSetHaveLocalData.send(JSON.stringify(data));
         })
     });
+
+    app.ports.sendSaveLocalData.subscribe((setData) => {
+        console.log("SAVE");
+        console.log(setData);
+    })
 };
 

+ 2 - 2
server/main.py

@@ -69,9 +69,9 @@ def read_item(set_name: str):
     if set_name not in discover_sets():
         return {"error": "Set not found"}
 
-    card_ratings_path = os.path.join("data", "sets", set_name, "card-ratings-all.json")
+    card_data_path = os.path.join("data", "sets", set_name, "card-data.json")
 
-    with open(card_ratings_path) as f:
+    with open(card_data_path) as f:
         card_data = json.load(f)
 
     card_ratings_path = os.path.join("data", "sets", set_name, "card-ratings-all.json")

+ 1 - 0
src/Card.elm

@@ -61,6 +61,7 @@ type alias CardDetails =
     , power : Maybe Power
     , toughness : Maybe Power
     , colors : List ManaColor
+    , rawManaCost : String
     , manaCost : Maybe (List ManaCost)
     , imageUrl : String
     }

+ 132 - 1
src/Database.elm

@@ -1,9 +1,10 @@
-module Database exposing (Database, decode, decoder, get, getAll)
+module Database exposing (Database, decode, decoder, encode, fromCardData, get, getAll)
 
 import Card exposing (CardData, CardDetails, CardPerformanceData, CardType(..), ManaColor(..), Power(..), parseManaCost)
 import Dict exposing (Dict)
 import Json.Decode as Decode exposing (Decoder, decodeString)
 import Json.Decode.Pipeline exposing (optional, required)
+import Json.Encode as Encode
 import Tuple exposing (pair)
 
 
@@ -11,6 +12,13 @@ type Database
     = Database (Dict String CardData)
 
 
+fromCardData : List CardData -> Database
+fromCardData cards =
+    List.map (\card -> ( card.details.name, card )) cards
+        |> Dict.fromList
+        |> Database
+
+
 get : String -> Database -> Maybe CardData
 get name (Database db) =
     Dict.get name db
@@ -40,6 +48,24 @@ decoder =
         )
 
 
+databaseToDict : Database -> Dict String CardData
+databaseToDict (Database db) =
+    db
+
+
+encode : ( String, Database ) -> Encode.Value
+encode ( setCode, Database db ) =
+    Encode.object
+        [ ( "code", Encode.string setCode )
+        , ( "data"
+          , Encode.object
+                [ ( "ratings", Encode.list encodeCardPerformance (Dict.values db) )
+                , ( "cards", Encode.list encodeCardDetails (Dict.values db) )
+                ]
+          )
+        ]
+
+
 createDatabase : Dict String CardPerformanceData -> Dict String CardDetails -> Database
 createDatabase performanceData detailsData =
     Dict.merge
@@ -64,6 +90,28 @@ decodeSetData =
         |> Decode.map Dict.fromList
 
 
+encodeCardPerformance : CardData -> Encode.Value
+encodeCardPerformance d =
+    Encode.object
+        [ ( "Name", Encode.string d.details.name )
+        , ( "# Seen", encodeIntString d.performance.totalTimesSeen )
+        , ( "# Picked", encodeIntString d.performance.totalTimesPicked )
+        , ( "ATA", encodeMaybeFloatString d.performance.averagePickPosition )
+        , ( "ALSA", encodeMaybeFloatString d.performance.averageSeenPosition )
+        , ( "GIH WR", encodeMaybePercentageString d.performance.gameInHandWinRate )
+        , ( "IWD"
+          , Encode.string
+                (case d.performance.improvementWhenDrawn of
+                    Just i ->
+                        String.fromFloat i ++ "pp"
+
+                    Nothing ->
+                        ""
+                )
+          )
+        ]
+
+
 decodeCardPerformance : Decoder ( String, CardPerformanceData )
 decodeCardPerformance =
     Decode.map2 pair
@@ -96,6 +144,11 @@ decodeImprovementWhenDrawn =
             )
 
 
+encodeIntString : Int -> Encode.Value
+encodeIntString i =
+    Encode.string (String.fromInt i)
+
+
 decodeIntString : Decoder Int
 decodeIntString =
     Decode.string
@@ -110,6 +163,16 @@ decodeIntString =
             )
 
 
+encodeMaybeFloatString : Maybe Float -> Encode.Value
+encodeMaybeFloatString m =
+    case m of
+        Just f ->
+            Encode.string (String.fromFloat f)
+
+        Nothing ->
+            Encode.string ""
+
+
 decodeMaybeFloatString : Decoder (Maybe Float)
 decodeMaybeFloatString =
     Decode.string
@@ -128,6 +191,16 @@ decodeMaybeFloatString =
             )
 
 
+encodeMaybePercentageString : Maybe Float -> Encode.Value
+encodeMaybePercentageString m =
+    case m of
+        Just f ->
+            Encode.string (String.fromFloat (f * 100) ++ "%")
+
+        Nothing ->
+            Encode.string ""
+
+
 decodeMaybePercentageString : Decoder (Maybe Float)
 decodeMaybePercentageString =
     Decode.string
@@ -146,6 +219,28 @@ decodeMaybePercentageString =
             )
 
 
+encodeCardDetails : CardData -> Encode.Value
+encodeCardDetails card =
+    Encode.object
+        [ ( "name", Encode.string card.details.name )
+        , ( "cmc", Encode.int card.details.cmc )
+        , ( "type_line", Encode.string card.details.typeLine )
+        , ( "oracle_text"
+          , case card.details.oracleText of
+                Just t ->
+                    Encode.string t
+
+                Nothing ->
+                    Encode.null
+          )
+        , ( "power", encodePower card.details.power )
+        , ( "toughness", encodePower card.details.toughness )
+        , ( "colors", Encode.list encodeManaColor card.details.colors )
+        , ( "mana_cost", Encode.string card.details.rawManaCost )
+        , ( "image_uris", Encode.object [ ( "large", Encode.string card.details.imageUrl ) ] )
+        ]
+
+
 decodeCardDetails : Decoder ( String, CardDetails )
 decodeCardDetails =
     Decode.map2 pair
@@ -159,6 +254,7 @@ decodeCardDetails =
             |> optional "power" (Decode.nullable decodePower) Nothing
             |> optional "toughness" (Decode.nullable decodePower) Nothing
             |> required "colors" (Decode.list decodeManaColor)
+            |> required "mana_cost" Decode.string
             |> required "mana_cost"
                 (Decode.string
                     |> Decode.andThen
@@ -175,6 +271,19 @@ decodeCardDetails =
         )
 
 
+encodePower : Maybe Power -> Encode.Value
+encodePower p =
+    case p of
+        Just (ConstantPower i) ->
+            Encode.string (String.fromInt i)
+
+        Just VariablePower ->
+            Encode.string "*"
+
+        Nothing ->
+            Encode.null
+
+
 decodePower : Decoder Power
 decodePower =
     Decode.string
@@ -193,6 +302,28 @@ decodePower =
             )
 
 
+encodeManaColor : ManaColor -> Encode.Value
+encodeManaColor c =
+    case c of
+        Red ->
+            Encode.string "R"
+
+        Blue ->
+            Encode.string "U"
+
+        Green ->
+            Encode.string "G"
+
+        White ->
+            Encode.string "W"
+
+        Black ->
+            Encode.string "B"
+
+        Colorless ->
+            Encode.string "C"
+
+
 decodeManaColor : Decoder ManaColor
 decodeManaColor =
     Decode.string

+ 8 - 0
src/Main.elm

@@ -206,6 +206,14 @@ update msg model =
                     , API.getSetData setCode IOGotSetData
                     )
 
+                IOGotSetData (Ok ( setCode, Just database )) ->
+                    ( ChooseSet { mdl | sets = Maybe.map (Dict.insert setCode (HasLocalData database)) mdl.sets }
+                    , sendSaveLocalData (Database.encode ( setCode, database ))
+                    )
+
+                IOGotSetData (Err e) ->
+                    ( Error { error = "Error fetching remote set data (" ++ e ++ ")" }, Cmd.none )
+
                 _ ->
                     ( ChooseSet mdl, Cmd.none )
 

+ 122 - 0
tests/TestDatabase.elm

@@ -0,0 +1,122 @@
+module TestDatabase exposing (..)
+
+import Card exposing (CardData, CardDetails, CardPerformanceData, CardType(..), ManaColor(..), ManaCost(..), Power(..))
+import Database exposing (Database)
+import Expect
+import Fuzz exposing (Fuzzer)
+import Json.Decode as Decode
+import Json.Encode as Encode
+import Test exposing (Test, describe, fuzz)
+
+
+niceFloat : Fuzzer Float
+niceFloat =
+    Fuzz.floatRange 0 100
+
+
+fuzzDatabase : Fuzzer Database
+fuzzDatabase =
+    Fuzz.listOfLengthBetween 1 2 fuzzCardData
+        |> Fuzz.map Database.fromCardData
+
+
+fuzzCardData : Fuzzer CardData
+fuzzCardData =
+    Fuzz.string
+        |> Fuzz.andThen
+            (\cardName ->
+                Fuzz.constant CardData
+                    |> Fuzz.andMap (fuzzCardDetails cardName)
+                    |> Fuzz.andMap fuzzCardPerformance
+            )
+
+
+fuzzCardPerformance : Fuzzer CardPerformanceData
+fuzzCardPerformance =
+    Fuzz.constant CardPerformanceData
+        |> Fuzz.andMap Fuzz.int
+        |> Fuzz.andMap Fuzz.int
+        |> Fuzz.andMap (Fuzz.maybe niceFloat)
+        |> Fuzz.andMap (Fuzz.maybe niceFloat)
+        |> Fuzz.andMap (Fuzz.maybe (Fuzz.constant 0.5))
+        |> Fuzz.andMap (Fuzz.maybe niceFloat)
+
+
+fuzzCardDetails : String -> Fuzzer CardDetails
+fuzzCardDetails name =
+    Fuzz.constant (CardDetails name)
+        |> Fuzz.andMap Fuzz.int
+        |> Fuzz.andMap (Fuzz.constant Creature)
+        |> Fuzz.andMap (Fuzz.constant "Creature // Pirate")
+        |> Fuzz.andMap (Fuzz.maybe Fuzz.string)
+        |> Fuzz.andMap (Fuzz.maybe fuzzPower)
+        |> Fuzz.andMap (Fuzz.maybe fuzzPower)
+        |> Fuzz.andMap (Fuzz.list fuzzManaColor)
+        |> Fuzz.andMap (Fuzz.constant "{X}{R}")
+        |> Fuzz.andMap (Fuzz.constant (Just [ X, Color Red ]))
+        |> Fuzz.andMap Fuzz.string
+
+
+fuzzCardType : Fuzzer CardType
+fuzzCardType =
+    Fuzz.oneOfValues
+        [ Creature
+        , Instant
+        , Sorcery
+        , Enchantment
+        , Artifact
+        , Planeswalker
+        , Land
+        , Other
+        ]
+
+
+fuzzPower : Fuzzer Power
+fuzzPower =
+    Fuzz.oneOf
+        [ Fuzz.constant VariablePower
+        , Fuzz.int |> Fuzz.map ConstantPower
+        ]
+
+
+fuzzManaColor : Fuzzer ManaColor
+fuzzManaColor =
+    Fuzz.oneOfValues
+        [ Red
+        , Green
+        , Blue
+        , White
+        , Black
+        , Colorless
+        ]
+
+
+fuzzManaCost : Fuzzer ManaCost
+fuzzManaCost =
+    Fuzz.oneOf
+        [ Fuzz.constant X
+        , Fuzz.constant Y
+        , Fuzz.constant Tap
+        , Fuzz.constant Pay
+        , Fuzz.int |> Fuzz.map AnyColor
+        , fuzzManaColor |> Fuzz.map Color
+        , fuzzManaColor |> Fuzz.map ColorPay
+        , fuzzManaColor |> Fuzz.map TwoOrColor
+        , Fuzz.map2 Hybrid fuzzManaColor fuzzManaColor
+        , Fuzz.map2 HybridPay fuzzManaColor fuzzManaColor
+        ]
+
+
+suite : Test
+suite =
+    describe "Database"
+        [ fuzz fuzzDatabase "encoder / decoder round trip" <|
+            \database ->
+                let
+                    roundTrip =
+                        Database.encode ( "mkm", database )
+                            |> Encode.encode 0
+                            |> Decode.decodeString Database.decoder
+                in
+                Expect.equal roundTrip (Ok ( "mkm", Just database ))
+        ]