105 lines
4.3 KiB
Haskell
105 lines
4.3 KiB
Haskell
{-# LANGUAGE OverloadedStrings #-} --needed for ByteString arguments
|
|
import Network.HTTP.Simple
|
|
import System.Environment (lookupEnv)
|
|
import Data.ByteString.UTF8 (fromString) --to convert acquired API key to ByteString
|
|
import Data.Time as T
|
|
|
|
import Plot
|
|
import qualified DataTypes as DT
|
|
|
|
data WhichDay = Yesterday | Today | Tomorrow
|
|
deriving (Show, Eq)
|
|
|
|
|
|
main :: IO ()
|
|
main = do
|
|
apiKey <- getWeatherKey
|
|
|
|
putStrLn "Enter a city name: "
|
|
city <- getLine
|
|
|
|
let cityRequest = apiRequestCity apiKey city
|
|
cityResponse <- httpLBS cityRequest
|
|
|
|
if getResponseBody cityResponse == "[]" || getResponseStatusCode cityResponse /= 200
|
|
then error "City not found!"
|
|
else do
|
|
currentDate <- getCurrentDate
|
|
|
|
--request for yesterday's weather
|
|
let yesterdayDate = formatDate $ addDays (-1) currentDate
|
|
let yesterdayRequest = apiRequestBuilder apiKey Yesterday city yesterdayDate
|
|
yesterdayResponse <- httpLBS yesterdayRequest
|
|
yesterdayDecoded <- DT.weatherDecode $ getResponseBody yesterdayResponse
|
|
putStr $ "Yesterday's temperature: " ++ show (DT.getTemperature yesterdayDecoded) ++ " celsius\n"
|
|
|
|
--request for today's weather
|
|
let todayDate = formatDate currentDate
|
|
let todayRequest = apiRequestBuilder apiKey Today city todayDate
|
|
todayResponse <- httpLBS todayRequest
|
|
todayDecoded <- DT.weatherDecode $ getResponseBody todayResponse
|
|
putStr $ "Today's temperature: " ++ show (DT.getTemperature todayDecoded) ++ " celsius\n"
|
|
|
|
--request for tomorrow's weather
|
|
let tomorrowDate = formatDate $ addDays 1 currentDate
|
|
let tomorrowRequest = apiRequestBuilder apiKey Tomorrow city tomorrowDate
|
|
tomorrowResponse <- httpLBS tomorrowRequest
|
|
tomorrowDecoded <- DT.weatherDecode $ getResponseBody tomorrowResponse
|
|
putStr $ "Tomorrow's temperature: " ++ show (DT.getTemperature tomorrowDecoded) ++ " celsius\n"
|
|
|
|
generatePlot (DT.getTemperature yesterdayDecoded) (DT.getTemperature todayDecoded) (DT.getTemperature tomorrowDecoded)
|
|
openPlot
|
|
|
|
|
|
--apiKey is stored in an env variable, not something that should be pushed onto git
|
|
getWeatherKey :: IO String
|
|
getWeatherKey = do
|
|
result <- lookupEnv "WEATHER_API_KEY"
|
|
case result of
|
|
Just a -> return a
|
|
Nothing -> error "API key not set in environmental variables!" -- exception thrown (but not handled) per project requirement
|
|
|
|
|
|
apiRequestCity :: String -> String -> Request
|
|
apiRequestCity apiKey city =
|
|
setRequestHost "api.weatherapi.com"
|
|
$ setRequestPath "/v1/search.json"
|
|
$ setRequestMethod "GET"
|
|
$ setRequestQueryString [("key", Just (fromString apiKey)), ("q", Just (fromString city))]
|
|
$ setRequestPort 443
|
|
$ setRequestSecure True defaultRequest
|
|
|
|
|
|
apiRequestBuilder :: String -> WhichDay -> String -> String -> Request
|
|
apiRequestBuilder apiKey day city date =
|
|
setRequestHost "api.weatherapi.com"
|
|
$ setRequestPath path
|
|
$ setRequestMethod "GET"
|
|
$ setRequestQueryString query
|
|
$ setRequestPort 443
|
|
$ setRequestSecure True defaultRequest
|
|
where {
|
|
path
|
|
| day == Yesterday = "/v1/history.json"
|
|
| day == Today = "/v1/current.json"
|
|
| day == Tomorrow = "/v1/forecast.json"
|
|
| otherwise = error "Invalid day argument!";
|
|
query
|
|
| day == Yesterday = [("key", Just (fromString apiKey)), ("q", Just (fromString city)), ("dt", Just (fromString date))]
|
|
| day == Today = [("key", Just (fromString apiKey)), ("q", Just (fromString city))]
|
|
| day == Tomorrow = [("key", Just (fromString apiKey)), ("q", Just (fromString city)), ("dt", Just (fromString date)), ("days", Just (fromString "1"))]
|
|
| otherwise = error "Invalid day argument!";
|
|
}
|
|
|
|
|
|
getCurrentDate :: IO T.Day
|
|
getCurrentDate = do
|
|
currentTime <- getCurrentTime
|
|
timeZone <- getCurrentTimeZone
|
|
let localTime = utcToLocalTime timeZone currentTime
|
|
let currentDate = localDay localTime
|
|
return currentDate
|
|
|
|
|
|
formatDate :: T.Day -> String
|
|
formatDate = formatTime defaultTimeLocale "%Y-%m-%d" |