congress_gov 0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +156 -0
- data/lib/congress_gov/client.rb +125 -0
- data/lib/congress_gov/configuration.rb +48 -0
- data/lib/congress_gov/error.rb +46 -0
- data/lib/congress_gov/resources/amendment.rb +107 -0
- data/lib/congress_gov/resources/base.rb +74 -0
- data/lib/congress_gov/resources/bill.rb +199 -0
- data/lib/congress_gov/resources/bound_congressional_record.rb +48 -0
- data/lib/congress_gov/resources/clerk_vote.rb +125 -0
- data/lib/congress_gov/resources/committee.rb +136 -0
- data/lib/congress_gov/resources/committee_meeting.rb +37 -0
- data/lib/congress_gov/resources/committee_print.rb +50 -0
- data/lib/congress_gov/resources/committee_report.rb +65 -0
- data/lib/congress_gov/resources/congress_info.rb +32 -0
- data/lib/congress_gov/resources/crs_report.rb +17 -0
- data/lib/congress_gov/resources/daily_congressional_record.rb +48 -0
- data/lib/congress_gov/resources/hearing.rb +37 -0
- data/lib/congress_gov/resources/house_communication.rb +51 -0
- data/lib/congress_gov/resources/house_requirement.rb +36 -0
- data/lib/congress_gov/resources/house_vote.rb +127 -0
- data/lib/congress_gov/resources/law.rb +53 -0
- data/lib/congress_gov/resources/member.rb +151 -0
- data/lib/congress_gov/resources/nomination.rb +74 -0
- data/lib/congress_gov/resources/senate_communication.rb +51 -0
- data/lib/congress_gov/resources/summary.rb +27 -0
- data/lib/congress_gov/resources/treaty.rb +75 -0
- data/lib/congress_gov/response.rb +109 -0
- data/lib/congress_gov/version.rb +6 -0
- data/lib/congress_gov.rb +180 -0
- metadata +237 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access Congressional Research Service reports from the Congress.gov API.
|
|
6
|
+
class CrsReport < Base
|
|
7
|
+
# List CRS reports.
|
|
8
|
+
#
|
|
9
|
+
# @param limit [Integer]
|
|
10
|
+
# @param offset [Integer]
|
|
11
|
+
# @return [CongressGov::Response]
|
|
12
|
+
def list(limit: 20, offset: 0)
|
|
13
|
+
client.get('crsreport', { limit: limit, offset: offset })
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access daily Congressional Record data from the Congress.gov API.
|
|
6
|
+
class DailyCongressionalRecord < Base
|
|
7
|
+
# List daily congressional record volumes.
|
|
8
|
+
#
|
|
9
|
+
# @param limit [Integer]
|
|
10
|
+
# @param offset [Integer]
|
|
11
|
+
# @return [CongressGov::Response]
|
|
12
|
+
def list(limit: 20, offset: 0)
|
|
13
|
+
client.get('daily-congressional-record', { limit: limit, offset: offset })
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# List issues for a specific volume.
|
|
17
|
+
#
|
|
18
|
+
# @param volume [Integer]
|
|
19
|
+
# @param limit [Integer]
|
|
20
|
+
# @param offset [Integer]
|
|
21
|
+
# @return [CongressGov::Response]
|
|
22
|
+
def list_by_volume(volume, limit: 20, offset: 0)
|
|
23
|
+
client.get("daily-congressional-record/#{volume}", { limit: limit, offset: offset })
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Get a specific issue.
|
|
27
|
+
#
|
|
28
|
+
# @param volume [Integer]
|
|
29
|
+
# @param issue [Integer]
|
|
30
|
+
# @return [CongressGov::Response]
|
|
31
|
+
def get_issue(volume, issue)
|
|
32
|
+
client.get("daily-congressional-record/#{volume}/#{issue}")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Articles for a specific issue.
|
|
36
|
+
#
|
|
37
|
+
# @param volume [Integer]
|
|
38
|
+
# @param issue [Integer]
|
|
39
|
+
# @param limit [Integer]
|
|
40
|
+
# @param offset [Integer]
|
|
41
|
+
# @return [CongressGov::Response]
|
|
42
|
+
def articles(volume, issue, limit: 20, offset: 0)
|
|
43
|
+
client.get("daily-congressional-record/#{volume}/#{issue}/articles",
|
|
44
|
+
{ limit: limit, offset: offset })
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access hearing data from the Congress.gov API.
|
|
6
|
+
class Hearing < Base
|
|
7
|
+
# List hearings, optionally filtered by congress and chamber.
|
|
8
|
+
#
|
|
9
|
+
# @param congress [Integer]
|
|
10
|
+
# @param chamber [String]
|
|
11
|
+
# @param limit [Integer]
|
|
12
|
+
# @param offset [Integer]
|
|
13
|
+
# @return [CongressGov::Response]
|
|
14
|
+
def list(congress: nil, chamber: nil, limit: 20, offset: 0)
|
|
15
|
+
params = { limit: limit, offset: offset }
|
|
16
|
+
|
|
17
|
+
if congress && chamber
|
|
18
|
+
client.get("hearing/#{congress}/#{chamber.downcase}", params)
|
|
19
|
+
elsif congress
|
|
20
|
+
client.get("hearing/#{congress}", params)
|
|
21
|
+
else
|
|
22
|
+
client.get('hearing', params)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Fetch a specific hearing.
|
|
27
|
+
#
|
|
28
|
+
# @param congress [Integer]
|
|
29
|
+
# @param chamber [String]
|
|
30
|
+
# @param jacket_number [Integer]
|
|
31
|
+
# @return [CongressGov::Response]
|
|
32
|
+
def get(congress, chamber, jacket_number)
|
|
33
|
+
client.get("hearing/#{congress}/#{chamber.downcase}/#{jacket_number}")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access House communication data from the Congress.gov API.
|
|
6
|
+
class HouseCommunication < Base
|
|
7
|
+
# Valid communication type codes: ec (Executive), ml (Memorial), pm (Presidential), pt (Petition).
|
|
8
|
+
COMMUNICATION_TYPES = %w[ec ml pm pt].freeze
|
|
9
|
+
|
|
10
|
+
# List house communications.
|
|
11
|
+
#
|
|
12
|
+
# @param congress [Integer] filter to a specific Congress
|
|
13
|
+
# @param communication_type [String] one of COMMUNICATION_TYPES
|
|
14
|
+
# @param limit [Integer]
|
|
15
|
+
# @param offset [Integer]
|
|
16
|
+
# @return [CongressGov::Response]
|
|
17
|
+
def list(congress: nil, communication_type: nil, limit: 20, offset: 0)
|
|
18
|
+
params = { limit: limit, offset: offset }
|
|
19
|
+
|
|
20
|
+
if congress && communication_type
|
|
21
|
+
validate_communication_type!(communication_type)
|
|
22
|
+
client.get("house-communication/#{congress}/#{communication_type.downcase}", params)
|
|
23
|
+
elsif congress
|
|
24
|
+
client.get("house-communication/#{congress}", params)
|
|
25
|
+
else
|
|
26
|
+
client.get('house-communication', params)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Fetch a specific house communication.
|
|
31
|
+
#
|
|
32
|
+
# @param congress [Integer]
|
|
33
|
+
# @param communication_type [String]
|
|
34
|
+
# @param number [Integer]
|
|
35
|
+
# @return [CongressGov::Response]
|
|
36
|
+
def get(congress, communication_type, number)
|
|
37
|
+
validate_communication_type!(communication_type)
|
|
38
|
+
client.get("house-communication/#{congress}/#{communication_type.downcase}/#{number}")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def validate_communication_type!(communication_type)
|
|
44
|
+
return if COMMUNICATION_TYPES.include?(communication_type.to_s.downcase)
|
|
45
|
+
|
|
46
|
+
raise ArgumentError,
|
|
47
|
+
"communication type must be one of: #{COMMUNICATION_TYPES.join(', ')}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access House requirement data from the Congress.gov API.
|
|
6
|
+
class HouseRequirement < Base
|
|
7
|
+
# List house requirements.
|
|
8
|
+
#
|
|
9
|
+
# @param limit [Integer]
|
|
10
|
+
# @param offset [Integer]
|
|
11
|
+
# @return [CongressGov::Response]
|
|
12
|
+
def list(limit: 20, offset: 0)
|
|
13
|
+
client.get('house-requirement', { limit: limit, offset: offset })
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Fetch a specific house requirement.
|
|
17
|
+
#
|
|
18
|
+
# @param number [Integer]
|
|
19
|
+
# @return [CongressGov::Response]
|
|
20
|
+
def get(number)
|
|
21
|
+
client.get("house-requirement/#{number}")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Matching communications for a house requirement.
|
|
25
|
+
#
|
|
26
|
+
# @param number [Integer]
|
|
27
|
+
# @param limit [Integer]
|
|
28
|
+
# @param offset [Integer]
|
|
29
|
+
# @return [CongressGov::Response]
|
|
30
|
+
def matching_communications(number, limit: 20, offset: 0)
|
|
31
|
+
client.get("house-requirement/#{number}/matching-communications",
|
|
32
|
+
{ limit: limit, offset: offset })
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access House roll call vote data from the Congress.gov API.
|
|
6
|
+
class HouseVote < Base
|
|
7
|
+
# List all House roll call votes (no filters).
|
|
8
|
+
#
|
|
9
|
+
# @param limit [Integer]
|
|
10
|
+
# @param offset [Integer]
|
|
11
|
+
# @return [CongressGov::Response]
|
|
12
|
+
def list_all(limit: 20, offset: 0)
|
|
13
|
+
client.get('house-vote', { limit: limit, offset: offset })
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# List House roll call votes for a specific Congress.
|
|
17
|
+
#
|
|
18
|
+
# @param congress [Integer] e.g. 119
|
|
19
|
+
# @param limit [Integer]
|
|
20
|
+
# @param offset [Integer]
|
|
21
|
+
# @return [CongressGov::Response]
|
|
22
|
+
def list_by_congress(congress:, limit: 20, offset: 0)
|
|
23
|
+
client.get("house-vote/#{congress}", { limit: limit, offset: offset })
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# List House roll call votes for a Congress and session.
|
|
27
|
+
#
|
|
28
|
+
# @param congress [Integer] e.g. 119
|
|
29
|
+
# @param session [Integer] 1 or 2
|
|
30
|
+
# @param limit [Integer]
|
|
31
|
+
# @param offset [Integer]
|
|
32
|
+
# @param from_date [String] "YYYY-MM-DD"
|
|
33
|
+
# @param to_date [String] "YYYY-MM-DD"
|
|
34
|
+
# @return [CongressGov::Response]
|
|
35
|
+
def list(congress:, session:, limit: 20, offset: 0,
|
|
36
|
+
from_date: nil, to_date: nil)
|
|
37
|
+
params = { limit: limit, offset: offset }
|
|
38
|
+
params[:startDate] = from_date if from_date
|
|
39
|
+
params[:endDate] = to_date if to_date
|
|
40
|
+
client.get("house-vote/#{congress}/#{session}", params)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Fetch a single House roll call vote record.
|
|
44
|
+
#
|
|
45
|
+
# @param congress [Integer]
|
|
46
|
+
# @param session [Integer]
|
|
47
|
+
# @param roll_call [Integer]
|
|
48
|
+
# @return [CongressGov::Response]
|
|
49
|
+
def get(congress:, session:, roll_call:)
|
|
50
|
+
client.get("house-vote/#{congress}/#{session}/#{roll_call}")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Fetch how each member voted on a specific roll call.
|
|
54
|
+
#
|
|
55
|
+
# @param congress [Integer]
|
|
56
|
+
# @param session [Integer]
|
|
57
|
+
# @param roll_call [Integer]
|
|
58
|
+
# @param limit [Integer]
|
|
59
|
+
# @param offset [Integer]
|
|
60
|
+
# @return [CongressGov::Response]
|
|
61
|
+
def members(congress:, session:, roll_call:, limit: 250, offset: 0)
|
|
62
|
+
client.get("house-vote/#{congress}/#{session}/#{roll_call}/members",
|
|
63
|
+
{ limit: limit, offset: offset })
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Convenience: return all member votes as a Hash keyed by bioguide ID.
|
|
67
|
+
#
|
|
68
|
+
# The members endpoint nests data under +houseRollCallVoteMemberVotes.results+.
|
|
69
|
+
# The bioguide field is +bioguideID+ (capital D).
|
|
70
|
+
#
|
|
71
|
+
# @param congress [Integer]
|
|
72
|
+
# @param session [Integer]
|
|
73
|
+
# @param roll_call [Integer]
|
|
74
|
+
# @return [Hash{String => String}] bioguide_id => voteCast
|
|
75
|
+
def member_votes_by_bioguide(congress:, session:, roll_call:)
|
|
76
|
+
result = {}
|
|
77
|
+
offset = 0
|
|
78
|
+
loop do
|
|
79
|
+
response = members(
|
|
80
|
+
congress: congress,
|
|
81
|
+
session: session,
|
|
82
|
+
roll_call: roll_call,
|
|
83
|
+
limit: 250,
|
|
84
|
+
offset: offset
|
|
85
|
+
)
|
|
86
|
+
member_list = extract_member_votes(response)
|
|
87
|
+
member_list.each do |member|
|
|
88
|
+
bioguide = member['bioguideID'] || member['bioguideId']
|
|
89
|
+
result[bioguide] = member['voteCast']
|
|
90
|
+
end
|
|
91
|
+
break unless response.has_next_page?
|
|
92
|
+
|
|
93
|
+
offset += 250
|
|
94
|
+
end
|
|
95
|
+
result
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Convenience: get the vote position for a single member on a roll call.
|
|
99
|
+
#
|
|
100
|
+
# @param congress [Integer]
|
|
101
|
+
# @param session [Integer]
|
|
102
|
+
# @param roll_call [Integer]
|
|
103
|
+
# @param bioguide_id [String]
|
|
104
|
+
# @return [String, nil] "Aye", "Nay", "Present", "Not Voting", or nil
|
|
105
|
+
def position_for_member(congress:, session:, roll_call:, bioguide_id:)
|
|
106
|
+
member_votes_by_bioguide(
|
|
107
|
+
congress: congress, session: session, roll_call: roll_call
|
|
108
|
+
)[bioguide_id]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
# Extract the member votes array from the nested response structure.
|
|
114
|
+
# The API nests member votes under +houseRollCallVoteMemberVotes.results+.
|
|
115
|
+
# Falls back to +Response#results+ for compatibility with stubbed tests.
|
|
116
|
+
#
|
|
117
|
+
# @param response [CongressGov::Response]
|
|
118
|
+
# @return [Array<Hash>]
|
|
119
|
+
def extract_member_votes(response)
|
|
120
|
+
nested = response.raw.dig('houseRollCallVoteMemberVotes', 'results')
|
|
121
|
+
return nested if nested.is_a?(Array)
|
|
122
|
+
|
|
123
|
+
response.results
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access law data from the Congress.gov API.
|
|
6
|
+
class Law < Base
|
|
7
|
+
# Valid law type codes: pub (public), priv (private).
|
|
8
|
+
LAW_TYPES = %w[pub priv].freeze
|
|
9
|
+
|
|
10
|
+
# List laws for a given congress.
|
|
11
|
+
#
|
|
12
|
+
# @param congress [Integer] e.g. 119
|
|
13
|
+
# @param limit [Integer]
|
|
14
|
+
# @param offset [Integer]
|
|
15
|
+
# @return [CongressGov::Response]
|
|
16
|
+
def list(congress, limit: 20, offset: 0)
|
|
17
|
+
client.get("law/#{congress}", { limit: limit, offset: offset })
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# List laws for a given congress filtered by type.
|
|
21
|
+
#
|
|
22
|
+
# @param congress [Integer]
|
|
23
|
+
# @param law_type [String] 'pub' or 'priv'
|
|
24
|
+
# @param limit [Integer]
|
|
25
|
+
# @param offset [Integer]
|
|
26
|
+
# @return [CongressGov::Response]
|
|
27
|
+
def list_by_type(congress, law_type, limit: 20, offset: 0)
|
|
28
|
+
validate_law_type!(law_type)
|
|
29
|
+
client.get("law/#{congress}/#{law_type.downcase}", { limit: limit, offset: offset })
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Fetch a specific law.
|
|
33
|
+
#
|
|
34
|
+
# @param congress [Integer]
|
|
35
|
+
# @param law_type [String] 'pub' or 'priv'
|
|
36
|
+
# @param number [Integer]
|
|
37
|
+
# @return [CongressGov::Response]
|
|
38
|
+
def get(congress, law_type, number)
|
|
39
|
+
validate_law_type!(law_type)
|
|
40
|
+
client.get("law/#{congress}/#{law_type.downcase}/#{number}")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def validate_law_type!(law_type)
|
|
46
|
+
return if LAW_TYPES.include?(law_type.to_s.downcase)
|
|
47
|
+
|
|
48
|
+
raise ArgumentError,
|
|
49
|
+
"law type must be one of: #{LAW_TYPES.join(', ')}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access member data from the Congress.gov API.
|
|
6
|
+
class Member < Base
|
|
7
|
+
# Fetch the current member representing a congressional district.
|
|
8
|
+
#
|
|
9
|
+
# @param state [String] two-letter state abbreviation e.g. "VA", "OH"
|
|
10
|
+
# @param district [Integer, String] district number e.g. 8 or "08"
|
|
11
|
+
# @param congress [Integer] congress number e.g. 119 (default: current 119th)
|
|
12
|
+
# @param current [Boolean] return only current members (default: true)
|
|
13
|
+
# @param limit [Integer]
|
|
14
|
+
# @param offset [Integer]
|
|
15
|
+
# @return [CongressGov::Response]
|
|
16
|
+
def by_district(state:, district:, congress: 119, current: true, limit: 10, offset: 0)
|
|
17
|
+
district_str = district.to_s.rjust(2, '0')
|
|
18
|
+
params = {
|
|
19
|
+
currentMember: current,
|
|
20
|
+
limit: limit,
|
|
21
|
+
offset: offset
|
|
22
|
+
}
|
|
23
|
+
client.get("member/congress/#{congress}/#{state.upcase}/#{district_str}", params)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Fetch a member's full profile by bioguide ID.
|
|
27
|
+
#
|
|
28
|
+
# @param bioguide_id [String] e.g. "B001292" (Don Beyer, VA-08)
|
|
29
|
+
# @return [CongressGov::Response]
|
|
30
|
+
def get(bioguide_id)
|
|
31
|
+
client.get("member/#{bioguide_id}")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Get a member's sponsored legislation.
|
|
35
|
+
#
|
|
36
|
+
# @param bioguide_id [String]
|
|
37
|
+
# @param limit [Integer] max 250
|
|
38
|
+
# @param offset [Integer]
|
|
39
|
+
# @return [CongressGov::Response]
|
|
40
|
+
def sponsored_legislation(bioguide_id, limit: 20, offset: 0)
|
|
41
|
+
client.get("member/#{bioguide_id}/sponsored-legislation",
|
|
42
|
+
{ limit: limit, offset: offset })
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get legislation a member has cosponsored.
|
|
46
|
+
#
|
|
47
|
+
# @param bioguide_id [String]
|
|
48
|
+
# @param limit [Integer]
|
|
49
|
+
# @param offset [Integer]
|
|
50
|
+
# @return [CongressGov::Response]
|
|
51
|
+
def cosponsored_legislation(bioguide_id, limit: 20, offset: 0)
|
|
52
|
+
client.get("member/#{bioguide_id}/cosponsored-legislation",
|
|
53
|
+
{ limit: limit, offset: offset })
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# List all current members of Congress (House + Senate).
|
|
57
|
+
#
|
|
58
|
+
# @param current [Boolean] only current members (default: true)
|
|
59
|
+
# @param limit [Integer]
|
|
60
|
+
# @param offset [Integer]
|
|
61
|
+
# @return [CongressGov::Response]
|
|
62
|
+
def list(current: true, limit: 250, offset: 0)
|
|
63
|
+
client.get('member', { currentMember: current, limit: limit, offset: offset })
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# List members from a specific state.
|
|
67
|
+
#
|
|
68
|
+
# @param state [String] two-letter state abbreviation e.g. "VA", "OH"
|
|
69
|
+
# @param current [Boolean] return only current members (default: true)
|
|
70
|
+
# @param limit [Integer]
|
|
71
|
+
# @param offset [Integer]
|
|
72
|
+
# @return [CongressGov::Response]
|
|
73
|
+
def by_state(state:, current: true, limit: 250, offset: 0)
|
|
74
|
+
params = {
|
|
75
|
+
currentMember: current,
|
|
76
|
+
limit: limit,
|
|
77
|
+
offset: offset
|
|
78
|
+
}
|
|
79
|
+
client.get("member/#{state.upcase}", params)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# List members from a specific state and district.
|
|
83
|
+
#
|
|
84
|
+
# Uses GET /v3/member/:stateCode/:district (distinct from
|
|
85
|
+
# #by_district which uses /member/congress/:congress/…).
|
|
86
|
+
#
|
|
87
|
+
# @param state [String] two-letter state abbreviation
|
|
88
|
+
# @param district [Integer, String] district number
|
|
89
|
+
# @param current [Boolean] return only current members (default: true)
|
|
90
|
+
# @param limit [Integer]
|
|
91
|
+
# @param offset [Integer]
|
|
92
|
+
# @return [CongressGov::Response]
|
|
93
|
+
def by_state_district(state:, district:, current: true, limit: 10, offset: 0)
|
|
94
|
+
district_str = district.to_s.rjust(2, '0')
|
|
95
|
+
params = {
|
|
96
|
+
currentMember: current,
|
|
97
|
+
limit: limit,
|
|
98
|
+
offset: offset
|
|
99
|
+
}
|
|
100
|
+
client.get("member/#{state.upcase}/#{district_str}", params)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# List members for a specific congress.
|
|
104
|
+
#
|
|
105
|
+
# @param congress [Integer] congress number e.g. 119
|
|
106
|
+
# @param current [Boolean, nil] filter by current status (nil = omit param)
|
|
107
|
+
# @param limit [Integer]
|
|
108
|
+
# @param offset [Integer]
|
|
109
|
+
# @return [CongressGov::Response]
|
|
110
|
+
def by_congress(congress:, current: nil, limit: 250, offset: 0)
|
|
111
|
+
params = {
|
|
112
|
+
limit: limit,
|
|
113
|
+
offset: offset
|
|
114
|
+
}
|
|
115
|
+
params[:currentMember] = current unless current.nil?
|
|
116
|
+
client.get("member/congress/#{congress}", params)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Convenience: return all pages of current members, yielding each page.
|
|
120
|
+
#
|
|
121
|
+
# @yield [Array] results from each page
|
|
122
|
+
def all_current
|
|
123
|
+
offset = 0
|
|
124
|
+
limit = 250
|
|
125
|
+
loop do
|
|
126
|
+
response = list(current: true, limit: limit, offset: offset)
|
|
127
|
+
yield response.results
|
|
128
|
+
break unless response.has_next_page?
|
|
129
|
+
|
|
130
|
+
offset += limit
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Convenience: returns the single current member for a district,
|
|
135
|
+
# or nil if none found.
|
|
136
|
+
#
|
|
137
|
+
# @param state [String]
|
|
138
|
+
# @param district [Integer, String]
|
|
139
|
+
# @param congress [Integer] defaults to 119th
|
|
140
|
+
# @return [Hash, nil]
|
|
141
|
+
def current_for_district(state:, district:, congress: 119)
|
|
142
|
+
response = by_district(state: state, district: district,
|
|
143
|
+
congress: congress, current: true, limit: 1)
|
|
144
|
+
members = response.results
|
|
145
|
+
return nil if members.empty?
|
|
146
|
+
|
|
147
|
+
members.first
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access nomination data from the Congress.gov API.
|
|
6
|
+
class Nomination < Base
|
|
7
|
+
# List nominations, optionally filtered by congress.
|
|
8
|
+
#
|
|
9
|
+
# @param congress [Integer, nil] filter to a specific Congress
|
|
10
|
+
# @param limit [Integer]
|
|
11
|
+
# @param offset [Integer]
|
|
12
|
+
# @return [CongressGov::Response]
|
|
13
|
+
def list(congress: nil, limit: 20, offset: 0)
|
|
14
|
+
path = congress ? "nomination/#{congress}" : 'nomination'
|
|
15
|
+
client.get(path, { limit: limit, offset: offset })
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Fetch a nomination's full detail record.
|
|
19
|
+
#
|
|
20
|
+
# @param congress [Integer] e.g. 119
|
|
21
|
+
# @param number [Integer] nomination number
|
|
22
|
+
# @return [CongressGov::Response]
|
|
23
|
+
def get(congress, number)
|
|
24
|
+
client.get("nomination/#{congress}/#{number}")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Fetch a specific ordinal for a nomination.
|
|
28
|
+
#
|
|
29
|
+
# @param congress [Integer]
|
|
30
|
+
# @param number [Integer]
|
|
31
|
+
# @param ordinal [Integer]
|
|
32
|
+
# @return [CongressGov::Response]
|
|
33
|
+
def get_ordinal(congress, number, ordinal)
|
|
34
|
+
client.get("nomination/#{congress}/#{number}/#{ordinal}")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Actions taken on a nomination.
|
|
38
|
+
#
|
|
39
|
+
# @param congress [Integer]
|
|
40
|
+
# @param number [Integer]
|
|
41
|
+
# @param limit [Integer]
|
|
42
|
+
# @param offset [Integer]
|
|
43
|
+
# @return [CongressGov::Response]
|
|
44
|
+
def actions(congress, number, limit: 20, offset: 0)
|
|
45
|
+
client.get("nomination/#{congress}/#{number}/actions",
|
|
46
|
+
{ limit: limit, offset: offset })
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Committees associated with a nomination.
|
|
50
|
+
#
|
|
51
|
+
# @param congress [Integer]
|
|
52
|
+
# @param number [Integer]
|
|
53
|
+
# @param limit [Integer]
|
|
54
|
+
# @param offset [Integer]
|
|
55
|
+
# @return [CongressGov::Response]
|
|
56
|
+
def committees(congress, number, limit: 20, offset: 0)
|
|
57
|
+
client.get("nomination/#{congress}/#{number}/committees",
|
|
58
|
+
{ limit: limit, offset: offset })
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Hearings for a nomination.
|
|
62
|
+
#
|
|
63
|
+
# @param congress [Integer]
|
|
64
|
+
# @param number [Integer]
|
|
65
|
+
# @param limit [Integer]
|
|
66
|
+
# @param offset [Integer]
|
|
67
|
+
# @return [CongressGov::Response]
|
|
68
|
+
def hearings(congress, number, limit: 20, offset: 0)
|
|
69
|
+
client.get("nomination/#{congress}/#{number}/hearings",
|
|
70
|
+
{ limit: limit, offset: offset })
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access Senate communication data from the Congress.gov API.
|
|
6
|
+
class SenateCommunication < Base
|
|
7
|
+
# Valid communication type codes: ec (Executive), ml (Memorial), pm (Presidential), pt (Petition).
|
|
8
|
+
COMMUNICATION_TYPES = %w[ec ml pm pt].freeze
|
|
9
|
+
|
|
10
|
+
# List senate communications.
|
|
11
|
+
#
|
|
12
|
+
# @param congress [Integer] filter to a specific Congress
|
|
13
|
+
# @param communication_type [String] one of COMMUNICATION_TYPES
|
|
14
|
+
# @param limit [Integer]
|
|
15
|
+
# @param offset [Integer]
|
|
16
|
+
# @return [CongressGov::Response]
|
|
17
|
+
def list(congress: nil, communication_type: nil, limit: 20, offset: 0)
|
|
18
|
+
params = { limit: limit, offset: offset }
|
|
19
|
+
|
|
20
|
+
if congress && communication_type
|
|
21
|
+
validate_communication_type!(communication_type)
|
|
22
|
+
client.get("senate-communication/#{congress}/#{communication_type.downcase}", params)
|
|
23
|
+
elsif congress
|
|
24
|
+
client.get("senate-communication/#{congress}", params)
|
|
25
|
+
else
|
|
26
|
+
client.get('senate-communication', params)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Fetch a specific senate communication.
|
|
31
|
+
#
|
|
32
|
+
# @param congress [Integer]
|
|
33
|
+
# @param communication_type [String]
|
|
34
|
+
# @param number [Integer]
|
|
35
|
+
# @return [CongressGov::Response]
|
|
36
|
+
def get(congress, communication_type, number)
|
|
37
|
+
validate_communication_type!(communication_type)
|
|
38
|
+
client.get("senate-communication/#{congress}/#{communication_type.downcase}/#{number}")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def validate_communication_type!(communication_type)
|
|
44
|
+
return if COMMUNICATION_TYPES.include?(communication_type.to_s.downcase)
|
|
45
|
+
|
|
46
|
+
raise ArgumentError,
|
|
47
|
+
"communication type must be one of: #{COMMUNICATION_TYPES.join(', ')}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CongressGov
|
|
4
|
+
module Resources
|
|
5
|
+
# Access bill summary data from the Congress.gov API.
|
|
6
|
+
class Summary < Base
|
|
7
|
+
# List bill summaries with optional congress and bill type filters.
|
|
8
|
+
#
|
|
9
|
+
# @param congress [Integer, nil] filter to a specific congress
|
|
10
|
+
# @param bill_type [String, nil] filter to a bill type (e.g. 'hr', 's')
|
|
11
|
+
# @param limit [Integer]
|
|
12
|
+
# @param offset [Integer]
|
|
13
|
+
# @return [CongressGov::Response]
|
|
14
|
+
def list(congress: nil, bill_type: nil, limit: 20, offset: 0)
|
|
15
|
+
params = { limit: limit, offset: offset }
|
|
16
|
+
|
|
17
|
+
if congress && bill_type
|
|
18
|
+
client.get("summaries/#{congress}/#{bill_type.downcase}", params)
|
|
19
|
+
elsif congress
|
|
20
|
+
client.get("summaries/#{congress}", params)
|
|
21
|
+
else
|
|
22
|
+
client.get('summaries', params)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|