packages feed

openweathermap 0.2.0 → 0.3.0

raw patch · 16 files changed

+240/−142 lines, 16 filesdep +bytestringdep +http-client-tlsdep +textdep −http-clientdep ~servant-clientPVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring, http-client-tls, text

Dependencies removed: http-client

Dependency ranges changed: servant-client

API changes (from Hackage documentation)

+ Web.OpenWeatherMap.Formulas: absoluteHumidity :: Main -> Maybe Double
+ Web.OpenWeatherMap.Types.CurrentWeather: [timezone] :: CurrentWeather -> Int
- Web.OpenWeatherMap.Types.CurrentWeather: CurrentWeather :: Coord -> [Weather] -> String -> Main -> Wind -> Clouds -> Int -> Sys -> Int -> String -> Int -> CurrentWeather
+ Web.OpenWeatherMap.Types.CurrentWeather: CurrentWeather :: Coord -> [Weather] -> String -> Main -> Wind -> Clouds -> Int -> Sys -> Int -> Int -> String -> Int -> CurrentWeather

Files

ChangeLog.md view
@@ -1,8 +1,22 @@+0.3.0+=====++  * Switch from HTTP to HTTPS API (https://api.openweathermap.org/data/2.5).+  * Command line utility prints absolute humidity and local time.+  * Require Servant >= 0.19.++ 0.2.0 =====    * (* BREAKING *) Made Location a part of the API to     reduce code duplication.++  * (* BREAKING *) Made all query parameters required.++  * Changed -c to -q for the command line utility.++  * Added forecast weather API.   0.1.0
cmd/Print.hs view
@@ -6,7 +6,9 @@ import Data.List (intercalate) import Data.Time.Clock.POSIX (posixSecondsToUTCTime) import Data.Time.LocalTime (TimeZone, minutesToTimeZone, utcToZonedTime)+import Text.Printf (printf) +import Web.OpenWeatherMap.Formulas (absoluteHumidity) import qualified Web.OpenWeatherMap.Types.City as City import qualified Web.OpenWeatherMap.Types.Coord as Coord import qualified Web.OpenWeatherMap.Types.CurrentWeather as CW@@ -21,16 +23,21 @@ printCurrectWeather cw =   putStrLn     (place +++     ", " +++     time ++      ": " ++      intercalate        ",  "        [ w        , showHumidity mainw+       , showAbsoluteHumidity mainw        , showPressure mainw        , showTemp mainw        , showWind wind        ])   where+    tz = secondsToTimeZone (CW.timezone cw)+    time = showLocalTime tz (CW.dt cw)     w = showWeather $ CW.weather cw     place = showLocation (CW.name cw) (Sys.country . CW.sys $ cw) (CW.coord cw)     mainw = CW.main cw@@ -39,28 +46,34 @@ printForecastWeather :: FW.ForecastWeather -> IO () printForecastWeather fw = do   let c = FW.city fw-      tz = minutesToTimeZone (City.timezone c `div` 60)+      tz = secondsToTimeZone (City.timezone c)       place = showLocation (City.name c) (City.country c) (City.coord c)   putStrLn place   mapM_ putStrLn (showForecast tz <$> FW.list fw) +secondsToTimeZone :: Int -> TimeZone+secondsToTimeZone s = minutesToTimeZone (s `div` 60)+ showForecast :: TimeZone -> FC.Forecast -> String showForecast tz fc =-  localtime +++  showLocalTime tz (FC.dt fc) ++   ": " ++   intercalate     ", "     [ showWeather (FC.weather fc)     , showHumidity mainw+    , showAbsoluteHumidity mainw     , showPressure mainw     , showTemp mainw     , showWind (FC.wind fc)     ]   where-    localtime =-      show . utcToZonedTime tz . posixSecondsToUTCTime . fromIntegral $ FC.dt fc     mainw = FC.main fc +showLocalTime :: TimeZone -> Int -> String+showLocalTime tz =+  show . utcToZonedTime tz . posixSecondsToUTCTime . fromIntegral+ showLocation :: String -> Maybe String -> Coord.Coord -> String showLocation name country coord =   name' ++ maybe "" ("," ++) country ++ " " ++ coords@@ -78,13 +91,19 @@   "°, " ++ maybe "?" show (Coord.lon coord) ++ "°)"  showWeather :: [Weather.Weather] -> String-showWeather w = intercalate "," $ Weather.main <$> w+showWeather w = intercalate "/" $ Weather.main <$> w  showHumidity :: Main.Main -> String showHumidity m = "H " ++ show hm ++ " %"   where     hm :: Int     hm = round . Main.humidity $ m++showAbsoluteHumidity :: Main.Main -> String+showAbsoluteHumidity m = "ρ " ++ rho ++ " g/m³"+  where+    r = absoluteHumidity m+    rho = maybe "??" (printf "%0.2f") r  -- https://en.wikipedia.org/wiki/Millimeter_of_mercury showPressure :: Main.Main -> String
lib/Web/OpenWeatherMap/Client.hs view
@@ -6,12 +6,12 @@   , getForecast   ) where -import Network.HTTP.Client (defaultManagerSettings, newManager)+import Network.HTTP.Client.TLS (newTlsManager) import Servant.Client   ( BaseUrl(BaseUrl)   , ClientEnv   , ClientError-  , Scheme(Http)+  , Scheme(Https)   , mkClientEnv   , runClientM   )@@ -40,10 +40,8 @@  defaultEnv :: IO ClientEnv defaultEnv = do-  manager <- newManager defaultManagerSettings+  manager <- newTlsManager   return $ mkClientEnv manager baseUrl --- XXX openweathermap.org does not support HTTPS,--- XXX appid is passed in clear text. Oops. baseUrl :: BaseUrl-baseUrl = BaseUrl Http "api.openweathermap.org" 80 "/data/2.5"+baseUrl = BaseUrl Https "api.openweathermap.org" 443 "/data/2.5"
+ lib/Web/OpenWeatherMap/Formulas.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE NamedFieldPuns #-}++module Web.OpenWeatherMap.Formulas+  ( absoluteHumidity+  ) where++import Web.OpenWeatherMap.Types.Main++{-- | Calculate absolute humidity (g/m³)++Returns 'Nothing' if the temperature is out of range (−30°C,  +35°C).++-}+absoluteHumidity :: Main -> Maybe Double+absoluteHumidity Main {temp, humidity}+  | tC > -30 && tC < 35 = Just $ ahBolton1980 temp humidity+  | otherwise = Nothing+  where+    tC = k2c temp++{-- | Calculate absolute humidity (g/m³)++Ref.: Bolton D. "The Computation of Equivalent Potential Temperature",+Monthly Weather Review, 1980, 108(7):1046–1053.++-}+ahBolton1980 ::+     Double -- ^ Temperature in Kelvins+  -> Double -- ^ Relative humidity in %+  -> Double+ahBolton1980 temp humidity =+  13.25 * humidity * exp (17.67 * tC / (tC + 243.5)) / temp+  where+    tC = k2c temp++k2c :: Double -> Double+k2c t = t - 273.15
lib/Web/OpenWeatherMap/Types/City.hs view
@@ -11,11 +11,13 @@  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)+data City =+  City+    { name :: String+    , country :: Maybe String+    , coord :: Coord+    , timezone :: Int+    , sunset :: Int+    , sunrise :: Int+    }+  deriving (Show, Generic, FromJSON)
lib/Web/OpenWeatherMap/Types/Clouds.hs view
@@ -9,6 +9,8 @@  import Data.Aeson (FromJSON) -data Clouds = Clouds-  { all :: Double-  } deriving (Show, Generic, FromJSON)+data Clouds =+  Clouds+    { all :: Double+    }+  deriving (Show, Generic, FromJSON)
lib/Web/OpenWeatherMap/Types/Coord.hs view
@@ -9,7 +9,9 @@  import Data.Aeson (FromJSON) -data Coord = Coord-  { lon :: Maybe Double-  , lat :: Maybe Double-  } deriving (Show, Generic, FromJSON)+data Coord =+  Coord+    { lon :: Maybe Double+    , lat :: Maybe Double+    }+  deriving (Show, Generic, FromJSON)
lib/Web/OpenWeatherMap/Types/CurrentWeather.hs view
@@ -19,16 +19,19 @@  -- | Response to requests for current weather. -- Refer to <https://openweathermap.org/current>.-data CurrentWeather = CurrentWeather-  { coord :: Coord-  , weather :: [Weather]-  , base :: String-  , main :: Main-  , wind :: Wind-  , clouds :: Clouds-  , dt :: Int-  , sys :: Sys-  , id :: Int-  , name :: String-  , cod :: Int-  } deriving (Show, Generic, FromJSON)+data CurrentWeather =+  CurrentWeather+    { coord :: Coord+    , weather :: [Weather]+    , base :: String+    , main :: Main+    , wind :: Wind+    , clouds :: Clouds+    , dt :: Int+    , sys :: Sys+    , timezone :: Int+    , id :: Int+    , name :: String+    , cod :: Int+    }+  deriving (Show, Generic, FromJSON)
lib/Web/OpenWeatherMap/Types/Forecast.hs view
@@ -14,10 +14,12 @@ 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)+data Forecast =+  Forecast+    { dt :: Int+    , clouds :: Clouds+    , main :: Main+    , weather :: [Weather]+    , wind :: Wind+    }+  deriving (Show, Generic, FromJSON)
lib/Web/OpenWeatherMap/Types/ForecastWeather.hs view
@@ -14,7 +14,9 @@  -- | Response to requests for forecast weather. -- Refer to <https://openweathermap.org/forecast5>.-data ForecastWeather = ForecastWeather-  { list :: [Forecast]-  , city :: City-  } deriving (Show, Generic, FromJSON)+data ForecastWeather =+  ForecastWeather+    { list :: [Forecast]+    , city :: City+    }+  deriving (Show, Generic, FromJSON)
lib/Web/OpenWeatherMap/Types/Location.hs view
@@ -10,25 +10,26 @@   ) where  import Data.Proxy (Proxy(..))-+import Data.Text (Text)+import Data.Text.Encoding (encodeUtf8) import Servant.API ((:>)) import Servant.Client (Client, HasClient, clientWithRoute, hoistClientMonad)-import Servant.Client.Core.Request (appendToQueryString)-import Web.HttpApiData (toQueryParam)+import Servant.Client.Core.Request (Request, appendToQueryString)+import Web.HttpApiData (ToHttpApiData, toQueryParam) --- | Various way to specify location.+-- | Various ways to specify location. data Location   = Name String -- ^ City name.   | Coord Double Double -- ^ Geographic coordinates: latitude and longitude. +addParam :: ToHttpApiData a => Text -> a -> Request -> Request+addParam name = appendToQueryString name . Just . encodeUtf8 . toQueryParam+ instance HasClient m api => HasClient m (Location :> api) where   type Client m (Location :> api) = Location -> Client m api+  hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f . cl   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)+      addParams (Name q) = addParam "q" q+      addParams (Coord lat lon) = addParam "lat" lat . addParam "lon" lon
lib/Web/OpenWeatherMap/Types/Main.hs view
@@ -11,12 +11,14 @@  {-# ANN module "HLint: ignore Use camelCase" #-} -data Main = Main-  { temp :: Double-  , pressure :: Double-  , humidity :: Double-  , temp_min :: Double-  , temp_max :: Double-  , sea_level :: Maybe Double-  , grnd_level :: Maybe Double-  } deriving (Show, Generic, FromJSON)+data Main =+  Main+    { temp :: Double+    , pressure :: Double+    , humidity :: Double+    , temp_min :: Double+    , temp_max :: Double+    , sea_level :: Maybe Double+    , grnd_level :: Maybe Double+    }+  deriving (Show, Generic, FromJSON)
lib/Web/OpenWeatherMap/Types/Sys.hs view
@@ -9,8 +9,10 @@  import Data.Aeson (FromJSON) -data Sys = Sys-  { country :: Maybe String-  , sunrise :: Int-  , sunset :: Int-  } deriving (Show, Generic, FromJSON)+data Sys =+  Sys+    { country :: Maybe String+    , sunrise :: Int+    , sunset :: Int+    }+  deriving (Show, Generic, FromJSON)
lib/Web/OpenWeatherMap/Types/Weather.hs view
@@ -9,9 +9,11 @@  import Data.Aeson (FromJSON) -data Weather = Weather-  { id :: Int-  , main :: String-  , description :: String-  , icon :: String-  } deriving (Show, Generic, FromJSON)+data Weather =+  Weather+    { id :: Int+    , main :: String+    , description :: String+    , icon :: String+    }+  deriving (Show, Generic, FromJSON)
lib/Web/OpenWeatherMap/Types/Wind.hs view
@@ -9,7 +9,9 @@  import Data.Aeson (FromJSON) -data Wind = Wind-  { speed :: Double-  , deg :: Double-  } deriving (Show, Generic, FromJSON)+data Wind =+  Wind+    { speed :: Double+    , deg :: Double+    }+  deriving (Show, Generic, FromJSON)
openweathermap.cabal view
@@ -1,67 +1,75 @@-name: openweathermap-version: 0.2.0-synopsis: Access data at OpenWeatherMap-description: Client library and command-line utility to access-  OpenWeatherMap https://openweathermap.org-license: PublicDomain-license-file: LICENSE-author: Igor Pashev-maintainer: Igor Pashev <pashev.igor@gmail.com>-copyright: 2017, Igor Pashev <pashev.igor@gmail.com>-category: Web-build-type: Simple-extra-source-files: README.md ChangeLog.md-cabal-version: 1.20+cabal-version:      1.20+name:               openweathermap+version:            0.3.0+license:            PublicDomain+license-file:       LICENSE+maintainer:         Igor Pashev <pashev.igor@gmail.com>+author:             Igor Pashev+synopsis:           Access data at OpenWeatherMap+description:+    Client library and command-line utility to access+    OpenWeatherMap https://openweathermap.org +category:           Web+build-type:         Simple+extra-source-files:+    README.md+    ChangeLog.md+ source-repository head-  type: git-  location: https://github.com/ip1981/openweathermap.git+    type:     git+    location: http://git.pashev.ru/openweathermap  flag cmd-  description: Build a command-line utility.-  default: True+    description: Build a command-line utility.  library-  default-language: Haskell2010-  ghc-options: -Wall-  hs-source-dirs: lib-  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-     Web.OpenWeatherMap.Types.Wind+    exposed-modules:+        Web.OpenWeatherMap.API+        Web.OpenWeatherMap.Client+        Web.OpenWeatherMap.Formulas+        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+        Web.OpenWeatherMap.Types.Wind -executable openweathermap-  default-language: Haskell2010-  ghc-options: -Wall -static-  hs-source-dirs: cmd-  main-is: Main.hs-  other-modules: Print-  if flag(cmd)+    hs-source-dirs:   lib+    default-language: Haskell2010+    ghc-options:      -Wall     build-depends:-        base >= 4.9 && < 5-      , directory-      , openweathermap-      , optparse-applicative >= 0.13.0.0-      , time-      , xdg-basedir-  else-    buildable: False+        base >=4.9 && <5,+        aeson -any,+        bytestring -any,+        http-api-data -any,+        http-client-tls -any,+        servant -any,+        servant-client >=0.19,+        servant-client-core -any,+        text -any++executable openweathermap+    main-is:          Main.hs+    hs-source-dirs:   cmd+    other-modules:    Print+    default-language: Haskell2010+    ghc-options:      -Wall -static++    if flag(cmd)+        build-depends:+            base >=4.9 && <5,+            directory -any,+            openweathermap -any,+            optparse-applicative >=0.13.0.0,+            time -any,+            xdg-basedir -any++    else+        buildable: False