-
Notifications
You must be signed in to change notification settings - Fork 1
/
weather.rb
90 lines (77 loc) · 2.43 KB
/
weather.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
require "wunderground"
require "active_support/time"
class Weather
def self.wunderground
@wunderground ||= Wunderground.new(ENV["WUNDERGROUND_API_KEY"])
end
def self.location_and_timezone(ip)
data = wunderground.get_geolookup_for(geo_ip: ip)
{lat: data["location"]["lat"], lon: data["location"]["lon"], timezone: data["location"]["tz_long"]}
end
def initialize(job)
Time.zone = ActiveSupport::TimeZone[job.timezone]
@hours = extract_hourly_forecast(job)
fill_in_data_with_history_if_required(@hours, job)
end
def forecast
{day_for: Date::DAYNAMES[Date.today.wday],
morning: summary_for_hours(8..11),
lunch: summary_for_hours(12..14),
afternoon: summary_for_hours(15..18),
evening: summary_for_hours(19..23)}
end
private
def extract_hourly_forecast(job)
data = self.class.wunderground.get_hourly_for("#{job.lat},#{job.lon}")
data["hourly_forecast"].inject({}) do |h, t|
time = Time.zone.at(t["FCTTIME"]["epoch"].to_i)
if time >= start_time && time <= end_time
h.merge({time => {
temp: t["temp"]["metric"],
condition: t["icon"]
}})
else
h
end
end
end
def fill_in_data_with_history_if_required(data, job)
if data.first.first > start_time
earlier_data = self.class.wunderground.send(:"get_history_#{start_time.strftime("%Y%m%d")}_for", "#{job.lat},#{job.lon}")
history_hours = earlier_data["history"]["observations"].inject({}) do |h, t|
if t["utcdate"]["min"] == "20"
time = Time.zone.parse(t["utcdate"]["pretty"]) - (20*60)
h.merge({time => {
temp: t["tempm"],
condition: t["icon"]
}})
else
h
end
end
data.merge!(history_hours)
end
end
def start_time
Time.zone.now.beginning_of_day
end
def end_time
@end_time ||= (start_time + 24.hours - 1.second)
end
def weather_for_hours(range)
@hours.select { |t| range.include?(t.hour) }
end
def average_temp(hours)
hours.inject(0) { |a, (t,f)| a + f[:temp].to_i } / hours.length
end
def average_symbol(hours)
counts = hours.map { |t,f| f[:condition] }.inject({}) { |h,c| h[c] ||= 0; h[c] += 1; h }.invert
counts[counts.keys.max]
end
def summary_for_hours(range)
hours = weather_for_hours(range)
temp = average_temp(hours)
symbol = average_symbol(hours)
{temperature: temp, symbol: symbol}
end
end