packages feed

datarobot (empty) → 0.1.0

raw patch · 10 files changed

+422/−0 lines, 10 filesdep +aesondep +basedep +bytestringsetup-changed

Dependencies added: aeson, base, bytestring, datarobot, exceptions, microlens, network-uri, safe, scientific, string-conversions, text, unordered-containers, vector, wreq

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Sean Hess (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Sean Hess nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ datarobot.cabal view
@@ -0,0 +1,72 @@+-- This file has been generated from package.yaml by hpack version 0.17.0.+--+-- see: https://github.com/sol/hpack++name:                datarobot+version:             0.1.0+synopsis:            Client for DataRobot API+description:         Client for DataRobot API+homepage:            https://github.com/orbital/datarobot#readme+bug-reports:         https://github.com/orbital/datarobot/issues+license:             BSD3+license-file:        LICENSE+author:              Sean Hess+maintainer:          seanhess@gmail.com+copyright:           Orbital Labs+category:            Web+build-type:          Simple+cabal-version:       >= 1.10++source-repository head+  type: git+  location: https://github.com/orbital/datarobot++library+  hs-source-dirs:+      src+  exposed-modules:+      DataRobot+      DataRobot.API+      DataRobot.Features+      DataRobot.Predict+      DataRobot.PredictResponse+      DataRobot.Types+  build-depends:+      base >=4.7 && <5+    , aeson+    , bytestring+    , exceptions+    , microlens+    , network-uri+    , safe+    , scientific+    , string-conversions+    , text+    , unordered-containers+    , vector+    , wreq+  default-language: Haskell2010++test-suite test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , aeson+    , bytestring+    , exceptions+    , microlens+    , network-uri+    , safe+    , scientific+    , string-conversions+    , text+    , unordered-containers+    , vector+    , wreq+    , base+    , datarobot+  default-language: Haskell2010
+ src/DataRobot.hs view
@@ -0,0 +1,42 @@+module DataRobot+  (+  -- * Types+    Credentials(..)+  , defaultBaseURL+  , defaultBaseURLPredict+  , ProjectID(..)+  , ModelID(..)++  -- * Features API+  , features+  , Features(..)++  -- * Predict API+  , predict+  , Fields+  , PredictError(..)+  , PredictResult(..)+  , responseResult+  , classProbability++++  ) where++import DataRobot.Features+import DataRobot.Predict+import DataRobot.PredictResponse+import DataRobot.Types+import Network.URI (URI, parseURI)+++defaultBaseURL :: URI+defaultBaseURL =+    let (Just u) = parseURI "https://app.datarobot.com/api/v2/"+    in u+++defaultBaseURLPredict :: URI+defaultBaseURLPredict =+    let (Just u) = parseURI "https://simplefinance.orm.datarobot.com/api/v1/"+    in u
+ src/DataRobot/API.hs view
@@ -0,0 +1,29 @@+module DataRobot.API where++import Lens.Micro ((^.))+import Data.ByteString.Lazy (ByteString)+import Data.Aeson (FromJSON, eitherDecode)+import Data.List (intercalate)+import Data.Typeable (Typeable)+import Data.Monoid ((<>))+import Control.Monad.Catch (MonadThrow, Exception, throwM)+import Network.URI (URI(..), pathSegments, uriToString)+import Network.Wreq (Response, responseBody)++data JSONError = JSONError String ByteString+               deriving (Typeable, Show)+instance Exception JSONError+++parseResponse :: (FromJSON a, MonadThrow m) => Response ByteString -> m a+parseResponse r =+  case eitherDecode $ r ^. responseBody of+    Left err -> throwM $ JSONError err (r ^. responseBody)+    Right p -> pure p+++endpoint :: URI -> [String] -> String+endpoint base ps =+    let ps' = "" : pathSegments base <> ps+        u = base { uriPath = intercalate "/" ps' }+    in uriToString id u ""
+ src/DataRobot/Features.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+module DataRobot.Features where++import Lens.Micro ((.~))+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Catch (MonadThrow)+import Data.Monoid ((<>))+import Data.ByteString (ByteString)+import Data.Function ((&))+import Data.String.Conversions (cs)++import DataRobot.Types+import DataRobot.API (parseResponse, endpoint)++import Network.URI (URI(..))+import qualified Network.Wreq as Wreq+import Network.Wreq (defaults, header, checkResponse)+import Network.Wreq.Types (ResponseChecker)++++-- GET https://app.datarobot.com/api/v2/projects/5988c39bc808917519a2acbb/models/5988d164c8089128924bd6cf/features+features :: (MonadIO m, MonadThrow m) => Credentials -> ProjectID -> ModelID -> m Features+features c pid mid = do+    let opts = httpOptions c+        url = featuresEndpoint (baseURL c) pid mid+    r <- liftIO $ Wreq.getWith opts url+    f <- parseResponse r+    pure f+++featuresEndpoint :: URI -> ProjectID -> ModelID -> String+featuresEndpoint base (ProjectID pid) (ModelID mid) =+    endpoint base ["projects", cs pid, "models", cs mid, "features"]+++++httpOptions :: Credentials -> Wreq.Options+httpOptions c =+    defaults+      & header "Authorization" .~ [authorization]+      & header "datarobot-key" .~ [apiKey c]+      & checkResponse .~ Just ignoreStatus+  where+    ignoreStatus :: ResponseChecker+    ignoreStatus _ _ = pure ()++    authorization :: ByteString+    authorization = "Token " <> (apiToken c)
+ src/DataRobot/Predict.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+module DataRobot.Predict+  ( predict+  , predictURI+  , Credentials(..)+  , ProjectID(..)+  , ModelID(..)+  ) where++import Lens.Micro ((?~), (.~))+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Catch (MonadThrow)+import Data.Aeson (Value(..))+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.PredictResponse++import Network.URI (URI(..))+import qualified Network.Wreq as Wreq+import Network.Wreq (defaults, Auth, basicAuth, auth, header, checkResponse)+import Network.Wreq.Types (ResponseChecker)+++-- https://app.datarobot.com/docs/users-guide/basics/predictions/prediction-api.html+predict :: (MonadIO m, MonadThrow m) => Credentials -> ProjectID -> ModelID -> Fields -> m (Either PredictError PredictResult)+predict c pid mid o = do+  let opts = httpOptions c+      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+++httpOptions :: Credentials -> Wreq.Options+httpOptions c =+    defaults+      & auth ?~ (authorization c)+      & header "datarobot-key" .~ [apiKey c]+      & checkResponse .~ Just ignoreStatus+  where+    ignoreStatus :: ResponseChecker+    ignoreStatus _ _ = pure ()++++predictURI :: URI -> ProjectID -> ModelID -> String+predictURI base (ProjectID pid) (ModelID mid) =+    endpoint base [cs pid, cs mid, "predict"]++++authorization :: Credentials -> Auth+authorization c =+  basicAuth (username c) (apiToken c)++
+ src/DataRobot/PredictResponse.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DuplicateRecordFields #-}+module DataRobot.PredictResponse+  ( PredictError(..)+  , PredictResult(..)+  , responseResult+  , classProbability+  ) 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.Text (Text)+import Data.Typeable (Typeable)+import GHC.Generics (Generic)+import Safe (headMay)+++data PredictError+    = APIError Code Text+    | MissingPrediction+    deriving (Typeable, Show)++instance Exception PredictError+++newtype PredictResponse = PredictResponse (Either PredictFailure PredictSuccess )+    deriving (Show, Eq)++instance FromJSON PredictResponse where+    parseJSON v = PredictResponse <$>+      ((Right <$> parseJSON v) <|> (Left <$> parseJSON v))++type Code = Int++data PredictFailure = PredictFailure+  { code :: Code+  , status :: Text+  } deriving (Show, Eq, Generic)++instance FromJSON PredictFailure++data PredictSuccess = PredictSuccess+  { predictions :: [Prediction]+  , execution_time :: Float+  , model_id :: Text+  -- , task :: Text+  } deriving (Show, Eq, Generic)++instance FromJSON PredictSuccess++data Prediction = Prediction+  { prediction :: Value+  , class_probabilities :: Maybe (HashMap Text Float)+  } deriving (Show, Eq, Generic)++instance FromJSON Prediction++++-- | Result from the prediction++data PredictResult = PredictResult+  { prediction :: Value+  , predictionTimeMs :: Float+  , modelId :: Text+  , classProbabilities :: Maybe (HashMap Text Float)+  } 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)+++classProbability :: Text -> PredictResult -> Maybe Float+classProbability c r = do+    cps <- classProbabilities r+    HM.lookup c cps+
+ src/DataRobot/Types.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveGeneric #-}+module DataRobot.Types where+++import Data.ByteString (ByteString)+import Data.Aeson (ToJSON(..), FromJSON(..), Value)+import Data.Text (Text)+import GHC.Generics (Generic)+import Network.URI (URI(..))++data Credentials = Credentials+    { apiToken :: ByteString+    , apiKey :: ByteString+    , username :: ByteString+    , baseURLPredict :: URI+    , baseURL :: URI+    } deriving (Show, Eq)+++newtype ProjectID = ProjectID Text+  deriving (Show, Eq, ToJSON, FromJSON)+++newtype ModelID = ModelID Text+  deriving (Show, Eq, ToJSON, FromJSON)+++data Features = Features+    { featureNames :: [Text]+    } deriving (Show, Eq, Generic)+instance ToJSON Features+instance FromJSON Features+++type Fields = [(Text, Value)]+
+ test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"