Skip to content

Commit

Permalink
rewrite state and congressional districts using single table inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
msimonborg committed Jun 18, 2017
1 parent 9975aab commit 3472e50
Show file tree
Hide file tree
Showing 20 changed files with 114 additions and 74 deletions.
5 changes: 5 additions & 0 deletions app/models/congressional_district.rb
@@ -0,0 +1,5 @@
# frozen_string_literal: true

class CongressionalDistrict < District
is_impressionable counter_cache: true, column_name: :requests
end
3 changes: 3 additions & 0 deletions app/models/congressional_district_geom.rb
@@ -0,0 +1,3 @@
# frozen_string_literal: true

class CongressionalDistrictGeom < DistrictGeom; end
11 changes: 7 additions & 4 deletions app/models/district.rb
Expand Up @@ -2,10 +2,13 @@

class District < ApplicationRecord
belongs_to :state, foreign_key: :state_code, primary_key: :state_code
has_many :district_geoms, foreign_key: :full_code, primary_key: :full_code
has_many :reps
has_many :zcta_districts, dependent: :destroy
has_many :zctas, through: :zcta_districts
has_many :district_geoms, foreign_key: :full_code, primary_key: :full_code
has_many :reps
has_many :zcta_districts, dependent: :destroy
has_many :zctas, through: :zcta_districts

is_impressionable counter_cache: true, column_name: :requests

scope :congressional, -> { where type: 'CongressionalDistrict' }
scope :state, -> { where type: 'StateDistrict' }
end
3 changes: 3 additions & 0 deletions app/models/district_geom.rb
Expand Up @@ -3,4 +3,7 @@
class DistrictGeom < ApplicationRecord
include Geographic
belongs_to :district, foreign_key: :full_code, primary_key: :full_code

scope :congressional, -> { where type: 'CongressionalDistrictGeom' }
scope :state, -> { where type: 'StateDistrictGeom' }
end
1 change: 0 additions & 1 deletion app/models/state.rb
Expand Up @@ -2,7 +2,6 @@

class State < ApplicationRecord
has_many :districts, foreign_key: :state_code, primary_key: :state_code
has_many :state_districts, foreign_key: :state_code, primary_key: :state_code
has_many :state_geoms, foreign_key: :state_code, primary_key: :state_code
has_many :reps
scope :by_abbr_with_districts, ->(abbr:) { where(abbr: abbr).includes(:districts).first }
Expand Down
5 changes: 1 addition & 4 deletions app/models/state_district.rb
@@ -1,10 +1,7 @@
# frozen_string_literal: true

class StateDistrict < ApplicationRecord
class StateDistrict < District
include StateDistrictScopes

belongs_to :state, foreign_key: :state_code, primary_key: :state_code
has_many :state_district_geoms, foreign_key: :full_code, primary_key: :full_code

is_impressionable counter_cache: true, column_name: :requests
end
4 changes: 1 addition & 3 deletions app/models/state_district_geom.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true

class StateDistrictGeom < ApplicationRecord
include Geographic
class StateDistrictGeom < DistrictGeom
include StateDistrictScopes
belongs_to :state_district, foreign_key: :full_code, primary_key: :full_code
end
38 changes: 28 additions & 10 deletions app/services/coordinates.rb
Expand Up @@ -43,31 +43,49 @@ def districts
NullObject.new
else
{
congress: district_geom.district,
state_lower: state_lower_district_geom.state_district,
state_upper: state_upper_district_geom.state_district
congress: congressional_district,
state_lower: state_lower_district,
state_upper: state_upper_district
}
end
end

def district_geom
@_district_geom ||= begin
DistrictGeom.containing_latlon(lat, lon).includes(district: :state).take || NullObject.new
def district_geoms
@_district_geoms ||= begin
DistrictGeom.containing_latlon(lat, lon).includes(district: :state) || NullObject.new
end
end

def congressional_district_geom
@_congressional_district_geom ||= district_geoms.detect do |dis_geom|
dis_geom.type == 'CongressionalDistrictGeom'
end
end

def state_district_geoms
@_state_district_geoms ||= begin
StateDistrictGeom.containing_latlon(lat, lon).includes(:state_district) || NullObject.new
@_state_district_geoms ||= district_geoms.select do |dis_geom|
dis_geom.type == 'StateDistrictGeom'
end
end

def state_lower_district_geom
state_district_geoms.lower.take || NullObject.new
state_district_geoms.detect { |d| d.chamber == 'lower' } || NullObject.new
end

def state_upper_district_geom
state_district_geoms.upper.take || NullObject.new
state_district_geoms.detect { |d| d.chamber == 'upper' } || NullObject.new
end

def congressional_district
@_congressional_district ||= congressional_district_geom.district
end

def state_lower_district
@_state_lower_district ||= state_lower_district_geom.district
end

def state_upper_district
@_state_upper_district ||= state_upper_district_geom.district
end

def state
Expand Down
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class AddStateDistrictColumnsToDistricts < ActiveRecord::Migration[5.0]
def change
add_column :districts, :name, :string
add_column :districts, :chamber, :string
add_column :districts, :type, :string
end
end
@@ -0,0 +1,8 @@
# frozen_string_literal: true

class AddChamberAndTypeColumnsToDistrictGeoms < ActiveRecord::Migration[5.0]
def change
add_column :district_geoms, :chamber, :string
add_column :district_geoms, :type, :string
end
end
26 changes: 6 additions & 20 deletions db/schema.rb
Expand Up @@ -12,7 +12,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20_170_618_161_141) do
ActiveRecord::Schema.define(version: 20_170_618_182_609) do
# These are extensions that must be enabled in order to support this database
enable_extension 'plpgsql'
enable_extension 'postgis'
Expand All @@ -29,6 +29,8 @@
t.integer 'district_id'
t.string 'full_code'
t.geometry 'geom', limit: { srid: 3857, type: 'geometry' }
t.string 'chamber'
t.string 'type'
t.index ['district_id'], name: 'index_district_geoms_on_district_id', using: :btree
end

Expand All @@ -37,6 +39,9 @@
t.string 'state_code'
t.string 'full_code'
t.integer 'requests', default: 0
t.string 'name'
t.string 'chamber'
t.string 'type'
end

create_table 'impressions', force: :cascade do |t|
Expand Down Expand Up @@ -131,25 +136,6 @@
t.index ['state_id'], name: 'index_reps_on_state_id', using: :btree
end

create_table 'state_district_geoms', force: :cascade do |t|
t.string 'full_code'
t.string 'chamber'
t.geometry 'geom', limit: { srid: 3857, type: 'geometry' }
t.datetime 'created_at', null: false
t.datetime 'updated_at', null: false
end

create_table 'state_districts', force: :cascade do |t|
t.string 'state_code'
t.string 'code'
t.string 'full_code'
t.string 'name'
t.string 'chamber'
t.integer 'requests', default: 0
t.datetime 'created_at', null: false
t.datetime 'updated_at', null: false
end

create_table 'state_geoms', force: :cascade do |t|
t.integer 'state_id'
t.string 'state_code'
Expand Down
1 change: 0 additions & 1 deletion db/seeds.rb
Expand Up @@ -10,7 +10,6 @@

Rake::Task['db:pyr:seed_states'].invoke
Rake::Task['db:pyr:seed_districts'].invoke
Rake::Task['db:pyr:seed_state_districts'].invoke
Rake::Task['db:pyr:shapefiles'].invoke
Rake::Task['db:pyr:seed_reps'].invoke
Rake::Task['db:pyr:zctas'].invoke if ENV['zctas'] == 'true'
2 changes: 1 addition & 1 deletion lib/db_pyr_update.rb
Expand Up @@ -49,7 +49,7 @@ def update_rep_term_info(rep, term)
dis_code = dis_code.size == 1 ? "0#{dis_code}" : dis_code if dis_code
rep.role = determine_current_rep_role(term)
rep.state = State.find_by(abbr: term['state'])
rep.district = District.where(code: dis_code, state: rep.state).take
rep.district = CongressionalDistrict.where(code: dis_code, state: rep.state).take
rep.party = term['party']
rep.url = term['url']
rep.contact_form = term['contact_form']
Expand Down
12 changes: 6 additions & 6 deletions lib/tasks/db_seed_tasks.rake
Expand Up @@ -10,7 +10,7 @@ namespace :db do
desc 'Import shapefiles'
task :shapefiles do
StateGeom.destroy_all
DistrictGeom.destroy_all
CongressionalDistrictGeom.destroy_all
states = Shapefiles.new(
'lib',
'shapefiles',
Expand All @@ -30,7 +30,7 @@ namespace :db do
'cb_2015_us_cd114_500k.shp'
)
districts.import(
model: DistrictGeom,
model: CongressionalDistrictGeom,
model_attr: :full_code,
record_attr: 'GEOID'
)
Expand Down Expand Up @@ -75,20 +75,20 @@ namespace :db do
end

desc 'Destroy all Districts and seed from Scratch'
task :seed_districts do
District.destroy_all
task seed_districts: [:seed_state_districts] do
CongressionalDistrict.destroy_all
csv_district_text = File.read(Rails.root.join('lib', 'seeds', 'districts.csv'))
csv_districts = CSV.parse(csv_district_text, headers: true, encoding: 'ISO-8859-1')
csv_districts.each do |row|
District.new do |d|
CongressionalDistrict.new do |d|
d.code = row['code']
d.state_code = row['state_code']
d.full_code = row['full_code']
d.save
puts "District #{d.code} of #{d.state.name} saved in database."
end
end
puts "There are now #{District.count} districts in the database."
puts "There are now #{CongressionalDistrict.count} districts in the database."
end

desc 'Destroy all State Districts and seed from scratch'
Expand Down
2 changes: 2 additions & 0 deletions spec/factories.rb
Expand Up @@ -22,11 +22,13 @@
factory :office_location { office_id 'office_id' }
factory :v_card
factory :state { state_code 'state_code' }
factory :congressional_district
factory :district
factory :avatar
factory :impression
factory :zcta
factory :zcta_district
factory :state_geom { geom GeomSpecHelper.nebraska_geometry }
factory :district_geom { geom GeomSpecHelper.nebraska_geometry }
factory :congressional_district_geom { geom GeomSpecHelper.nebraska_geometry }
end
2 changes: 1 addition & 1 deletion spec/models/district_geom_spec.rb
Expand Up @@ -3,7 +3,7 @@
require 'rails_helper'

describe DistrictGeom, type: :model do
let!(:nebraska_at_large) { create :district_geom }
let!(:nebraska_at_large) { create :congressional_district_geom }
let(:cozad_ne) { [41.0, -100.0] }

context '.containing_latlon' do
Expand Down
8 changes: 4 additions & 4 deletions spec/requests/api/beta/reps_spec.rb
Expand Up @@ -50,9 +50,9 @@

context 'searching by location' do
let! :state { create :state }
let! :district { create :district, full_code: '1', state: state }
let! :district_geom { create :district_geom, full_code: '1' }
let! :rep_one { create :rep, bioguide_id: 'rep_one', district: district }
let! :congressional_district { create :congressional_district, full_code: '1', state: state }
let! :congressional_district_geom { create :congressional_district_geom, full_code: '1' }
let! :rep_one { create :rep, bioguide_id: 'rep_one', district: congressional_district }
let! :rep_two { create :rep, bioguide_id: 'rep_two', state: state }
let! :rep_three { create :rep }

Expand Down Expand Up @@ -116,7 +116,7 @@

expect(Impression.count).to eq(1)
expect(Impression.last.impressionable_type).to eq('District')
expect(Impression.last.impressionable_id).to eq(district.id)
expect(Impression.last.impressionable_id).to eq(congressional_district.id)
end

it 'only leaves unique impressions by IP' do
Expand Down
18 changes: 9 additions & 9 deletions spec/requests/reps_spec.rb
Expand Up @@ -53,9 +53,9 @@

context 'searching by location' do
let! :state { create :state }
let! :district { create :district, full_code: '1', state: state }
let! :district_geom { create :district_geom, full_code: '1' }
let! :rep_one { create :rep, bioguide_id: 'rep_one', district: district }
let! :congressional_district { create :congressional_district, full_code: '1', state: state }
let! :congressional_district_geom { create :congressional_district_geom, full_code: '1' }
let! :rep_one { create :rep, bioguide_id: 'rep_one', district: congressional_district }
let! :rep_two { create :rep, bioguide_id: 'rep_two', state: state }
let! :rep_three { create :rep }

Expand Down Expand Up @@ -114,7 +114,7 @@

expect(Impression.count).to eq(1)
expect(Impression.last.impressionable_type).to eq('District')
expect(Impression.last.impressionable_id).to eq(district.id)
expect(Impression.last.impressionable_id).to eq(congressional_district.id)
end

it 'only leaves unique impressions by IP' do
Expand All @@ -134,11 +134,11 @@
before(:all) do
@new_york = create :state, name: 'New York', abbr: 'NY', state_code: '1'
@california = create :state, name: 'California', abbr: 'CA', state_code: '2'
@ny_one = create :district, code: '1', full_code: '11', state: @new_york
@ny_two = create :district, code: '2', full_code: '12', state: @new_york
@ca_one = create :district, code: '1', full_code: '21', state: @california
@ca_two = create :district, code: '2', full_code: '22', state: @california
@ny_one_geom = create :district_geom, full_code: '11'
@ny_one = create :congressional_district, code: '1', full_code: '11', state: @new_york
@ny_two = create :congressional_district, code: '2', full_code: '12', state: @new_york
@ca_one = create :congressional_district, code: '1', full_code: '21', state: @california
@ca_two = create :congressional_district, code: '2', full_code: '22', state: @california
@ny_one_geom = create :congressional_district_geom, full_code: '11'

2.times do
create :rep,
Expand Down
6 changes: 4 additions & 2 deletions spec/services/coordinates_spec.rb
Expand Up @@ -3,8 +3,10 @@
require 'rails_helper'

describe Coordinates do
let!(:district_geom) { create :district_geom }
let!(:district) { create :district, district_geoms: [district_geom] }
let!(:congressional_district_geom) { create :congressional_district_geom }
let!(:congressional_district) do
create :congressional_district, district_geoms: [congressional_district_geom]
end

context '#initialize' do
it 'sets latlon when passed an address' do
Expand Down
24 changes: 16 additions & 8 deletions spec/services/geo_lookup_spec.rb
Expand Up @@ -17,28 +17,36 @@
let!(:rep_two) { create :rep }
let!(:rep_three) { create :rep }
let!(:inactive_rep) { create :rep, active: false }
let!(:district_geom) { create :district_geom, full_code: '1' }
let!(:congressional_district_geom) { create :congressional_district_geom, full_code: '1' }

let!(:district) do
create :district, full_code: '1', state_code: '1', reps: [rep_one, inactive_rep]
let!(:congressional_district) do
create :congressional_district, full_code: '1', state_code: '1', reps: [rep_one, inactive_rep]
end

let!(:state) do
create :state, state_code: '1', reps: [rep_two, inactive_rep]
end

after(:all) { [Rep, OfficeLocation, District, State, DistrictGeom].each(&:destroy_all) }
after(:all) do
[
Rep,
OfficeLocation,
CongressionalDistrict,
State,
CongressionalDistrictGeom
].each(&:destroy_all)
end

context '#initialize' do
it 'finds the correct District and State when passed coordinates' do
expect(geo_lookup.congressional_district).to eq(district)
it 'finds the correct Congressional District and State when passed coordinates' do
expect(geo_lookup.congressional_district).to eq(congressional_district)
expect(geo_lookup.state).to eq(state)
end

it 'finds the correct District and state when passed an address' do
it 'finds the correct Congressional District and state when passed an address' do
geo_lookup_by_address = GeoLookup.new(address: 'Cozad, NE 69130, USA')

expect(geo_lookup_by_address.congressional_district).to eq(district)
expect(geo_lookup_by_address.congressional_district).to eq(congressional_district)
expect(geo_lookup_by_address.state).to eq(state)
end
end
Expand Down

0 comments on commit 3472e50

Please sign in to comment.