kovid 0.6.7 → 0.6.12

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: a77d1ad2686877dfe810f0df3cbc817b46c287f2a6025ce999108213ad0e54c8
4
- data.tar.gz: 888dab7db56dc55241512e24bef98fb9f9117abd9e09790a19894630d691e0c2
3
+ metadata.gz: 26d90f05df4a479c2865ce9e3bff414453680fa72f37dd841ce149410a144bb5
4
+ data.tar.gz: 8f69a755906441b1b81df5786e1594e17ee9cccd11f293f555c9c047cf470b00
5
5
  SHA512:
6
- metadata.gz: 6394d6904a073fef5c4fa1d6429c3789a84dfc329a519346dd90e53b082b22284405a044a13c984cf231f7cdd02fb790c49450c47c1d6c0fecb124fbc67783e5
7
- data.tar.gz: 7c45571d9683f723068c6d17a97427348c5d3f8ed64db76e2bd71db72af322f3611bd0d66af8dc3732d4a251f1253d2ea0d49c0db34f8c905739f7cc7dd885cc
6
+ metadata.gz: baa1784856b2544065f1883f19dddb3f3b4269da62c8a727f372f41e245c6646a8221dfd187eb804fd3919effa9dc3e0a29ba39f9d838635072b463920bc7535
7
+ data.tar.gz: c74e0235df9d9511d241c9d0f162a691222ce8abbad6065ffe4cf670ca5238dd7fc497306445bedebe94365f850060a37524413a8b7322fee200a20da2215ff8
data/Gemfile CHANGED
@@ -7,6 +7,3 @@ gemspec
7
7
 
8
8
  gem 'rake', '~> 12.0'
9
9
  gem 'rspec', '~> 3.0'
10
-
11
- # Repo of geogrpahic locations
12
- gem 'carmen', '~> 1.1.3'
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kovid (0.6.6)
5
- ascii_charts (~> 0.9.1)
4
+ kovid (0.6.11)
5
+ carmen (~> 1.1.3)
6
6
  rainbow (~> 3.0)
7
7
  terminal-table (~> 1.8)
8
8
  thor (~> 1.0)
@@ -17,7 +17,6 @@ GEM
17
17
  minitest (~> 5.1)
18
18
  tzinfo (~> 1.1)
19
19
  zeitwerk (~> 2.2)
20
- ascii_charts (0.9.1)
21
20
  carmen (1.1.3)
22
21
  activesupport (>= 3.0.0)
23
22
  concurrent-ruby (1.1.6)
@@ -63,7 +62,6 @@ PLATFORMS
63
62
  ruby
64
63
 
65
64
  DEPENDENCIES
66
- carmen (~> 1.1.3)
67
65
  kovid!
68
66
  rake (~> 12.0)
69
67
  rspec (~> 3.0)
data/README.md CHANGED
@@ -74,6 +74,7 @@ ___
74
74
  😷 **History**
75
75
  * `kovid history COUNTRY` (full history).
76
76
  * `kovid history COUNTRY N` (history in the last N days).
77
+ * `kovid history STATE --usa`
77
78
  ___
78
79
 
79
80
  **NOTE:** If you find it irritating to have to type `kovid state STATE`, `covid state STATE` works as well.
data/kovid.gemspec CHANGED
@@ -22,12 +22,11 @@ Gem::Specification.new do |spec|
22
22
  spec.metadata['source_code_uri'] = 'https://github.com/siaw23/kovid'
23
23
  spec.metadata['changelog_uri'] = 'https://github.com/siaw23/kovid'
24
24
 
25
- spec.add_dependency 'ascii_charts', '~> 0.9.1'
25
+ spec.add_dependency 'carmen', '~> 1.1.3'
26
26
  spec.add_dependency 'rainbow', '~> 3.0'
27
27
  spec.add_dependency 'terminal-table', '~> 1.8'
28
28
  spec.add_dependency 'thor', '~> 1.0'
29
29
  spec.add_dependency 'typhoeus', '~> 1.3'
30
-
31
30
  spec.add_development_dependency 'simplecov', '~> 0.18'
32
31
 
33
32
  # Specify which files should be added to the gem when it is released.
data/lib/kovid.rb CHANGED
@@ -68,8 +68,12 @@ module Kovid
68
68
  Kovid::Request.cases
69
69
  end
70
70
 
71
- def history(country, last)
72
- 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)
73
77
  end
74
78
 
75
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
data/lib/kovid/cli.rb CHANGED
@@ -75,13 +75,15 @@ module Kovid
75
75
  data_source
76
76
  end
77
77
 
78
- desc 'history COUNTRY or history COUNTRY N',
79
- 'Return history of incidents of COUNTRY (in the last N days)'
80
- def history(*params)
81
- if params.size == 2
82
- puts Kovid.history(params.first, params.last)
78
+ desc 'history COUNTRY or history COUNTRY N or' \
79
+ 'history STATE --usa',
80
+ 'Return history of incidents of COUNTRY|STATE (in the last N days)'
81
+ option :usa, type: :boolean
82
+ def history(location, days = 30)
83
+ if options[:usa]
84
+ puts Kovid.history_us_state(location, days)
83
85
  else
84
- puts Kovid.history(params.first, nil)
86
+ puts Kovid.history(location, days)
85
87
  end
86
88
  data_source
87
89
  end
@@ -145,8 +147,8 @@ module Kovid
145
147
  source = <<~TEXT
146
148
  #{Time.now}
147
149
  Sources:
148
- * JHU CSSE GISand Data
149
- * https://www.worldometers.info/coronavirus/
150
+ * Johns Hopkins University
151
+ * https://www.worldometers.info/coronavirus
150
152
  TEXT
151
153
  puts source
152
154
  end
@@ -13,7 +13,8 @@ module Kovid
13
13
  'Cases Today'.paint_white,
14
14
  'Deaths'.paint_red,
15
15
  'Deaths Today'.paint_red,
16
- 'Recovered'.paint_green
16
+ 'Recovered'.paint_green,
17
+ 'Tests'.paint_white,
17
18
  ].freeze
18
19
 
19
20
  DATE_CASES_DEATHS_RECOVERED = [
@@ -23,6 +24,12 @@ module Kovid
23
24
  'Recovered'.paint_green
24
25
  ].freeze
25
26
 
27
+ DATE_CASES_DEATHS = [
28
+ 'Date'.paint_white,
29
+ 'Cases'.paint_white,
30
+ 'Deaths'.paint_red
31
+ ].freeze
32
+
26
33
  CONTINENTAL_AGGREGATE_HEADINGS = [
27
34
  'Cases'.paint_white,
28
35
  'Cases Today'.paint_white,
@@ -50,7 +57,8 @@ module Kovid
50
57
  'Cases Today'.paint_white,
51
58
  'Deaths'.paint_red,
52
59
  'Deaths Today'.paint_red,
53
- 'Recovered'.paint_green
60
+ 'Recovered'.paint_green,
61
+ 'Tests'.paint_white
54
62
  ].freeze
55
63
 
56
64
  FULL_COUNTRY_TABLE_HEADINGS = [
@@ -74,7 +82,8 @@ module Kovid
74
82
  'Cases Today'.paint_white,
75
83
  'Deaths'.paint_red,
76
84
  'Deaths Today'.paint_red,
77
- 'Active'.paint_yellow
85
+ 'Active'.paint_yellow,
86
+ 'Tests'.paint_white
78
87
  ].freeze
79
88
 
80
89
  COMPARE_STATES_HEADINGS = [
@@ -93,7 +102,13 @@ module Kovid
93
102
  'Recovered'.paint_green
94
103
  ].freeze
95
104
 
96
- FOOTER_LINE = [
105
+ FOOTER_LINE_THREE_COLUMNS = [
106
+ '------------',
107
+ '------------',
108
+ '------------'
109
+ ].freeze
110
+
111
+ FOOTER_LINE_FOUR_COLUMNS = [
97
112
  '------------',
98
113
  '------------',
99
114
  '------------',
@@ -3,24 +3,15 @@
3
3
  module Kovid
4
4
  module Historians
5
5
  include Constants
6
+ include AsciiCharts
6
7
 
7
- def history(country, last)
8
- # TODO: Write checks for when country is spelt wrong.
8
+ def history(location, days)
9
9
  rows = []
10
10
 
11
- stats = if last
12
- Kovid.format_country_history_numbers(country).last(last.to_i)
13
- else
14
- Kovid.format_country_history_numbers(country)
15
- end
11
+ stats = Kovid.format_country_history_numbers(location).last(days.to_i)
12
+ dates = location['timeline']['cases'].keys.last(days.to_i)
16
13
 
17
- dates = if last
18
- country['timeline']['cases'].keys.last(last.to_i)
19
- else
20
- country['timeline']['cases'].keys
21
- end
22
-
23
- unless last
14
+ if days.to_i >= 30
24
15
  stats = stats.reject { |stat| stat[0].to_i.zero? && stat[1].to_i.zero? }
25
16
  dates = dates.last(stats.count)
26
17
  end
@@ -31,14 +22,25 @@ module Kovid
31
22
  rows << row
32
23
  end
33
24
 
25
+ # Title and Column format if Country or US State
26
+ if location['country']
27
+ title = location['country'].try(:upcase)
28
+ col_format = DATE_CASES_DEATHS_RECOVERED
29
+ footer = FOOTER_LINE_FOUR_COLUMNS
30
+ else
31
+ title = location['state'].try(:upcase)
32
+ col_format = DATE_CASES_DEATHS
33
+ footer = FOOTER_LINE_THREE_COLUMNS
34
+ end
35
+
34
36
  if stats.size > 10
35
- rows << FOOTER_LINE
36
- rows << DATE_CASES_DEATHS_RECOVERED
37
+ rows << footer
38
+ rows << col_format
37
39
  end
38
40
 
39
41
  Terminal::Table.new(
40
- title: country['country'].upcase,
41
- headings: DATE_CASES_DEATHS_RECOVERED,
42
+ title: title,
43
+ headings: col_format,
42
44
  rows: rows
43
45
  )
44
46
  end
@@ -88,7 +90,7 @@ module Kovid
88
90
  dates.each_with_index do |val, index|
89
91
  data << [val, positive_cases_figures[index]]
90
92
  end
91
- y_range = AsciiCharts::Cartesian.new(
93
+ y_range = Kovid::AsciiCharts::Cartesian.new(
92
94
  data, bar: true, hide_zero: true
93
95
  ).y_range
94
96
 
data/lib/kovid/request.rb CHANGED
@@ -8,26 +8,28 @@ require_relative 'uri_builder'
8
8
 
9
9
  module Kovid
10
10
  class Request
11
- COUNTRIES_PATH = UriBuilder.new('/countries').url
12
- STATES_URL = UriBuilder.new('/states').url
13
- JHUCSSE_URL = UriBuilder.new('/v2/jhucsse').url
14
-
15
- SERVER_DOWN = 'Server overwhelmed. Please try again in a moment.'
16
-
17
- EU_ISOS = %w[AT BE BG CY CZ DE DK EE ES FI FR GR HR HU IE IT LT \
18
- LU LV MT NL PL PT RO SE SI SK].freeze
19
- EUROPE_ISOS = EU_ISOS + %w[GB IS NO CH MC AD SM VA BA RS ME MK AL \
20
- BY UA RU MD].freeze
21
- AFRICA_ISOS = %w[DZ AO BJ BW BF BI CM CV CF TD KM CD CG CI DJ EG \
22
- GQ ER SZ ET GA GM GH GN GW KE LS LR LY MG MW ML \
23
- MR MU MA MZ NA NE NG RW ST SN SC SL SO ZA SS SD \
24
- TZ TG TN UG ZM ZW EH].freeze
25
- SOUTH_AMERICA_ISOS = %w[AR BO BV BR CL CO EC FK GF GY PY PE GS SR \
26
- UY VE].freeze
27
- ASIA_ISOS = %w[AE AF AM AZ BD BH BN BT CC CN CX GE HK ID IL IN \
28
- IQ IR JO JP KG KH KP KR KW KZ LA LB LK MM MN MO \
29
- MY NP OM PH PK PS QA SA SG SY TH TJ TL TM TR TW \
30
- UZ VN YE].freeze
11
+ COUNTRIES_PATH = UriBuilder.new('/v2/countries').url
12
+ STATES_URL = UriBuilder.new('/v2/states').url
13
+ JHUCSSE_URL = UriBuilder.new('/v2/jhucsse').url
14
+ HISTORICAL_URL = UriBuilder.new('/v2/historical').url
15
+ HISTORICAL_US_URL = UriBuilder.new('/v2/historical/usacounties').url
16
+
17
+ SERVER_DOWN = 'Server overwhelmed. Please try again in a moment.'
18
+
19
+ EU_ISOS = %w[AT BE BG CY CZ DE DK EE ES FI FR GR HR HU IE IT LT \
20
+ LU LV MT NL PL PT RO SE SI SK].freeze
21
+ EUROPE_ISOS = EU_ISOS + %w[GB IS NO CH MC AD SM VA BA RS ME MK AL \
22
+ BY UA RU MD].freeze
23
+ AFRICA_ISOS = %w[DZ AO BJ BW BF BI CM CV CF TD KM CD CG CI DJ EG \
24
+ GQ ER SZ ET GA GM GH GN GW KE LS LR LY MG MW ML \
25
+ MR MU MA MZ NA NE NG RW ST SN SC SL SO ZA SS SD \
26
+ TZ TG TN UG ZM ZW EH].freeze
27
+ SOUTH_AMERICA_ISOS = %w[AR BO BV BR CL CO EC FK GF GY PY PE GS SR \
28
+ UY VE].freeze
29
+ ASIA_ISOS = %w[AE AF AM AZ BD BH BN BT CC CN CX GE HK ID IL IN \
30
+ IQ IR JO JP KG KH KP KR KW KZ LA LB LK MM MN MO \
31
+ MY NP OM PH PK PS QA SA SG SY TH TJ TL TM TR TW \
32
+ UZ VN YE].freeze
31
33
 
32
34
  class << self
33
35
  def eu_aggregate
@@ -111,6 +113,7 @@ module Kovid
111
113
 
112
114
  def state(state)
113
115
  response = fetch_state(Kovid.lookup_us_state(state))
116
+
114
117
  if response.nil?
115
118
  not_found(state)
116
119
  else
@@ -150,7 +153,7 @@ module Kovid
150
153
 
151
154
  def cases
152
155
  response = JSON.parse(
153
- Typhoeus.get(UriBuilder.new('/all').url, cache_ttl: 900).response_body
156
+ Typhoeus.get(UriBuilder.new('/v2/all').url, cache_ttl: 900).response_body
154
157
  )
155
158
 
156
159
  Kovid::Tablelize.cases(response)
@@ -158,15 +161,53 @@ module Kovid
158
161
  puts SERVER_DOWN
159
162
  end
160
163
 
161
- def history(country, last)
164
+ def history(country, days)
162
165
  history_path = UriBuilder.new('/v2/historical').url
166
+
163
167
  response = JSON.parse(
164
168
  Typhoeus.get(
165
169
  history_path + "/#{country}", cache_ttl: 900
166
170
  ).response_body
167
171
  )
168
172
 
169
- Kovid::Tablelize.history(response, last)
173
+ if response.key?('message')
174
+ not_found(country) do |c|
175
+ "Could not find cases for #{c}.\nIf searching United States, add --usa option"
176
+ end
177
+ else
178
+ Kovid::Tablelize.history(response, days)
179
+ end
180
+ rescue JSON::ParserError
181
+ puts SERVER_DOWN
182
+ end
183
+
184
+ def history_us_state(state, days)
185
+ history_path = UriBuilder.new('/v2/historical/usacounties').url
186
+ state = Kovid.lookup_us_state(state).downcase
187
+
188
+ response = JSON.parse(
189
+ Typhoeus.get(
190
+ history_path + "/#{state}", cache_ttl: 900
191
+ ).response_body
192
+ )
193
+
194
+ if response.respond_to?(:key?) && response.key?('message')
195
+ return not_found(state)
196
+ end
197
+
198
+ # API Endpoint returns list of counties for given state, so
199
+ # we aggreage cases for all counties
200
+ # Note: no data for 'Recovered'
201
+ cases = usacounties_aggregator(response, 'cases')
202
+ deaths = usacounties_aggregator(response, 'deaths')
203
+
204
+ # normalize data so we can call Kovid::Tablelize.history on US State data
205
+ response = {
206
+ 'state' => state,
207
+ 'timeline' => { 'cases' => cases, 'deaths' => deaths }
208
+ }
209
+
210
+ Kovid::Tablelize.history(response, days)
170
211
  rescue JSON::ParserError
171
212
  puts SERVER_DOWN
172
213
  end
@@ -188,9 +229,17 @@ module Kovid
188
229
 
189
230
  private
190
231
 
191
- def not_found(country)
192
- rows = [["Wrong spelling/No reported cases on #{country.upcase}."]]
193
- Terminal::Table.new title: "You checked: #{country.upcase}", rows: rows
232
+ def not_found(location)
233
+ rows = []
234
+ default_warning = "Wrong spelling/No reported cases on #{location.upcase}."
235
+
236
+ rows << if block_given?
237
+ [yield(location)]
238
+ else
239
+ [default_warning]
240
+ end
241
+
242
+ Terminal::Table.new title: "You checked: #{location.upcase}", rows: rows
194
243
  end
195
244
 
196
245
  def fetch_countries(list)
@@ -264,7 +313,7 @@ module Kovid
264
313
 
265
314
  def countries_request
266
315
  Typhoeus.get(
267
- UriBuilder.new('/countries').url, cache_ttl: 900
316
+ UriBuilder.new('/v2/countries').url, cache_ttl: 900
268
317
  ).response_body
269
318
  end
270
319
 
@@ -278,6 +327,16 @@ module Kovid
278
327
  end
279
328
  end.compact
280
329
  end
330
+
331
+ def usacounties_aggregator(data, key = nil)
332
+ data.inject({}) do |base, other|
333
+ base.merge(other['timeline'][key]) do |_k, l, r|
334
+ l ||= 0
335
+ l ||= 0
336
+ l + r
337
+ end
338
+ end.compact
339
+ end
281
340
  end
282
341
  end
283
342
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'terminal-table'
4
4
  require 'date'
5
- require 'ascii_charts'
5
+ require_relative 'ascii_charts'
6
6
  require_relative 'painter'
7
7
  require_relative 'constants'
8
8
  require_relative 'aggregators'
@@ -127,7 +127,8 @@ module Kovid
127
127
  Kovid.add_plus_sign(data['todayCases']),
128
128
  Kovid.comma_delimit(data['deaths']),
129
129
  Kovid.add_plus_sign(data['todayDeaths']),
130
- Kovid.comma_delimit(data['recovered'])
130
+ Kovid.comma_delimit(data['active']),
131
+ Kovid.comma_delimit(data['tests'])
131
132
  ]
132
133
  end
133
134
 
data/lib/kovid/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kovid
4
- VERSION = '0.6.7'
4
+ VERSION = '0.6.12'
5
5
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kovid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.7
4
+ version: 0.6.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emmanuel Hayford
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-16 00:00:00.000000000 Z
11
+ date: 2020-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: ascii_charts
14
+ name: carmen
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.9.1
19
+ version: 1.1.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.9.1
26
+ version: 1.1.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rainbow
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +121,7 @@ files:
121
121
  - kovid.gemspec
122
122
  - lib/kovid.rb
123
123
  - lib/kovid/aggregators.rb
124
+ - lib/kovid/ascii_charts.rb
124
125
  - lib/kovid/cache.rb
125
126
  - lib/kovid/cli.rb
126
127
  - lib/kovid/constants.rb