ons_openapi 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b1a1edf4c26311e209664f2c74d01601f169c77d
4
+ data.tar.gz: d25742c9874d3bd14602b0cd1a0bafc89ea83ed4
5
+ SHA512:
6
+ metadata.gz: 211e09eae5a302907f2a1f06256fbe88e592c48d85609bfc901b3802d3a960b0cd14bc12b1ea18f75aa89688c1dcbbeaaad61bd2b295277786cd0a72c04d1694
7
+ data.tar.gz: e172c11cf23355fcc0db0519b37bbde324beb677023cf7e4e05df9434f5ea666b823705e7d2724b2f2338a93c6b3f1280422bf03a585dac2c65ec880cc124966
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Rob McKinnon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,218 @@
1
+ ons-openapi
2
+ ===========
3
+
4
+ Ruby wrapper around the [ONS OpenAPI](https://www.ons.gov.uk/ons/apiservice/web/apiservice/home) - the UK Office of National Statistics's data API. The intention is to make it easy to quickly retrieve data. It may not expose the full functionality of the ONS OpenAPI.
5
+
6
+ This gem was partially written at [Accountability Hack 2014](https://twitter.com/search?q=%23AccHack14), an event organised by Parliament, ONS, and NAO.
7
+
8
+ Please provide feedback via [GitHub issues](https://github.com/robmckinnon/ons-openapi/issues).
9
+
10
+ # Installation
11
+
12
+ ```
13
+ gem install ons_openapi
14
+ ```
15
+
16
+ then in Ruby code
17
+
18
+ ```
19
+ require 'ons_openapi'
20
+ ```
21
+
22
+ or if using bundler (as with Rails), add to the Gemfile
23
+
24
+ ```
25
+ gem 'ons_openapi'
26
+ ```
27
+
28
+ You must have an ExecJS supported runtime when running this gem. If you are using Mac OS X or Windows, you already have a JavaScript runtime installed in your operating system. Check [ExecJS documentation](https://github.com/sstephenson/execjs#readme) to know all supported JavaScript runtimes.
29
+
30
+ The gem includes code from the [JSON-stat Javascript Toolkit](https://github.com/badosa/JSON-stat) to parse JSON-stat formatted results from the ONS OpenAPI. ExecJS is used to run JSON-stat JavaScript code from within Ruby.
31
+
32
+ # Register for API key
33
+
34
+ The ONS OpenAPI requires you [register for an API key](https://www.ons.gov.uk/ons/apiservice/web/apiservice/home#reg-main-content).
35
+
36
+ When using the gem, set an environment variable named `ONS_APIKEY` with your API key value.
37
+
38
+ ```
39
+ $ ONS_APIKEY=<your_ons_openapi_key>
40
+ ```
41
+
42
+ # Getting started
43
+
44
+ Running in irb:
45
+
46
+ $ ONS_APIKEY=<your_ons_openapi_key> irb
47
+
48
+ require 'ons_openapi'
49
+
50
+ census = OnsOpenApi.context('Census')
51
+
52
+ religion_detailed = census.collection('Religion (detailed)')
53
+
54
+ religion_detailed.data_for('Islington')
55
+ #=> [["2011 Administrative Hierarchy", "Religion (Flat) (T059A)", "Time Dimension", "Value"],
56
+ # ["Islington", "All categories: Religion", "2011", 206125],
57
+ # ["Islington", "Christian", "2011", 82879],
58
+ # ["Islington", "Buddhist", "2011", 2117],
59
+ # ...
60
+
61
+ religion_detailed.data_for('Islington S')
62
+ #=> [["2011 Westminster Parliamentary Constituency Hierarchy", "Religion (Flat) (T059A)", "Time", "Value"],
63
+ # ["Islington South and Finsbury", "All categories: Religion", "2011", 102656],
64
+ # ["Islington South and Finsbury", "Christian", "2011", 42222],
65
+ # ["Islington South and Finsbury", "Buddhist", "2011", 1126],
66
+ # ...
67
+
68
+ religion_detailed.data_for('England')
69
+ #=> [["2011 Administrative Hierarchy", "Religion (Flat) (T059A)", "Time Dimension", "Value"],
70
+ # ["England", "All categories: Religion", "2011", 53012456],
71
+ # ["England", "Christian", "2011", 31479876],
72
+ # ["England", "Buddhist", "2011", 238626],
73
+ # ...
74
+
75
+ religion_detailed.data_for('North West')
76
+ #=> [["2011 Administrative Hierarchy", "Religion (Flat) (T059A)", "Time Dimension", "Value"],
77
+ # ["North West", "All categories: Religion", "2011", 7052177],
78
+ # ["North West", "Christian", "2011", 4742860],
79
+ # ["North West", "Buddhist", "2011", 20695]
80
+
81
+ religion_detailed.data_for('Scotland')
82
+ # RuntimeError: ONS Exception: 404 INTERNAL ERROR: Invalid dimension item code S92000003
83
+
84
+ religion_detailed.data_for('Woodlands')
85
+ # RuntimeError: more than one match, try one of:
86
+ #
87
+ # data_for('E05006341 Woodlands') or data_for('E05006341') see http://statistics.data.gov.uk/doc/statistical-geography/E05006341
88
+ #
89
+ # data_for('E05008891 Woodlands') or data_for('E05008891') see http://statistics.data.gov.uk/doc/statistical-geography/E05008891
90
+ #
91
+ # data_for('E05001234 Woodlands') or data_for('E05001234') see http://statistics.data.gov.uk/doc/statistical-geography/E05001234
92
+ #
93
+ # data_for('E05004981 Woodlands') or data_for('E05004981') see http://statistics.data.gov.uk/doc/statistical-geography/E05004981
94
+
95
+
96
+ # Contexts
97
+
98
+ The datastore underneath the ONS OpenAPI is divided into four sections called contexts.
99
+
100
+ OnsOpenApi.context_names
101
+ #=> ["Census", "Socio-Economic", "Economic", "Social"]
102
+
103
+ OnsOpenApi.contexts.size
104
+ #=> 4
105
+
106
+ The Census context contains data from the 2011 Census in England and Wales.
107
+
108
+ # Collections
109
+
110
+ Each context consists of several dataset collections.
111
+
112
+ economic = OnsOpenApi.context('Economic')
113
+ economic.collections.size
114
+ #=> 26
115
+
116
+ census = OnsOpenApi.context('Census')
117
+ census.collections.size
118
+ #=> 340
119
+
120
+ Use `collection_names()` to view a list of collection names:
121
+
122
+ puts census.collection_names.select {|n| n[/religion/i]}
123
+ # DC1202EW Household composition by religion of Household Reference Person (HRP)
124
+ # DC2107EW Religion by sex by age
125
+ # DC2201EW Ethnic group by religion
126
+ # ...
127
+ # QS208EW Religion
128
+ # QS210EW Religion (detailed)
129
+ # ST210EWla Religion (non-UK born short-term residents)
130
+
131
+ Retrieve a collection using `collection()`. Pass in the collection code, collection name, or collection code and name:
132
+
133
+ census.collection('QS210EW')
134
+ census.collection('Religion (detailed)')
135
+ census.collection('QS210EW Religion (detailed)')
136
+
137
+ # Geographical hierarchies
138
+
139
+ Each collection can be filtered with different geographical hierarchies.
140
+
141
+ census = OnsOpenApi.context('Census')
142
+ religion = census.collection('Religion')
143
+
144
+ religion.geography_codes
145
+ # [
146
+ # ["2011STATH", "2011 Statistical Geography Hierarchy"],
147
+ # ["2011WARDH", "2011 Administrative Hierarchy"],
148
+ # ["2011PCONH", "2011 Westminster Parliamentary Constituency Hierarchy"]
149
+ # ]
150
+
151
+ # Listing areas from geography hierarchy
152
+
153
+ Use `geographies()` to get a hash of geography hierarchies. To get a list of geography items for a hierarchy, say '2011PCONH', do:
154
+
155
+ census = OnsOpenApi.context('Census')
156
+ religion = census.collection('Religion')
157
+ constituencies = religion.geographies['2011PCONH']
158
+
159
+ constituencies.map(&:label)
160
+ # ["Aberavon", "Aberconwy", "Aldershot", "Aldridge-Brownhills", ...
161
+
162
+ constituencies.map(&:item_code)
163
+ # ["E14000759", "E14000531", "E14000967", "E14000904", ...
164
+
165
+ To see `area_types` in the hierarchy:
166
+
167
+ constituencies.map(&:area_type).map {|a| [a.codename, a.level]}.uniq
168
+ # [["Westminster Parliamentary Constituency", 3],
169
+ # ["Region", 2],
170
+ # ["Country", 1],
171
+ # ["England and Wales", 0]]
172
+
173
+ Or for hierarchy '2011WARDH', do:
174
+
175
+ areas = religion.geographies['2011WARDH']
176
+
177
+ areas.map(&:label)
178
+ # ["Aldwick East", "West Mersea", "Stowmarket Central", ...
179
+
180
+ areas.map {|a| [a.item_code, a.label, a.area_type.codename, a.area_type.level]}
181
+ # [["E05007576", "Aldwick East", "Electoral Ward/Division ", 7],
182
+ # ["E05004144", "West Mersea", "Electoral Ward/Division ", 7],
183
+ # ["E05007153", "Stowmarket Central", "Electoral Ward/Division ", 7]], ...
184
+
185
+ To see `area_types` in the hierarchy:
186
+
187
+ areas.map(&:area_type).map {|a| [a.codename, a.level]}.uniq
188
+ # [["Electoral Ward/Division ", 7],
189
+ # ["Electoral Division", 7],
190
+ # ["Metropolitan District ", 6],
191
+ # ["Council Area", 4],
192
+ # ["County", 5],
193
+ # ["Non-metropolitan District", 6],
194
+ # ["London Borough ", 6],
195
+ # ["Unitary Authority", 5],
196
+ # ["Metropolitan County", 5],
197
+ # ["Country", 3],
198
+ # ["Great Britain", 1],
199
+ # ["United Kingdom", 0],
200
+ # ["Region", 4],
201
+ # ["England and Wales", 2],
202
+ # ["Inner and Outer London", 5]]
203
+
204
+ # Data for a geographic area
205
+
206
+ Use `data_for()` to retrieve data for a geography that matches given label_or_code.
207
+
208
+ census = OnsOpenApi.context('Census')
209
+ religion = census.collection('Religion')
210
+
211
+ religion.data_for('England')
212
+ religion.data_for('Islington S')
213
+ religion.data_for('E05002040')
214
+
215
+ # Feedback
216
+
217
+ Please provide feedback via [GitHub issues](https://github.com/robmckinnon/ons-openapi/issues).
218
+
@@ -0,0 +1,10 @@
1
+ class OnsOpenApi::Classification
2
+
3
+ include Morph
4
+ include OnsOpenApi::DataHelper
5
+
6
+ def classification_detail
7
+ OnsOpenApi::get(url.first, url.last).classification_detail
8
+ end
9
+
10
+ end
@@ -0,0 +1,151 @@
1
+ class OnsOpenApi::Collection
2
+
3
+ include Morph
4
+ include OnsOpenApi::DataHelper
5
+
6
+ # Returns title, i.e. "id name"
7
+ def title
8
+ [id, name].join(' ')
9
+ end
10
+
11
+ # Returns data as array of arrays, for a geography that matches label_or_code.
12
+ # e.g. data_for('England'), data_for('Islington S'), data_for('E05002040')
13
+ # Raises exception if no match or more than one match.
14
+ def data_for label_or_code
15
+ if geographies = geography(label_or_code)
16
+ if geographies.size > 1
17
+ cmds = geographies.map {|g| "data_for('#{g.title}') or data_for('#{g.item_code}') see http://statistics.data.gov.uk/doc/statistical-geography/#{g.item_code}"}
18
+ raise "more than one match, try one of:\n\n #{cmds.join(" \n\n ") }\n\n"
19
+ else
20
+ geo = geographies.first
21
+ data geo.item_code, geo.geography_code
22
+ end
23
+ end
24
+ end
25
+
26
+ # Returns data for given geog_code, geog, and optional JSON-stat command.
27
+ def data geog_code, geog, stat='.toTable()'
28
+ @json_stats ||={}
29
+ @json_stats[geog_code + geog] ||= OnsOpenApi::request("dataset/#{id}",
30
+ context: context_name,
31
+ geog: geog,
32
+ "dm/#{geog}" => geog_code,
33
+ totals: 'false',
34
+ jsontype: 'json-stat')
35
+
36
+ raw_json_stat = @json_stats[geog_code + geog].gsub("\n", ' ').gsub("'", "").squeeze(' ')
37
+
38
+ if raw_json_stat.include?('ns1faultstring')
39
+ raise "ONS Exception: #{raw_json_stat.gsub(/(<.?ns1XMLFault>)|(<.?ns1faultstring>)/,'')}"
40
+ elsif raw_json_stat.include?('errorMessage')
41
+ raise "ONS Error: #{raw_json_stat}"
42
+ end
43
+
44
+ begin
45
+ table = js_context.eval( %Q| JSONstat( JSON.parse(' #{raw_json_stat} ') ).Dataset(0)#{stat} | )
46
+ rescue Encoding::UndefinedConversionError => e
47
+ if e.to_s[/ASCII-8BIT to UTF-8/]
48
+ raw_json_stat = raw_json_stat.force_encoding('ASCII-8BIT').encode('UTF-8', :invalid => :replace, :undef => :replace, :replace => '?')
49
+ table = js_context.eval( %Q| JSONstat( JSON.parse(' #{raw_json_stat} ') ).Dataset(0)#{stat} | )
50
+ else
51
+ raise "#{e.to_s}: #{raw_json_stat}"
52
+ end
53
+ rescue ExecJS::RuntimeError, ExecJS::ProgramError => e
54
+ raise "#{e.to_s}: #{raw_json_stat}"
55
+ end
56
+
57
+ if table.is_a?(Array) && (index = table[1].index('Segment_1'))
58
+ table.each {|row| row.delete_at(index)}
59
+ end
60
+ table
61
+ end
62
+
63
+ #
64
+ # Returns array of [geography_code, description] that are supported by this
65
+ # collection.
66
+ #
67
+ # e.g.
68
+ # 2011WARDH - 2011 Administrative Hierarchy
69
+ # 2011STATH - 2011 Statistical Geography Hierarchy
70
+ # 2011PCONH - 2011 Westminster Parliamentary Constituency Hierarchy
71
+ # 2011HTWARDH - 2011 Census Merged Ward Hierarchy
72
+ # 2011CMLADH - 2011 Census merged local authority district hierarchy
73
+ # 2011PARISH - 2011 Parish Hierarchy
74
+ #
75
+ def geography_codes
76
+ gh ||= geographical_hierarchies
77
+ @geographies_data ||= if gh.respond_to?(:geographical_hierarchy) && gh.geographical_hierarchy
78
+ url, args = gh.geographical_hierarchy.url
79
+ OnsOpenApi::get(url, args)
80
+ elsif gh.respond_to?(:geographical_hierarchies) && gh.geographical_hierarchies
81
+ url, args = gh.geographical_hierarchies.first.url
82
+ OnsOpenApi::get(url, args)
83
+ end
84
+
85
+ if @geographies_data
86
+ list = @geographies_data.geographical_hierarchy_list
87
+ if list.respond_to?(:geographical_hierarchy) && list.geographical_hierarchy
88
+ [ [list.geographical_hierarchy.id, list.geographical_hierarchy.name] ]
89
+ elsif list.respond_to?(:geographical_hierarchies) && list.geographical_hierarchies
90
+ list.geographical_hierarchies.map{|x| [x.id, x.name]}
91
+ else
92
+ []
93
+ end
94
+ else
95
+ []
96
+ end
97
+ end
98
+
99
+ # Returns geography item that matches given label_or_code.
100
+ # Raises exception if there is no match.
101
+ def geography label_or_code
102
+ if matches = geography_exact_match(label_or_code) || geography_partial_match(label_or_code)
103
+ matches
104
+ else
105
+ raise "no geography match found for: #{label_or_code}"
106
+ end
107
+ end
108
+
109
+ # Returns collection_detail object.
110
+ def collection_detail
111
+ OnsOpenApi::get(url.first, url.last).collection_detail
112
+ end
113
+
114
+ # Returns hash of geography code to list of geography items.
115
+ def geographies
116
+ codes = geography_codes.map(&:first)
117
+ codes.each_with_object({}) do |code, hash|
118
+ hash[code] = OnsOpenApi.context(context_name).geographies(code)
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def geography_exact_match label_or_code
125
+ matches = []
126
+ geographies.values.each do |list|
127
+ found = list.select{|c| (c.label == label_or_code) || (c.item_code == label_or_code) || (c.title == label_or_code) }
128
+ matches += found unless found.empty?
129
+ end
130
+ matches.empty? ? nil : matches.uniq {|m| m.item_code}
131
+ end
132
+
133
+ def geography_partial_match label_or_code
134
+ matches = []
135
+ geographies.values.each do |list|
136
+ found = list.select{|c| c.label[label_or_code] }
137
+ matches += found unless found.empty?
138
+ end
139
+ matches.empty? ? nil : matches.uniq {|m| m.item_code}
140
+ end
141
+
142
+ def js_context
143
+ unless @js_context
144
+ jsonstatjs = File.expand_path('../../../vendor/json-stat.max.js', __FILE__)
145
+ source = open(jsonstatjs).read
146
+ @js_context = ExecJS.compile(source)
147
+ end
148
+ @js_context
149
+ end
150
+
151
+ end
@@ -0,0 +1,6 @@
1
+ class OnsOpenApi::Concept
2
+
3
+ include Morph
4
+ include OnsOpenApi::DataHelper
5
+
6
+ end
@@ -0,0 +1,65 @@
1
+ require 'net/https'
2
+
3
+ module OnsOpenApi::Connection
4
+
5
+ BASE_URI = 'http://data.ons.gov.uk/ons/api/data/'
6
+ API_KEY = ENV['ONS_APIKEY']
7
+
8
+ def get(resource, args={})
9
+ to_object request(resource, "get", args)
10
+ end
11
+
12
+ def post(resource, args={})
13
+ to_object request(resource, "post", args)
14
+ end
15
+
16
+ def to_object json
17
+ json.gsub!('"@', '"')
18
+ json.gsub!('xml.lang','xml_lang')
19
+ json.gsub!('"$":','"text":')
20
+ json.gsub!('"2011WARDH" : {', '"X2011WARDH" : {')
21
+ json.gsub!('"2011HTWARDH" : {', '"X2011HTWARDH" : {')
22
+ hash = JSON.parse json
23
+ Morph.from_hash hash, OnsOpenApi
24
+ end
25
+
26
+ def request_uri(resource, args)
27
+ unless API_KEY
28
+ raise 'No ONS OpenAPI key found. Set this environment variable: ONS_APIKEY=<your_ons_openapi_key>'
29
+ end
30
+ uri = URI.join(BASE_URI, (resource+'.json').sub('.json.json','.json') )
31
+
32
+ args ||= {}
33
+ args.delete('apikey')
34
+ args.merge!( apikey: API_KEY )
35
+ uri.query = args.map { |k,v| "%s=%s" % [URI.encode(k.to_s), URI.encode(v.to_s)] }.join("&") if args
36
+
37
+ puts uri.to_s
38
+ uri
39
+ end
40
+
41
+ def request(resource, method="get", args)
42
+ uri = request_uri resource, args
43
+ case method
44
+ when "get"
45
+ req = Net::HTTP::Get.new(uri.request_uri)
46
+ when "post"
47
+ req = Net::HTTP::Post.new(uri.request_uri)
48
+ end
49
+
50
+ http = Net::HTTP.new(uri.host, uri.port)
51
+ http.use_ssl = (uri.port == 443)
52
+
53
+ res = http.start() { |conn| conn.request(req) }
54
+ res.body
55
+ end
56
+
57
+ end
58
+
59
+ module OnsOpenApi
60
+
61
+ class << self
62
+ include OnsOpenApi::Connection
63
+ end
64
+
65
+ end
@@ -0,0 +1,106 @@
1
+ class OnsOpenApi::Context
2
+
3
+ include Morph
4
+
5
+ class << self
6
+ def all
7
+ @contexts ||= OnsOpenApi::get('contexts').context_list.statistical_contexts.map {|x| new x}
8
+ end
9
+ end
10
+
11
+ def initialize x
12
+ self.id = x.context_id
13
+ self.name = x.context_name
14
+ end
15
+
16
+ def concepts
17
+ @concepts ||= OnsOpenApi::get('concepts', context: @name).concept_list.concepts
18
+ end
19
+
20
+ def collections
21
+ @collections ||= OnsOpenApi::get('collections', context: @name).collection_list.collections
22
+ unless @collections.first.respond_to?(:context_name) && @collections.first.context_name
23
+ @collections.each {|c| c.context_name = @name}
24
+ end
25
+ @collections
26
+ end
27
+
28
+ def classifications
29
+ @classifications ||= OnsOpenApi::get('classifications', context: @name).classification_list.classifications
30
+ end
31
+
32
+ def collection id_or_name
33
+ collection = collections.detect{|c| (c.id == id_or_name || c.title == id_or_name) }
34
+
35
+ unless collection
36
+ list = collections.select{|c| c.name == id_or_name}
37
+ if list.size > 1
38
+ cmds = list.map{|c| [c.id,c.title]}.flatten.map{|n| "collection('#{n}')"}
39
+ raise "more than one match, try one of:\n\n #{cmds.join(" \n\n ") }\n\n"
40
+ else
41
+ collection = list.first
42
+ end
43
+ end
44
+
45
+ collection
46
+ end
47
+
48
+ def concept_names
49
+ names_for concepts
50
+ end
51
+
52
+ def collection_names
53
+ names_for collections
54
+ end
55
+
56
+ def classification_names
57
+ names_for classifications
58
+ end
59
+
60
+ # Returns geography objects for given geography code.
61
+ #
62
+ # Parameter +code+ defaults to +'2011WARDH'+ for the 2011 Administrative
63
+ # Hierarchy, if no +code+ supplied.
64
+ #
65
+ # Codes include:
66
+ # +2011WARDH+ - 2011 Administrative Hierarchy
67
+ # +2011STATH+ - 2011 Statistical Geography Hierarchy
68
+ # +2011PCONH+ - 2011 Westminster Parliamentary Constituency Hierarchy
69
+ # +2011HTWARDH+ - 2011 Census Merged Ward Hierarchy
70
+ # +2011CMLADH+ - 2011 Census merged local authority district hierarchy
71
+ # +2011PARISH+ - 2011 Parish Hierarchy
72
+ def geographies code='2011WARDH'
73
+ @geographies ||= {}
74
+ unless @geographies[code]
75
+ params = { context: @name }
76
+
77
+ if code == '2011STATH'
78
+ params.merge!({ levels: '0,1,2,3,4,5' }) # restrict levels to reduce delay
79
+ end
80
+
81
+ result = OnsOpenApi::get "hierarchies/hierarchy/#{code}", params
82
+ @geographies[code] = result.geography_list.items.items
83
+ end
84
+ @geographies[code].each {|g| g.geography_code = code }
85
+ @geographies[code]
86
+ end
87
+
88
+ # Returns geography objects from the 2011 Administrative Hierarchy
89
+ # with area type 'Electoral Division'
90
+ def electoral_divisions
91
+ geographies('2011WARDH').select {|z| z.area_type.codename['Electoral Division']}
92
+ end
93
+
94
+ # Returns geography objects from the 2011 Administrative Hierarchy
95
+ # with area type 'Electoral Ward/Division'
96
+ def electoral_wards
97
+ geographies('2011WARDH').select {|z| z.area_type.codename['Electoral Ward/Division']}
98
+ end
99
+
100
+ private
101
+
102
+ def names_for list
103
+ list.map(&:title)
104
+ end
105
+
106
+ end
@@ -0,0 +1,5 @@
1
+ module OnsOpenApi::DataHelper
2
+
3
+ include OnsOpenApi::NameHelper
4
+ include OnsOpenApi::UrlHelper
5
+ end
@@ -0,0 +1,46 @@
1
+ class OnsOpenApi::Dimension
2
+
3
+ include Morph
4
+
5
+ def values
6
+ ids.each_with_object({}) do |x, h|
7
+ h[alt_label(x)] = send(method(x))
8
+ end
9
+ end
10
+
11
+ def labels
12
+ data = values
13
+ non_data_keys = role.morph_attributes.values.flatten.map{|x| alt_label x}
14
+ non_data_keys.each do |k|
15
+ data.delete(k)
16
+ end
17
+ data = data.values.first.category
18
+
19
+ key_to_index = data.index.morph_attributes
20
+ key_to_label = data.label.morph_attributes
21
+
22
+ labels = key_to_index.to_a.each_with_object([]) do |k_i, a|
23
+ if index = k_i[1]
24
+ key = k_i[0]
25
+ a[index] = key_to_label[key]
26
+ end
27
+ end
28
+
29
+ # label = data.label
30
+ # items = data.category.index
31
+ # labels = data.category.label
32
+ end
33
+
34
+ private
35
+
36
+ def alt_label x
37
+ x['2011WARDH'] ? 'X2011_WARDH' : x
38
+ x['2011HTWARDH'] ? 'X2011_HTWARDH' : x
39
+ end
40
+
41
+ def method x
42
+ x = alt_label x
43
+ method = Morph::InstanceMethods::Helper.convert_to_morph_method_name x
44
+ end
45
+
46
+ end
@@ -0,0 +1,6 @@
1
+ class OnsOpenApi::GeographicalHierarchy
2
+
3
+ include Morph
4
+ include OnsOpenApi::DataHelper
5
+
6
+ end
@@ -0,0 +1,13 @@
1
+ class OnsOpenApi::Item
2
+
3
+ include Morph
4
+
5
+ def label
6
+ labels.labels.first.text
7
+ end
8
+
9
+ def title
10
+ [item_code, label].join(' ')
11
+ end
12
+
13
+ end
@@ -0,0 +1,7 @@
1
+ module OnsOpenApi::NameHelper
2
+
3
+ def name
4
+ names.names.first.text
5
+ end
6
+
7
+ end
@@ -0,0 +1,12 @@
1
+ module OnsOpenApi::UrlHelper
2
+
3
+ def url
4
+ require 'cgi'
5
+ if respond_to?(:urls) && urls
6
+ uri = URI.parse urls.urls.detect{|x| x.representation['json'] }.href
7
+ params = Rack::Utils.parse_query(uri.query)
8
+ params.delete('apikey')
9
+ return [uri.path, params]
10
+ end
11
+ end
12
+ end