kovid 0.6.6 β†’ 0.6.11

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: 6abc6282e6ec84a393eee2b5383d70f88643d7633f8bd7537c207dae341ead1b
4
- data.tar.gz: a93e83c437e3b1f740fe7471cca72ad2e83c837d0a637c5fe8c71636bdd545dd
3
+ metadata.gz: 14f3598372d551bf07a3acd856b1eaa965aa6802794d8e7020fd9a234bb14df9
4
+ data.tar.gz: 5717fd5599aa8b3a6521c2dda5ccbe4e74580df794a0613d30e4bb09071abd9e
5
5
  SHA512:
6
- metadata.gz: dbc95ba0e85745d9369e15b0e81b6f5d36a7199b1227aaaec56eb57b8aac067750e4e2c71f937a97b333fdf57693547d6f98a09e70d214a5300b5b09cd5eeb25
7
- data.tar.gz: a2a458068aefa1f001ad85426ef5eba0317ba23a0320fab970eed818a9009186cc966f757c0a4f1b693a85cf668098ffa477acc3587849ed5864dcfc53a3b5fe
6
+ metadata.gz: 355d8fcc18cc07dd0e080722aa8d45a034b5da18c0ade97d2b052fb7603f4e2463d98447ea3dc985ec5bfb8682b12b88e7e74c0b26d2a55c99bad2f1fbaf04b0
7
+ data.tar.gz: d80ec1720c3b47b6906fe4be773e696a494b2f3877dc851b60ce8b6d8d8a00653b39cda43682082bbee3afd76a2bf08f1028d18d35056332b8d7ed71513939ae
@@ -2,3 +2,6 @@ inherit_from: .rubocop_todo.yml
2
2
 
3
3
  AllCops:
4
4
  TargetRubyVersion: 2.6
5
+
6
+ Style/Documentation:
7
+ Enabled: false
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kovid (0.6.5)
5
- ascii_charts (~> 0.9.1)
4
+ kovid (0.6.10)
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.
@@ -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.
@@ -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)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kovid
4
- # Sends info about continents to Tablelize
5
4
  module Aggregators
6
5
  def eu_aggregate(eu_data)
7
6
  aggregated_table(eu_data, 'The EU', Kovid::Request::EU_ISOS, 'πŸ‡ͺπŸ‡Ί')
@@ -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
@@ -1,11 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'typhoeus'
4
-
5
- # rubocop:disable Style/Documentation
6
4
  module Kovid
7
- # rubocop:enable Style/Documentation
8
- # Caches HTTP requests
9
5
  class Cache
10
6
  def initialize
11
7
  @memory = {}
@@ -4,8 +4,6 @@ require 'thor'
4
4
  require 'kovid'
5
5
 
6
6
  module Kovid
7
- # Describes the commands that can be run by the user
8
- # Descriptions are returned to STDOUT
9
7
  class CLI < Thor
10
8
  def self.exit_on_failure?
11
9
  true
@@ -77,13 +75,15 @@ module Kovid
77
75
  data_source
78
76
  end
79
77
 
80
- desc 'history COUNTRY or history COUNTRY N',
81
- 'Return history of incidents of COUNTRY (in the last N days)'
82
- def history(*params)
83
- if params.size == 2
84
- 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)
85
85
  else
86
- puts Kovid.history(params.first, nil)
86
+ puts Kovid.history(location, days)
87
87
  end
88
88
  data_source
89
89
  end
@@ -147,8 +147,8 @@ module Kovid
147
147
  source = <<~TEXT
148
148
  #{Time.now}
149
149
  Sources:
150
- * JHU CSSE GISand Data
151
- * https://www.worldometers.info/coronavirus/
150
+ * Johns Hopkins University
151
+ * https://www.worldometers.info/coronavirus
152
152
  TEXT
153
153
  puts source
154
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,
@@ -74,7 +81,8 @@ module Kovid
74
81
  'Cases Today'.paint_white,
75
82
  'Deaths'.paint_red,
76
83
  'Deaths Today'.paint_red,
77
- 'Active'.paint_yellow
84
+ 'Active'.paint_yellow,
85
+ 'Tests'.paint_white
78
86
  ].freeze
79
87
 
80
88
  COMPARE_STATES_HEADINGS = [
@@ -93,7 +101,13 @@ module Kovid
93
101
  'Recovered'.paint_green
94
102
  ].freeze
95
103
 
96
- FOOTER_LINE = [
104
+ FOOTER_LINE_THREE_COLUMNS = [
105
+ '------------',
106
+ '------------',
107
+ '------------'
108
+ ].freeze
109
+
110
+ FOOTER_LINE_FOUR_COLUMNS = [
97
111
  '------------',
98
112
  '------------',
99
113
  '------------',
@@ -30,4 +30,10 @@ module Kovid
30
30
  data.map! { |number| Kovid.comma_delimit(number) }
31
31
  end
32
32
  end
33
+
34
+ def lookup_us_state(state)
35
+ us = Carmen::Country.coded('USA')
36
+ lookup = us.subregions.coded(state) || us.subregions.named(state)
37
+ lookup ? lookup.name : state
38
+ end
33
39
  end
@@ -1,28 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kovid
4
- # Constructs history data for specified country
5
4
  module Historians
6
5
  include Constants
6
+ include AsciiCharts
7
7
 
8
- def history(country, last)
9
- # Write checks for when country is spelt wrong.
10
- headings = DATE_CASES_DEATHS_RECOVERED
8
+ def history(location, days)
11
9
  rows = []
12
10
 
13
- stats = if last
14
- Kovid.format_country_history_numbers(country).last(last.to_i)
15
- else
16
- Kovid.format_country_history_numbers(country)
17
- end
11
+ stats = Kovid.format_country_history_numbers(location).last(days.to_i)
12
+ dates = location['timeline']['cases'].keys.last(days.to_i)
18
13
 
19
- dates = if last
20
- country['timeline']['cases'].keys.last(last.to_i)
21
- else
22
- country['timeline']['cases'].keys
23
- end
24
-
25
- unless last
14
+ if days.to_i >= 30
26
15
  stats = stats.reject { |stat| stat[0].to_i.zero? && stat[1].to_i.zero? }
27
16
  dates = dates.last(stats.count)
28
17
  end
@@ -33,14 +22,25 @@ module Kovid
33
22
  rows << row
34
23
  end
35
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
+
36
36
  if stats.size > 10
37
- rows << FOOTER_LINE
38
- rows << DATE_CASES_DEATHS_RECOVERED
37
+ rows << footer
38
+ rows << col_format
39
39
  end
40
40
 
41
41
  Terminal::Table.new(
42
- title: country['country'].upcase,
43
- headings: headings,
42
+ title: title,
43
+ headings: col_format,
44
44
  rows: rows
45
45
  )
46
46
  end
@@ -90,7 +90,7 @@ module Kovid
90
90
  dates.each_with_index do |val, index|
91
91
  data << [val, positive_cases_figures[index]]
92
92
  end
93
- y_range = AsciiCharts::Cartesian.new(
93
+ y_range = Kovid::AsciiCharts::Cartesian.new(
94
94
  data, bar: true, hide_zero: true
95
95
  ).y_range
96
96
 
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rainbow'
4
-
5
- # Adds text color functionalities to String class
6
4
  class String
7
5
  def paint_white
8
6
  Rainbow(self).white.bg(:black).bold
@@ -1,33 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
+ require 'carmen'
4
5
  require_relative 'tablelize'
5
6
  require_relative 'cache'
6
7
  require_relative 'uri_builder'
7
8
 
8
9
  module Kovid
9
- # Makes requests to external source to collect cases data
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
@@ -110,7 +112,8 @@ module Kovid
110
112
  end
111
113
 
112
114
  def state(state)
113
- response = fetch_state(state)
115
+ response = fetch_state(Kovid.lookup_us_state(state))
116
+
114
117
  if response.nil?
115
118
  not_found(state)
116
119
  else
@@ -122,7 +125,6 @@ module Kovid
122
125
 
123
126
  def states(states)
124
127
  compared_states = fetch_compared_states(states)
125
-
126
128
  Kovid::Tablelize.compare_us_states(compared_states)
127
129
  rescue JSON::ParserError
128
130
  puts SERVER_DOWN
@@ -151,7 +153,7 @@ module Kovid
151
153
 
152
154
  def cases
153
155
  response = JSON.parse(
154
- Typhoeus.get(UriBuilder.new('/all').url, cache_ttl: 900).response_body
156
+ Typhoeus.get(UriBuilder.new('/v2/all').url, cache_ttl: 900).response_body
155
157
  )
156
158
 
157
159
  Kovid::Tablelize.cases(response)
@@ -159,15 +161,53 @@ module Kovid
159
161
  puts SERVER_DOWN
160
162
  end
161
163
 
162
- def history(country, last)
164
+ def history(country, days)
163
165
  history_path = UriBuilder.new('/v2/historical').url
166
+
164
167
  response = JSON.parse(
165
168
  Typhoeus.get(
166
169
  history_path + "/#{country}", cache_ttl: 900
167
170
  ).response_body
168
171
  )
169
172
 
170
- 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)
171
211
  rescue JSON::ParserError
172
212
  puts SERVER_DOWN
173
213
  end
@@ -189,9 +229,17 @@ module Kovid
189
229
 
190
230
  private
191
231
 
192
- def not_found(country)
193
- rows = [["Wrong spelling/No reported cases on #{country.upcase}."]]
194
- 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
195
243
  end
196
244
 
197
245
  def fetch_countries(list)
@@ -205,10 +253,10 @@ module Kovid
205
253
  end
206
254
 
207
255
  def fetch_compared_states(submitted_states)
208
- state_data = fetch_state_data
256
+ submitted_states.map! { |s| Kovid.lookup_us_state(s) }
209
257
 
210
- state_data.select do |state|
211
- submitted_states.include?(state['state'].downcase)
258
+ fetch_state_data.select do |state|
259
+ submitted_states.include?(state['state'])
212
260
  end
213
261
  end
214
262
 
@@ -218,9 +266,7 @@ module Kovid
218
266
 
219
267
  def fetch_country(country_name)
220
268
  # TODO: Match ISOs to full country names
221
- if country_name == "netherlands"
222
- country_name = "nl"
223
- end
269
+ country_name = 'nl' if country_name == 'netherlands'
224
270
  country_url = COUNTRIES_PATH + "/#{country_name}"
225
271
 
226
272
  JSON.parse(Typhoeus.get(country_url, cache_ttl: 900).response_body)
@@ -267,7 +313,7 @@ module Kovid
267
313
 
268
314
  def countries_request
269
315
  Typhoeus.get(
270
- UriBuilder.new('/countries').url, cache_ttl: 900
316
+ UriBuilder.new('/v2/countries').url, cache_ttl: 900
271
317
  ).response_body
272
318
  end
273
319
 
@@ -281,6 +327,16 @@ module Kovid
281
327
  end
282
328
  end.compact
283
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
284
340
  end
285
341
  end
286
342
  end
@@ -2,14 +2,13 @@
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'
9
9
  require_relative 'historians'
10
10
 
11
11
  module Kovid
12
- # Constructs the tables according to specified input
13
12
  class Tablelize
14
13
  extend Kovid::Constants
15
14
  extend Kovid::Aggregators
@@ -128,7 +127,8 @@ module Kovid
128
127
  Kovid.add_plus_sign(data['todayCases']),
129
128
  Kovid.comma_delimit(data['deaths']),
130
129
  Kovid.add_plus_sign(data['todayDeaths']),
131
- Kovid.comma_delimit(data['recovered'])
130
+ Kovid.comma_delimit(data['active']),
131
+ Kovid.comma_delimit(data['tests'])
132
132
  ]
133
133
  end
134
134
 
@@ -3,7 +3,6 @@
3
3
  require 'uri'
4
4
 
5
5
  module Kovid
6
- # Prepares base URI before requests
7
6
  class UriBuilder
8
7
  attr_reader :path
9
8
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kovid
4
- VERSION = '0.6.6'
4
+ VERSION = '0.6.11'
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.6
4
+ version: 0.6.11
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-11 00:00:00.000000000 Z
11
+ date: 2020-05-07 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