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 +14/−0
- cmd/Print.hs +24/−5
- lib/Web/OpenWeatherMap/Client.hs +4/−6
- lib/Web/OpenWeatherMap/Formulas.hs +37/−0
- lib/Web/OpenWeatherMap/Types/City.hs +10/−8
- lib/Web/OpenWeatherMap/Types/Clouds.hs +5/−3
- lib/Web/OpenWeatherMap/Types/Coord.hs +6/−4
- lib/Web/OpenWeatherMap/Types/CurrentWeather.hs +16/−13
- lib/Web/OpenWeatherMap/Types/Forecast.hs +9/−7
- lib/Web/OpenWeatherMap/Types/ForecastWeather.hs +6/−4
- lib/Web/OpenWeatherMap/Types/Location.hs +11/−10
- lib/Web/OpenWeatherMap/Types/Main.hs +11/−9
- lib/Web/OpenWeatherMap/Types/Sys.hs +7/−5
- lib/Web/OpenWeatherMap/Types/Weather.hs +8/−6
- lib/Web/OpenWeatherMap/Types/Wind.hs +6/−4
- openweathermap.cabal +66/−58
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