netroots-ruby-votesmart 0.1.1 → 0.2.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/History.txt +4 -0
- data/README.rdoc +48 -0
- data/VERSION.yml +4 -0
- data/lib/mcll4r/MIT-LICENSE +20 -0
- data/lib/mcll4r/README +19 -0
- data/lib/mcll4r/mcll4r.rb +24 -0
- data/lib/mcll4r/mcll4r_test.rb +35 -0
- data/lib/ruby-votesmart.rb +22 -0
- data/lib/vote_smart/address.rb +42 -0
- data/lib/vote_smart/candidate.rb +32 -0
- data/lib/vote_smart/candidate_bio.rb +16 -0
- data/lib/vote_smart/candidate_office.rb +15 -0
- data/lib/vote_smart/committee.rb +23 -0
- data/lib/vote_smart/common.rb +91 -0
- data/lib/vote_smart/district.rb +36 -0
- data/lib/vote_smart/election.rb +21 -0
- data/lib/vote_smart/leadership.rb +16 -0
- data/lib/vote_smart/local.rb +21 -0
- data/lib/vote_smart/measure.rb +16 -0
- data/lib/vote_smart/notes.rb +11 -0
- data/lib/vote_smart/npat.rb +11 -0
- data/lib/vote_smart/office.rb +113 -0
- data/lib/vote_smart/official.rb +111 -0
- data/lib/vote_smart/phone.rb +12 -0
- data/lib/vote_smart/rating.rb +26 -0
- data/lib/vote_smart/state.rb +29 -0
- data/lib/vote_smart/vote.rb +70 -0
- data/spec/responses/Address.get_office.1721.js +1 -0
- data/spec/responses/District.get_by_office_state.7.GA.js +1 -0
- data/spec/responses/District.get_by_office_state.8.GA.js +1 -0
- data/spec/responses/District.get_by_office_state.9.GA.js +1 -0
- data/spec/responses/Office.get_offices_by_type.C.js +1 -0
- data/spec/responses/Office.get_offices_by_type.L.js +1 -0
- data/spec/responses/Office.get_offices_by_type.P.js +1 -0
- data/spec/responses/Office.get_offices_by_type.S.js +1 -0
- data/spec/responses/Office.get_types.js +1 -0
- data/spec/responses/Official.get_by_district.20451.js +1 -0
- data/spec/responses/Official.get_by_district.20689.js +1 -0
- data/spec/responses/Official.get_by_district.21946.js +1 -0
- data/spec/responses/Official.get_by_office_state.12.GA.js +1 -0
- data/spec/responses/Official.get_by_office_state.13.GA.js +1 -0
- data/spec/responses/Official.get_by_office_state.33.GA.js +1 -0
- data/spec/responses/Official.get_by_office_state.42.GA.js +1 -0
- data/spec/responses/Official.get_by_office_state.44.GA.js +1 -0
- data/spec/responses/Official.get_by_office_state.45.GA.js +1 -0
- data/spec/responses/Official.get_by_office_state.53.GA.js +1 -0
- data/spec/responses/State.get_state.GA.js +1 -0
- data/spec/responses/State.get_state_ids.js +1 -0
- data/spec/responses/authorization_failed.js +1 -0
- data/spec/spec_helper.rb +98 -0
- data/spec/vote_smart/district_spec.rb +36 -0
- data/spec/vote_smart/office_spec.rb +96 -0
- data/spec/vote_smart/official_spec.rb +44 -0
- data/spec/vote_smart/state_spec.rb +28 -0
- metadata +68 -29
data/History.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= project-vote-smart
|
2
|
+
|
3
|
+
* FIX (url)
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
FIX (describe your package)
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
* FIX (list of features or problems)
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
FIX (code sample of usage)
|
16
|
+
|
17
|
+
== REQUIREMENTS:
|
18
|
+
|
19
|
+
* FIX (list of requirements)
|
20
|
+
|
21
|
+
== INSTALL:
|
22
|
+
|
23
|
+
* FIX (sudo gem install, anything else)
|
24
|
+
|
25
|
+
== LICENSE:
|
26
|
+
|
27
|
+
(The MIT License)
|
28
|
+
|
29
|
+
Copyright (c) 2009 FIXME full name
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
32
|
+
a copy of this software and associated documentation files (the
|
33
|
+
'Software'), to deal in the Software without restriction, including
|
34
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
35
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
36
|
+
permit persons to whom the Software is furnished to do so, subject to
|
37
|
+
the following conditions:
|
38
|
+
|
39
|
+
The above copyright notice and this permission notice shall be
|
40
|
+
included in all copies or substantial portions of the Software.
|
41
|
+
|
42
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
43
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
44
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
45
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
46
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
47
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
48
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/VERSION.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Mobile Commons
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/mcll4r/README
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
=== mcll4r
|
2
|
+
|
3
|
+
- [Code on GitHub](http://github.com/mcommons/mcll4r)
|
4
|
+
|
5
|
+
=== Description
|
6
|
+
|
7
|
+
Ruby client for Mobile Commons Legislative Lookup API
|
8
|
+
|
9
|
+
Based on the API described at http://congress.mcommons.com
|
10
|
+
|
11
|
+
|
12
|
+
=== Authors
|
13
|
+
|
14
|
+
- Maintained by [Benjamin Stein](mailto:ben@mcommons.com), [Mal McKay](mailto:mal@mcommons.com) & [Nathan Woodhull](mailto:nathan@mcommons.com)
|
15
|
+
|
16
|
+
=== License
|
17
|
+
|
18
|
+
Copyright (c) 2008 Mobile Commons
|
19
|
+
See MIT-LICENSE in this directory.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
class Mcll4r
|
5
|
+
include HTTParty
|
6
|
+
base_uri "http://congress.mcommons.com"
|
7
|
+
format :xml
|
8
|
+
|
9
|
+
def district_lookup(lat, lng)
|
10
|
+
filter_for_errors self.class.get("/districts/lookup.xml", :query=>{:lat=>lat, :lng=>lng})
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def filter_for_errors(hash)
|
16
|
+
if hash['response']['error']
|
17
|
+
raise DistrictNotFound.new(hash['response']['error'])
|
18
|
+
end
|
19
|
+
hash
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class DistrictNotFound < Exception; end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'mcll4r'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class Mcll4rTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@mcll4r = Mcll4r.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_assert_we_get_back_correct_district_data
|
11
|
+
expected = {
|
12
|
+
"response" => {
|
13
|
+
"state_upper" => { "district" => "029", "display_name" => "TX 29th", "state" => "TX" },
|
14
|
+
"federal" => { "district" => "16", "display_name" => "TX 16th", "state" => "TX" },
|
15
|
+
"state_lower" => { "district" => "077", "display_name" => "TX 77th", "state" => "TX" },
|
16
|
+
"lng" => "-106.490969",
|
17
|
+
"lat" => "31.76321"
|
18
|
+
}
|
19
|
+
}
|
20
|
+
assert_equal expected, @mcll4r.district_lookup(31.76321, -106.490969)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_assert_raise_on_error
|
24
|
+
assert_raise DistrictNotFound do
|
25
|
+
@mcll4r.district_lookup(nil,nil)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_assert_raise_on_district_not_found
|
30
|
+
assert_raise DistrictNotFound do
|
31
|
+
@mcll4r.district_lookup( 1.0, 1.0 )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ym4r/google_maps/geocoding'
|
3
|
+
require 'active_support'
|
4
|
+
include Ym4r::GoogleMaps
|
5
|
+
|
6
|
+
module VoteSmart
|
7
|
+
API_URL = "http://api.votesmart.org/"
|
8
|
+
API_FORMAT = "JSON"
|
9
|
+
|
10
|
+
mattr_accessor :api_key
|
11
|
+
|
12
|
+
class RequestFailed < Exception; end
|
13
|
+
end
|
14
|
+
|
15
|
+
VoteSmart.api_key = "key"
|
16
|
+
|
17
|
+
require "#{File.dirname(__FILE__)}/mcll4r/mcll4r.rb"
|
18
|
+
require "#{File.dirname(__FILE__)}/vote_smart/common.rb"
|
19
|
+
|
20
|
+
Dir["#{File.dirname(__FILE__)}/vote_smart/*.rb"].each do |source_file|
|
21
|
+
require source_file unless source_file == "#{File.dirname(__FILE__)}/vote_smart/common.rb"
|
22
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module VoteSmart
|
2
|
+
|
3
|
+
class Address < Common
|
4
|
+
|
5
|
+
attr_accessor :type, :street, :city, :state, :zip
|
6
|
+
|
7
|
+
set_attribute_map "type" => :type, "street" => :street, "city" => :city, "state" => :state, "zip" => :zip
|
8
|
+
|
9
|
+
|
10
|
+
# Returns a campaign office's contact information
|
11
|
+
def self.get_campaign can_id
|
12
|
+
request("Address.getCampaign", "candidateId" => can_id)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns a campaign office's contact information
|
16
|
+
def self.get_campaign_web_address can_id
|
17
|
+
request("Address.getCampaignWebAddress", "candidateId" => can_id)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a campaign office's contact information
|
21
|
+
def self.get_campaign_by_election election_id
|
22
|
+
request("Address.getCampaignByElection", "electionId" => election_id)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns an incumbent office's contact information
|
26
|
+
def self.get_office candidate_id
|
27
|
+
request("Address.getOffice", "candidateId" => candidate_id)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns an incumbent office's contact information
|
31
|
+
def self.get_office_web_address can_id
|
32
|
+
request("Address.getOfficeWebAddress", "candidateId" => can_id)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a (sometimes)list of offices that fit office_id and state_id
|
36
|
+
def self.get_office_by_office_state office_id, state_id = 'NA'
|
37
|
+
request("Address.getOfficeByOfficeState", "officeId" => office_id, "stateId" => state_id)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module VoteSmart
|
2
|
+
|
3
|
+
class Candidate < Common
|
4
|
+
|
5
|
+
# Returns a list of candidates/incumbents that fit the criteria
|
6
|
+
def self.get_by_office_state office_id, state_id = "NA", election_year = nil
|
7
|
+
request("Candidates.getByOfficeState", "officeId" => office_id, "stateId" => state_id, "electionYear" => election_year)
|
8
|
+
end
|
9
|
+
|
10
|
+
#Searches for candidates with exact lastname matches
|
11
|
+
def self.get_by_lastname last_name, election_year = nil
|
12
|
+
request("Candidates.getByLastname", "lastName" => last_name, "electionYear" => election_year)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Searches for candidates with fuzzy lastname match
|
16
|
+
def self.get_by_levenstein last_name, election_year = nil
|
17
|
+
request("Candidates.getByLevenstein", "lastName" => last_name, "electionYear" => election_year)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns candidates in the provided election_id
|
21
|
+
def self.get_by_election election_id
|
22
|
+
request("Candidates.getByElection", "electionId" => election_id)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns candidates in the provided district_id
|
26
|
+
def self.get_by_district district_id, election_year = nil
|
27
|
+
request("Candidates.getByDistrict", "districtId" => district_id, "electionYear" => election_year)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module VoteSmart
|
2
|
+
|
3
|
+
class CandidateBio < Common
|
4
|
+
|
5
|
+
# Returns basic bio details on a candidate
|
6
|
+
def self.get_bio can_id
|
7
|
+
request("CandidateBio.getBio", "candidateId" => can_id)
|
8
|
+
end
|
9
|
+
|
10
|
+
#
|
11
|
+
def self.get_addl_bio can_id
|
12
|
+
request("CandidateBio.getAddlBio", "candidateId" => can_id)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module VoteSmart
|
2
|
+
|
3
|
+
class CandidateOffice < Common
|
4
|
+
|
5
|
+
attr_accessor :address, :phone, :notes
|
6
|
+
|
7
|
+
def initialize attributes
|
8
|
+
self.address = Address.new(attributes["address"])
|
9
|
+
self.phone = Phone.new(attributes["phone"])
|
10
|
+
self.notes = Notes.new(attributes["notes"])
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module VoteSmart
|
2
|
+
|
3
|
+
class Committee < Common
|
4
|
+
|
5
|
+
# Returns committee types for use in other methods
|
6
|
+
def self.get_types
|
7
|
+
request("Committee.getTypes")
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.get_committees_by_type_state type_id = nil, state_id = 'NA'
|
11
|
+
request("Committee.getCommitteesByTypeState", "typeId" => type_id, "stateId" => state_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get_committee committee_id
|
15
|
+
request("Committee.getCommittee", "committeeId" => committee_id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.get_committee_members committee_id
|
19
|
+
request("Committee.getCommitteeMembers", "committeeId" => committee_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'cgi'
|
3
|
+
require 'net/http'
|
4
|
+
require 'ym4r/google_maps/geocoding'
|
5
|
+
|
6
|
+
module VoteSmart
|
7
|
+
|
8
|
+
class Common
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_reader :attribute_map
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize attributes = {}
|
15
|
+
update_attributes attributes
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.set_attribute_map map
|
19
|
+
@attribute_map = map
|
20
|
+
end
|
21
|
+
|
22
|
+
def update_attributes attributes
|
23
|
+
map = self.class.attribute_map
|
24
|
+
raise "map not set over-ride needed" unless map
|
25
|
+
|
26
|
+
attributes.each do |key, value|
|
27
|
+
if key.kind_of?(Symbol)
|
28
|
+
send("#{key}=", value)
|
29
|
+
else
|
30
|
+
map_to = map[key]
|
31
|
+
send("#{map_to}=", value) if map_to
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.response_child response, *children
|
37
|
+
for child in children
|
38
|
+
response = response[child] if response
|
39
|
+
end
|
40
|
+
|
41
|
+
response || {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.request(api_method, params = {})
|
45
|
+
url = construct_url api_method, params
|
46
|
+
|
47
|
+
json = get_json_data(url)
|
48
|
+
|
49
|
+
if json['error'] and json['error']['errorMessage'] == 'Authorization failed'
|
50
|
+
raise RequestFailed.new(json['error']['errorMessage'])
|
51
|
+
end
|
52
|
+
|
53
|
+
json
|
54
|
+
end
|
55
|
+
|
56
|
+
# Constructs a VoteSmart API-friendly URL
|
57
|
+
def self.construct_url(api_method, params = {})
|
58
|
+
"#{API_URL}#{api_method}?key=#{VoteSmart.api_key}&o=#{API_FORMAT}#{hash2get(params)}"
|
59
|
+
end
|
60
|
+
|
61
|
+
# Converts a hash to a GET string
|
62
|
+
def self.hash2get(h)
|
63
|
+
|
64
|
+
get_string = ""
|
65
|
+
|
66
|
+
h.each_pair do |key, value|
|
67
|
+
get_string += "&#{key.to_s}=#{CGI::escape(value.to_s)}" unless value.nil?
|
68
|
+
end
|
69
|
+
|
70
|
+
get_string
|
71
|
+
|
72
|
+
end # def hash2get
|
73
|
+
|
74
|
+
|
75
|
+
# Use the Net::HTTP and JSON libraries to make the API call
|
76
|
+
#
|
77
|
+
# Usage:
|
78
|
+
# District.get_json_data("http://someurl.com") # returns Hash of data or nil
|
79
|
+
def self.get_json_data(url)
|
80
|
+
response = Net::HTTP.get_response(URI.parse(url))
|
81
|
+
if response.class == Net::HTTPOK
|
82
|
+
result = JSON.parse(response.body)
|
83
|
+
else
|
84
|
+
raise RequestFailed.new("Request was not OK: #{response.class}: #{response.body}")
|
85
|
+
end
|
86
|
+
|
87
|
+
end # self.get_json_data
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module VoteSmart
|
2
|
+
|
3
|
+
class District < Common
|
4
|
+
|
5
|
+
attr_accessor :id, :name, :office_id, :state_id
|
6
|
+
|
7
|
+
set_attribute_map "districtId" => :id, "name" => :name, "officeId" => :office_id, "stateId" => :state_id
|
8
|
+
|
9
|
+
def number
|
10
|
+
return unless name
|
11
|
+
|
12
|
+
scan = name.scan(/District (\d[0-9]*)/) || []
|
13
|
+
scan = scan.first || []
|
14
|
+
num = scan.first
|
15
|
+
num ? num.to_i : nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def official
|
19
|
+
@official ||= Official.find_by_district(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.find_all_by_office_and_state office, state
|
23
|
+
find_all_by_office_id_and_state_id(office.id, state.id)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.find_all_by_office_id_and_state_id office_id, state_id
|
27
|
+
response_child(get_by_office_state(office_id, state_id), "districtList", "district").collect {|attributes| District.new(attributes)}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns districts service the office and state provided
|
31
|
+
def self.get_by_office_state office_id, state_id = 'NA', district_name = ''
|
32
|
+
request("District.getByOfficeState", "officeId" => office_id, "stateId" => state_id, "districtName" => district_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|