govkit-h 0.7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +3 -0
- data/Gemfile +13 -0
- data/LICENSE +20 -0
- data/README.md +70 -0
- data/Rakefile +82 -0
- data/USAGE +1 -0
- data/VERSION +1 -0
- data/generators/govkit/govkit_generator.rb +24 -0
- data/generators/govkit/templates/govkit.rb +24 -0
- data/govkit.gemspec +130 -0
- data/init.rb +4 -0
- data/lib/generators/govkit/govkit_generator.rb +21 -0
- data/lib/generators/govkit/templates/create_mentions.rb +21 -0
- data/lib/generators/govkit/templates/govkit.rb +24 -0
- data/lib/generators/govkit/templates/mention.rb +15 -0
- data/lib/gov_kit.rb +45 -0
- data/lib/gov_kit/acts_as_noteworthy.rb +63 -0
- data/lib/gov_kit/configuration.rb +58 -0
- data/lib/gov_kit/follow_the_money.rb +176 -0
- data/lib/gov_kit/open_congress.rb +125 -0
- data/lib/gov_kit/open_congress/bill.rb +171 -0
- data/lib/gov_kit/open_congress/blog_post.rb +15 -0
- data/lib/gov_kit/open_congress/news_post.rb +15 -0
- data/lib/gov_kit/open_congress/person.rb +141 -0
- data/lib/gov_kit/open_congress/person_stat.rb +13 -0
- data/lib/gov_kit/open_congress/roll_call.rb +14 -0
- data/lib/gov_kit/open_congress/roll_call_comparison.rb +28 -0
- data/lib/gov_kit/open_congress/voting_comparison.rb +44 -0
- data/lib/gov_kit/open_states.rb +132 -0
- data/lib/gov_kit/railtie.rb +24 -0
- data/lib/gov_kit/resource.rb +190 -0
- data/lib/gov_kit/search_engines.rb +7 -0
- data/lib/gov_kit/search_engines/bing.rb +38 -0
- data/lib/gov_kit/search_engines/google_blog.rb +32 -0
- data/lib/gov_kit/search_engines/google_news.rb +47 -0
- data/lib/gov_kit/search_engines/technorati.rb +35 -0
- data/lib/gov_kit/search_engines/wikipedia.rb +27 -0
- data/lib/gov_kit/transparency_data.rb +144 -0
- data/lib/gov_kit/vote_smart.rb +126 -0
- data/lib/govkit.rb +1 -0
- data/spec/fixtures/bing/news_search.response +1 -0
- data/spec/fixtures/bing/no_results.response +1 -0
- data/spec/fixtures/follow_the_money/business-page0.response +28 -0
- data/spec/fixtures/follow_the_money/business-page1.response +12 -0
- data/spec/fixtures/follow_the_money/contribution.response +12 -0
- data/spec/fixtures/follow_the_money/unauthorized.response +8 -0
- data/spec/fixtures/open_congress/person.response +8 -0
- data/spec/fixtures/open_states/401.response +10 -0
- data/spec/fixtures/open_states/404.response +9 -0
- data/spec/fixtures/open_states/410.response +6 -0
- data/spec/fixtures/open_states/bill.response +240 -0
- data/spec/fixtures/open_states/bill_query.response +1990 -0
- data/spec/fixtures/open_states/committee_find.response +53 -0
- data/spec/fixtures/open_states/committee_query.response +190 -0
- data/spec/fixtures/open_states/legislator.response +34 -0
- data/spec/fixtures/open_states/legislator_query.response +144 -0
- data/spec/fixtures/open_states/state.response +60 -0
- data/spec/fixtures/search_engines/google_news.response +8 -0
- data/spec/fixtures/transparency_data/contributions.response +18 -0
- data/spec/fixtures/transparency_data/entities_search.response +7 -0
- data/spec/fixtures/transparency_data/entities_search_limit_0.response +7 -0
- data/spec/fixtures/transparency_data/entities_search_limit_1.response +7 -0
- data/spec/fixtures/transparency_data/grants_find_all.response +7 -0
- data/spec/fixtures/transparency_data/lobbyists_find_all.response +7 -0
- data/spec/follow_the_money_spec.rb +61 -0
- data/spec/open_congress_spec.rb +108 -0
- data/spec/open_states_spec.rb +213 -0
- data/spec/search_engines_spec.rb +44 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/transparency_data_spec.rb +106 -0
- metadata +258 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
module GovKit
|
2
|
+
|
3
|
+
# Parent class for OpenStates resources
|
4
|
+
# See http://openstates.sunlightlabs.com/api/
|
5
|
+
class OpenStatesResource < Resource
|
6
|
+
|
7
|
+
# Uses default_params from the HTTParty gem.
|
8
|
+
# See HTTParty::ClassMethods:
|
9
|
+
# http://rubydoc.info/gems/httparty/0.7.4/HTTParty/ClassMethods#default_params-instance_method
|
10
|
+
default_params :output => 'json', :apikey => GovKit::configuration.sunlight_apikey
|
11
|
+
base_uri GovKit::configuration.openstates_base_url
|
12
|
+
|
13
|
+
# Do a GET query, with optional parameters.
|
14
|
+
#
|
15
|
+
# OpenStates returns a 404 error when a query
|
16
|
+
# returns nothing.
|
17
|
+
#
|
18
|
+
# So, if a query result is a resource not found error,
|
19
|
+
# we return an empty set.
|
20
|
+
def self.get_uri(uri, options={})
|
21
|
+
begin
|
22
|
+
response = get(uri, options)
|
23
|
+
result = parse(response)
|
24
|
+
rescue ResourceNotFound
|
25
|
+
return []
|
26
|
+
end
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
# Ruby module for interacting with the Open States Project API
|
33
|
+
# See http://openstates.sunlightlabs.com/api/
|
34
|
+
# Most +find+ and +search+ methods:
|
35
|
+
# * call HTTParty::ClassMethods#get
|
36
|
+
# * which returns an HTTParty::Response object
|
37
|
+
# * which is passed to GovKit::Resource#parse
|
38
|
+
# * which uses the response to populate a Resource
|
39
|
+
#
|
40
|
+
module OpenStates
|
41
|
+
ROLE_MEMBER = "member"
|
42
|
+
ROLE_COMMITTEE_MEMBER = "committee member"
|
43
|
+
CHAMBER_UPPER = "upper"
|
44
|
+
CHAMBER_LOWER = "lower"
|
45
|
+
|
46
|
+
# The State class represents the state data returned from Open States.
|
47
|
+
#
|
48
|
+
# For details about fields returned, see the Open States documentation, at
|
49
|
+
# http://openstates.sunlightlabs.com/api/metadata/,
|
50
|
+
#
|
51
|
+
class State < OpenStatesResource
|
52
|
+
def self.find_by_abbreviation(abbreviation)
|
53
|
+
get_uri("/metadata/#{abbreviation}/")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# The Bill class represents the bill data returned from Open States.
|
58
|
+
#
|
59
|
+
# For details about fields returned, see the Open States documentation, at
|
60
|
+
# http://openstates.sunlightlabs.com/api/bills/,
|
61
|
+
#
|
62
|
+
class Bill < OpenStatesResource
|
63
|
+
# http://openstates.sunlightlabs.com/api/v1/bills/ca/20092010/AB 667/
|
64
|
+
def self.find(state_abbrev, session, bill_id, chamber = '')
|
65
|
+
escaped_bill_id = bill_id.gsub(/ /, '%20')
|
66
|
+
escaped_session = session.gsub(/ /, '%20')
|
67
|
+
|
68
|
+
get_uri("/bills/#{state_abbrev.downcase}/#{escaped_session}/#{chamber.blank? ? '' : chamber + '/'}#{escaped_bill_id}/")
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.search(query, options = {})
|
72
|
+
result = get_uri('/bills/', :query => {:q => query}.merge(options))
|
73
|
+
return Array(result)
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.latest(updated_since, ops = {})
|
77
|
+
response = get('/bills/', :query => {:updated_since => updated_since.to_s}.merge(ops))
|
78
|
+
parse(response)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# The Legislator class represents the legislator data returned from Open States.
|
83
|
+
#
|
84
|
+
# For details about fields returned, see the Open States documentation, at
|
85
|
+
# http://openstates.sunlightlabs.com/api/legislators/,
|
86
|
+
#
|
87
|
+
class Legislator < OpenStatesResource
|
88
|
+
def self.find(legislator_id)
|
89
|
+
get_uri("/legislators/#{legislator_id}/")
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.search(options = {})
|
93
|
+
result = get_uri('/legislators/', :query => options)
|
94
|
+
return Array(result)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# The Committee class represents the committee data returned from Open States.
|
99
|
+
#
|
100
|
+
# For details about fields returned, see the Open States documentation, at
|
101
|
+
# http://openstates.sunlightlabs.com/api/committees/,
|
102
|
+
#
|
103
|
+
class Committee < OpenStatesResource
|
104
|
+
def self.find(committee_id)
|
105
|
+
get_uri("/committees/#{committee_id}/")
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.search(options = {})
|
109
|
+
get_uri('/committees/', :query => options)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Role < OpenStatesResource; end
|
114
|
+
|
115
|
+
class Sponsor < OpenStatesResource; end
|
116
|
+
|
117
|
+
class Version < OpenStatesResource; end
|
118
|
+
|
119
|
+
class Source < OpenStatesResource; end
|
120
|
+
|
121
|
+
class Address < OpenStatesResource; end
|
122
|
+
|
123
|
+
class Action < OpenStatesResource; end
|
124
|
+
|
125
|
+
class Vote < OpenStatesResource
|
126
|
+
def self.find(vote_id)
|
127
|
+
response = get("/votes/#{vote_id}/")
|
128
|
+
parse(response)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'govkit'
|
2
|
+
|
3
|
+
module GovKit
|
4
|
+
if defined? Rails::Railtie
|
5
|
+
require 'rails'
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
initializer 'govkit.insert_into_active_record' do
|
8
|
+
ActiveSupport.on_load :active_record do
|
9
|
+
GovKit::Railtie.insert
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# This class exists in order to run its insert method while
|
16
|
+
# Rails is loading.
|
17
|
+
# This then adds GovKit::ActsAsNoteworthy to ActiveRecord::Base.
|
18
|
+
# See http://api.rubyonrails.org/classes/Rails/Railtie.html
|
19
|
+
class Railtie
|
20
|
+
def self.insert
|
21
|
+
ActiveRecord::Base.send(:include, GovKit::ActsAsNoteworthy)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module GovKit
|
2
|
+
|
3
|
+
|
4
|
+
# This is the parent class for the classes that wrap
|
5
|
+
# the data returned to govkit.
|
6
|
+
#
|
7
|
+
# Subclasses are responsible for fetching the data from
|
8
|
+
# different web services; Resource will then parse the returned data,
|
9
|
+
# converting returned fields to instance methods.
|
10
|
+
#
|
11
|
+
# Initialize a Resource with a hash of attributes, or an array of hashes.
|
12
|
+
# For each attribute, add a getter and setter to this instance.
|
13
|
+
# @example
|
14
|
+
# res = Resource.new { "aaa" => "111", "bbb" => "222", "ccc" => "333" }
|
15
|
+
# res.aaa == "111"
|
16
|
+
# res.bbb == "222"
|
17
|
+
# res.ccc == "333"
|
18
|
+
#
|
19
|
+
# Includes {http://rdoc.info/github/jnunemaker/httparty/master/HTTParty/ClassMethods HTTParty}, which provides convenience methods like get().
|
20
|
+
class Resource
|
21
|
+
include HTTParty
|
22
|
+
format :json
|
23
|
+
|
24
|
+
# The attributes data returned by the service.
|
25
|
+
attr_reader :attributes
|
26
|
+
|
27
|
+
# The response returned by the service.
|
28
|
+
attr_reader :raw_response
|
29
|
+
|
30
|
+
def initialize(attributes = {})
|
31
|
+
@attributes = {}
|
32
|
+
@raw_response = attributes
|
33
|
+
|
34
|
+
unload(attributes)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Hash] the response object, potentially useful for comparison on sync
|
38
|
+
#
|
39
|
+
def to_md5
|
40
|
+
Digest::MD5.hexdigest(@raw_response.body)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Handles the basic responses we might get back from a web service.
|
44
|
+
#
|
45
|
+
# On failure, throws an error.
|
46
|
+
#
|
47
|
+
# If a service returns something other than a 404 when an object is not found,
|
48
|
+
# you'll need to handle that in the subclass.
|
49
|
+
#
|
50
|
+
# @param [Object] response the object.
|
51
|
+
# @return [Resource] a new Resource created from the response.
|
52
|
+
#
|
53
|
+
def self.parse(response)
|
54
|
+
|
55
|
+
if response.class == HTTParty::Response
|
56
|
+
case response.response
|
57
|
+
when Net::HTTPNotFound
|
58
|
+
raise ResourceNotFound, "404 Not Found"
|
59
|
+
when Net::HTTPGone
|
60
|
+
raise ResourceNotFound, "404 Not Found"
|
61
|
+
when Net::HTTPUnauthorized
|
62
|
+
raise NotAuthorized, "401 Not Authorized; have you set up your API key?"
|
63
|
+
when Net::HTTPServerError
|
64
|
+
raise ServerError, '5xx server error'
|
65
|
+
when Net::HTTPClientError
|
66
|
+
raise ClientError, '4xx client error'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
return [] unless !response.blank?
|
71
|
+
|
72
|
+
instantiate(response)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Instantiate new GovKit::Resources.
|
76
|
+
#
|
77
|
+
# @param [Hash] record a hash of values returned by a service, or an array of hashes.
|
78
|
+
# @return [Resource]
|
79
|
+
#
|
80
|
+
# If +record+ is a hash, return a single GovKit::Resource.
|
81
|
+
# If it is an array, return an array of GovKit::Resources.
|
82
|
+
#
|
83
|
+
def self.instantiate(record)
|
84
|
+
if record.is_a?(Array)
|
85
|
+
instantiate_collection(record)
|
86
|
+
else
|
87
|
+
new(record)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Instantiate a set of records.
|
92
|
+
#
|
93
|
+
# @return [Array] Array of records
|
94
|
+
# @param [Array] collection An array of records
|
95
|
+
def self.instantiate_collection(collection)
|
96
|
+
collection.collect! { |record| new(record) }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Given a hash of attributes, assign it to the @attributes member.
|
100
|
+
# Then for each attribute, create or set a pair of member accessors with the name
|
101
|
+
# of the attribute's key.
|
102
|
+
#
|
103
|
+
# If the value of the attribute is itself an array or a hash,
|
104
|
+
# then create a new class with the (singularized) key as a name, and with a parent class of Resource,
|
105
|
+
# and initialize it with the hash.
|
106
|
+
#
|
107
|
+
# @param [Hash] attributes the attributes returned by the web service.
|
108
|
+
#
|
109
|
+
def unload(attributes)
|
110
|
+
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
|
111
|
+
|
112
|
+
attributes.each do |key, value|
|
113
|
+
@attributes[key.to_s] =
|
114
|
+
case value
|
115
|
+
when Array
|
116
|
+
resource = resource_for_collection(key)
|
117
|
+
value.map do |attrs|
|
118
|
+
if attrs.is_a?(String) || attrs.is_a?(Numeric)
|
119
|
+
attrs.duplicable? ? attrs.dup : attrs
|
120
|
+
else
|
121
|
+
resource.new(attrs)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
when Hash
|
125
|
+
resource = find_or_create_resource_for(key)
|
126
|
+
resource.new(value)
|
127
|
+
else
|
128
|
+
value.dup rescue value
|
129
|
+
end
|
130
|
+
end
|
131
|
+
self
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# Finds a member of the GovKit module with the given name.
|
137
|
+
# If the resource doesn't exist, creates it.
|
138
|
+
#
|
139
|
+
def resource_for_collection(name)
|
140
|
+
find_or_create_resource_for(name.to_s.singularize)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Searches each module in +ancestors+ for members named +resource_name+
|
144
|
+
# Returns the named resource
|
145
|
+
# Throws a NameError if none of the resources in the list contains +resource_name+
|
146
|
+
#
|
147
|
+
def find_resource_in_modules(resource_name, ancestors)
|
148
|
+
if namespace = ancestors.detect { |a| a.constants.include?(resource_name.to_sym) }
|
149
|
+
return namespace.const_get(resource_name)
|
150
|
+
else
|
151
|
+
raise NameError, "Namespace for #{namespace} not found"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Searches the GovKit module for a resource with the name +name+, cleaned and camelized
|
156
|
+
# Returns that resource.
|
157
|
+
# If the resource isn't found, it's created.
|
158
|
+
#
|
159
|
+
def find_or_create_resource_for(name)
|
160
|
+
resource_name = name.to_s.gsub(/^[_\-+]/,'').gsub(/^(\-?\d)/, "n#{$1}").gsub(/(\s|-)/, '').camelize
|
161
|
+
if self.class.parents.size > 1
|
162
|
+
find_resource_in_modules(resource_name, self.class.parents)
|
163
|
+
else
|
164
|
+
self.class.const_get(resource_name)
|
165
|
+
end
|
166
|
+
rescue NameError
|
167
|
+
if self.class.const_defined?(resource_name)
|
168
|
+
resource = self.class.const_get(resource_name)
|
169
|
+
else
|
170
|
+
resource = self.class.const_set(resource_name, Class.new(GovKit::Resource))
|
171
|
+
end
|
172
|
+
resource
|
173
|
+
end
|
174
|
+
|
175
|
+
def method_missing(method_symbol, * arguments) #:nodoc:
|
176
|
+
method_name = method_symbol.to_s
|
177
|
+
|
178
|
+
case method_name.last
|
179
|
+
when "="
|
180
|
+
attributes[method_name.first(-1)] = arguments.first
|
181
|
+
when "?"
|
182
|
+
!attributes[method_name.first(-1)].blank?
|
183
|
+
when "]"
|
184
|
+
attributes[arguments.first.to_s]
|
185
|
+
else
|
186
|
+
attributes.has_key?(method_name) ? attributes[method_name] : super
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module GovKit::SearchEngines
|
2
|
+
autoload :GoogleNews, 'gov_kit/search_engines/google_news'
|
3
|
+
autoload :GoogleBlog, 'gov_kit/search_engines/google_blog'
|
4
|
+
autoload :Technorati, 'gov_kit/search_engines/technorati'
|
5
|
+
autoload :Bing, 'gov_kit/search_engines/bing'
|
6
|
+
autoload :Wikipedia, 'gov_kit/search_engines/wikipedia'
|
7
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module GovKit
|
2
|
+
module SearchEngines
|
3
|
+
class Bing
|
4
|
+
def self.search(query=[], options={})
|
5
|
+
host = GovKit::configuration.bing_base_url
|
6
|
+
query = [query, options[:geo]].compact.join('+')
|
7
|
+
|
8
|
+
options['Sources'] ||= 'news'
|
9
|
+
|
10
|
+
path = "/json.aspx?Query=#{URI::encode(query)}&AppId=#{GovKit::configuration.bing_appid}&Sources=#{options['Sources']}"
|
11
|
+
|
12
|
+
doc = JSON.parse(make_request(host, path))
|
13
|
+
|
14
|
+
mentions = []
|
15
|
+
|
16
|
+
if news_items = doc['SearchResponse']['News']
|
17
|
+
puts "#{news_items['Results'].size} from Bing"
|
18
|
+
news_items['Results'].each do |i|
|
19
|
+
mention = GovKit::Mention.new
|
20
|
+
mention.title = i['Title']
|
21
|
+
mention.search_source = 'Bing'
|
22
|
+
mention.date = DateTime.parse(i['Date'])
|
23
|
+
mention.excerpt = i['Snippet']
|
24
|
+
mention.source = i['Source']
|
25
|
+
mention.url = i['Url']
|
26
|
+
|
27
|
+
mentions << mention
|
28
|
+
end
|
29
|
+
end
|
30
|
+
mentions
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.make_request(host, path)
|
34
|
+
Net::HTTP.get(host, path)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module GovKit
|
2
|
+
module SearchEngines
|
3
|
+
class GoogleBlog
|
4
|
+
def self.search(query=[], options = {})
|
5
|
+
query = [query, options[:geo]].compact.join('+')
|
6
|
+
host = GovKit::configuration.google_blog_base_url
|
7
|
+
path = "/blogsearch_feeds?q=#{URI::encode(query)}&hl=en&output=rss&num=50"
|
8
|
+
|
9
|
+
doc = Nokogiri::XML(make_request(host, path))
|
10
|
+
|
11
|
+
mentions = []
|
12
|
+
|
13
|
+
doc.xpath('//item').each do |i|
|
14
|
+
mention = GovKit::Mention.new
|
15
|
+
mention.title = i.xpath('title').inner_text
|
16
|
+
mention.search_source = 'Google Blogs'
|
17
|
+
mention.date = i.xpath('dc:date').inner_text
|
18
|
+
mention.excerpt = i.xpath('description').inner_text
|
19
|
+
mention.source = i.xpath('dc:publisher').inner_text
|
20
|
+
mention.url = i.xpath('link').inner_text
|
21
|
+
|
22
|
+
mentions << mention
|
23
|
+
end
|
24
|
+
mentions
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.make_request(host, path)
|
28
|
+
response = Net::HTTP.get(host, path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module GovKit
|
2
|
+
module SearchEngines
|
3
|
+
|
4
|
+
# Class to wrap access to Google News.
|
5
|
+
class GoogleNews
|
6
|
+
|
7
|
+
# Fetches stories about a topic from google news.
|
8
|
+
# Returns an array of GovKit::Mention objects.
|
9
|
+
#
|
10
|
+
# query: The query wanted For example:
|
11
|
+
# mentions = GoogleNews.search("Nancy Pelosi")
|
12
|
+
#
|
13
|
+
# options: Any additional parameters to the search. eg.:
|
14
|
+
# :geo => 'Texas' will add &geo=Texas to the URL.
|
15
|
+
# :num => 100 will show 100 results per page.
|
16
|
+
#
|
17
|
+
def self.search(query=[], options={})
|
18
|
+
query = Array(query).join('+')
|
19
|
+
host = GovKit::configuration.google_news_base_url
|
20
|
+
options[:num] ||= 50
|
21
|
+
|
22
|
+
path = "/news?q=#{URI::encode(query)}&output=rss" + '&' + options.map { |k, v| URI::encode(k.to_s) + '=' + URI::encode(v.to_s) }.join('&')
|
23
|
+
|
24
|
+
doc = Nokogiri::XML(make_request(host, path))
|
25
|
+
|
26
|
+
mentions = []
|
27
|
+
|
28
|
+
doc.xpath('//item').each do |i|
|
29
|
+
mention = GovKit::Mention.new
|
30
|
+
mention.title = i.xpath('title').inner_text.split(" - ").first
|
31
|
+
mention.date = i.xpath('pubDate').inner_text
|
32
|
+
mention.excerpt = i.xpath('description').inner_text
|
33
|
+
mention.source = i.xpath('title').inner_text.split(" - ").last
|
34
|
+
mention.url = i.xpath('link').inner_text
|
35
|
+
|
36
|
+
mentions << mention
|
37
|
+
end
|
38
|
+
|
39
|
+
mentions
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.make_request(host, path)
|
43
|
+
response = Net::HTTP.get(host, path)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|