kovid 0.6.4 → 0.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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