packages feed

openweathermap (empty) → 0.0.0

raw patch · 15 files changed

+549/−0 lines, 15 filesdep +aesondep +basedep +directorysetup-changed

Dependencies added: aeson, base, directory, http-client, openweathermap, optparse-applicative, servant, servant-client, xdg-basedir

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+0.0.0+=====++  * Initial version.+
+ LICENSE view
@@ -0,0 +1,13 @@+        DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE+                    Version 2, December 2004++ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>++ Everyone is permitted to copy and distribute verbatim or modified+ copies of this license document, and changing it is allowed as long+ as the name is changed.++            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION++  0. You just DO WHAT THE FUCK YOU WANT TO.
+ README.md view
@@ -0,0 +1,57 @@+OpenWeatherMap+==============++OpenWeatherMap is a haskell library to access <https://openweathermap.org/api>.++Requirements+============++OpenWeatherMap is written in Haskell with [GHC](http://www.haskell.org/ghc/).+All required Haskell libraries are listed in [openweathermap.cabal](ldapply.cabal).+Use [cabal-install](http://www.haskell.org/haskellwiki/Cabal-Install) to fetch+and build all pre-requisites automatically.+++Command-line utility+====================++The command-line utility `openweathermap` provides a means+to get human readable weather infromation.++Usage+-----++```+Usage: openweathermap ([-K|--api-key-file APIKEYFILE] | [-k|--api-key APIKEY])+                      ((-c|--city CITY) | --lat NUM --lon NUM) [-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+  --lat NUM                Latitude in decimal degrees+  --lon NUM                Longitude in decimal degrees+  -d,--debug               Enable debug+  -h,--help                Show this help text++```+By default, `openweathermap` read the API key+from the `$XDG_CONFIG_HOME/openweathermap/key` file, where+[`$XDG_CONFIG_HOME`](https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html)+is typically `~/.config` on Linux systems.+++Examples+--------++```+$ openweathermap -c 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+```+++
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cmd/Main.hs view
@@ -0,0 +1,173 @@+{-# LANGUAGE LambdaCase #-}++module Main (+  main+) where++import Control.Monad (when)+import Data.List (intercalate)+import Data.Semigroup ((<>))+import Data.Version (showVersion)+import System.Exit (die)+import System.IO (IOMode(ReadMode), hGetLine, hPrint, stderr, withFile)++import Options.Applicative (+    (<**>), (<|>), Parser, auto, execParser, fullDesc, header, help, helper,+    info, long, metavar, option, optional, short, strOption, switch+  )+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++appName :: String+appName = "openweathermap"++parseLocation :: Parser Client.Location+parseLocation = byName <|> byCoord+  where+    byName = Client.Name <$> strOption+      (  long "city"+      <> short 'c'+      <> metavar "CITY"+      <> help "City name" )++    byCoord = Client.Coord+      <$> option auto+        (  long "lat"+        <> metavar "NUM"+        <> help "Latitude in decimal degrees" )+      <*> option auto+        (  long "lon"+        <> metavar "NUM"+        <> help "Longitude in decimal degrees" )+++data ApiKey+  = ApiKeyFile FilePath+  | ApiKey String++parseApiKey :: Parser ApiKey+parseApiKey = fromFile <|> inCmdLine+  where+    fromFile = ApiKeyFile <$> strOption+      (  long "api-key-file"+      <> short 'K'+      <> metavar "APIKEYFILE"+      <> help "Read API key from this file" )++    inCmdLine = ApiKey <$> strOption+      (  long "api-key"+      <> short 'k'+      <> metavar "APIKEY"+      <> help "API key" )+++data Config = Config+  { apikey :: Maybe ApiKey+  , location :: Client.Location+  , debug :: Bool+  }++parseConfig :: Parser Config+parseConfig = Config+          <$> optional parseApiKey+          <*> parseLocation+          <*> switch (long "debug" <> short 'd' <> help "Enable debug")++getApiKey :: Maybe ApiKey -> IO String+getApiKey (Just (ApiKey key)) = return key+getApiKey (Just (ApiKeyFile f)) = withFile f ReadMode hGetLine+getApiKey Nothing = do+  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++main :: IO ()+main = run =<< execParser opts+  where+    opts = info (parseConfig <**> helper) (fullDesc <> header desc)+    desc = "openweathermap "+        ++ showVersion version+        ++ " - command-line client for https://openweathermap.org/api"+
+ lib/Web/OpenWeatherMap/API.hs view
@@ -0,0 +1,45 @@+{-|+Direct API functions.+For API key (a.k.a appid) refer to <http://openweathermap.org/appid>.+-}++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}++module Web.OpenWeatherMap.API (+  weatherByName,+  weatherByCoord+) where++import Data.Proxy (Proxy(..))++import Servant.API ((:>), (:<|>)(..), JSON, Get, QueryParam)+import Servant.Client (client)+import Servant.Common.Req (ClientM)++import Web.OpenWeatherMap.Types.CurrentWeather (CurrentWeather)+++type GetCurrentWeather = AppId :> Get '[JSON] CurrentWeather+type AppId = QueryParam "appid" String++type API+     = "weather" :> QueryParam "q" String :> GetCurrentWeather+  :<|> "weather" :> QueryParam "lat" Double :> QueryParam "lon" Double+                 :> GetCurrentWeather++-- | 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)+
+ lib/Web/OpenWeatherMap/Client.hs view
@@ -0,0 +1,49 @@+{-|+High-level client functions perfoming requests to OpenWeatherMap API.+-}+module Web.OpenWeatherMap.Client (+  Location(..),+  getWeather+) where++import Network.HTTP.Client (newManager, defaultManagerSettings)+import Servant.Client (ClientEnv(..), runClientM, ServantError)+import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..))+import Servant.Common.Req (ClientM)++import Web.OpenWeatherMap.Types.CurrentWeather (CurrentWeather)+import qualified Web.OpenWeatherMap.API as API+++-- | Various way to specify location.+data Location+  = Name String          -- ^ City name.+  | Coord Double Double  -- ^ Geographic coordinates: latitude and longitude.+++-- | Make a request to OpenWeatherMap API+--   and return current weather in given location.+getWeather+  :: String -- ^ API key.+  -> Location+  -> IO (Either ServantError CurrentWeather)+getWeather appid loc =+  defaultEnv >>= runClientM (api loc appid)++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++defaultEnv :: IO ClientEnv+defaultEnv = do+  manager <- newManager defaultManagerSettings+  return $ ClientEnv 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"+
+ lib/Web/OpenWeatherMap/Types/Clouds.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.OpenWeatherMap.Types.Clouds (+  Clouds(..)+) where++import GHC.Generics (Generic)++import Data.Aeson (FromJSON)+ ++data Clouds = Clouds+  { all :: Double+  } deriving (Show, Generic, FromJSON)+
+ lib/Web/OpenWeatherMap/Types/Coord.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.OpenWeatherMap.Types.Coord (+  Coord(..)+) where++import GHC.Generics (Generic)++import Data.Aeson (FromJSON)+++data Coord = Coord+  { lon :: Double+  , lat :: Double+  } deriving (Show, Generic, FromJSON)+
+ lib/Web/OpenWeatherMap/Types/CurrentWeather.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.OpenWeatherMap.Types.CurrentWeather (+  CurrentWeather(..)+) where++import Prelude hiding (id)+import GHC.Generics (Generic)++import Data.Aeson (FromJSON)++import Web.OpenWeatherMap.Types.Clouds (Clouds)+import Web.OpenWeatherMap.Types.Coord (Coord)+import Web.OpenWeatherMap.Types.Main (Main)+import Web.OpenWeatherMap.Types.Sys (Sys)+import Web.OpenWeatherMap.Types.Weather (Weather)+import Web.OpenWeatherMap.Types.Wind (Wind)++-- | 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)+
+ lib/Web/OpenWeatherMap/Types/Main.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.OpenWeatherMap.Types.Main (+  Main(..)+) where++import GHC.Generics (Generic)++import Data.Aeson (FromJSON)+ ++{-# 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)+
+ lib/Web/OpenWeatherMap/Types/Sys.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.OpenWeatherMap.Types.Sys (+  Sys(..)+) where++import GHC.Generics (Generic)++import Data.Aeson (FromJSON)+ ++data Sys = Sys+  { message :: Double+  , country :: Maybe String+  , sunrise :: Int+  , sunset :: Int+  } deriving (Show, Generic, FromJSON)+
+ lib/Web/OpenWeatherMap/Types/Weather.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.OpenWeatherMap.Types.Weather (+  Weather(..)+) where++import GHC.Generics (Generic)++import Data.Aeson (FromJSON)+++data Weather = Weather+  { id :: Int+  , main :: String+  , description :: String+  , icon :: String+  } deriving (Show, Generic, FromJSON)+
+ lib/Web/OpenWeatherMap/Types/Wind.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.OpenWeatherMap.Types.Wind (+  Wind(..)+) where++import GHC.Generics (Generic)++import Data.Aeson (FromJSON)+ ++data Wind = Wind+  { speed :: Double+  , deg :: Double+  } deriving (Show, Generic, FromJSON)+
+ openweathermap.cabal view
@@ -0,0 +1,59 @@+name: openweathermap+version: 0.0.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++source-repository head+  type: git+  location: https://github.com/ip1981/openweathermap.git++flag cmd+  description: Build a command-line utility.+  default: True++library+  default-language: Haskell2010+  ghc-options: -Wall+  hs-source-dirs: lib+  build-depends:+      base >= 4.9 && < 5+    , aeson+    , http-client+    , servant+    , servant-client >= 0.9+  exposed-modules:+     Web.OpenWeatherMap.API+     Web.OpenWeatherMap.Client+     Web.OpenWeatherMap.Types.Clouds+     Web.OpenWeatherMap.Types.Coord+     Web.OpenWeatherMap.Types.CurrentWeather+     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+  if flag(cmd)+    build-depends:+        base >= 4.9 && < 5+      , directory+      , openweathermap+      , optparse-applicative >= 0.13.0.0+      , xdg-basedir+  else+    buildable: False+