Class: Fech::Search

Inherits:
Object
  • Object
show all
Defined in:
lib/fech-search/search.rb,
lib/fech-search/version.rb

Overview

Fech::Search is an interface for the FEC’s electronic filing search (www.fec.gov/finance/disclosure/efile_search.shtml)

Constant Summary collapse

VERSION =
"0.0.2"

Instance Method Summary collapse

Constructor Details

#initialize(search_params = {}) ⇒ Search

passed to the search form.

Parameters:

  • search_params (Hash) (defaults to: {})

    a hash of parameters to be



11
12
13
14
15
# File 'lib/fech-search/search.rb', line 11

def initialize(search_params={})
  @search_params = validate_params(make_params(search_params))
  @search_url = 'http://query.nictusa.com/cgi-bin/dcdev/forms/'
  @response = search
end

Instance Method Details

#bodyObject



55
56
57
# File 'lib/fech-search/search.rb', line 55

def body
  @response ? @response.body : nil
end

#make_params(search_params) ⇒ Hash

Convert the search parameters passed to @initialize to use the format and keys needed for the form submission.

Returns:

  • (Hash)


20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/fech-search/search.rb', line 20

def make_params(search_params)
  {
    'comid' => search_params[:committee_id] || '',
    'name' => search_params[:committee_name] || '',
    'state' => search_params[:state] || '',
    'party' => search_params[:party] || '',
    'type' => search_params[:committee_type] || '',
    'rpttype' => search_params[:report_type] || '',
    'date' => search_params[:date] ? search_params[:date].strftime('%m/%d/%Y') : '',
    'frmtype' => search_params[:form_type] || ''
  }
end

#parse_committee_row(row) ⇒ Hash

For results of a search that includes a date, parse the portion of the results with information on the committee that submitted the filing.

Parameters:

  • row (String)

Returns:

  • (Hash)

    the committee name and ID



141
142
143
144
145
146
147
148
149
150
# File 'lib/fech-search/search.rb', line 141

def parse_committee_row(row)
  regex = /
          '>
          (.*?)
          \s-\s
          (C\d{8})
          /x
  match = row.match regex
  {:committee_name => match[1], :committee_id => match[2]}
end

#parse_committee_section(section) ⇒ Object

For results of a search that does not include a date, parse the section giving information on the committee that submitted the filing.

Parameters:

  • section (String)


102
103
104
105
106
107
108
109
110
111
# File 'lib/fech-search/search.rb', line 102

def parse_committee_section(section)
  data = []
  section.gsub!(/^<BR>/, '')
  rows = section.split(/\n/)
  committee_data = parse_committee_row(rows.first)
  rows[1..-1].each do |row|
    data << committee_data.merge(parse_filing_row(row))
  end
  data
end

#parse_filing_row(row) ⇒ Hash

Parse a result row with information on the filing itself. and, optionally, the filing that amended this filing.

Parameters:

  • row (String)

Returns:

  • (Hash)

    the filing ID, form type, period, date filed, description



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/fech-search/search.rb', line 156

def parse_filing_row(row)
  regex = /
          FEC-(\d+)
          \s
          Form
          \s
          (F.*?)
          \s\s-\s
          (period\s([-\/\d]+),\s)?
          filed
          \s
          ([\/\d]+)
          \s
          (-\s
           (.*?)
           ($|<BR>.*?FEC-(\d+))
          )?
          /x
  match = row.match regex
  {:filing_id => match[1],
   :form_type => match[2],
   :period => match[4],
   :date_filed => match[5],
   :description => match[7],
   :amended_by => match[9]
  }
end

#results(&block) ⇒ Object

The results page is formatted differently depending on whether the search includes a date. Use the correct method for parsing the results depending on whether a date was used in the search. Will return an array of results if called directly, or will yield the results one by one if a block is passed.



65
66
67
68
69
70
71
# File 'lib/fech-search/search.rb', line 65

def results(&block)
  if @search_params['date'] != ''
    results_from_date_search(&block)
  else
    results_from_nondate_search(&block)
  end
end

#results_from_date_search(&block) ⇒ Object

Parse the results from a search that includes a date. Will return an array of results if called directly, or will yield the results one by one if a block is passed.



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/fech-search/search.rb', line 116

def results_from_date_search(&block)
  parsed_results = []
  dl = body.match(/<DL>(.*?)<BR><P/m)[0]
  rows = dl.split('<DT>')[1..-1].map { |row| row.split("\n") }
  rows.each do |committee, *filings|
    committee = parse_committee_row(committee)
    filings.each do |filing|
      next if filing == "<BR><P"
      data = committee.merge(parse_filing_row(filing))
      search_result = SearchResult.new(data)
      if block_given?
        yield search_result
      else
        parsed_results << search_result
      end
    end
  end
  block_given? ? nil : parsed_results
end

#results_from_nondate_search(&block) ⇒ Object

Parse the results from a search that does not include a date. Will return an array of results if called directly, or will yield the results one by one if a block is passed.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/fech-search/search.rb', line 76

def results_from_nondate_search(&block)
  parsed_results = []
  regex = /<DT>(.*?)<P/m
  match = body.match regex
  return [] if match.nil?
  content = match[1]
  committee_sections = content.split(/<DT>/)
  committee_sections.each do |section|
    data = parse_committee_section(section)
    data.each do |result|
      search_result = SearchResult.new(result)

      if block_given?
        yield search_result
      else
        parsed_results << search_result
      end
    end
  end
  block_given? ? nil : parsed_results
end

#searchObject

Performs the search of the FEC’s electronic filing database.



42
43
44
45
46
47
48
# File 'lib/fech-search/search.rb', line 42

def search
  http = Net::HTTP.new(uri.host, uri.port)
  http.read_timeout = 5000
  request = Net::HTTP::Post.new(uri.request_uri)
  request.set_form_data(@search_params)
  @response = http.request(request)
end

#uriObject

A parsed URI for the search



51
52
53
# File 'lib/fech-search/search.rb', line 51

def uri
  uri = URI.parse(@search_url)
end

#validate_params(params) ⇒ Object

Raises:

  • (ArgumentError)


33
34
35
36
37
38
39
# File 'lib/fech-search/search.rb', line 33

def validate_params(params)
  raise ArgumentError, "At least one search parameter must be given" if params.values.all? { |x| x.empty? }
  nonempty_keys = params.select { |k, v| !v.empty? }.keys
  raise ArgumentError, ":committee_id cannot be used with other search parameters" if nonempty_keys.include?("comid") && nonempty_keys.size > 1
  raise ArgumentError, ":form_type must be used with at least one other search parameter" if nonempty_keys.include?("frmtype") && nonempty_keys.size == 1
  params
end