Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to specify temps in C or F on weather #910

Merged
merged 4 commits into from Apr 1, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
111 changes: 27 additions & 84 deletions src/yetibot/commands/weather.clj
Expand Up @@ -6,7 +6,8 @@
[taoensso.timbre :refer [info warn error]]
[yetibot.core.config :refer [get-config]]
[yetibot.core.hooks :refer [cmd-hook]]
[yetibot.models.postal-code :refer [chk-postal-code]]))
[yetibot.models.postal-code :refer [chk-postal-code]]
[yetibot.commands.weather.formatters :as fmt]))

(def config (:value (get-config sch/Any [:weather :weatherbitio])))

Expand Down Expand Up @@ -57,74 +58,12 @@
error {:result/error error}
(= 429 status_code) {:result/error status_message}))

(defn c-to-f [c] (-> (* c 9/5) (+ 32) float))
(defn km-to-mi [km] (-> (/ km 1.609) float))

(defn get-units
[cc]
(let [cc (-> cc str/lower-case keyword)]
(condp = cc
;; Liberia
:lbr {:temp {:sym "F" :conv c-to-f}
:speed {:sym "mph" :conv km-to-mi}}

;; Myanmar
:mm {:temp {:sym "F" :conv c-to-f}
:speed {:sym "mph" :conv km-to-mi}}

;; The United States of America
:us {:temp {:sym "F" :conv c-to-f}
:speed {:sym "mph" :conv km-to-mi}}

;; THE ENTIRE REST OF THE WORLD
{:temp {:sym "C" :conv float}
:speed {:sym "km/h" :conv float}})))

(defn- l10n-value
[v u cc]
(let [{:keys [sym conv]} (get (get-units cc) u)]
[(conv v) sym]))

(defn- l10n-temp [temp cc] (l10n-value temp :temp cc))

(defn- l10n-speed [speed cc] (l10n-value speed :speed cc))

(defn fmt-location-title
[{:keys [city_name state_code country_code]}]
(let [loc (if (re-matches #"\d+" state_code)
city_name
(str city_name ", " state_code))]
(format "%s (%s)" loc country_code)))

(defn fmt-temp
[temp unit]
(format "%.1f°%s" temp unit))

(defn fmt-description
[{cc :country_code temp :temp {:keys [icon code description]} :weather}]
(let [[temp unit] (l10n-temp temp cc)]
(format "%s - %s"
(fmt-temp temp unit)
(str/join (map str/capitalize (str/split description #"\b"))))))

(defn fmt-feels-like
[{cc :country_code app_temp :app_temp}]
(let [[app_temp unit] (l10n-temp app_temp cc)]
(format "Feels like %s"
(fmt-temp app_temp unit))))

(defn fmt-wind
[{cc :country_code :keys [wind_spd wind_cdir]}]
(let [[wind_spd unit] (l10n-speed wind_spd cc)]
(format "Winds %.1f %s %s"
wind_spd unit wind_cdir)))

(defn- format-current
[c]
(map #(% c) [fmt-location-title
fmt-description
fmt-feels-like
fmt-wind]))
[formatters c]
(cons (fmt/location-title c)
(map #(% formatters c) [fmt/description
fmt/feels-like
fmt/wind])))

(defn current
[loc]
Expand All @@ -134,15 +73,25 @@
[loc]
(get-by-loc "forecast/daily" loc))

(defn parse-args
"parse args to vec of unit kw and args str"
[s]
(let [[_ unit args] (re-matches #"(?i)(?:\s*(-[micf]))?\s*(.+)", s)
unit (when-not (nil? unit)
(if (or (= unit "-i") (= unit "-f")) :i :m))]
[unit args]))

(defn weather-cmd
"weather <location> # look up current weather for <location> by name or postal code, with optional country code"
"weather <location> # look up current weather for <location> by name or postal code, optional country code, -c or -f to force units"
{:yb/cat #{:info}}
[{:keys [match]}]
(let [result (current match)]
(let [[unit loc] (parse-args match)
result (current loc)]
(or
(error-response result)
(let [{[cs] :data} result]
{:result/value (format-current cs)
(let [{[cs] :data} result
formatters (fmt/get-formatters unit (:country_code cs))]
{:result/value (format-current formatters cs)
:result/data cs}))))

(defn default-weather-cmd
Expand All @@ -154,26 +103,20 @@
{:result/error "A default zip code is not configured.
Configure it at path weather.weatherbitio.default.zip"}))

(defn fmt-forecast-item
"Format a forecast item like: date: min - max"
[cc {:keys [min_temp max_temp valid_date]}]
(format "%s: %s - %s"
valid_date
(apply fmt-temp (l10n-temp min_temp cc))
(apply fmt-temp (l10n-temp max_temp cc))))

(defn forecast-cmd
"weather forecast <location> # look up forecast for <location> by name or postal code, with optional country code"
"weather forecast <location> # look up forecast for <location> by name or postal code, optional country code, -c or -f to force units"
{:yb/cat #{:info}}
[{[_ match] :match}]
(let [result (forecast match)]
(let [[unit loc] (parse-args match)
result (forecast loc)]
(or
(error-response result)
(let [{:keys [city_name country_code data]} result
location (fmt-location-title result)]
formatters (fmt/get-formatters unit country_code)
location (fmt/location-title result)]
{:result/value (into [location]
(map
(partial fmt-forecast-item country_code)
(partial fmt/forecast-item formatters)
data))
:result/data result}))))

Expand Down
62 changes: 62 additions & 0 deletions src/yetibot/commands/weather/formatters.clj
@@ -0,0 +1,62 @@
(ns yetibot.commands.weather.formatters
(:require
[clojure.string :as str]))

(defn c-to-f [c] (-> (* c 9/5) (+ 32) float))

(defn km-to-mi [km] (-> (/ km 1.609) float))

(def imperial-units
{:temp (fn [v] (format "%.1f°%s" (c-to-f v) "F"))
:speed (fn [v] (format "%.1f %s" (km-to-mi v) "mph"))})

(def metric-units
{:temp (fn [v] (format "%.1f°%s" (float v) "C"))
:speed (fn [v] (format "%.1f %s" (float v) "km/h"))})

(defn- get-formatters-by-cc
[cc]
(let [cc (-> cc str/lower-case keyword)]
(condp = cc
:lbr imperial-units ;; Liberia
:mm imperial-units ;; Myanmar
:us imperial-units ;; The United States of America
;; THE ENTIRE REST OF THE WORLD
metric-units)))

(defn get-formatters
[unit cc]
(if (nil? unit)
(get-formatters-by-cc cc)
(if (= unit :i)
imperial-units
metric-units)))

(defn location-title
[{:keys [city_name state_code country_code]}]
(let [loc (if (re-matches #"\d+" state_code)
city_name
(str city_name ", " state_code))]
(format "%s (%s)" loc country_code)))

(defn description
[{fmt :temp} {temp :temp {:keys [icon code description]} :weather}]
(format "%s - %s"
(fmt temp)
(str/join (map str/capitalize (str/split description #"\b")))))

(defn feels-like
[{fmt :temp} {app_temp :app_temp}]
(format "Feels like %s" (fmt app_temp)))

(defn wind
[{fmt :speed} {:keys [wind_spd wind_cdir]}]
(format "Winds %s %s" (fmt wind_spd) wind_cdir))

(defn forecast-item
"Format a forecast item like: date: min - max"
[{fmt :temp} {:keys [min_temp max_temp valid_date]}]
(format "%s: %s - %s"
valid_date
(fmt min_temp)
(fmt max_temp)))
37 changes: 24 additions & 13 deletions test/yetibot/test/commands/weather.clj
@@ -1,7 +1,8 @@
(ns yetibot.test.commands.weather
(:require
[midje.sweet :refer [facts fact =>]]
[yetibot.commands.weather :refer :all]))
[yetibot.commands.weather :refer :all]
[yetibot.commands.weather.formatters :as fmt]))

(def loc-nyc
{:city_name "New York"
Expand All @@ -23,16 +24,26 @@
:wind_spd 10
:wind_cdir "SSE"})

(def formatters-us (fmt/get-formatters nil (:country_code loc-nyc)))
(def formatters-us-metric (fmt/get-formatters :m (:country_code loc-nyc)))

(def formatters-ro (fmt/get-formatters nil (:country_code loc-bcr)))
(def formatters-ro-imperl (fmt/get-formatters :i (:country_code loc-bcr)))

(facts "about fomatting fns"
(fact fmt-location-title
(fmt-location-title loc-nyc) => "New York, NY (US)"
(fmt-location-title loc-bcr) => "Bucharest (RO)")
(fact fmt-description
(fmt-description loc-nyc) => "32.0°F - Titlecase Me"
(fmt-description loc-bcr) => "50.0°C - Titlecase Me")
(fact fmt-feels-like
(fmt-feels-like loc-nyc) => "Feels like 77.0°F"
(fmt-feels-like loc-bcr) => "Feels like 100.0°C")
(fact fmt-wind
(fmt-wind loc-nyc) => "Winds 6.2 mph N"
(fmt-wind loc-bcr) => "Winds 10.0 km/h SSE"))
(fact fmt/location-title
(fmt/location-title loc-nyc) => "New York, NY (US)"
(fmt/location-title loc-bcr) => "Bucharest (RO)")
(fact fmt/description
(fmt/description formatters-us loc-nyc) => "32.0°F - Titlecase Me"
(fmt/description formatters-us-metric loc-nyc) => "0.0°C - Titlecase Me"
(fmt/description formatters-ro loc-bcr) => "50.0°C - Titlecase Me"
(fmt/description formatters-ro-imperl loc-bcr) => "122.0°F - Titlecase Me")
(fact fmt/feels-like
(fmt/feels-like formatters-us loc-nyc) => "Feels like 77.0°F"
(fmt/feels-like formatters-ro loc-bcr) => "Feels like 100.0°C")
(fact fmt/wind
(fmt/wind formatters-us loc-nyc) => "Winds 6.2 mph N"
(fmt/wind formatters-us-metric loc-nyc) => "Winds 10.0 km/h N"
(fmt/wind formatters-ro loc-bcr) => "Winds 10.0 km/h SSE"
(fmt/wind formatters-ro-imperl loc-bcr) => "Winds 6.2 mph SSE"))