packages feed

openweathermap 0.1.0 → 0.2.0

raw patch · 12 files changed

+345/−144 lines, 12 filesdep +http-api-datadep +servant-client-coredep +timePVP ok

version bump matches the API change (PVP)

Dependencies added: http-api-data, servant-client-core, time

API changes (from Hackage documentation)

- Web.OpenWeatherMap.API: weatherByCoord :: Maybe Double -> Maybe Double -> Maybe String -> ClientM CurrentWeather
- Web.OpenWeatherMap.API: weatherByName :: Maybe String -> Maybe String -> ClientM CurrentWeather
- Web.OpenWeatherMap.Client: Coord :: Double -> Double -> Location
- Web.OpenWeatherMap.Client: Name :: String -> Location
- Web.OpenWeatherMap.Client: data Location
+ Web.OpenWeatherMap.API: currentWeather :: String -> Location -> ClientM CurrentWeather
+ Web.OpenWeatherMap.API: forecastWeather :: String -> Location -> ClientM ForecastWeather
+ Web.OpenWeatherMap.Client: getForecast :: String -> Location -> IO (Either ClientError ForecastWeather)
+ Web.OpenWeatherMap.Types.City: City :: String -> Maybe String -> Coord -> Int -> Int -> Int -> City
+ Web.OpenWeatherMap.Types.City: [coord] :: City -> Coord
+ Web.OpenWeatherMap.Types.City: [country] :: City -> Maybe String
+ Web.OpenWeatherMap.Types.City: [name] :: City -> String
+ Web.OpenWeatherMap.Types.City: [sunrise] :: City -> Int
+ Web.OpenWeatherMap.Types.City: [sunset] :: City -> Int
+ Web.OpenWeatherMap.Types.City: [timezone] :: City -> Int
+ Web.OpenWeatherMap.Types.City: data City
+ Web.OpenWeatherMap.Types.City: instance Data.Aeson.Types.FromJSON.FromJSON Web.OpenWeatherMap.Types.City.City
+ Web.OpenWeatherMap.Types.City: instance GHC.Generics.Generic Web.OpenWeatherMap.Types.City.City
+ Web.OpenWeatherMap.Types.City: instance GHC.Show.Show Web.OpenWeatherMap.Types.City.City
+ Web.OpenWeatherMap.Types.Forecast: Forecast :: Int -> Clouds -> Main -> [Weather] -> Wind -> Forecast
+ Web.OpenWeatherMap.Types.Forecast: [clouds] :: Forecast -> Clouds
+ Web.OpenWeatherMap.Types.Forecast: [dt] :: Forecast -> Int
+ Web.OpenWeatherMap.Types.Forecast: [main] :: Forecast -> Main
+ Web.OpenWeatherMap.Types.Forecast: [weather] :: Forecast -> [Weather]
+ Web.OpenWeatherMap.Types.Forecast: [wind] :: Forecast -> Wind
+ Web.OpenWeatherMap.Types.Forecast: data Forecast
+ Web.OpenWeatherMap.Types.Forecast: instance Data.Aeson.Types.FromJSON.FromJSON Web.OpenWeatherMap.Types.Forecast.Forecast
+ Web.OpenWeatherMap.Types.Forecast: instance GHC.Generics.Generic Web.OpenWeatherMap.Types.Forecast.Forecast
+ Web.OpenWeatherMap.Types.Forecast: instance GHC.Show.Show Web.OpenWeatherMap.Types.Forecast.Forecast
+ Web.OpenWeatherMap.Types.ForecastWeather: ForecastWeather :: [Forecast] -> City -> ForecastWeather
+ Web.OpenWeatherMap.Types.ForecastWeather: [city] :: ForecastWeather -> City
+ Web.OpenWeatherMap.Types.ForecastWeather: [list] :: ForecastWeather -> [Forecast]
+ Web.OpenWeatherMap.Types.ForecastWeather: data ForecastWeather
+ Web.OpenWeatherMap.Types.ForecastWeather: instance Data.Aeson.Types.FromJSON.FromJSON Web.OpenWeatherMap.Types.ForecastWeather.ForecastWeather
+ Web.OpenWeatherMap.Types.ForecastWeather: instance GHC.Generics.Generic Web.OpenWeatherMap.Types.ForecastWeather.ForecastWeather
+ Web.OpenWeatherMap.Types.ForecastWeather: instance GHC.Show.Show Web.OpenWeatherMap.Types.ForecastWeather.ForecastWeather
+ Web.OpenWeatherMap.Types.Location: Coord :: Double -> Double -> Location
+ Web.OpenWeatherMap.Types.Location: Name :: String -> Location
+ Web.OpenWeatherMap.Types.Location: data Location
+ Web.OpenWeatherMap.Types.Location: instance Servant.Client.Core.HasClient.HasClient m api => Servant.Client.Core.HasClient.HasClient m (Web.OpenWeatherMap.Types.Location.Location Servant.API.Sub.:> api)
- Web.OpenWeatherMap.Types.Coord: Coord :: Double -> Double -> Coord
+ Web.OpenWeatherMap.Types.Coord: Coord :: Maybe Double -> Maybe Double -> Coord
- Web.OpenWeatherMap.Types.Coord: [lat] :: Coord -> Double
+ Web.OpenWeatherMap.Types.Coord: [lat] :: Coord -> Maybe Double
- Web.OpenWeatherMap.Types.Coord: [lon] :: Coord -> Double
+ Web.OpenWeatherMap.Types.Coord: [lon] :: Coord -> Maybe Double

Files

ChangeLog.md view
@@ -1,3 +1,10 @@+0.2.0+=====++  * (* BREAKING *) Made Location a part of the API to+    reduce code duplication.++ 0.1.0 ===== 
README.md view
@@ -22,16 +22,19 @@ -----  ```-Usage: openweathermap ([-K|--api-key-file APIKEYFILE] | [-k|--api-key APIKEY])-                      ((-c|--city CITY) | --lat NUM --lon NUM) [-d|--debug]+Usage: openweathermap [(-K|--api-key-file APIKEYFILE) | (-k|--api-key APIKEY)]+                      ((-q|--query STRING) | --lat NUM --lon NUM)+                      [(-n|--current) | (-f|--forecast)] [-d|--debug]  Available options:   -K,--api-key-file APIKEYFILE                            Read API key from this file   -k,--api-key APIKEY      API key-  -c,--city CITY           City name+  -q,--query STRING        City name, e. g. Santiago or Santiago,CU   --lat NUM                Latitude in decimal degrees   --lon NUM                Longitude in decimal degrees+  -n,--current             current weather (default)+  -f,--forecast            forecast weather   -d,--debug               Enable debug   -h,--help                Show this help text @@ -46,11 +49,36 @@ --------  ```-$ openweathermap -c norilsk+$ openweathermap -q norilsk Norilsk,RU (69.35°, 88.2°): Clouds,  H 100 %,  P 753 mmHg,  T +4 °C,  ↓ 1 m/s  $ openweathermap --lat 55.7522200 --lon 37.6155600 Moscow,RU (55.75°, 37.62°): Clear,  H 45 %,  P 762 mmHg,  T +18..+21 °C,  → 4 m/s++$ openweathermap -q kaliningrad -f+Kaliningrad,RU (54.7065°, 20.511°)+2020-04-19 17:00:00 +0200: Clear, H 66 %, P 767 mmHg, T +7 °C, ↓ 7 m/s+2020-04-19 20:00:00 +0200: Clear, H 79 %, P 768 mmHg, T +4 °C, ↓ 5 m/s+2020-04-19 23:00:00 +0200: Clear, H 84 %, P 769 mmHg, T +3 °C, ↓ 4 m/s+2020-04-20 02:00:00 +0200: Clear, H 85 %, P 770 mmHg, T +3 °C, ↓ 3 m/s+2020-04-20 05:00:00 +0200: Clear, H 84 %, P 770 mmHg, T +2 °C, ↓ 3 m/s+2020-04-20 08:00:00 +0200: Clear, H 76 %, P 770 mmHg, T +5 °C, ↓ 4 m/s+2020-04-20 11:00:00 +0200: Clear, H 65 %, P 771 mmHg, T +8 °C, ↓ 4 m/s+2020-04-20 14:00:00 +0200: Clear, H 62 %, P 771 mmHg, T +9 °C, ↓ 4 m/s+...++$ openweathermap --lat -12.0432 --lon -77.0282 -f+Lima,PE (-12.0432°, -77.0282°)+2020-04-19 10:00:00 -0500: Clear, H 70 %, P 764 mmHg, T +22..+24 °C, ↗ 3 m/s+2020-04-19 13:00:00 -0500: Clear, H 64 %, P 762 mmHg, T +23..+25 °C, ↗ 4 m/s+2020-04-19 16:00:00 -0500: Clouds, H 66 %, P 761 mmHg, T +23..+24 °C, ↑ 4 m/s+2020-04-19 19:00:00 -0500: Clouds, H 72 %, P 763 mmHg, T +22 °C, ↑ 4 m/s+2020-04-19 22:00:00 -0500: Clouds, H 72 %, P 764 mmHg, T +22 °C, ↑ 3 m/s+2020-04-20 01:00:00 -0500: Clouds, H 76 %, P 763 mmHg, T +21 °C, ↑ 2 m/s+2020-04-20 04:00:00 -0500: Clear, H 80 %, P 763 mmHg, T +20 °C, ↑ 3 m/s+2020-04-20 07:00:00 -0500: Clouds, H 79 %, P 764 mmHg, T +20 °C, ↑ 3 m/s+2020-04-20 10:00:00 -0500: Clouds, H 72 %, P 764 mmHg, T +21 °C, ↗ 3 m/s+... ```  
cmd/Main.hs view
@@ -5,7 +5,6 @@   ) where  import Control.Monad (when)-import Data.List (intercalate) import Data.Semigroup ((<>)) import Data.Version (showVersion) import System.Exit (die)@@ -17,6 +16,7 @@   , (<|>)   , auto   , execParser+  , flag'   , fullDesc   , header   , help@@ -33,26 +33,25 @@ import System.Directory (createDirectoryIfMissing) import System.Environment.XDG.BaseDir (getUserConfigDir, getUserConfigFile) -import Paths_openweathermap (version) -- from cabal import qualified Web.OpenWeatherMap.Client as Client-import qualified Web.OpenWeatherMap.Types.Coord as Coord-import qualified Web.OpenWeatherMap.Types.CurrentWeather as CurrentWeather-import qualified Web.OpenWeatherMap.Types.Main as Main-import qualified Web.OpenWeatherMap.Types.Sys as Sys-import qualified Web.OpenWeatherMap.Types.Weather as Weather-import qualified Web.OpenWeatherMap.Types.Wind as Wind+import Web.OpenWeatherMap.Types.Location (Location(..)) +import Paths_openweathermap (version) -- from cabal+import Print (printCurrectWeather, printForecastWeather)+ appName :: String appName = "openweathermap" -parseLocation :: Parser Client.Location+parseLocation :: Parser Location parseLocation = byName <|> byCoord   where     byName =-      Client.Name <$>-      strOption (long "city" <> short 'c' <> metavar "CITY" <> help "City name")+      Name <$>+      strOption+        (long "query" <> short 'q' <> metavar "STRING" <>+         help "City name, e. g. Santiago or Santiago,CU")     byCoord =-      Client.Coord <$>+      Coord <$>       option         auto         (long "lat" <> metavar "NUM" <> help "Latitude in decimal degrees") <*>@@ -77,15 +76,29 @@       strOption         (long "api-key" <> short 'k' <> metavar "APIKEY" <> help "API key") -data Config = Config-  { apikey :: Maybe ApiKey-  , location :: Client.Location-  , debug :: Bool-  }+data Weather+  = Current+  | Forecast +parseWeather :: Parser Weather+parseWeather =+  flag'+    Current+    (long "current" <> short 'n' <> help "current weather (default)") <|>+  flag' Forecast (long "forecast" <> short 'f' <> help "forecast weather") <|>+  pure Current++data Config =+  Config+    { apikey :: Maybe ApiKey+    , location :: Location+    , weather :: Weather+    , debug :: Bool+    }+ parseConfig :: Parser Config parseConfig =-  Config <$> optional parseApiKey <*> parseLocation <*>+  Config <$> optional parseApiKey <*> parseLocation <*> parseWeather <*>   switch (long "debug" <> short 'd' <> help "Enable debug")  getApiKey :: Maybe ApiKey -> IO String@@ -95,93 +108,22 @@   createDirectoryIfMissing True =<< getUserConfigDir appName   getUserConfigFile appName "key" >>= getApiKey . Just . ApiKeyFile -showLocation :: CurrentWeather.CurrentWeather -> String-showLocation w = city ++ maybe "" ("," ++) country ++ " " ++ coords-  where-    name = CurrentWeather.name w-    coord = CurrentWeather.coord w-    country = Sys.country . CurrentWeather.sys $ w-    city =-      if name /= ""-        then name-        else "<unknown>"-    coords =-      "(" ++ show (Coord.lat coord) ++ "°, " ++ show (Coord.lon coord) ++ "°)"--showWeather :: [Weather.Weather] -> String-showWeather w = intercalate "," $ Weather.main <$> w--showHumidity :: Main.Main -> String-showHumidity m = "H " ++ show hm ++ " %"-  where-    hm :: Int-    hm = round . Main.humidity $ m---- https://en.wikipedia.org/wiki/Millimeter_of_mercury-showPressure :: Main.Main -> String-showPressure m = "P " ++ show p ++ " mmHg"-  where-    hPa2mmHg hpa = hpa * 0.750061561303-    p :: Int-    p = round . hPa2mmHg . Main.pressure $ m---- https://stackoverflow.com/q/7490660/933161-showWind :: Wind.Wind -> String-showWind w = dir ++ " " ++ show speed ++ " m/s"-  where-    speed :: Int-    speed = round . Wind.speed $ w-    deg = Wind.deg w-    --     [ "N", "NE", "E", "SE", "S", "SW", "W", "NW" ]-    dirs = ["↓", "↙", "←", "↖", "↑", "↗", "→", "↘"]-    l = length dirs-    sector = round $ (deg * fromIntegral l) / 360.0-    dir = dirs !! (sector `rem` l)--showTemp :: Main.Main -> String-showTemp m = "T " ++ temp ++ " °C"-  where-    k2c k = k - 273.15 -- Kelvin to Celsius-    tmax :: Int-    tmin :: Int-    tmax = round . k2c . Main.temp_max $ m-    tmin = round . k2c . Main.temp_min $ m-    show' t =-      if t > 0-        then "+" ++ show t-        else show t-    temp =-      if tmax /= tmin-        then show' tmin ++ ".." ++ show' tmax-        else show' tmin--printWeather :: CurrentWeather.CurrentWeather -> IO ()-printWeather w = putStrLn out-  where-    weather = showWeather $ CurrentWeather.weather w-    place = showLocation w-    mainw = CurrentWeather.main w-    wind = CurrentWeather.wind w-    out =-      place ++-      ": " ++-      intercalate-        ",  "-        [ weather-        , showHumidity mainw-        , showPressure mainw-        , showTemp mainw-        , showWind wind-        ]- run :: Config -> IO () run cfg = do   appid <- getApiKey . apikey $ cfg-  Client.getWeather appid (location cfg) >>= \case-    Left err -> die $ show err-    Right weather -> do-      when (debug cfg) $ hPrint stderr weather-      printWeather weather+  case weather cfg of+    Current ->+      Client.getWeather appid (location cfg) >>= \case+        Left err -> die $ show err+        Right cw -> do+          when (debug cfg) $ hPrint stderr cw+          printCurrectWeather cw+    Forecast ->+      Client.getForecast appid (location cfg) >>= \case+        Left err -> die $ show err+        Right fw -> do+          when (debug cfg) $ hPrint stderr fw+          printForecastWeather fw  main :: IO () main = run =<< execParser opts
+ cmd/Print.hs view
@@ -0,0 +1,125 @@+module Print+  ( printCurrectWeather+  , printForecastWeather+  ) where++import Data.List (intercalate)+import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import Data.Time.LocalTime (TimeZone, minutesToTimeZone, utcToZonedTime)++import qualified Web.OpenWeatherMap.Types.City as City+import qualified Web.OpenWeatherMap.Types.Coord as Coord+import qualified Web.OpenWeatherMap.Types.CurrentWeather as CW+import qualified Web.OpenWeatherMap.Types.Forecast as FC+import qualified Web.OpenWeatherMap.Types.ForecastWeather as FW+import qualified Web.OpenWeatherMap.Types.Main as Main+import qualified Web.OpenWeatherMap.Types.Sys as Sys+import qualified Web.OpenWeatherMap.Types.Weather as Weather+import qualified Web.OpenWeatherMap.Types.Wind as Wind++printCurrectWeather :: CW.CurrentWeather -> IO ()+printCurrectWeather cw =+  putStrLn+    (place +++     ": " +++     intercalate+       ",  "+       [ w+       , showHumidity mainw+       , showPressure mainw+       , showTemp mainw+       , showWind wind+       ])+  where+    w = showWeather $ CW.weather cw+    place = showLocation (CW.name cw) (Sys.country . CW.sys $ cw) (CW.coord cw)+    mainw = CW.main cw+    wind = CW.wind cw++printForecastWeather :: FW.ForecastWeather -> IO ()+printForecastWeather fw = do+  let c = FW.city fw+      tz = minutesToTimeZone (City.timezone c `div` 60)+      place = showLocation (City.name c) (City.country c) (City.coord c)+  putStrLn place+  mapM_ putStrLn (showForecast tz <$> FW.list fw)++showForecast :: TimeZone -> FC.Forecast -> String+showForecast tz fc =+  localtime +++  ": " +++  intercalate+    ", "+    [ showWeather (FC.weather fc)+    , showHumidity mainw+    , showPressure mainw+    , showTemp mainw+    , showWind (FC.wind fc)+    ]+  where+    localtime =+      show . utcToZonedTime tz . posixSecondsToUTCTime . fromIntegral $ FC.dt fc+    mainw = FC.main fc++showLocation :: String -> Maybe String -> Coord.Coord -> String+showLocation name country coord =+  name' ++ maybe "" ("," ++) country ++ " " ++ coords+  where+    coords = showCoord coord+    name' =+      if name /= ""+        then name+        else "<unknown>"++showCoord :: Coord.Coord -> String+showCoord coord =+  "(" +++  maybe "?" show (Coord.lat coord) +++  "°, " ++ maybe "?" show (Coord.lon coord) ++ "°)"++showWeather :: [Weather.Weather] -> String+showWeather w = intercalate "," $ Weather.main <$> w++showHumidity :: Main.Main -> String+showHumidity m = "H " ++ show hm ++ " %"+  where+    hm :: Int+    hm = round . Main.humidity $ m++-- https://en.wikipedia.org/wiki/Millimeter_of_mercury+showPressure :: Main.Main -> String+showPressure m = "P " ++ show p ++ " mmHg"+  where+    hPa2mmHg hpa = hpa * 0.750061561303+    p :: Int+    p = round . hPa2mmHg . Main.pressure $ m++-- https://stackoverflow.com/q/7490660/933161+showWind :: Wind.Wind -> String+showWind w = dir ++ " " ++ show speed ++ " m/s"+  where+    speed :: Int+    speed = round . Wind.speed $ w+    deg = Wind.deg w+    --     [ "N", "NE", "E", "SE", "S", "SW", "W", "NW" ]+    dirs = ["↓", "↙", "←", "↖", "↑", "↗", "→", "↘"]+    l = length dirs+    sector = round $ (deg * fromIntegral l) / 360.0+    dir = dirs !! (sector `rem` l)++showTemp :: Main.Main -> String+showTemp m = "T " ++ temp ++ " °C"+  where+    k2c k = k - 273.15 -- Kelvin to Celsius+    tmax :: Int+    tmin :: Int+    tmax = round . k2c . Main.temp_max $ m+    tmin = round . k2c . Main.temp_min $ m+    show' t =+      if t > 0+        then "+" ++ show t+        else show t+    temp =+      if tmax /= tmin+        then show' tmin ++ ".." ++ show' tmax+        else show' tmin
lib/Web/OpenWeatherMap/API.hs view
@@ -6,33 +6,29 @@ {-# LANGUAGE TypeOperators #-}  module Web.OpenWeatherMap.API-  ( weatherByName-  , weatherByCoord+  ( currentWeather+  , forecastWeather   ) where  import Data.Proxy (Proxy(..)) -import Servant.API ((:<|>)(..), (:>), Get, JSON, QueryParam)+import Servant.API ((:<|>)(..), (:>), Get, JSON, QueryParam', Required, Strict) import Servant.Client (ClientM, client)  import Web.OpenWeatherMap.Types.CurrentWeather (CurrentWeather)+import Web.OpenWeatherMap.Types.ForecastWeather (ForecastWeather)+import Web.OpenWeatherMap.Types.Location (Location) -type GetCurrentWeather = AppId :> Get '[ JSON] CurrentWeather+type QueryParam = QueryParam' '[ Required, Strict] -type AppId = QueryParam "appid" String+type Current+   = "weather" :> QueryParam "appid" String :> Location :> Get '[ JSON] CurrentWeather -type API-   = "weather" :> QueryParam "q" String :> GetCurrentWeather :<|> "weather" :> QueryParam "lat" Double :> QueryParam "lon" Double :> GetCurrentWeather+type Forecast+   = "forecast" :> QueryParam "appid" String :> Location :> Get '[ JSON] ForecastWeather --- | Request current weather in the city.-weatherByName ::-     Maybe String -- ^ City name, e. g. \"Moscow\" or \"Moscow,ru\".-  -> Maybe String -- ^ API key.-  -> ClientM CurrentWeather--- | Request current weather at the geographic coordinates (in decimal degrees).-weatherByCoord ::-     Maybe Double -- ^ Latitude, e. g. 55.7522200 for Moscow.-  -> Maybe Double -- ^ Longitude, e. g. 37.6155600 for Moscow.-  -> Maybe String -- ^ API key.-  -> ClientM CurrentWeather-weatherByName :<|> weatherByCoord = client (Proxy :: Proxy API)+type API = Current :<|> Forecast++forecastWeather :: String -> Location -> ClientM ForecastWeather+currentWeather :: String -> Location -> ClientM CurrentWeather+(currentWeather :<|> forecastWeather) = client (Proxy :: Proxy API)
lib/Web/OpenWeatherMap/Client.hs view
@@ -2,8 +2,8 @@ High-level client functions perfoming requests to OpenWeatherMap API. -} module Web.OpenWeatherMap.Client-  ( Location(..)-  , getWeather+  ( getWeather+  , getForecast   ) where  import Network.HTTP.Client (defaultManagerSettings, newManager)@@ -11,7 +11,6 @@   ( BaseUrl(BaseUrl)   , ClientEnv   , ClientError-  , ClientM   , Scheme(Http)   , mkClientEnv   , runClientM@@ -19,12 +18,8 @@  import qualified Web.OpenWeatherMap.API as API import Web.OpenWeatherMap.Types.CurrentWeather (CurrentWeather)---- | Various way to specify location.-data Location-  = Name String -- ^ City name.-  | Coord Double-          Double -- ^ Geographic coordinates: latitude and longitude.+import Web.OpenWeatherMap.Types.ForecastWeather (ForecastWeather)+import Web.OpenWeatherMap.Types.Location (Location)  -- | Make a request to OpenWeatherMap API --   and return current weather in given location.@@ -32,14 +27,16 @@      String -- ^ API key.   -> Location   -> IO (Either ClientError CurrentWeather)-getWeather appid loc = defaultEnv >>= runClientM (api loc appid)+getWeather appid loc = defaultEnv >>= runClientM (API.currentWeather appid loc) -api ::-     Location-  -> String -- ^ API key.-  -> ClientM CurrentWeather-api (Name city) = API.weatherByName (Just city) . Just-api (Coord lat lon) = API.weatherByCoord (Just lat) (Just lon) . Just+-- | Make a request to OpenWeatherMap API+--   and return forecast weather in given location.+getForecast ::+     String -- ^ API key.+  -> Location+  -> IO (Either ClientError ForecastWeather)+getForecast appid loc =+  defaultEnv >>= runClientM (API.forecastWeather appid loc)  defaultEnv :: IO ClientEnv defaultEnv = do
+ lib/Web/OpenWeatherMap/Types/City.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.OpenWeatherMap.Types.City+  ( City(..)+  ) where++import GHC.Generics (Generic)++import Data.Aeson (FromJSON)++import Web.OpenWeatherMap.Types.Coord (Coord)++data City = City+  { name :: String+  , country :: Maybe String+  , coord :: Coord+  , timezone :: Int+  , sunset :: Int+  , sunrise :: Int+  } deriving (Show, Generic, FromJSON)
lib/Web/OpenWeatherMap/Types/Coord.hs view
@@ -10,6 +10,6 @@ import Data.Aeson (FromJSON)  data Coord = Coord-  { lon :: Double-  , lat :: Double+  { lon :: Maybe Double+  , lat :: Maybe Double   } deriving (Show, Generic, FromJSON)
+ lib/Web/OpenWeatherMap/Types/Forecast.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.OpenWeatherMap.Types.Forecast+  ( Forecast(..)+  ) where++import GHC.Generics (Generic)++import Data.Aeson (FromJSON)++import Web.OpenWeatherMap.Types.Clouds (Clouds)+import Web.OpenWeatherMap.Types.Main (Main)+import Web.OpenWeatherMap.Types.Weather (Weather)+import Web.OpenWeatherMap.Types.Wind (Wind)++data Forecast = Forecast+  { dt :: Int+  , clouds :: Clouds+  , main :: Main+  , weather :: [Weather]+  , wind :: Wind+  } deriving (Show, Generic, FromJSON)
+ lib/Web/OpenWeatherMap/Types/ForecastWeather.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.OpenWeatherMap.Types.ForecastWeather+  ( ForecastWeather(..)+  ) where++import GHC.Generics (Generic)++import Data.Aeson (FromJSON)++import Web.OpenWeatherMap.Types.City (City)+import Web.OpenWeatherMap.Types.Forecast (Forecast)++-- | Response to requests for forecast weather.+-- Refer to <https://openweathermap.org/forecast5>.+data ForecastWeather = ForecastWeather+  { list :: [Forecast]+  , city :: City+  } deriving (Show, Generic, FromJSON)
+ lib/Web/OpenWeatherMap/Types/Location.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++module Web.OpenWeatherMap.Types.Location+  ( Location(..)+  ) where++import Data.Proxy (Proxy(..))++import Servant.API ((:>))+import Servant.Client (Client, HasClient, clientWithRoute, hoistClientMonad)+import Servant.Client.Core.Request (appendToQueryString)+import Web.HttpApiData (toQueryParam)++-- | Various way to specify location.+data Location+  = Name String -- ^ City name.+  | Coord Double Double -- ^ Geographic coordinates: latitude and longitude.++instance HasClient m api => HasClient m (Location :> api) where+  type Client m (Location :> api) = Location -> Client m api+  clientWithRoute pm Proxy req loc =+    clientWithRoute pm (Proxy :: Proxy api) (addParams loc req)+    where+      addParams (Name q) = appendToQueryString "q" (Just $ toQueryParam q)+      addParams (Coord lat lon) =+        appendToQueryString "lat" (Just $ toQueryParam lat) .+        appendToQueryString "lon" (Just $ toQueryParam lon)+  hoistClientMonad pm _ f cl =+    \a -> hoistClientMonad pm (Proxy :: Proxy api) f (cl a)
openweathermap.cabal view
@@ -1,5 +1,5 @@ name: openweathermap-version: 0.1.0+version: 0.2.0 synopsis: Access data at OpenWeatherMap description: Client library and command-line utility to access   OpenWeatherMap https://openweathermap.org@@ -28,15 +28,21 @@   build-depends:       base >= 4.9 && < 5     , aeson+    , http-api-data     , http-client     , servant     , servant-client >= 0.16+    , servant-client-core   exposed-modules:      Web.OpenWeatherMap.API      Web.OpenWeatherMap.Client+     Web.OpenWeatherMap.Types.City      Web.OpenWeatherMap.Types.Clouds      Web.OpenWeatherMap.Types.Coord      Web.OpenWeatherMap.Types.CurrentWeather+     Web.OpenWeatherMap.Types.Forecast+     Web.OpenWeatherMap.Types.ForecastWeather+     Web.OpenWeatherMap.Types.Location      Web.OpenWeatherMap.Types.Main      Web.OpenWeatherMap.Types.Sys      Web.OpenWeatherMap.Types.Weather@@ -47,12 +53,14 @@   ghc-options: -Wall -static   hs-source-dirs: cmd   main-is: Main.hs+  other-modules: Print   if flag(cmd)     build-depends:         base >= 4.9 && < 5       , directory       , openweathermap       , optparse-applicative >= 0.13.0.0+      , time       , xdg-basedir   else     buildable: False