kovid 0.6.4 → 0.6.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e96265776776cbca749c7c1651fd462883deeeefc52150afffbd444f1752bae3
4
- data.tar.gz: 7721bcc8cf1e5d342b8808f020515d6167a7dee2b8a1bda04d1b56d3a0fd11ab
3
+ metadata.gz: aa46d5f734b53509d084570bc4a0abc2527d02320ea67c9a85120389492a0d64
4
+ data.tar.gz: 72c0dd54efb153df1996c238a74f42bec0c46062876669051e346f4906feba38
5
5
  SHA512:
6
- metadata.gz: dd21b726c0a68398e565c470477da359f05e23b7b356dd95e1f18232168caa922262974db6aa5c2ddc7fc6223c6fae31ed824a8b41d61c73627d0fc5013a740e
7
- data.tar.gz: 81a35f27819d5f6eeadb699bcc0ab9a73ab61cb8f3fb0f4d487d3fa13bbc333afb1138d99f413e6b30bab437144513a4fc461dc924f3f94dc9d9ec0c4def0445
6
+ metadata.gz: d61d1956c6f309ad70beeb8fe1289bb9e7c15c0b8b26c7ddd72304e888803681f529bdb1c091be603351271b981d8e4b2e0f033972996bed4580aa077d8b4c4a
7
+ data.tar.gz: 82d702ef83e0307424191235d239cc27aad835e0191677e0540cecf359a3756e427fa6c427036e70d2550ef0c08679966d7d21b33bc2b7e691f603e33077bec4
data/.rubocop.yml ADDED
@@ -0,0 +1,7 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.6
5
+
6
+ Style/Documentation:
7
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,59 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2020-04-07 20:38:28 -0300 using RuboCop version 0.67.2.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ Metrics/AbcSize:
11
+ Max: 54
12
+ Exclude:
13
+ - 'lib/kovid/historians.rb'
14
+
15
+ # Offense count: 2
16
+ Metrics/BlockLength:
17
+ Exclude:
18
+ - 'kovid.gemspec'
19
+ - 'spec/kovid_spec.rb'
20
+
21
+ # Offense count: 3
22
+ # Configuration parameters: CountComments.
23
+ Metrics/ClassLength:
24
+ Max: 220
25
+ Exclude:
26
+ - 'lib/kovid/cli.rb'
27
+ - 'lib/kovid/request.rb'
28
+ - 'lib/kovid/tablelize.rb'
29
+
30
+ # Offense count: 2
31
+ # Configuration parameters: CountComments, ExcludedMethods.
32
+ Metrics/MethodLength:
33
+ Max: 44
34
+ Exclude:
35
+ - 'lib/kovid/historians.rb'
36
+
37
+ # Offense count: 1
38
+ Metrics/PerceivedComplexity:
39
+ Max: 8
40
+ Exclude:
41
+ - 'lib/kovid/historians.rb'
42
+
43
+ # Offense count: 1
44
+ Style/CaseEquality:
45
+ Exclude:
46
+ - 'lib/kovid/request.rb'
47
+
48
+ Style/Documentation:
49
+ Exclude:
50
+ - 'spec/**/*'
51
+ - 'test/**/*'
52
+ - 'lib/kovid.rb'
53
+ - 'lib/kovid/helpers.rb'
54
+
55
+ # Offense count: 5
56
+ Style/MultilineBlockChain:
57
+ Exclude:
58
+ - 'lib/kovid/historians.rb'
59
+ - 'lib/kovid/request.rb'
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kovid (0.6.3)
5
- ascii_charts (~> 0.9.1)
4
+ kovid (0.6.8)
5
+ carmen (~> 1.1.3)
6
6
  rainbow (~> 3.0)
7
7
  terminal-table (~> 1.8)
8
8
  thor (~> 1.0)
@@ -11,12 +11,23 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- ascii_charts (0.9.1)
14
+ activesupport (6.0.2.2)
15
+ concurrent-ruby (~> 1.0, >= 1.0.2)
16
+ i18n (>= 0.7, < 2)
17
+ minitest (~> 5.1)
18
+ tzinfo (~> 1.1)
19
+ zeitwerk (~> 2.2)
20
+ carmen (1.1.3)
21
+ activesupport (>= 3.0.0)
22
+ concurrent-ruby (1.1.6)
15
23
  diff-lcs (1.3)
16
24
  docile (1.3.2)
17
25
  ethon (0.12.0)
18
26
  ffi (>= 1.3.0)
19
27
  ffi (1.12.2)
28
+ i18n (1.8.2)
29
+ concurrent-ruby (~> 1.0)
30
+ minitest (5.14.0)
20
31
  rainbow (3.0.0)
21
32
  rake (12.3.3)
22
33
  rspec (3.9.0)
@@ -39,9 +50,13 @@ GEM
39
50
  terminal-table (1.8.0)
40
51
  unicode-display_width (~> 1.1, >= 1.1.1)
41
52
  thor (1.0.1)
53
+ thread_safe (0.3.6)
42
54
  typhoeus (1.3.1)
43
55
  ethon (>= 0.9.0)
56
+ tzinfo (1.2.7)
57
+ thread_safe (~> 0.1)
44
58
  unicode-display_width (1.7.0)
59
+ zeitwerk (2.3.0)
45
60
 
46
61
  PLATFORMS
47
62
  ruby
data/README.md CHANGED
@@ -46,6 +46,8 @@ You can fetch US state-specific data:
46
46
  * `kovid state STATE` OR `kovid state "STATE NAME"`.
47
47
  * `kovid states --all` or `kovid states -a` for data on all US states.
48
48
 
49
+ You can also use USPS abbreviations. Example: `kovid state me`
50
+
49
51
  Provinces
50
52
 
51
53
  You can fetch province specific data:
@@ -64,7 +66,7 @@ You can compare as many countries as you want; `kovid compare FOO BAR BAZ` OR `k
64
66
  🇺🇸🇺🇸🇺🇸
65
67
 
66
68
  You can compare US states with:
67
- * `kovid states STATE STATE` Example: `kovid states illinois "new york" california`
69
+ * `kovid states STATE STATE` Example: `kovid states illinois "new york" california` OR `kovid states il ny ca`
68
70
 
69
71
  You can compare provicnes with:
70
72
  * `kovid provinces PROVINCE PROVINCE` Example: `kovid provinces ontario manitoba`
@@ -72,6 +74,7 @@ ___
72
74
  😷 **History**
73
75
  * `kovid history COUNTRY` (full history).
74
76
  * `kovid history COUNTRY N` (history in the last N days).
77
+ * `kovid history STATE --usa`
75
78
  ___
76
79
 
77
80
  **NOTE:** If you find it irritating to have to type `kovid state STATE`, `covid state STATE` works as well.
data/Rakefile CHANGED
@@ -7,3 +7,4 @@ RSpec::Core::RakeTask.new(:spec)
7
7
 
8
8
  # rubocop:disable Style/HashSyntax
9
9
  task :default => :spec
10
+ # rubocop:enable Style/HashSyntax
data/kovid.gemspec CHANGED
@@ -8,8 +8,10 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ['Emmanuel Hayford']
9
9
  spec.email = ['siawmensah@gmail.com']
10
10
 
11
- spec.summary = 'A CLI to fetch and compare the 2019 coronavirus pandemic statistics.'
12
- spec.description = 'A CLI to fetch and compare the 2019 coronavirus pandemic statistics.'
11
+ summary = 'A CLI to fetch and compare the 2019 ' \
12
+ 'coronavirus pandemic statistics.'
13
+ spec.summary = summary
14
+ spec.description = summary
13
15
  spec.homepage = 'https://github.com/siaw23/kovid'
14
16
  spec.license = 'MIT'
15
17
  spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
@@ -20,18 +22,20 @@ Gem::Specification.new do |spec|
20
22
  spec.metadata['source_code_uri'] = 'https://github.com/siaw23/kovid'
21
23
  spec.metadata['changelog_uri'] = 'https://github.com/siaw23/kovid'
22
24
 
23
- spec.add_dependency 'ascii_charts', '~> 0.9.1'
25
+ spec.add_dependency 'carmen', '~> 1.1.3'
24
26
  spec.add_dependency 'rainbow', '~> 3.0'
25
27
  spec.add_dependency 'terminal-table', '~> 1.8'
26
28
  spec.add_dependency 'thor', '~> 1.0'
27
29
  spec.add_dependency 'typhoeus', '~> 1.3'
28
-
29
30
  spec.add_development_dependency 'simplecov', '~> 0.18'
30
31
 
31
32
  # Specify which files should be added to the gem when it is released.
32
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
33
+ # The `git ls-files -z` loads the files in the RubyGem
34
+ # that have been added into git.
33
35
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
34
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
36
+ `git ls-files -z`.split("\x0").reject do |f|
37
+ f.match(%r{^(test|spec|features)/})
38
+ end
35
39
  end
36
40
  spec.bindir = 'exe'
37
41
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
data/lib/kovid.rb CHANGED
@@ -6,8 +6,6 @@ require 'kovid/request'
6
6
  module Kovid
7
7
  require 'kovid/helpers'
8
8
 
9
- class Error < StandardError; end
10
-
11
9
  module_function
12
10
 
13
11
  def eu_aggregate
@@ -70,8 +68,12 @@ module Kovid
70
68
  Kovid::Request.cases
71
69
  end
72
70
 
73
- def history(country, last)
74
- Kovid::Request.history(country, last)
71
+ def history(country, days=30)
72
+ Kovid::Request.history(country, days)
73
+ end
74
+
75
+ def history_us_state(state, days=30)
76
+ Kovid::Request.history_us_state(state, days)
75
77
  end
76
78
 
77
79
  def histogram(country, date)
@@ -0,0 +1,276 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2011 Ben Lund
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
13
+ # all 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
21
+ # THE SOFTWARE.
22
+
23
+ module Kovid
24
+ module AsciiCharts
25
+ VERSION = '0.9.1'
26
+
27
+ class Chart
28
+ attr_reader :options, :data
29
+
30
+ DEFAULT_MAX_Y_VALS = 20
31
+ DEFAULT_MIN_Y_VALS = 10
32
+
33
+ # data is a sorted array of [x, y] pairs
34
+
35
+ def initialize(data, options = {})
36
+ @data = data
37
+ @options = options
38
+ end
39
+
40
+ def rounded_data
41
+ @rounded_data ||= data.map { |pair| [pair[0], round_value(pair[1])] }
42
+ end
43
+
44
+ def step_size
45
+ unless defined? @step_size
46
+ if options[:y_step_size]
47
+ @step_size = options[:y_step_size]
48
+ else
49
+ max_y_vals = options[:max_y_vals] || DEFAULT_MAX_Y_VALS
50
+ min_y_vals = options[:max_y_vals] || DEFAULT_MIN_Y_VALS
51
+ y_span = (max_yval - min_yval).to_f
52
+
53
+ step_size = nearest_step(y_span.to_f / (data.size + 1))
54
+
55
+ if @all_ints && (step_size < 1)
56
+ step_size = 1
57
+ else
58
+ while (y_span / step_size) < min_y_vals
59
+ candidate_step_size = next_step_down(step_size)
60
+ if @all_ints && (candidate_step_size < 1)
61
+ break
62
+ end ## don't go below one
63
+
64
+ step_size = candidate_step_size
65
+ end
66
+ end
67
+
68
+ # go up if we undershot, or were never over
69
+ while (y_span / step_size) > max_y_vals
70
+ step_size = next_step_up(step_size)
71
+ end
72
+ @step_size = step_size
73
+ end
74
+ if !@all_ints && @step_size.is_a?(Integer)
75
+ @step_size = @step_size.to_f
76
+ end
77
+ end
78
+ @step_size
79
+ end
80
+
81
+ STEPS = [1, 2, 5].freeze
82
+
83
+ def from_step(val)
84
+ if val == 0
85
+ [0, 0]
86
+ else
87
+ order = Math.log10(val).floor.to_i
88
+ num = (val / (10**order))
89
+ [num, order]
90
+ end
91
+ end
92
+
93
+ def to_step(num, order)
94
+ s = num * (10**order)
95
+ if order < 0
96
+ s.to_f
97
+ else
98
+ s
99
+ end
100
+ end
101
+
102
+ def nearest_step(val)
103
+ num, order = from_step(val)
104
+ to_step(2, order) # #@@
105
+ end
106
+
107
+ def next_step_up(val)
108
+ num, order = from_step(val)
109
+ next_index = STEPS.index(num.to_i) + 1
110
+ if STEPS.size == next_index
111
+ next_index = 0
112
+ order += 1
113
+ end
114
+ to_step(STEPS[next_index], order)
115
+ end
116
+
117
+ def next_step_down(val)
118
+ num, order = from_step(val)
119
+ next_index = STEPS.index(num.to_i) - 1
120
+ if next_index == -1
121
+ STEPS.size - 1
122
+ order -= 1
123
+ end
124
+ to_step(STEPS[next_index], order)
125
+ end
126
+
127
+ # round to nearest step size, making sure we curtail precision to same order of magnitude as the step size to avoid 0.4 + 0.2 = 0.6000000000000001
128
+ def round_value(val)
129
+ remainder = val % step_size
130
+ unprecised = if (remainder * 2) >= step_size
131
+ (val - remainder) + step_size
132
+ else
133
+ val - remainder
134
+ end
135
+ if step_size < 1
136
+ precision = -Math.log10(step_size).floor
137
+ (unprecised * (10**precision)).to_i.to_f / (10**precision)
138
+ else
139
+ unprecised
140
+ end
141
+ end
142
+
143
+ def max_yval
144
+ scan_data unless defined? @max_yval
145
+ @max_yval
146
+ end
147
+
148
+ def min_yval
149
+ scan_data unless defined? @min_yval
150
+ @min_yval
151
+ end
152
+
153
+ def all_ints
154
+ scan_data unless defined? @all_ints
155
+ @all_ints
156
+ end
157
+
158
+ def scan_data
159
+ @max_yval = 0
160
+ @min_yval = 0
161
+ @all_ints = true
162
+
163
+ @max_xval_width = 1
164
+
165
+ data.each do |pair|
166
+ @max_yval = pair[1] if pair[1] > @max_yval
167
+ @min_yval = pair[1] if pair[1] < @min_yval
168
+ @all_ints = false if @all_ints && !pair[1].is_a?(Integer)
169
+
170
+ if (xw = pair[0].to_s.length) > @max_xval_width
171
+ @max_xval_width = xw
172
+ end
173
+ end
174
+ end
175
+
176
+ def max_xval_width
177
+ scan_data unless defined? @max_xval_width
178
+ @max_xval_width
179
+ end
180
+
181
+ def max_yval_width
182
+ scan_y_range unless defined? @max_yval_width
183
+ @max_yval_width
184
+ end
185
+
186
+ def scan_y_range
187
+ @max_yval_width = 1
188
+
189
+ y_range.each do |yval|
190
+ if (yw = yval.to_s.length) > @max_yval_width
191
+ @max_yval_width = yw
192
+ end
193
+ end
194
+ end
195
+
196
+ def y_range
197
+ unless defined? @y_range
198
+ @y_range = []
199
+ first_y = round_value(min_yval)
200
+ first_y -= step_size if first_y > min_yval
201
+ last_y = round_value(max_yval)
202
+ last_y += step_size if last_y < max_yval
203
+ current_y = first_y
204
+ while current_y <= last_y
205
+ @y_range << current_y
206
+ current_y = round_value(current_y + step_size) ## to avoid fp arithmetic oddness
207
+ end
208
+ end
209
+ @y_range
210
+ end
211
+
212
+ def lines
213
+ raise 'lines must be overridden'
214
+ end
215
+
216
+ def draw
217
+ lines.join("\n")
218
+ end
219
+
220
+ def to_string
221
+ draw
222
+ end
223
+ end
224
+
225
+ class Cartesian < Chart
226
+ def lines
227
+ return [[' ', options[:title], ' ', '|', '+-', ' ']] if data.empty?
228
+
229
+ lines = [' ']
230
+
231
+ bar_width = max_xval_width + 1
232
+
233
+ lines << (' ' * max_yval_width) + ' ' + rounded_data.map { |pair| pair[0].to_s.center(bar_width) }.join('')
234
+
235
+ y_range.each_with_index do |current_y, i|
236
+ yval = current_y.to_s
237
+ bar = if i == 0
238
+ '+'
239
+ else
240
+ '|'
241
+ end
242
+ current_line = [(' ' * (max_yval_width - yval.length)) + "#{current_y}#{bar}"]
243
+
244
+ rounded_data.each do |pair|
245
+ marker = if (i == 0) && options[:hide_zero]
246
+ '-'
247
+ else
248
+ '*'
249
+ end
250
+ filler = if i == 0
251
+ '-'
252
+ else
253
+ ' '
254
+ end
255
+ comparison = if options[:bar]
256
+ current_y <= pair[1]
257
+ else
258
+ current_y == pair[1]
259
+ end
260
+ current_line << if comparison
261
+ marker.center(bar_width, filler)
262
+ else
263
+ filler * bar_width
264
+ end
265
+ end
266
+ lines << current_line.join('')
267
+ current_y += step_size
268
+ end
269
+ lines << ' '
270
+ lines << options[:title].center(lines[1].length) if options[:title]
271
+ lines << ' '
272
+ lines.reverse
273
+ end
274
+ end
275
+ end
276
+ end