packages feed

datarobot 0.1.1 → 1.0.0

raw patch · 4 files changed

+102/−64 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- DataRobot: classProbability :: Text -> PredictResult -> Maybe Float
- DataRobot: responseResult :: PredictResponse -> Either PredictError PredictResult
- DataRobot.PredictResponse: classProbability :: Text -> PredictResult -> Maybe Float
- DataRobot.PredictResponse: instance Data.Aeson.Types.FromJSON.FromJSON DataRobot.PredictResponse.PredictFailure
- DataRobot.PredictResponse: instance Data.Aeson.Types.FromJSON.FromJSON DataRobot.PredictResponse.PredictResponse
- DataRobot.PredictResponse: instance Data.Aeson.Types.FromJSON.FromJSON DataRobot.PredictResponse.PredictSuccess
- DataRobot.PredictResponse: instance Data.Aeson.Types.ToJSON.ToJSON DataRobot.PredictResponse.PredictResult
- DataRobot.PredictResponse: instance GHC.Classes.Eq DataRobot.PredictResponse.PredictFailure
- DataRobot.PredictResponse: instance GHC.Classes.Eq DataRobot.PredictResponse.PredictResponse
- DataRobot.PredictResponse: instance GHC.Classes.Eq DataRobot.PredictResponse.PredictSuccess
- DataRobot.PredictResponse: instance GHC.Generics.Generic DataRobot.PredictResponse.PredictFailure
- DataRobot.PredictResponse: instance GHC.Generics.Generic DataRobot.PredictResponse.PredictSuccess
- DataRobot.PredictResponse: instance GHC.Show.Show DataRobot.PredictResponse.PredictFailure
- DataRobot.PredictResponse: instance GHC.Show.Show DataRobot.PredictResponse.PredictResponse
- DataRobot.PredictResponse: instance GHC.Show.Show DataRobot.PredictResponse.PredictSuccess
- DataRobot.PredictResponse: responseResult :: PredictResponse -> Either PredictError PredictResult
+ DataRobot: [$sel:prediction:PredictResult] :: PredictResult -> Value
+ DataRobot: [$sel:predictionTimeMs:PredictResult] :: PredictResult -> Float
+ DataRobot: [$sel:predictionValues:PredictResult] :: PredictResult -> Maybe [PredictionValue]
+ DataRobot: predictionValue :: Text -> PredictResult -> Maybe Float
+ DataRobot.PredictResponse: PredictionValue :: Text -> Float -> PredictionValue
+ DataRobot.PredictResponse: [$sel:label:PredictionValue] :: PredictionValue -> Text
+ DataRobot.PredictResponse: [$sel:prediction:PredictResult] :: PredictResult -> Value
+ DataRobot.PredictResponse: [$sel:predictionTimeMs:PredictResult] :: PredictResult -> Float
+ DataRobot.PredictResponse: [$sel:predictionValues:PredictResult] :: PredictResult -> Maybe [PredictionValue]
+ DataRobot.PredictResponse: [$sel:value:PredictionValue] :: PredictionValue -> Float
+ DataRobot.PredictResponse: data PredictionValue
+ DataRobot.PredictResponse: instance Data.Aeson.Types.FromJSON.FromJSON DataRobot.PredictResponse.PredictionValue
+ DataRobot.PredictResponse: instance Data.Aeson.Types.FromJSON.FromJSON DataRobot.PredictResponse.ResponseFailure
+ DataRobot.PredictResponse: instance Data.Aeson.Types.FromJSON.FromJSON DataRobot.PredictResponse.ResponseSuccess
+ DataRobot.PredictResponse: instance Data.Aeson.Types.ToJSON.ToJSON DataRobot.PredictResponse.PredictionValue
+ DataRobot.PredictResponse: instance GHC.Classes.Eq DataRobot.PredictResponse.PredictionValue
+ DataRobot.PredictResponse: instance GHC.Classes.Eq DataRobot.PredictResponse.ResponseFailure
+ DataRobot.PredictResponse: instance GHC.Classes.Eq DataRobot.PredictResponse.ResponseSuccess
+ DataRobot.PredictResponse: instance GHC.Generics.Generic DataRobot.PredictResponse.PredictionValue
+ DataRobot.PredictResponse: instance GHC.Generics.Generic DataRobot.PredictResponse.ResponseFailure
+ DataRobot.PredictResponse: instance GHC.Generics.Generic DataRobot.PredictResponse.ResponseSuccess
+ DataRobot.PredictResponse: instance GHC.Show.Show DataRobot.PredictResponse.PredictionValue
+ DataRobot.PredictResponse: instance GHC.Show.Show DataRobot.PredictResponse.ResponseFailure
+ DataRobot.PredictResponse: instance GHC.Show.Show DataRobot.PredictResponse.ResponseSuccess
+ DataRobot.PredictResponse: parseResponse :: Response ByteString -> Either PredictError PredictResult
+ DataRobot.PredictResponse: predictionValue :: Text -> PredictResult -> Maybe Float
- DataRobot: PredictResult :: Value -> Float -> Text -> Maybe (HashMap Text Float) -> PredictResult
+ DataRobot: PredictResult :: Value -> Float -> Maybe [PredictionValue] -> PredictResult
- DataRobot.PredictResponse: PredictResult :: Value -> Float -> Text -> Maybe (HashMap Text Float) -> PredictResult
+ DataRobot.PredictResponse: PredictResult :: Value -> Float -> Maybe [PredictionValue] -> PredictResult

Files

datarobot.cabal view
@@ -1,9 +1,11 @@--- This file has been generated from package.yaml by hpack version 0.17.0.+-- This file has been generated from package.yaml by hpack version 0.20.0. -- -- see: https://github.com/sol/hpack+--+-- hash: d659e0f40eb6ebe5c04a98ee4a259744bfffb63a3269437ce8661118f03926d4  name:                datarobot-version:             0.1.1+version:             1.0.0 synopsis:            Client for DataRobot API description:         Client for DataRobot API homepage:            https://github.com/orbital/datarobot-haskell#readme@@ -31,9 +33,11 @@       DataRobot.Predict       DataRobot.PredictResponse       DataRobot.Types+  other-modules:+      Paths_datarobot   build-depends:-      base >=4.7 && <5-    , aeson+      aeson+    , base >=4.7 && <5     , bytestring     , exceptions     , microlens@@ -54,9 +58,10 @@       test   ghc-options: -threaded -rtsopts -with-rtsopts=-N   build-depends:-      base >=4.7 && <5-    , aeson+      aeson+    , base     , bytestring+    , datarobot     , exceptions     , microlens     , network-uri@@ -67,6 +72,6 @@     , unordered-containers     , vector     , wreq-    , base-    , datarobot+  other-modules:+      Paths_datarobot   default-language: Haskell2010
src/DataRobot.hs view
@@ -16,8 +16,7 @@   , Fields   , PredictError(..)   , PredictResult(..)-  , responseResult-  , classProbability+  , predictionValue   
src/DataRobot/Predict.hs view
@@ -11,14 +11,14 @@ import Lens.Micro ((?~), (.~)) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Catch (MonadThrow)-import Data.Aeson (Value(..))+import Data.Aeson (Value(..), encode) import Data.Function ((&)) import qualified Data.HashMap.Strict as HM import qualified Data.Vector as V import Data.String.Conversions (cs)  import DataRobot.Types-import DataRobot.API (parseResponse, endpoint)+import DataRobot.API (endpoint) import DataRobot.PredictResponse  import Network.URI (URI(..))@@ -34,8 +34,7 @@       url = predictURI (baseURLPredict c) pid mid       body = Array $ V.singleton $ Object $ HM.fromList o   r <- liftIO $ Wreq.postWith opts url body-  pr <- parseResponse r-  pure $ responseResult pr+  pure $ parseResponse r   httpOptions :: Credentials -> Wreq.Options
src/DataRobot/PredictResponse.hs view
@@ -4,22 +4,32 @@ module DataRobot.PredictResponse   ( PredictError(..)   , PredictResult(..)-  , responseResult-  , classProbability+  , PredictionValue(..)+  , parseResponse+  , predictionValue   ) where--import Control.Applicative ((<|>)) import Control.Monad.Catch (Exception)-import Data.Aeson (FromJSON(..), ToJSON, Value)-import Data.HashMap.Strict (HashMap)-import qualified Data.HashMap.Strict as HM-import Data.Maybe (fromMaybe)+import Data.Aeson (FromJSON(..), ToJSON, Value(..), decode, defaultOptions, genericParseJSON, withObject, (.:), eitherDecode)+import Data.Aeson.Types (Options(..), typeMismatch)+import Data.List (find)+import Data.Maybe (fromMaybe, maybe) import Data.Text (Text)+import Data.String.Conversions (cs) import Data.Typeable (Typeable) import GHC.Generics (Generic) import Safe (headMay) +import Lens.Micro ((^.))+import Network.Wreq (Response, responseBody, responseHeader)+import Data.ByteString.Lazy (ByteString) ++underscorePrefixOptions :: Options+underscorePrefixOptions =+  defaultOptions { fieldLabelModifier = dropWhile (== '_') }++type Code = Int+ data PredictError     = APIError Code Text     | MissingPrediction@@ -28,67 +38,92 @@ instance Exception PredictError  -newtype PredictResponse = PredictResponse (Either PredictFailure PredictSuccess )-    deriving (Show, Eq)+-- Datarobot successful response+data ResponseSuccess = ResponseSuccess+  { _data :: [Prediction]+  } deriving (Eq, Show, Generic) -instance FromJSON PredictResponse where-    parseJSON v = PredictResponse <$>-      ((Right <$> parseJSON v) <|> (Left <$> parseJSON v))+instance FromJSON ResponseSuccess where+  parseJSON = genericParseJSON underscorePrefixOptions -type Code = Int -data PredictFailure = PredictFailure-  { code :: Code-  , status :: Text+-- Datarobot failure response+data ResponseFailure = ResponseFailure+  { _message :: Text   } deriving (Show, Eq, Generic) -instance FromJSON PredictFailure+instance FromJSON ResponseFailure where+  parseJSON = genericParseJSON underscorePrefixOptions -data PredictSuccess = PredictSuccess-  { predictions :: [Prediction]-  , execution_time :: Float-  , model_id :: Text-  -- , task :: Text-  } deriving (Show, Eq, Generic) -instance FromJSON PredictSuccess+-- A single prediction value+data PredictionValue = PredictionValue+  { label :: Text+  , value :: Float+  } deriving (Eq, Show, Generic) +instance ToJSON PredictionValue+instance FromJSON PredictionValue where+  parseJSON = withObject "prediction_value" $ \o -> do+      value' <- o .: "value"+      label' <- labelText =<< o .: "label"+      return $ PredictionValue label' value'+    where+      -- Always treat the label as text even though the JSON also allows numbers+      -- This makes key-based lookup easier on the API consumer+      labelText (Number n) = pure $ (cs .show) n+      labelText (String s) = pure s+      labelText invalid    = typeMismatch "label" invalid+++-- Combination of prediction values and prediction label data Prediction = Prediction-  { prediction :: Value-  , class_probabilities :: Maybe (HashMap Text Float)-  } deriving (Show, Eq, Generic)+  { _prediction       :: Value  -- label or float+  , _predictionValues :: Maybe [PredictionValue]+  } deriving (Eq, Show, Generic) -instance FromJSON Prediction+instance FromJSON Prediction where+  parseJSON = genericParseJSON underscorePrefixOptions    -- | Result from the prediction- data PredictResult = PredictResult-  { prediction :: Value+  { prediction       :: Value   , predictionTimeMs :: Float-  , modelId :: Text-  , classProbabilities :: Maybe (HashMap Text Float)+  , predictionValues :: Maybe [PredictionValue]   } deriving (Show, Eq, Generic) -instance ToJSON PredictResult -responseResult :: PredictResponse -> Either PredictError PredictResult-responseResult (PredictResponse (Right ps)) =-      fromMaybe (Left MissingPrediction) $ do-        p <- headMay (predictions ps)-        return $ Right $ PredictResult-          { prediction = prediction (p :: Prediction)-          , predictionTimeMs = execution_time ps-          , modelId = model_id ps-          , classProbabilities = class_probabilities p-          }-responseResult (PredictResponse (Left pf)) =-    Left $ APIError (code pf) (status pf)+-- Create a result for a successful response+responseSuccess :: Float -> ResponseSuccess -> Either PredictError PredictResult+responseSuccess et rs =+    maybe (Left MissingPrediction) Right $ do+        p <- headMay (_data rs)+        pure PredictResult+            { prediction       = _prediction p+            , predictionValues = _predictionValues p+            , predictionTimeMs = et+            } +-- Create a result for a failed response+responseFailure :: Text -> Either PredictError PredictResult+responseFailure e = Left $ APIError 422 e -classProbability :: Text -> PredictResult -> Maybe Float-classProbability c r = do-    cps <- classProbabilities r-    HM.lookup c cps+-- Parse the entire prediction response+-- This is needed because some of the data is delivered in the body and some is delivered via headers+parseResponse :: Response ByteString -> Either PredictError PredictResult+parseResponse r = do+    either (responseFailure . cs) (responseSuccess tm) $ eitherDecode b+    where+      b  = r ^. responseBody+      et = r ^. responseHeader "X-DataRobot-Execution-Time"+      tm = fromMaybe 0.0 $ decode (cs et)++-- Find a prediction value probability from a given label+predictionValue :: Text -> PredictResult -> Maybe Float+predictionValue c r = do+    ps <- predictionValues r+    pd <- find ((== c) . label) ps+    pure $ value pd