{-| Module : Web.Yahoo.Finance.YQL.Internal.Types Description : Internal data types and type class instances. Copyright : (c) James M.C. Haver II, 2016 License : BSD3 This is an internal module. Use at your own risk. -} {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeOperators #-} module Web.Yahoo.Finance.YQL.Internal.Types ( Quote(..) , StockSymbol(..) , YQLQuery(..) , YQLResponse(..) ) where import Control.Applicative ((<|>)) import Data.Aeson import Data.Aeson.Types (Parser) import Data.Foldable (fold) import Data.List (intersperse) import Data.Monoid ((<>)) import Data.Text (Text) import Data.Time import qualified Data.Vector as V import GHC.Generics import Web.HttpApiData #if !MIN_VERSION_servant(0, 5, 0) import Servant.Common.Text #endif #if !MIN_VERSION_base(4,8,0) import Control.Applicative #endif -- | Query for yahoo finance api. data YQLQuery = YQLQuery { yqlQuery :: [StockSymbol] } deriving (Eq, Show, Generic) -- | Automate a simple YQL query. -- -- >>> toUrlPiece $ YQLQuery [StockSymbol "GOOG", StockSymbol "YHOO"] -- "select * from yahoo.finance.quotes where symbol in (\"GOOG\",\"YHOO\")" instance ToHttpApiData YQLQuery where toUrlPiece :: YQLQuery -> Text toUrlPiece (YQLQuery {..}) = "select * from yahoo.finance.quotes where symbol in (" <> toUrlPiece yqlQuery <> ")" #if !MIN_VERSION_servant(0, 5, 0) -- | Automate a simple YQL query. -- -- >>> toText $ YQLQuery [StockSymbol "GOOG", StockSymbol "YHOO"] -- "select * from yahoo.finance.quotes where symbol in (\"GOOG\",\"YHOO\")" instance ToText YQLQuery where toText (YQLQuery {..}) = "select * from yahoo.finance.quotes where symbol in (" <> toText yqlQuery <> ")" #endif -- | Use this type to encode 'toUrlPiece' or 'toText' (depending on Servant -- version) for queries. newtype StockSymbol = StockSymbol { unStockSymbol :: Text } deriving (Eq, Generic, Ord, Show) -- | Surround 'StockSymbol' with double quotes. -- -- >>> toUrlPiece (StockSymbol "GOOG") -- "\"GOOG\"" instance ToHttpApiData StockSymbol where toUrlPiece :: StockSymbol -> Text toUrlPiece (StockSymbol {..}) = "\"" <> unStockSymbol <> "\"" #if !MIN_VERSION_servant(0, 5, 0) -- | Surround 'StockSymbol' with double quotes. -- -- >>> toText (StockSymbol "GOOG") -- "\"GOOG\"" instance ToText StockSymbol where toText (StockSymbol {..}) = "\"" <> unStockSymbol <> "\"" #endif -- | Connect separate 'StockSymbol's with a comma. -- -- >>> toUrlPiece ([StockSymbol "GOOG", StockSymbol "YHOO", StockSymbol "GSPC"] :: [StockSymbol]) -- "\"GOOG\",\"YHOO\",\"GSPC\"" instance ToHttpApiData [StockSymbol] where toUrlPiece :: [StockSymbol] -> Text toUrlPiece = fold . intersperse "," . fmap toUrlPiece #if !MIN_VERSION_servant(0, 5, 0) -- | Connect separate 'StockSymbol's with a comma. -- -- >>> toText ([StockSymbol "GOOG", StockSymbol "YHOO", StockSymbol "GSPC"] :: [StockSymbol]) -- "GOOG,YHOO,GSPC" instance ToText [StockSymbol] where toText = fold . intersperse "," . fmap toUrlPiece #endif -- | Response from a YQL query. data YQLResponse = YQLResponse { responseCount :: Int , responseCreated :: UTCTime , responseLang :: Text , responseQuotes :: [Maybe Quote] } deriving (Eq, Read, Show, Generic) instance FromJSON YQLResponse where parseJSON = withObject "YQLResponse" $ \o -> do innerO <- o .: "query" results <- innerO .: "results" innerQuotes <- results .: "quote" -- YQL returns either a single quote as an object or multiple quotes as an -- array of objects. If the queried 'StockSymbol' does not exist then YQL -- returns an object with all fields as null. quotes <- case innerQuotes of (Object _) -> (:[]) <$> (parseJSON innerQuotes <|> pure Nothing) :: Parser [Maybe Quote] (Array a) -> sequence $ (\x -> parseJSON x <|> pure Nothing) <$> (V.toList a) _ -> fail "responseQuotes expects to find an object or array with the key 'quote'" YQLResponse <$> innerO .: "count" <*> innerO .: "created" <*> innerO .: "lang" <*> pure quotes -- | Quote data received from YQL. data Quote = Quote { quoteAverageDailyVolume :: Text , quoteChange :: Text , quoteDaysLow :: Text , quoteDaysHigh :: Text , quoteYearLow :: Text , quoteYearHigh :: Text , quoteMarketCapitalization :: Text , quoteLastTradePriceOnly :: Text , quoteDaysRange :: Text , quoteName :: Text , quoteSymbol :: Text , quoteVolume :: Text , quoteStockExchange :: Text } deriving (Eq, Read, Show, Generic) instance FromJSON Quote where parseJSON = withObject "Quote" $ \o -> Quote <$> o .: "AverageDailyVolume" <*> o .: "Change" <*> o .: "DaysLow" <*> o .: "DaysHigh" <*> o .: "YearLow" <*> o .: "YearHigh" <*> o .: "MarketCapitalization" <*> o .: "LastTradePriceOnly" <*> o .: "DaysRange" <*> o .: "Name" <*> o .: "Symbol" <*> o .: "Volume" <*> o .: "StockExchange"