Skip to content

Commit

Permalink
Merge pull request #375 from alphagov/add-constituency-to-api
Browse files Browse the repository at this point in the history
Add constituency/country signature counts to API
  • Loading branch information
alanth committed Aug 24, 2015
2 parents 1944466 + 42ddbd9 commit e762d38
Show file tree
Hide file tree
Showing 51 changed files with 1,209 additions and 637 deletions.
7 changes: 4 additions & 3 deletions app/controllers/admin/petitions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,11 @@ def render_csv
self.response_body = PetitionsCSVPresenter.new(@petitions).render
end


def set_file_headers
headers["Content-Type"] = "text/csv"
headers["Content-disposition"] = "attachment; filename=petitions.csv"
headers["Content-disposition"] = "attachment; filename=#{csv_filename}"
end


def set_streaming_headers
#nginx doc: Setting this to "no" will allow unbuffered responses suitable for Comet and HTTP streaming applications
headers['X-Accel-Buffering'] = 'no'
Expand All @@ -57,6 +55,9 @@ def set_streaming_headers
headers.delete("Content-Length")
end

def csv_filename
"#{@petitions.scope.to_s.dasherize}-petitions-#{Time.current.to_s(:number)}.csv"
end

def filter_by_tag?
params[:t].present? && !(filter_by_state? || filter_by_keyword?)
Expand Down
25 changes: 19 additions & 6 deletions app/controllers/local_petitions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
class LocalPetitionsController < ApplicationController
respond_to :html

before_action :sanitize_postcode
before_action :find_constituency, if: :postcode?
before_action :find_petitions, if: :constituency?
before_action :sanitize_postcode, only: :index
before_action :find_by_postcode, if: :postcode?, only: :index
before_action :find_by_slug, only: :show
before_action :find_petitions, if: :constituency?, only: :show
before_action :redirect_to_constituency, if: :constituency?, only: :index

def index
end

def show
respond_with(@petitions)
end

Expand All @@ -21,15 +26,23 @@ def postcode?
@postcode.present?
end

def find_constituency
@constituency = ConstituencyApi.constituency(@postcode)
def find_by_postcode
@constituency = Constituency.find_by_postcode(@postcode)
end

def find_by_slug
@constituency = Constituency.find_by_slug!(params[:id])
end

def constituency?
@constituency.present?
end

def find_petitions
@petitions = Petition.popular_in_constituency(@constituency.id, 50)
@petitions = Petition.popular_in_constituency(@constituency.external_id, 50)
end

def redirect_to_constituency
redirect_to local_petition_url(@constituency.slug)
end
end
4 changes: 2 additions & 2 deletions app/helpers/page_title_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ def options
opts[:postcode] = formatted_postcode if postcode?

if postcode?
opts[:count] = constituency? ? 1 : 0
opts[:count] = 1
else
opts[:count] = -1
opts[:count] = 0
end

if petition?
Expand Down
138 changes: 0 additions & 138 deletions app/lib/constituency_api.rb

This file was deleted.

45 changes: 45 additions & 0 deletions app/models/constituency.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require_dependency 'constituency/api_client'
require_dependency 'constituency/api_query'

class Constituency < ActiveRecord::Base
MP_URL = "http://www.parliament.uk/biographies/commons"

has_many :signatures, primary_key: :external_id
has_many :petitions, through: :signatures

validates :name, presence: true, length: { maximum: 100 }
validates :external_id, presence: true, length: { maximum: 30 }
validates :ons_code, presence: true, format: %r[\A(?:E|W|S|N)\d{8}\z]
validates :mp_id, length: { maximum: 30 }
validates :mp_name, length: { maximum: 100 }

before_validation if: :name_changed? do
self.slug = name.parameterize
end

class << self
def find_by_postcode(postcode)
results = query.fetch(postcode)

if attributes = results.first
find_or_initialize_by(external_id: attributes[:external_id]) do |constituency|
constituency.attributes = attributes

if constituency.changed? || constituency.new_record?
constituency.save!
end
end
end
end

private

def query
ApiQuery.new
end
end

def mp_url
"#{MP_URL}/#{mp_name.parameterize}/#{mp_id}"
end
end
39 changes: 39 additions & 0 deletions app/models/constituency/api_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'faraday'
require 'postcode_sanitizer'

class Constituency < ActiveRecord::Base
class ApiClient
HOST = 'http://data.parliament.uk'
ENDPOINT = '/membersdataplatform/services/mnis/Constituencies/%{postcode}/'
TIMEOUT = 5

def call(postcode)
faraday.get(path(postcode)) do |request|
request.options[:timeout] = TIMEOUT
request.options[:open_timeout] = TIMEOUT
end
end

private

def faraday
@faraday ||= Faraday.new(HOST) do |f|
f.response :follow_redirects
f.response :raise_error
f.adapter Faraday.default_adapter
end
end

def path(postcode)
ENDPOINT % { postcode: escape_path(postcode) }
end

def escape_path(value)
Rack::Utils.escape_path(sanitize(value))
end

def sanitize(value)
PostcodeSanitizer.call(value)
end
end
end
54 changes: 54 additions & 0 deletions app/models/constituency/api_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require 'nokogiri'

class Constituency < ActiveRecord::Base
class ApiQuery
CONSTITUENCIES = '//Constituencies/Constituency'
CONSTITUENCY_ID = './Constituency_Id'
CONSTITUENCY_NAME = './Name'
CONSTITUENCY_CODE = './ONSCode'

CURRENT_MP = './RepresentingMembers/RepresentingMember[1]'
MP_ID = './Member_Id'
MP_NAME = './Member'
MP_DATE = './StartDate'

def fetch(postcode)
response = client.call(postcode)

if response.success?
parse(response.body)
else
[]
end
rescue Faraday::ResourceNotFound, Faraday::ClientError => e
return []
rescue Faraday::Error => e
Appsignal.send_exception(e) if defined?(Appsignal)
return []
end

private

def client
@client ||= ApiClient.new
end

def parse(body)
xml = Nokogiri::XML(body)

xml.xpath(CONSTITUENCIES).map do |node|
{}.tap do |attrs|
attrs[:name] = node.xpath(CONSTITUENCY_NAME).text
attrs[:external_id] = node.xpath(CONSTITUENCY_ID).text
attrs[:ons_code] = node.xpath(CONSTITUENCY_CODE).text

if mp = node.at_xpath(CURRENT_MP)
attrs[:mp_id] = mp.xpath(MP_ID).text
attrs[:mp_name] = mp.xpath(MP_NAME).text
attrs[:mp_date] = mp.xpath(MP_DATE).text
end
end
end
end
end
end
3 changes: 3 additions & 0 deletions app/models/constituency_petition_journal.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
class ConstituencyPetitionJournal < ActiveRecord::Base
belongs_to :petition
belongs_to :constituency, primary_key: :external_id

validates :petition, presence: true
validates :constituency_id, presence: true, length: { maximum: 255 }
validates :constituency_id, uniqueness: { scope: [:petition_id] }
validates :signature_count, presence: true

delegate :name, :ons_code, :mp_name, to: :constituency

scope :ordered, -> {
order("#{table_name}.signature_count DESC")
}
Expand Down
Loading

0 comments on commit e762d38

Please sign in to comment.