govkit-ca 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,4 +1,6 @@
1
1
  *.gem
2
2
  .bundle
3
+ .yardoc
3
4
  Gemfile.lock
5
+ doc/*
4
6
  pkg/*
data/README.md CHANGED
@@ -7,12 +7,15 @@ GovKit-CA is a Ruby gem that provides easy access to Canadian civic data around
7
7
  * [ndp.ca](http://www.ndp.ca/)
8
8
  * [digital-copyright.ca](http://www.digital-copyright.ca/)
9
9
  * [liberal.ca](http://www.liberal.ca/)
10
+ * [greenparty.ca](http://www.greenparty.ca/)
11
+ * [parl.gc.ca](http://www.parl.gc.ca/)
12
+ * [conservative.ca](http://www.conservative.ca/)
10
13
 
11
14
  GovKit-CA follows from [Participatory Politics Foundation](http://www.participatorypolitics.org/)'s [GovKit](https://github.com/opengovernment/govkit) gem. GovKit-CA is not affiliated with the Participatory Politics Foundation or GovKit.
12
15
 
13
16
  # Installation
14
17
 
15
- gem install govkit
18
+ gem install govkit-ca
16
19
 
17
20
  # Examples
18
21
 
@@ -21,24 +24,25 @@ GovKit-CA follows from [Participatory Politics Foundation](http://www.participat
21
24
  >> GovKit::CA::PostalCode.find_electoral_districts_by_postal_code('A1A1A1')
22
25
  => [10007]
23
26
  >> GovKit::CA::PostalCode.find_electoral_districts_by_postal_code('K0A1K0')
24
- => [35087, 35025, 35052, 35012, 35040, 35063, 35064]
27
+ => [35012, 35025, 35040, 35052, 35063, 35064, 35087]
28
+ >> GovKit::CA::PostalCode.find_electoral_districts_by_postal_code('H0H0H0')
29
+ => GovKit::CA::ResourceNotFound
25
30
 
26
31
  >> GovKit::CA::PostalCode.find_province_by_postal_code('A1A1A1')
27
32
  => "Newfoundland and Labrador"
28
33
  >> GovKit::CA::PostalCode.find_province_by_postal_code('K0A1K0')
29
34
  => "Ontario"
35
+ >> GovKit::CA::PostalCode.find_province_by_postal_code('H0H0H0')
36
+ => "Quebec"
30
37
 
31
38
  Postal codes may contain lowercase letters. Spaces and non-alphanumeric characters are removed before processing.
32
39
 
33
- GovKit-CA will raise GovKit::CA::ResourceNotFound if the electoral districts within a postal code cannot be determined.
40
+ GovKit-CA will raise GovKit::CA::ResourceNotFound if the electoral districts within a postal code cannot be determined, and GovKit::CA::InvalidRequest if a postal code is not properly formatted.
34
41
 
35
42
  # Bugs? Questions?
36
43
 
37
- Govkit-CA's main repository is on GitHub: [http://github.com/jpmckinney/govkit-ca](http://github.com/jpmckinney/govkit-ca), where your contributions, forks, bug reports, feature requests, and feedback are greatly welcomed.
38
-
39
- # TODOs
44
+ GovKit-CA interoperates with [Participatory Politics Foundation](http://www.participatorypolitics.org/)'s [GovKit](https://github.com/opengovernment/govkit). Please join the [GovKit Google Group](http://groups.google.com/group/govkit), especially if you'd like to talk about a new feature and get announcements.
40
45
 
41
- * Write tests
42
- * Add a `ElectoralDistrict` class to convert IDs to English and french names.
46
+ Govkit-CA's main repository is on GitHub: [http://github.com/jpmckinney/govkit-ca](http://github.com/jpmckinney/govkit-ca), where your contributions, forks, bug reports, feature requests, and feedback are greatly welcomed.
43
47
 
44
48
  Copyright (c) 2011 James McKinney, released under the MIT license
data/Rakefile CHANGED
@@ -6,4 +6,13 @@ RSpec::Core::RakeTask.new(:spec)
6
6
 
7
7
  task :default => :spec
8
8
 
9
+ begin
10
+ require 'yard'
11
+ YARD::Rake::YardocTask.new
12
+ rescue LoadError
13
+ task :yard do
14
+ abort 'YARD is not available. In order to run yard, you must: gem install yard'
15
+ end
16
+ end
17
+
9
18
  require File.expand_path('../tasks/rid_to_edid', __FILE__)
@@ -22,7 +22,7 @@ module GovKit
22
22
  # Returns the electoral districts within a postal code.
23
23
  # @return [Array<Fixnum>] the electoral districts within the postal code
24
24
  def electoral_districts
25
- valid? && electoral_districts!.map(&:to_i)
25
+ valid? && electoral_districts!.map(&:to_i).sort
26
26
  end
27
27
 
28
28
  private
@@ -2,6 +2,7 @@ module GovKit
2
2
  module CA
3
3
  module PostalCode
4
4
  module Strategy
5
+ # Occasionally suffers from timeout errors.
5
6
  class DigitalCopyrightCa < Base
6
7
  base_uri 'www.digital-copyright.ca'
7
8
 
@@ -0,0 +1,29 @@
1
+ module GovKit
2
+ module CA
3
+ module PostalCode
4
+ module Strategy
5
+ # greenparty.ca does not return electoral districts for postal codes
6
+ # that contain multiple electoral districts.
7
+ class GreenPartyCa < Base
8
+ base_uri 'greenparty.ca'
9
+
10
+ private
11
+
12
+ def electoral_districts!
13
+ [ response.headers['location'][/\d+\Z/] ]
14
+ end
15
+
16
+ def valid?
17
+ response.headers['location'] != 'http://greenparty.ca/find_your_riding'
18
+ end
19
+
20
+ def response
21
+ @response ||= self.class.head "/search/civicrm_lookup/#{@postal_code}"
22
+ end
23
+ end
24
+
25
+ StrategySet.register GreenPartyCa
26
+ end
27
+ end
28
+ end
29
+ end
@@ -2,6 +2,7 @@ module GovKit
2
2
  module CA
3
3
  module PostalCode
4
4
  module Strategy
5
+ # liberal.ca is unreliable during elections.
5
6
  class LiberalCa < Base
6
7
  base_uri 'www.liberal.ca'
7
8
 
@@ -18,7 +18,7 @@ module GovKit
18
18
  end
19
19
 
20
20
  def response
21
- @response ||= self.class.head "http://www.ndp.ca/riding/#{@postal_code}"
21
+ @response ||= self.class.head "/riding/#{@postal_code}"
22
22
  end
23
23
  end
24
24
 
@@ -24,8 +24,14 @@ module GovKit
24
24
  # @raise [ResourceNotFound] if no strategy succeeds
25
25
  def self.run(postal_code)
26
26
  strategies.each do |strategy|
27
- electoral_districts = strategy.new(postal_code).electoral_districts
28
- return electoral_districts if electoral_districts
27
+ begin
28
+ electoral_districts = strategy.new(postal_code).electoral_districts
29
+ return electoral_districts if electoral_districts
30
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
31
+ Errno::ETIMEDOUT, EOFError, Net::HTTPBadResponse,
32
+ Net::HTTPHeaderSyntaxError, Net::ProtocolError
33
+ # Do nothing. Continue.
34
+ end
29
35
  end
30
36
 
31
37
  raise ResourceNotFound
@@ -39,7 +45,4 @@ require 'gov_kit-ca/postal_code/strategy/base'
39
45
  require 'gov_kit-ca/postal_code/strategy/elections-ca'
40
46
  require 'gov_kit-ca/postal_code/strategy/cbc-ca'
41
47
  require 'gov_kit-ca/postal_code/strategy/ndp-ca'
42
- require 'gov_kit-ca/postal_code/strategy/digital_copyright-ca'
43
- require 'gov_kit-ca/postal_code/strategy/liberal-ca'
44
- #require 'gov_kit-ca/postal_code/strategy/parl-gc-ca'
45
- #require 'gov_kit-ca/postal_code/strategy/conservative-ca'
48
+ require 'gov_kit-ca/postal_code/strategy/greenparty-ca'
@@ -5,6 +5,13 @@ module GovKit
5
5
  # A collection of postal code helpers.
6
6
  # @see http://en.wikipedia.org/wiki/Postal_codes_in_Canada Postal codes in Canada
7
7
  module PostalCode
8
+ # @param [String] postal_code a postal code
9
+ # @return [Boolean] whether the postal code is properly formatted
10
+ # @see http://en.wikipedia.org/wiki/Postal_codes_in_Canada#Number_of_possible_postal_codes Possible postal codes
11
+ def self.valid?(postal_code)
12
+ !!postal_code.match(/\A[ABCEGHJKLMNPRSTVXY][0-9][ABCEGHJKLMNPRSTVWXYZ][0-9][ABCEGHJKLMNPRSTVWXYZ][0-9]\Z/)
13
+ end
14
+
8
15
  # Returns the electoral districts within a postal code.
9
16
  #
10
17
  # Statistics Canada charges for its Postal Codes by Federal Ridings File
@@ -12,10 +19,15 @@ module GovKit
12
19
  #
13
20
  # @param [String] postal_code a postal code
14
21
  # @return [Array<Fixnum>] the electoral districts within the postal code
22
+ # @raise [InvalidRequest] if the postal code is not properly formatted
15
23
  # @raise [ResourceNotFound] if the electoral districts cannot be determined
16
24
  # @see http://www.statcan.gc.ca/bsolc/olc-cel/olc-cel?lang=eng&catno=92F0193X Statistics Canada's product page for the Postal Codes by Federal Ridings File (PCFRF)
17
25
  def self.find_electoral_districts_by_postal_code(postal_code)
18
- StrategySet.run format_postal_code(postal_code)
26
+ if valid?(format_postal_code(postal_code))
27
+ StrategySet.run format_postal_code(postal_code)
28
+ else
29
+ raise InvalidRequest, "The postal code is not properly formatted"
30
+ end
19
31
  end
20
32
 
21
33
  # Returns the province that a postal code belongs to.
@@ -1,5 +1,5 @@
1
1
  module GovKit
2
2
  module CA
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
data/lib/gov_kit-ca.rb CHANGED
@@ -14,5 +14,6 @@ module GovKit
14
14
 
15
15
  class GovKitError < StandardError; end
16
16
  class ResourceNotFound < GovKitError; end
17
+ class InvalidRequest < GovKitError; end
17
18
  end
18
19
  end
@@ -0,0 +1,24 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ class GovKit::CA::PostalCode::Strategy::CbcCa
4
+ describe GovKit::CA::PostalCode::Strategy::CbcCa do
5
+ describe '#electoral_districts' do
6
+ it 'should return the electoral districts within a postal code' do
7
+ { 'G0C2Y0' => [24019],
8
+ 'T5S2B9' => [48015, 48017], # differs from elections.ca, ndp.ca
9
+ 'K0A1K0' => [35012, 35025, 35040, 35052, 35063, 35064, 35087],
10
+ }.each do |postal_code,electoral_districts|
11
+ GovKit::CA::PostalCode::Strategy::CbcCa.new(postal_code).electoral_districts.should == electoral_districts
12
+ end
13
+ end
14
+
15
+ it 'should return false if a postal code contains no electoral districts' do
16
+ GovKit::CA::PostalCode::Strategy::CbcCa.new('H0H0H0').electoral_districts.should be_false
17
+ end
18
+
19
+ it 'should return false if a postal code does not exist' do
20
+ GovKit::CA::PostalCode::Strategy::CbcCa.new('X1B1B1').electoral_districts.should be_false
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ class GovKit::CA::PostalCode::Strategy::ElectionsCa
4
+ describe GovKit::CA::PostalCode::Strategy::ElectionsCa do
5
+ describe '#electoral_districts' do
6
+ it 'should return the electoral districts within a postal code' do
7
+ { 'G0C2Y0' => [24019],
8
+ 'T5S2B9' => [48015], # differs from cbc.ca
9
+ }.each do |postal_code,electoral_districts|
10
+ GovKit::CA::PostalCode::Strategy::ElectionsCa.new(postal_code).electoral_districts.should == electoral_districts
11
+ end
12
+ end
13
+
14
+ it 'should (unfortunately) return false if a postal code contains multiple electoral districts' do
15
+ GovKit::CA::PostalCode::Strategy::ElectionsCa.new('K0A1K0').electoral_districts.should be_false
16
+ end
17
+
18
+ it 'should return false if a postal code contains no electoral districts' do
19
+ GovKit::CA::PostalCode::Strategy::ElectionsCa.new('H0H0H0').electoral_districts.should be_false
20
+ end
21
+
22
+ it 'should return false if a postal code does not exist' do
23
+ GovKit::CA::PostalCode::Strategy::ElectionsCa.new('X1B1B1').electoral_districts.should be_false
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ class GovKit::CA::PostalCode::Strategy::GreenPartyCa
4
+ describe GovKit::CA::PostalCode::Strategy::GreenPartyCa do
5
+ describe '#electoral_districts' do
6
+ it 'should return the electoral districts within a postal code' do
7
+ { 'G0C2Y0' => [24019],
8
+ 'T5S2B9' => [48015], # differs from cbc.ca
9
+ }.each do |postal_code,electoral_districts|
10
+ GovKit::CA::PostalCode::Strategy::GreenPartyCa.new(postal_code).electoral_districts.should == electoral_districts
11
+ end
12
+ end
13
+
14
+ it 'should (unfortunately) return false if a postal code contains multiple electoral districts' do
15
+ GovKit::CA::PostalCode::Strategy::GreenPartyCa.new('K0A1K0').electoral_districts.should be_false
16
+ end
17
+
18
+ it 'should return false if a postal code contains no electoral districts' do
19
+ GovKit::CA::PostalCode::Strategy::GreenPartyCa.new('H0H0H0').electoral_districts.should be_false
20
+ end
21
+
22
+ it 'should return false if a postal code does not exist' do
23
+ GovKit::CA::PostalCode::Strategy::GreenPartyCa.new('X1B1B1').electoral_districts.should be_false
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ class GovKit::CA::PostalCode::Strategy::LiberalCa
4
+ describe GovKit::CA::PostalCode::Strategy::LiberalCa do
5
+ describe '#electoral_districts' do
6
+ it 'should return the electoral districts within a postal code' do
7
+ { 'G0C2Y0' => [24019],
8
+ 'T5S2B9' => [48015, 48017], # differs from elections.ca, ndp.ca
9
+ 'K0A1K0' => [35012, 35025, 35040, 35052, 35063, 35064, 35087],
10
+ }.each do |postal_code,electoral_districts|
11
+ GovKit::CA::PostalCode::Strategy::LiberalCa.new(postal_code).electoral_districts.should == electoral_districts
12
+ end
13
+ end
14
+
15
+ it 'should return false if a postal code contains no electoral districts' do
16
+ GovKit::CA::PostalCode::Strategy::LiberalCa.new('H0H0H0').electoral_districts.should be_false
17
+ end
18
+
19
+ it 'should return false if a postal code does not exist' do
20
+ GovKit::CA::PostalCode::Strategy::LiberalCa.new('X1B1B1').electoral_districts.should be_false
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ class GovKit::CA::PostalCode::Strategy::NDPCa
4
+ describe GovKit::CA::PostalCode::Strategy::NDPCa do
5
+ describe '#electoral_districts' do
6
+ it 'should return the electoral districts within a postal code' do
7
+ { 'G0C2Y0' => [24019],
8
+ 'T5S2B9' => [48015], # differs from cbc.ca
9
+ }.each do |postal_code,electoral_districts|
10
+ GovKit::CA::PostalCode::Strategy::NDPCa.new(postal_code).electoral_districts.should == electoral_districts
11
+ end
12
+ end
13
+
14
+ it 'should (unfortunately) return false if a postal code contains multiple electoral districts' do
15
+ GovKit::CA::PostalCode::Strategy::NDPCa.new('K0A1K0').electoral_districts.should be_false
16
+ end
17
+
18
+ it 'should return false if a postal code contains no electoral districts' do
19
+ GovKit::CA::PostalCode::Strategy::NDPCa.new('H0H0H0').electoral_districts.should be_false
20
+ end
21
+
22
+ it 'should return false if a postal code does not exist' do
23
+ GovKit::CA::PostalCode::Strategy::NDPCa.new('X1B1B1').electoral_districts.should be_false
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,90 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- # TODO
3
+ module GovKit::CA::PostalCode
4
+ describe GovKit::CA::PostalCode do
5
+ describe '#valid?' do
6
+ it 'should return false if the postal code is not properly formatted' do
7
+ [ 'A1A1A', # too short
8
+ 'A1A1A1A', # too long
9
+ "A1A1A1\nA1A1A1", # multiline
10
+ 'AAAAAA', # no digits
11
+ '111111', # no letters
12
+ '1A1A1A', # wrong order
13
+ 'Z1Z1Z1', # Z as first letter
14
+ 'Q1Q1Q1', # Q as letter
15
+ 'a1a1a1', # lowercase
16
+ ].each do |postal_code|
17
+ subject.valid?(postal_code).should be_false
18
+ end
19
+ end
20
+
21
+ it 'should return true if the postal code is properly formatted' do
22
+ [ 'A1Z1Z1', # Z not as first letter
23
+ 'H0H0H0', # doesn't exist
24
+ 'A1A1A1', # does exist
25
+ ].each do |postal_code|
26
+ subject.valid?(postal_code).should be_true
27
+ end
28
+ end
29
+ end
30
+
31
+ describe '#find_electoral_districts_by_postal_code' do
32
+ it 'should return the electoral districts within a postal code' do
33
+ { 'A1A1A1' => [10007],
34
+ 'K0A1K0' => [35012, 35025, 35040, 35052, 35063, 35064, 35087],
35
+ }.each do |postal_code,electoral_districts|
36
+ subject.find_electoral_districts_by_postal_code(postal_code).should == electoral_districts
37
+ end
38
+ end
39
+
40
+ it 'should raise an error if the postal code cannot be determined' do
41
+ lambda{subject.find_electoral_districts_by_postal_code('H0H0H0')}.should raise_error(GovKit::CA::ResourceNotFound)
42
+ end
43
+
44
+ it 'should raise an error if the postal code is invalid' do
45
+ lambda{subject.find_electoral_districts_by_postal_code('AAAAAA')}.should raise_error(GovKit::CA::InvalidRequest)
46
+ end
47
+ end
48
+
49
+ describe '#find_province_by_postal_code' do
50
+ it 'should return the province that a postal code belongs to' do
51
+ { 'A' => 'Newfoundland and Labrador',
52
+ 'B' => 'Nova Scotia',
53
+ 'C' => 'Prince Edward Island',
54
+ 'E' => 'New Brunswick',
55
+ 'G' => 'Quebec',
56
+ 'H' => 'Quebec',
57
+ 'J' => 'Quebec',
58
+ 'K' => 'Ontario',
59
+ 'L' => 'Ontario',
60
+ 'M' => 'Ontario',
61
+ 'N' => 'Ontario',
62
+ 'P' => 'Ontario',
63
+ 'R' => 'Manitoba',
64
+ 'S' => 'Saskatchewan',
65
+ 'T' => 'Alberta',
66
+ 'V' => 'British Columbia',
67
+ 'X0A' => 'Nunavut',
68
+ 'X0B' => 'Nunavut',
69
+ 'X0C' => 'Nunavut',
70
+ 'X0E' => 'Northwest Territories',
71
+ 'X0G' => 'Northwest Territories',
72
+ 'X1A' => 'Northwest Territories',
73
+ 'Y' => 'Yukon',
74
+ }.each do |postal_code, province|
75
+ subject.find_province_by_postal_code(postal_code).should == province
76
+ end
77
+ end
78
+
79
+ it 'should raise an error if the province cannot be determined' do
80
+ lambda{subject.find_province_by_postal_code('X1B1B1')}.should raise_error(GovKit::CA::ResourceNotFound)
81
+ end
82
+ end
83
+
84
+ describe '#format_postal_code' do
85
+ it 'should format a postal code' do
86
+ subject.format_postal_code("+a1a 1a1\n").should == 'A1A1A1'
87
+ end
88
+ end
89
+ end
90
+ end
data/tasks/rid_to_edid.rb CHANGED
@@ -5,7 +5,7 @@ require File.expand_path('../../lib/gov_kit-ca', __FILE__)
5
5
  # http://www.digital-copyright.ca/pcfrf/pcfrf.tgz contains
6
6
  # `postal-code-for-districts.csv`, "which is 308 postal codes that should
7
7
  # map to each of the 308 different electoral districts." However, six of the
8
- # postal codes are invalid (G0A2C0, J8M1R8, J0W1B0, J0B1H0, L0J1B0, N2A1A3),
8
+ # postal codes do not exist (G0A2C0, J8M1R8, J0W1B0, J0B1H0, L0J1B0, N2A1A3),
9
9
  # 14 are duplicate, and the remaining 294 map to 246 electoral districts.
10
10
  desc "Picks the set cover for postal codes to cbc.ca riding IDs"
11
11
  task :trim_postal_codes do |t,args|
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 2
9
+ version: 0.0.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - James McKinney
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-03-01 00:00:00 -05:00
17
+ date: 2011-04-16 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -101,11 +101,17 @@ files:
101
101
  - lib/gov_kit-ca/postal_code/strategy/conservative-ca.rb
102
102
  - lib/gov_kit-ca/postal_code/strategy/digital_copyright-ca.rb
103
103
  - lib/gov_kit-ca/postal_code/strategy/elections-ca.rb
104
+ - lib/gov_kit-ca/postal_code/strategy/greenparty-ca.rb
104
105
  - lib/gov_kit-ca/postal_code/strategy/liberal-ca.rb
105
106
  - lib/gov_kit-ca/postal_code/strategy/ndp-ca.rb
106
107
  - lib/gov_kit-ca/postal_code/strategy/parl-gc-ca.rb
107
108
  - lib/gov_kit-ca/version.rb
108
109
  - lib/govkit-ca.rb
110
+ - spec/cbc_ca_spec.rb
111
+ - spec/elections_ca_spec.rb
112
+ - spec/greenparty_ca_spec.rb
113
+ - spec/liberal_ca_spec.rb
114
+ - spec/ndp_ca_spec.rb
109
115
  - spec/postal_code_spec.rb
110
116
  - spec/spec.opts
111
117
  - spec/spec_helper.rb
@@ -143,6 +149,11 @@ signing_key:
143
149
  specification_version: 3
144
150
  summary: Easy access to Canadian civic data around the web
145
151
  test_files:
152
+ - spec/cbc_ca_spec.rb
153
+ - spec/elections_ca_spec.rb
154
+ - spec/greenparty_ca_spec.rb
155
+ - spec/liberal_ca_spec.rb
156
+ - spec/ndp_ca_spec.rb
146
157
  - spec/postal_code_spec.rb
147
158
  - spec/spec.opts
148
159
  - spec/spec_helper.rb