pm_25 0.0.1

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: 00dac5016f468d768d295752677967f3073c757e
4
+ data.tar.gz: c592db0adc0ac25c6e4732a5849fd3319014b06a
5
+ SHA512:
6
+ metadata.gz: debccc45f01e29b5b3104fed7452429c1c556dc674bf3015896657331f33da8e5821bf1304e254c2ee07ec8e85ba3b0069dd188a1b27a602b7cab165b4a1c413
7
+ data.tar.gz: 4e9f749a2b868045f9dc9c0bbd93f88eecc258677bf43f605a3b781f62171287395a182fdbbff4e049b3202cdf6bd2104d6cc9e8fa4705e552ca8a1a26849a38
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.idea/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pm25.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Jakukyo Friel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # Pm25
2
+
3
+ A Ruby wrapper for pm25.in API and other PM 2.5 related utility functions.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'pm_25'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install pm_25
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ require 'pm_25'
25
+ PM25.Func(args)
26
+ ```
27
+
28
+ The following APIs of pm25.in are implemented:
29
+
30
+ - 1.1 `pm25` Get PM 2.5 info of all stations in the specified city.
31
+ - 1.11 `available_cities` Get a list of cites providing PM 2.5 data.
32
+ - 1.12 `all_cities` Get PM 2.5 data for all cities.
33
+ - 1.13 `aqi_ranking` Get average data for all cities. (Cities are sorted by
34
+ AQI.)
35
+
36
+ `1.1`, `1.11` etc are for reference of [pm25.in official api
37
+ documentation][api_doc].
38
+
39
+ [api_doc]: http://www.pm25.in/api_doc
40
+
41
+ I only implemented APIs I care, and I guess in most cases these is the APIs
42
+ you actually want to use.
43
+
44
+ However, if you do need other APIs, feel free to send a pull request.
45
+ You can use `PM25.access_api`. (Actually implementing other APIs is trivial
46
+ with this.)
47
+
48
+ pm25.in requires a token to access its api.
49
+ You can apply one at [here][api_doc].
50
+
51
+ `pm25` will look for a token in the following order:
52
+
53
+ - Token argument when invoking functions.
54
+ - Environment virable `PM25_IN_TOKEN`
55
+ - `"PM25_IN_TOKEN": 'your_token'` in `config.json` at the current directory.
56
+ - The default test token at [pm25.in official api documentation][api_doc].
57
+
58
+ Note that the test token is barely usable (too many people use it).
59
+
60
+ If you does not have a token, you can use `bing_pm25` to get the average
61
+ PM 2.5 value for the specified city.
62
+ This function uses data from bing.com, thus does not need a token.
63
+ For example:
64
+
65
+ ```ruby
66
+ PM25.bing_pm25 '北京'
67
+ ```
68
+
69
+ pm25.in uses CN standard for AQI category.
70
+ If you want to use US standard instead, use `pm25_level`.
71
+ For example:
72
+
73
+ ```ruby
74
+ PM25.pm25_level(123)
75
+ ```
76
+
77
+ It will return a hash containing:
78
+
79
+ - PM 2.5 value
80
+ - AQI category
81
+ - AQI category meaning
82
+ - suggested action
83
+
84
+ For other functions and usage details, check API documentation:
85
+
86
+ http://www.rubydoc.info/gems/pm25/
87
+
88
+ ### Command line usage
89
+
90
+ We also provide a command line utility to query PM 2.5 info for the
91
+ specified city:
92
+
93
+ ```sh
94
+ ; PM25_IN_TOKEN='your_token' pm25 北京
95
+ 北京: 106, Unhealthy
96
+ Everyone may begin to experience health effects; members of sensitive groups may
97
+ experience more serious health effects.
98
+ People with heart or lung disease, children and older adults should avoid
99
+ prolonged or heavy exertion. Everyone else should reduce prolonged or heavy
100
+ exertion.
101
+ ```
102
+
103
+ If you does not provide a city argument, it will use environment variable `PM25_IN_CITY`.
104
+
105
+ If something goes wrong with pm25.in, (say, you does not provide a token), it
106
+ will use bing.com instead.
107
+
108
+ ## Contributing
109
+
110
+ 1. Fork it ( https://github.com/[my-github-username]/pm25/fork )
111
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
112
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
113
+ 4. Push to the branch (`git push origin my-new-feature`)
114
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/pm25 ADDED
@@ -0,0 +1,13 @@
1
+ require 'pm25'
2
+
3
+ if ARGV.empty?
4
+ city = ENV['PM25_IN_CITY']
5
+ else
6
+ city = ARGV[0]
7
+ end
8
+
9
+ pm25_info = PM25.pm25_level(PM25.just_pm25(city, ENV['PM25_IN_TOKEN']))
10
+
11
+ puts "#{city}: #{pm25_info[:pm25]}, #{pm25_info[:category]}"
12
+ puts pm25_info[:meaning]
13
+ puts pm25_info[:action]
@@ -0,0 +1,3 @@
1
+ module PM25
2
+ VERSION = "0.0.1"
3
+ end
data/lib/pm_25.rb ADDED
@@ -0,0 +1,243 @@
1
+ require 'pm_25/version'
2
+
3
+ require 'nokogiri'
4
+ require 'json'
5
+ require 'open-uri'
6
+ require 'rest_client'
7
+ require 'time-lord'
8
+
9
+ module PM25
10
+ # Yes, pm25.in requires a token but uses http!
11
+ API_base = 'http://www.pm25.in/api/querys/'
12
+
13
+ module_function
14
+
15
+ # Use environment variable or the value
16
+ # from config at the current directory.
17
+ # If all failed, use the default value.
18
+ #
19
+ # @param [String] constant_name
20
+ # @param [String] default_value
21
+ # @return [String]
22
+ def get_config(constant_name, default_value=nil)
23
+ if ENV[constant_name]
24
+ ENV[constant_name]
25
+ elsif File.exist?('config.json')
26
+ open('config.json') do |f|
27
+ JSON.parse(f.read)[constant_name]
28
+ end
29
+ else
30
+ default_value
31
+ end
32
+ end
33
+
34
+ # Use environment variable PM25_IN_TOKEN
35
+ # or the value from config at the current directory.
36
+ # If all failed, use the test token.
37
+ # You can apply a token at pm25.in:
38
+ # http://www.pm25.in/api_doc
39
+ #
40
+ # @return [String] token
41
+ def get_token
42
+ test_token= '5j1znBVAsnSf5xQyNQyq'
43
+ get_config('PM25_IN_TOKEN', test_token)
44
+ end
45
+
46
+ # Use environment variable PM25_IN_CITY
47
+ # or the value from config at the current directory.
48
+ #
49
+ # @return [String] city
50
+ def get_default_city
51
+ get_config('PM25_IN_CITY')
52
+ end
53
+
54
+ # @param [String] interface of api
55
+ # @param [Hash] params (additional) options
56
+ # @return [Hash] result
57
+ # @return [Fixnum] error code
58
+ # TODO pm25.in tends to 502, need to email complaints and handle this.
59
+ def access_api(interface, params={})
60
+ params[:token] ||= get_token
61
+ res = RestClient.get(API_base + interface, {params: params})
62
+ if res.code == 200
63
+ JSON.parse res.body
64
+ else
65
+ res.code
66
+ end
67
+ end
68
+
69
+
70
+ # Get PM 2.5 info of all stations in the specified city.
71
+ # API frequency limit: 500 per hour.
72
+ #
73
+ # @param [String] city You can use
74
+ # - Chinese (e.g. 广州)
75
+ # - area code (e.g. 020)
76
+ # - Pinyin (e.g. guangzhou)
77
+ # If there is ambiguity in Pinyin, use `shi` as postfix, for example:
78
+ # 泰州 is taizhoushi, and 台州 is taizhou.
79
+ # For more information, see http://www.pm25.in/
80
+ # @return [Array<Hash>] an array of all stations
81
+ # Every station includes:
82
+ # * aqi (according to CN standard)
83
+ # * area
84
+ # * pm2_5
85
+ # * pm2_5_24h (moving average)
86
+ # * position_name (station name)
87
+ # * primary_pollutant
88
+ # * quality (优、良、轻度污染、中度污染、重度污染、严重污染 according to CN standard)
89
+ # * station_code
90
+ # * time_point (publish time of air condition)
91
+ #
92
+ # Example:
93
+ #
94
+ # pm25('zhuhai')
95
+ #
96
+ # [
97
+ # {
98
+ # "aqi"=> 82,
99
+ # "area"=> "珠海",
100
+ # "pm2_5"=> 31,
101
+ # "pm2_5_24h"=> 60,
102
+ # "position_name"=> "吉大",
103
+ # "primary_pollutant"=> "颗粒物(PM2.5)",
104
+ # "quality"=> "良",
105
+ # "station_code"=> "1367A",
106
+ # "time_point"=> "2013-03-07T19:00:00Z"
107
+ # },
108
+ # ...
109
+ # ...
110
+ # ...
111
+ # {
112
+ # "aqi"=> 108,
113
+ # "area"=> "珠海",
114
+ # "pm2_5"=> 0,
115
+ # "pm2_5_24h"=> 53,
116
+ # "position_name"=> "斗门",
117
+ # "primary_pollutant"=> "臭氧8小时",
118
+ # "quality"=> "轻度污染",
119
+ # "station_code"=> "1370A",
120
+ # "time_point"=> "2013-03-07T19:00:00Z"
121
+ # },
122
+ # {
123
+ # "aqi"=> 99,
124
+ # "area"=> "珠海",
125
+ # "pm2_5"=> 39,
126
+ # "pm2_5_24h"=> 67,
127
+ # "position_name"=> null,
128
+ # "primary_pollutant"=> null,
129
+ # "quality"=> "良",
130
+ # "station_code"=> null,
131
+ # "time_point"=> "2013-03-07T19:00:00Z"
132
+ # }
133
+ # ]
134
+ def pm25(city=get_default_city, token=nil)
135
+ access_api('pm2_5.json', city: city, token: token)
136
+ end
137
+
138
+ # Get a list of cites providing PM 2.5 data.
139
+ # API frequency limit: 10 per hour.
140
+ # @param [String] token
141
+ # @return [Array] city list
142
+ # @return [Fixnum] error code
143
+ def available_cities(token=get_token)
144
+ res = RestClient.get 'http://www.pm25.in/api/querys.json', {params: {token: token}}
145
+ if res.code == 200
146
+ JSON.parse(res.body)['cities']
147
+ else
148
+ res.code
149
+ end
150
+ end
151
+
152
+ # Get PM 2.5 data for all cities.
153
+ # API frequency limit: 5 per hour.
154
+ # @param [String] token
155
+ # @return [Hash]
156
+ # @return [Fixnum] error code
157
+ def all_cities(token=nil)
158
+ access_api('all_cities.json', token: token)
159
+ end
160
+
161
+ # Get average data for all cities. (Cities are sorted by AQI.)
162
+ # API frequency limit: 15 per hour.
163
+ # @param [String] token
164
+ # @return [Hash]
165
+ # @return [Fixnum] error code
166
+ def aqi_ranking(token=nil)
167
+ access_api('aqi_ranking.json', token: token)
168
+ end
169
+
170
+
171
+ # Get PM 2.5 from bing.com
172
+ # @param [String] city only accept Chinese
173
+ # @return [Fixnum] PM 25 value
174
+ def bing_pm25(city)
175
+ city = URI.encode(city)
176
+ bing_url = "http://cn.bing.com/search?q=#{city}+pm2.5"
177
+ html = Nokogiri.parse(open(bing_url).read)
178
+ html.at('#msn_pm25rt .b_xlText').content.to_i
179
+ end
180
+
181
+ # Get PM 2.5 value for city.
182
+ # Fallback to bing.com.
183
+ # Return PM 2.5 value only.
184
+ # @param [String] city only accept Chinese
185
+ # @param [String] token
186
+ # @return [Fixnum] average PM 2.5 value for stations
187
+ def just_pm25(city=get_default_city, token=nil)
188
+ result = pm25(city, token)
189
+ # error on pm25.in api
190
+ if result.is_a? Fixnum
191
+ bing_pm25(city)
192
+ elsif result.include? 'error'
193
+ bing_pm25(city)
194
+ else
195
+ publish_time = Time.parse(result[-1]['time_point'])
196
+ # Data on pm25.in is too old.
197
+ # According to GB 3095-2013, PM 2.5 should have hourly
198
+ # average values for at least 20 hours a day.
199
+ # http://hbj.shanghang.gov.cn/hjjc/gldt/201411/P020141110361645902554.pdf
200
+ if Time.now - publish_time > 2.hours
201
+ bing_pm25(city)
202
+ else
203
+ result[-1]['pm2_5']
204
+ end
205
+ end
206
+ end
207
+
208
+
209
+ # Get AQI category, meaning and action according to US standard.
210
+ # @param [Fixnum]
211
+ # @return [Hash{Symbol: Fixnum, Symbol: String, Symbol: String, Symbol: String}]
212
+ def pm25_level(pm25)
213
+ if pm25 <= 12
214
+ aqi_category = 'Good'
215
+ aqi_meaning = 'Air quality is considered satisfactory, and air pollution poses little or no risk.'
216
+ aqi_action = 'None'
217
+ elsif pm25 <= 35.4
218
+ aqi_category = 'Moderate'
219
+ aqi_meaning = 'Air quality is acceptable; however, for some pollutants there may be a moderate health concern for a very small number of people who are unusually sensitive to air pollution.'
220
+ aqi_action = 'Unusually sensitive people should consider reducing prolonged or heavy exertion.'
221
+ elsif pm25 <= 55.4
222
+ aqi_category = 'Unhealthy for sensitive Groups'
223
+ aqi_meaning = 'Members of sensitive groups may experience health effects. The general public is not likely to be affected.'
224
+ aqi_action = 'People with heart or lung disease, children and older adults should reduc e prolonged or heavy exertion'
225
+ elsif pm25 <= 150.4
226
+ aqi_category = 'Unhealthy'
227
+ aqi_meaning = 'Everyone may begin to experience health effects; members of sensitive groups may experience more serious health effects.'
228
+ aqi_action = 'People with heart or lung disease, children and older adults should avoid prolonged or heavy exertion. Everyone else should reduce prolonged or heavy exertion.'
229
+ elsif pm25 <= 250.4
230
+ aqi_category = 'Very Unhealthy'
231
+ aqi_meaning = 'Health warnings of emergency conditions. The entire population is more likely to be affected.'
232
+ aqi_action = 'People with heart or lung disease, children and older adults should avoid all physical activity outdoors. Everyone else should avoid prolonged or heavy exertion.'
233
+ else
234
+ aqi_category = 'Hazardous'
235
+ aqi_meaning = 'Health alert: everyone may experience more serious health effects'
236
+ aqi_action = 'Avoid all physical activity outdoors.'
237
+ end
238
+ {pm25: pm25,
239
+ category: aqi_category,
240
+ meaning: aqi_meaning,
241
+ action: aqi_action}
242
+ end
243
+ end
data/pm_25.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pm_25/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'pm_25'
8
+ spec.version = PM25::VERSION
9
+ spec.authors = ['Jakukyo Friel']
10
+ spec.email = ['weakish@gmail.com']
11
+ spec.summary = %q{Fetch PM 2.5 data in China.}
12
+ spec.description = %q{A Ruby wrapper for pm25.in API and other PM 2.5
13
+ related utility functions.}
14
+ spec.homepage = 'https://github.com/weakish/pm_25'
15
+ spec.license = 'MIT'
16
+ spec.metadata = {
17
+ 'repository' => 'https://github.com/weakish/pm_25.git',
18
+ 'documentation' => 'http://www.rubydoc.info/gems/pm_25/',
19
+ 'issues' => 'https://github.com/weakish/pm_25/issues',
20
+ 'wiki' => 'https://github.com/weakish/pm_25/wiki'
21
+ }
22
+
23
+ spec.files = `git ls-files -z`.split("\x0")
24
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
26
+ spec.require_paths = ['lib']
27
+
28
+ spec.add_runtime_dependency 'nokogiri', '~> 1.6'
29
+ spec.add_runtime_dependency 'rest_client', '~> 1.8'
30
+ spec.add_runtime_dependency 'time-lord', '~> 1.0'
31
+ spec.add_development_dependency 'bundler', '~> 1.7'
32
+ spec.add_development_dependency 'rake', '~> 10.0'
33
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pm_25
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jakukyo Friel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rest_client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.8'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: time-lord
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ description: |-
84
+ A Ruby wrapper for pm25.in API and other PM 2.5
85
+ related utility functions.
86
+ email:
87
+ - weakish@gmail.com
88
+ executables:
89
+ - pm25
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - ".gitignore"
94
+ - Gemfile
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - bin/pm25
99
+ - lib/pm_25.rb
100
+ - lib/pm_25/version.rb
101
+ - pm_25.gemspec
102
+ homepage: https://github.com/weakish/pm_25
103
+ licenses:
104
+ - MIT
105
+ metadata:
106
+ repository: https://github.com/weakish/pm_25.git
107
+ documentation: http://www.rubydoc.info/gems/pm_25/
108
+ issues: https://github.com/weakish/pm_25/issues
109
+ wiki: https://github.com/weakish/pm_25/wiki
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.2.2
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Fetch PM 2.5 data in China.
130
+ test_files: []
131
+ has_rdoc: