packages feed

hid-examples-0.4: suntimes/SunTimes.hs

{-# LANGUAGE RecordWildCards, DeriveGeneric #-}

module SunTimes (getSunTimes) where

import Data.Aeson
import Network.HTTP.Req
import Data.Default
import Control.Exception.Safe
import Control.Monad.Reader
import Control.Monad.IO.Class
import qualified Data.Text as T
import Data.Time
import GHC.Generics

import Types
import STExcept
import App

newtype SunTimesWrapper dt = SunTimesWrapper {results :: SunTimes dt}
  deriving (Show, Generic)

instance FromJSON dt => FromJSON (SunTimesWrapper dt)

getSunTimesUTC :: GeoCoords -> When -> MyApp (SunTimes UTCTime)
getSunTimesUTC GeoCoords {..} w = handle rethrowReqException $ liftIO $ runReq def $ do
    r <- req GET ep NoReqBody jsonResponse reqParams
    pure (results $ responseBody r)
  where      
    ep = https "api.sunrise-sunset.org" /: "json"
    reqParams =
      mconcat $ [ "lat" =: lat
                , "lng" =: lon
                , "formatted" =: (0 :: Int)
                ] ++ whenToOptions w
    whenToOptions Now = []
    whenToOptions (On day) = ["date" =: formatTime defaultTimeLocale "%Y-%m-%d" day]

getSunTimes :: GeoCoords -> When -> MyApp (SunTimes ZonedTime)
getSunTimes gc@GeoCoords {..} d = do
    SunTimes {..} <- getSunTimesUTC gc d `catch` noTimeHandler
    ltz <- lookupTimeZone gc sunrise `catchAny` (const $ pure utc)
    return $ SunTimes (utcToZonedTime ltz sunrise)
                      (utcToZonedTime ltz sunset)
  where
    noTimeHandler :: MonadThrow m => SunInfoException -> m a
    noTimeHandler (ServiceAPIError _) = throw (UnknownTime gc)
    noTimeHandler e = throw e

data TimeZoneInfo = TimeZoneInfo { gmtOffset :: Int
                                 , abbreviation :: String
                                 , dst :: String  
                                 }  
  deriving (Show, Generic)

instance FromJSON TimeZoneInfo

lookupTimeZone :: GeoCoords -> UTCTime -> MyApp TimeZone
lookupTimeZone gc@GeoCoords {..} t = do
    key <- asks timeZoneDBkey
    let
      ep = http "api.timezonedb.com" /: "v2.1" /: "get-time-zone"
      reqParams = mconcat [ "key" =: key
                          , "lat" =: lat
                          , "lng" =: lon
                          , "time" =: formatTime defaultTimeLocale "%s" t
                          , "format" =: ("json" :: T.Text)
                          , "fields" =: ("gmtOffset,abbreviation,dst" :: T.Text)
                          , "by" =: ("position" :: T.Text)
                          ]
    r <- liftIO $ runReq def $ req GET ep NoReqBody jsonResponse reqParams
    pure (timeZoneInfo2TimeZone $ responseBody r)
  where
    secondsToTimeZone s = minutesToTimeZone (s `div` 60)
    timeZoneInfo2TimeZone TimeZoneInfo {..} =
      (secondsToTimeZone gmtOffset) { timeZoneName = abbreviation
                                    , timeZoneSummerOnly = dst == "1"}