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

Remove support for the Weather Underground API #2396

Merged
merged 8 commits into from
Aug 17, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ end
gem 'twilio-ruby', '~> 3.11.5' # TwilioAgent
gem 'ruby-growl', '~> 4.1.0' # GrowlAgent
gem 'net-ftp-list', '~> 3.2.8' # FtpsiteAgent
gem 'wunderground', '~> 1.2.0' # WeatherAgent
gem 'forecast_io', '~> 2.0.0' # WeatherAgent
gem 'rturk', '~> 2.12.1' # HumanTaskAgent
gem 'erector', github: 'dsander/erector', branch: 'fix-fixnum-warning'
Expand Down
5 changes: 0 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -655,10 +655,6 @@ GEM
websocket-driver (0.7.0)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
wunderground (1.2.0)
addressable
httparty (> 0.6.0)
json (> 1.4.0)
xmpp4r (0.5.6)
xpath (3.0.0)
nokogiri (~> 1.8)
Expand Down Expand Up @@ -774,7 +770,6 @@ DEPENDENCIES
web-console (>= 3.3.0)
webmock (~> 3.5.1)
weibo_2!
wunderground (~> 1.2.0)
xmpp4r (~> 0.5.6)

RUBY VERSION
Expand Down
189 changes: 78 additions & 111 deletions app/models/agents/weather_agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,23 @@ module Agents
class WeatherAgent < Agent
cannot_receive_events!

gem_dependency_check { defined?(Wunderground) && defined?(ForecastIO) }
gem_dependency_check { defined?(ForecastIO) }

description <<-MD
The Weather Agent creates an event for the day's weather at a given `location`.

#{'## Include `forecast_io` and `wunderground` in your Gemfile to use this Agent!' if dependencies_missing?}
#{'## Include `forecast_io` in your Gemfile to use this Agent!' if dependencies_missing?}

You also must select when you would like to get the weather forecast for using the `which_day` option, where the number 1 represents today, 2 represents tomorrow and so on. Weather forecast inforation is only returned for at most one week at a time.

The weather forecast information can be provided by either Wunderground or Dark Sky. To choose which `service` to use, enter either `darksky` or `wunderground`.
The weather forecast information is provided by Dark Sky.

The `location` should be:
The `location` must be a comma-separated string of map co-ordinates (longitude, latitude). For example, San Francisco would be `37.7771,-122.4196`.

* For Wunderground: A US zipcode, or any location that Wunderground supports. To find one, search [wunderground.com](https://wunderground.com) and copy the location part of the URL. For example, a result for San Francisco gives `https://www.wunderground.com/US/CA/San_Francisco.html` and London, England gives `https://www.wunderground.com/q/zmw:00000.1.03772`. The locations in each are `US/CA/San_Francisco` and `zmw:00000.1.03772`, respectively.
* For Dark Sky: `location` must be a comma-separated string of map co-ordinates (longitude, latitude). For example, San Francisco would be `37.7771,-122.4196`.

You must set up an [API key for Wunderground](https://www.wunderground.com/weather/api/) in order to use this Agent with Wunderground.

You must set up an [API key for Dark Sky](https://darksky.net/dev/) in order to use this Agent with Dark Sky.
You must set up an [API key for Dark Sky](https://darksky.net/dev/) in order to use this Agent.

Set `expected_update_period_in_days` to the maximum amount of time that you'd expect to pass between Events being created by this Agent.

If you want to see the returned texts in your language, set the `language` parameter in ISO 639-1 format.
MD

event_description <<-MD
Expand Down Expand Up @@ -67,37 +61,22 @@ def key_setup?

def default_options
{
'service' => 'wunderground',
'api_key' => 'your-key',
'location' => '94103',
'location' => '37.779329,-122.41915',
'which_day' => '1',
'language' => 'EN',
'expected_update_period_in_days' => '2'
'expected_update_period_in_days' => '2',
'language' => 'en'
}
end

def check
if key_setup?
create_event :payload => model(weather_provider, which_day).merge('location' => location)
create_event :payload => model(which_day).merge('location' => location)
end
end

private

def weather_provider
interpolated["service"].presence || "wunderground"
end

# a check to see if the weather provider is wunderground.
def wunderground?
weather_provider.downcase == "wunderground"
end

# a check to see if the weather provider is one of the valid aliases for Dark Sky.
def dark_sky?
["dark_sky", "darksky", "forecast_io", "forecastio"].include? weather_provider.downcase
end

def which_day
(interpolated["which_day"].presence || 1).to_i
end
Expand All @@ -111,14 +90,17 @@ def coordinates
end

def language
interpolated['language'].presence || 'EN'
interpolated["language"].presence || "en"
end

def wunderground?
interpolated["service"].presence && interpolated["service"].presence.downcase == "wunderground"
end

VALID_COORDS_REGEX = /^\s*-?\d{1,3}\.\d+\s*,\s*-?\d{1,3}\.\d+\s*$/

def validate_location
errors.add(:base, "location is required") unless location.present?
return if wunderground?
if location =~ VALID_COORDS_REGEX
lat, lon = coordinates
errors.add :base, "too low of a latitude" unless lat > -90
Expand All @@ -135,22 +117,12 @@ def validate_location
end

def validate_options
errors.add(:base, "service must be set to 'darksky' or 'wunderground'") unless wunderground? || dark_sky?
errors.add(:base, "The Weather Underground API has been disables since Jan 1st 2018, please switch to DarkSky") if wunderground?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a trivial typo disables and maybe the date should be updated.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good cach ;-). I just pushed an update

validate_location
errors.add(:base, "api_key is required") unless interpolated['api_key'].present?
errors.add(:base, "which_day selection is required") unless which_day.present?
end

def wunderground
if key_setup?
forecast = Wunderground.new(interpolated['api_key'], language: language.upcase).forecast_for(location)
merged = {}
forecast['forecast']['simpleforecast']['forecastday'].each { |daily| merged[daily['period']] = daily }
forecast['forecast']['txt_forecast']['forecastday'].each { |daily| (merged[daily['period']] || {}).merge!(daily) }
merged
end
end

def dark_sky
if key_setup?
ForecastIO.api_key = interpolated['api_key']
Expand All @@ -159,74 +131,69 @@ def dark_sky
end
end

def model(weather_provider,which_day)
if wunderground?
wunderground[which_day]
elsif dark_sky?
dark_sky.each do |value|
timestamp = Time.at(value.time)
if (timestamp.to_date - Time.now.to_date).to_i == which_day
day = {
'date' => {
'epoch' => value.time.to_s,
'pretty' => timestamp.strftime("%l:%M %p %Z on %B %d, %Y"),
'day' => timestamp.day,
'month' => timestamp.month,
'year' => timestamp.year,
'yday' => timestamp.yday,
'hour' => timestamp.hour,
'min' => timestamp.strftime("%M"),
'sec' => timestamp.sec,
'isdst' => timestamp.isdst ? 1 : 0 ,
'monthname' => timestamp.strftime("%B"),
'monthname_short' => timestamp.strftime("%b"),
'weekday_short' => timestamp.strftime("%a"),
'weekday' => timestamp.strftime("%A"),
'ampm' => timestamp.strftime("%p"),
'tz_short' => timestamp.zone
},
'period' => which_day.to_i,
'high' => {
'fahrenheit' => value.temperatureMax.round().to_s,
'epoch' => value.temperatureMaxTime.to_s,
'fahrenheit_apparent' => value.apparentTemperatureMax.round().to_s,
'epoch_apparent' => value.apparentTemperatureMaxTime.to_s,
'celsius' => ((5*(Float(value.temperatureMax) - 32))/9).round().to_s
},
'low' => {
'fahrenheit' => value.temperatureMin.round().to_s,
'epoch' => value.temperatureMinTime.to_s,
'fahrenheit_apparent' => value.apparentTemperatureMin.round().to_s,
'epoch_apparent' => value.apparentTemperatureMinTime.to_s,
'celsius' => ((5*(Float(value.temperatureMin) - 32))/9).round().to_s
},
'conditions' => value.summary,
'icon' => value.icon,
'avehumidity' => (value.humidity * 100).to_i,
'sunriseTime' => value.sunriseTime.to_s,
'sunsetTime' => value.sunsetTime.to_s,
'moonPhase' => value.moonPhase.to_s,
'precip' => {
'intensity' => value.precipIntensity.to_s,
'intensity_max' => value.precipIntensityMax.to_s,
'intensity_max_epoch' => value.precipIntensityMaxTime.to_s,
'probability' => value.precipProbability.to_s,
'type' => value.precipType
},
'dewPoint' => value.dewPoint.to_s,
'avewind' => {
'mph' => value.windSpeed.round().to_s,
'kph' => (Float(value.windSpeed) * 1.609344).round().to_s,
'degrees' => value.windBearing.to_s
},
'visibility' => value.visibility.to_s,
'cloudCover' => value.cloudCover.to_s,
'pressure' => value.pressure.to_s,
'ozone' => value.ozone.to_s
}
return day
end
end
def model(which_day)
value = dark_sky[which_day - 1]
if value
timestamp = Time.at(value.time)
day = {
'date' => {
'epoch' => value.time.to_s,
'pretty' => timestamp.strftime("%l:%M %p %Z on %B %d, %Y"),
'day' => timestamp.day,
'month' => timestamp.month,
'year' => timestamp.year,
'yday' => timestamp.yday,
'hour' => timestamp.hour,
'min' => timestamp.strftime("%M"),
'sec' => timestamp.sec,
'isdst' => timestamp.isdst ? 1 : 0 ,
'monthname' => timestamp.strftime("%B"),
'monthname_short' => timestamp.strftime("%b"),
'weekday_short' => timestamp.strftime("%a"),
'weekday' => timestamp.strftime("%A"),
'ampm' => timestamp.strftime("%p"),
'tz_short' => timestamp.zone
},
'period' => which_day.to_i,
'high' => {
'fahrenheit' => value.temperatureMax.round().to_s,
'epoch' => value.temperatureMaxTime.to_s,
'fahrenheit_apparent' => value.apparentTemperatureMax.round().to_s,
'epoch_apparent' => value.apparentTemperatureMaxTime.to_s,
'celsius' => ((5*(Float(value.temperatureMax) - 32))/9).round().to_s
},
'low' => {
'fahrenheit' => value.temperatureMin.round().to_s,
'epoch' => value.temperatureMinTime.to_s,
'fahrenheit_apparent' => value.apparentTemperatureMin.round().to_s,
'epoch_apparent' => value.apparentTemperatureMinTime.to_s,
'celsius' => ((5*(Float(value.temperatureMin) - 32))/9).round().to_s
},
'conditions' => value.summary,
'icon' => value.icon,
'avehumidity' => (value.humidity * 100).to_i,
'sunriseTime' => value.sunriseTime.to_s,
'sunsetTime' => value.sunsetTime.to_s,
'moonPhase' => value.moonPhase.to_s,
'precip' => {
'intensity' => value.precipIntensity.to_s,
'intensity_max' => value.precipIntensityMax.to_s,
'intensity_max_epoch' => value.precipIntensityMaxTime.to_s,
'probability' => value.precipProbability.to_s,
'type' => value.precipType
},
'dewPoint' => value.dewPoint.to_s,
'avewind' => {
'mph' => value.windSpeed.round().to_s,
'kph' => (Float(value.windSpeed) * 1.609344).round().to_s,
'degrees' => value.windBearing.to_s
},
'visibility' => value.visibility.to_s,
'cloudCover' => value.cloudCover.to_s,
'pressure' => value.pressure.to_s,
'ozone' => value.ozone.to_s
}
return day
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion data/default_scenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
"disabled": false,
"guid": "bdae6dfdf9d01a123ddd513e695fd466",
"options": {
"location": "94103",
"location": "42.3601,-71.0589",
"api_key": "put-your-key-here"
},
"schedule": "10pm",
Expand Down
2 changes: 1 addition & 1 deletion spec/data_fixtures/weather.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions spec/fixtures/agents.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ bob_weather_agent:
name: "SF Weather"
guid: <%= SecureRandom.hex %>
keep_events_for: <%= 45.days %>
options: <%= { :location => 94102, :lat => 37.779329, :lng => -122.41915, :api_key => 'test' }.to_json.inspect %>
options: <%= { :location => "37.779329,-122.41915", :api_key => 'test' }.to_json.inspect %>

bob_formatting_agent:
type: Agents::EventFormattingAgent
Expand All @@ -75,7 +75,7 @@ jane_weather_agent:
name: "SF Weather"
guid: <%= SecureRandom.hex %>
keep_events_for: <%= 30.days %>
options: <%= { :location => 94103, :lat => 37.779329, :lng => -122.41915, :api_key => 'test' }.to_json.inspect %>
options: <%= { :location => "37.779329,-122.41915", :api_key => 'test' }.to_json.inspect %>

jane_rain_notifier_agent:
type: Agents::TriggerAgent
Expand Down
2 changes: 1 addition & 1 deletion spec/fixtures/test_default_scenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"disabled": false,
"guid": "bdae6dfdf9d01a123ddd513e695fd466",
"options": {
"location": "94103",
"location": "42.3601,-71.0589",
"api_key": "put-your-key-here"
},
"schedule": "10pm",
Expand Down
2 changes: 1 addition & 1 deletion spec/importers/scenario_import_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
let(:weather_agent_options) {
{
'api_key' => 'some-api-key',
'location' => '12345'
'location' => '42.3601,-71.0589'
}
}
let(:trigger_agent_options) {
Expand Down
7 changes: 3 additions & 4 deletions spec/models/agent_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def receive(events)

describe ".receive!" do
before do
stub_request(:any, /wunderground/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/weather.json")), :status => 200)
stub_request(:any, /darksky/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/weather.json")), :status => 200)
stub.any_instance_of(Agents::WeatherAgent).is_tomorrow?(anything) { true }
end

Expand Down Expand Up @@ -910,8 +910,7 @@ def @agent.receive_webhook(params)

describe ".drop_pending_events" do
before do
stub_request(:any, /wunderground/).to_return(body: File.read(Rails.root.join("spec/data_fixtures/weather.json")), status: 200)
stub.any_instance_of(Agents::WeatherAgent).is_tomorrow?(anything) { true }
stub_request(:any, /darksky/).to_return(body: File.read(Rails.root.join("spec/data_fixtures/weather.json")), status: 200)
end

it "should drop pending events while the agent was disabled when set to true" do
Expand All @@ -923,7 +922,7 @@ def @agent.receive_webhook(params)
Agent.async_check(agent1.id)
Agent.receive!
}.to change { agent1.events.count }.by(1)
}.to change { agent2.events.count }.by(1)
}.to change { agent2.events.count }.by(0)

agent2.disabled = true
agent2.save!
Expand Down
2 changes: 1 addition & 1 deletion spec/models/agents/email_agent_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def get_message_part(mail, content_type)
end

it "can receive complex events and send them on" do
stub_request(:any, /wunderground/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/weather.json")), :status => 200)
stub_request(:any, /darksky/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/weather.json")), :status => 200)
stub.any_instance_of(Agents::WeatherAgent).is_tomorrow?(anything) { true }
@checker.sources << agents(:bob_weather_agent)

Expand Down
2 changes: 1 addition & 1 deletion spec/models/agents/email_digest_agent_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def get_message_part(mail, content_type)
end

it "can receive complex events and send them on" do
stub_request(:any, /wunderground/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/weather.json")), :status => 200)
stub_request(:any, /darksky/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/weather.json")), :status => 200)
stub.any_instance_of(Agents::WeatherAgent).is_tomorrow?(anything) { true }
@checker.sources << agents(:bob_weather_agent)

Expand Down
Loading