covid19 0.1.3 → 1.0.0

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: 8668b8285c3111972349d7c38b95be0dd3cc0e801485f96c0d3b6f225b09f0f3
4
- data.tar.gz: 70822cf4c46cee7358df27cd6cd30c928a714304b0e10ca2f3a74e6dcb3868aa
3
+ metadata.gz: dc1038b03cac572d43eb89be333e2f96b296b3dec003f5201caad9562e0436fa
4
+ data.tar.gz: 3e289944a83f9d3e4b7a33325048e8da6d5ddadc03c4810eb674438fbd513781
5
5
  SHA512:
6
- metadata.gz: 5376e2689957f74daa8e5801cf7aadbbb4ea0d9e1c349061874282c573ac91f8b5e152a6e7dc7d087384940c5c38921c02e71648caf720b9af8852194039f7a3
7
- data.tar.gz: 9dfcbded2200f24163080712ed5a3c6cc6534c4f67350180393ee5a92253e376ef0f0c7348477bb61196a50558c3175d43f4f0ba2dad72803e2d4a8eff769ed1
6
+ metadata.gz: a4439749f50f0f13701b63578443f2a62ac9468645d46ac005d9ad749d3537207b1c61aaad90dc1bffe69d7a02be10ed3b15f9c4da58699035c53d0fd20a8eb7
7
+ data.tar.gz: 9ca8f27fdd2df65fffa2db508c0e26a1ec4e6278b5eed407375ebce055b0f7bfe7aed2785b9a409a2ae09bc1163502884d6c374d50868ea0e0206a86794676c6
@@ -1,12 +1,21 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 2.5
3
3
 
4
+ Layout/CaseIndentation:
5
+ Enabled: false
6
+
7
+ Layout/HashAlignment:
8
+ EnforcedHashRocketStyle: table
9
+
4
10
  Layout/IndentationWidth:
5
11
  Width: 4
6
12
 
7
13
  Layout/LineLength:
8
14
  Enabled: false
9
15
 
16
+ Layout/SpaceInsideArrayLiteralBrackets:
17
+ Enabled: false
18
+
10
19
  Layout/SpaceAroundOperators:
11
20
  Enabled: false
12
21
 
@@ -37,6 +46,9 @@ Style/FormatStringToken:
37
46
  Style/FrozenStringLiteralComment:
38
47
  Enabled: false
39
48
 
49
+ Style/GlobalVars:
50
+ Enabled: false
51
+
40
52
  Style/HashEachMethods:
41
53
  Enabled: false
42
54
 
@@ -5,11 +5,19 @@ All notable changes to this project will be documented in this file.
5
5
 
6
6
  This changelog was automatically generated using [Caretaker](https://github.com/WolfAtheneum/covid19) by [Wolf Software](https://github.com/WolfSoftware)
7
7
 
8
+ ### [v1.0.0](https://github.com/WolfAtheneum/covid19/compare/v0.1.3...v1.0.0)
9
+
10
+ > Released on June, 14th 2020
11
+
12
+ - Update the gem version [`[62e4ebd]`](https://github.com/WolfAtheneum/covid19/commit/62e4ebd730975d8199a0a5a4ba0e9e0130c74716) [`[TGWolf]`](https://github.com/TGWolf)
13
+
14
+ - Complete rewrite using a new faster API [`[2bed745]`](https://github.com/WolfAtheneum/covid19/commit/2bed7459acdee4036c1f31e585cf65fdee704d4e) [`[TGWolf]`](https://github.com/TGWolf)
15
+
8
16
  ### [v0.1.3](https://github.com/WolfAtheneum/covid19/compare/v0.1.2...v0.1.3)
9
17
 
10
18
  > Released on March, 26th 2020
11
19
 
12
- - Minor tweak to the output death rate percentage [`[b2181fa]`](https://github.com/WolfAtheneum/covid19/commit/b2181fa254c9d049cbde07d03b2646b4646c4782) [`[TGWolf]`](https://github.com/TGWolf)
20
+ - Minor tweak to the output death rate percentage [`[24fdf44]`](https://github.com/WolfAtheneum/covid19/commit/24fdf44a1eb22499b47044e580d9a52ff298bfc0) [`[TGWolf]`](https://github.com/TGWolf)
13
21
 
14
22
  ### [v0.1.2](https://github.com/WolfAtheneum/covid19/compare/v0.1.0...v0.1.2)
15
23
 
data/README.md CHANGED
@@ -33,28 +33,22 @@ gem install covid19
33
33
 
34
34
  ## Usage
35
35
 
36
- Get the latest amount of total confirmed cases, deaths, and recoveries globally:
36
+ Get the latest stats by in summary form and broken down country by country.
37
37
 
38
38
  ```ruby
39
- Covid19.latest_stats
39
+ Covid19.latest_stats_all
40
40
  ```
41
41
 
42
- Get case data for all locations with reported cases:
43
-
44
- ```ruby
45
- Covid19.latest_stats_split_by_country
46
- ```
47
-
48
- Find case data by country code with an optional argument to include timelines:
42
+ Get the latest stats by in summary form.
49
43
 
50
44
  ```ruby
51
- Covid19.latest_stats_by_country_code('GB', true)
45
+ Covid19.latest_stats_global
52
46
  ```
53
47
 
54
- Find case data by location:
48
+ Get case data for all locations with reported cases:
55
49
 
56
50
  ```ruby
57
- Covid19.latest_stats_by_country_id(id)
51
+ Covid19.latest_stats_split_by_country
58
52
  ```
59
53
 
60
54
  ## Error Handling
@@ -68,11 +62,19 @@ The gem also comes with a command line tool called covid19.
68
62
  ```
69
63
  Usage: covid19
70
64
  -h, --help Display this screen
71
- -s, --split View the latestest information split by country
72
- -c, --confirmed Order results by confirmed cases
73
- -d, --deaths Order results by deaths
74
- -p, --death-rate Order results by percentage of deaths (death rate)
75
- -r, --recovered Order results by recovered numbers
65
+ -s, --summary Show a summary of the totals [Default]
66
+ -l, --latest Show the latest (todays) totals split by country (same as -t)
67
+ -t, --today Show the latest (todays) totals split by country (same as -l)
68
+ -T, --totals Show the full totals split by country
69
+
70
+ Coloumn Sorting Options:
71
+ -c, --confirmed Order the output based on 'Confirmed' column
72
+ -r, --recovered Order the output based on 'Recovered' column
73
+ -d, --deaths Order the output based on 'Deaths' column
74
+ -a, --active Order the output based on 'Active' column
75
+ -D, --death-rate Order the output based on 'Death Rate' column
76
+ -R, --recovery-rate Order the output based on 'Recovery Rate' column
77
+ -C, --cases-per-million Order the output based on 'Cases P/M' column
76
78
 
77
79
  ```
78
80
 
@@ -1 +1 @@
1
- 0.1.3
1
+ 1.0.0
@@ -22,9 +22,11 @@ Gem::Specification.new do |spec|
22
22
  spec.required_ruby_version = '>= 2.5'
23
23
 
24
24
  spec.add_development_dependency 'bundler', '~> 2'
25
+ spec.add_development_dependency 'colorize', '~> 0.8'
25
26
  spec.add_development_dependency 'rake', '~> 13.0'
26
27
  spec.add_development_dependency 'rspec', '~> 3.0'
27
28
  spec.add_development_dependency 'terminal-table', '~> 1.8'
28
29
 
30
+ spec.add_runtime_dependency 'colorize', '~> 0.8'
29
31
  spec.add_runtime_dependency 'terminal-table', '~> 1.8'
30
32
  end
@@ -2,83 +2,187 @@
2
2
 
3
3
  $LOAD_PATH.unshift('./lib')
4
4
 
5
+ require 'colorize'
5
6
  require 'optparse'
6
7
  require 'covid19'
7
8
  require 'terminal-table'
8
9
 
9
10
  # -------------------------------------------------------------------------------- #
10
- # Format Global Stats #
11
+ # Global Variables #
11
12
  # -------------------------------------------------------------------------------- #
12
- # Create a nice table view of the latest global stats. #
13
+ # A simple set of global variables to avoid having to do multiple string compares #
14
+ # as one typo can cause weird things to happen. #
13
15
  # -------------------------------------------------------------------------------- #
14
16
 
15
- def format_latest_stats(results)
16
- formatted = []
17
- results['latest'].each do |key, value|
18
- formatted << [key, value]
19
- end
17
+ $_confirmed = 1
18
+ $_recovered = 2
19
+ $_deaths = 3
20
+ $_active = 4
21
+ $_death_rate = 5
22
+ $_recovery_rate = 6
23
+ $_cases_pm = 7
20
24
 
21
- confirmed = results['latest']['confirmed']
22
- deaths = results['latest']['deaths']
25
+ # -------------------------------------------------------------------------------- #
26
+ # Get Hash Value #
27
+ # -------------------------------------------------------------------------------- #
28
+ # Simple wrapper to locate a value from a hash and handle error states. #
29
+ # -------------------------------------------------------------------------------- #
23
30
 
24
- percentage = ((deaths.to_f / confirmed) * 100)
31
+ def get_hash_value(hash, name, default_val = 0)
32
+ return default_val unless hash.is_a?(Hash)
25
33
 
26
- formatted << ['death rate', "%.2f%%" % percentage]
34
+ return hash.fetch(name, default_val)
35
+ end
27
36
 
28
- puts Terminal::Table.new :title => 'Latest Global Statistics', :rows => formatted, :style => { :width => 80 }
37
+ # -------------------------------------------------------------------------------- #
38
+ # Commify #
39
+ # -------------------------------------------------------------------------------- #
40
+ # Simple wrapper to make numbers a little more human readable. #
41
+ # -------------------------------------------------------------------------------- #
42
+
43
+ def commify(number)
44
+ return number.to_s.reverse.scan(/(?:\d*\.)?\d{1,3}-?/).join(',').reverse
29
45
  end
30
46
 
31
47
  # -------------------------------------------------------------------------------- #
32
- # Format Country Stats #
48
+ # Add Heading #
33
49
  # -------------------------------------------------------------------------------- #
34
- # Create a nice table view of the latest country stats. #
50
+ # Simple wrapper to generate a heading entry with optional alignment override. #
35
51
  # -------------------------------------------------------------------------------- #
36
- def format_latest_country_stats(results, options = {})
37
- processed = {}
38
52
 
39
- results['locations'].each do |item|
40
- country_code = item['country_code'].to_s
53
+ def add_heading(name, alignment = :center)
54
+ return { :value => name, :alignment => alignment }
55
+ end
41
56
 
42
- if processed[country_code]
43
- confirmed = processed[country_code]['confirmed'] + item['latest']['confirmed']
44
- deaths = processed[country_code]['deaths'] + item['latest']['deaths']
45
- recovered = processed[country_code]['recovered'] + item['latest']['recovered']
46
- else
47
- confirmed = item['latest']['confirmed']
48
- deaths = item['latest']['deaths']
49
- recovered = item['latest']['recovered']
50
- end
51
- percentage = ((deaths.to_f / confirmed) * 100)
57
+ # -------------------------------------------------------------------------------- #
58
+ # Show Summary Data #
59
+ # -------------------------------------------------------------------------------- #
60
+ # Create a nice table view of the latest global statistics. #
61
+ # -------------------------------------------------------------------------------- #
52
62
 
53
- percentage = 0 if percentage.nil?
63
+ def show_summary_data(results)
64
+ title = 'Global Statistics Summary'
65
+ headings = []
66
+ rows = []
54
67
 
55
- processed[country_code] = { 'country' => item['country'], 'country_code' => country_code, 'id' => item['id'], 'confirmed' => confirmed, 'deaths' => deaths, 'recovered' => recovered, 'death_rate' => percentage }
56
- end
68
+ headings << ''
69
+ headings << add_heading('Today')
70
+ headings << add_heading('Total')
71
+
72
+ rows << [ 'Confirmed Cases', commify(get_hash_value(results, 'ConfirmedToday')), commify(get_hash_value(results, 'ConfirmedTotal')) ]
73
+ rows << [ 'Recovered', commify(get_hash_value(results, 'RecoveredToday')), commify(get_hash_value(results, 'RecoveredTotal')) ]
74
+ rows << [ 'Deaths', commify(get_hash_value(results, 'DeathsToday')), commify(get_hash_value(results, 'DeathsTotal')) ]
75
+ rows << [ 'Active', '', commify(get_hash_value(results, 'ActiveTotal')) ]
76
+
77
+ table = Terminal::Table.new :title => title.colorize(:light_green), :headings => headings, :rows => rows, :style => { :padding_left => 2, :padding_right => 2 }
78
+ table.align_column(1, :right)
79
+ table.align_column(2, :right)
80
+ puts table
81
+ end
82
+
83
+ # -------------------------------------------------------------------------------- #
84
+ # Show Todays Totals By Country #
85
+ # -------------------------------------------------------------------------------- #
86
+ # Create a nice table view of todays totals available per country. #
87
+ # -------------------------------------------------------------------------------- #
88
+
89
+ def show_todays_totals_by_country(results, options = {})
90
+ title = 'Latest Totals by Country'
91
+ headings = []
92
+ rows = []
93
+
94
+ headings << add_heading('Country', :left)
95
+ headings << add_heading('Confirmed')
96
+ headings << add_heading('Recovered')
97
+ headings << add_heading('Deaths')
98
+
99
+ unless results.nil? && results.length.positive?
100
+ case options[:order]
101
+ when $_confirmed
102
+ results = results.sort_by { |k| k['ConfirmedToday'] }.reverse
103
+ title += ' Ordered by \'Confirmed\''
104
+ when $_recovered
105
+ results = results.sort_by { |k| k['RecoveredToday'] }.reverse
106
+ title += ' Ordered by \'Recovered\''
107
+ when $_deaths
108
+ results = results.sort_by { |k| k['DeathsToday'] }.reverse
109
+ title += ' Ordered by \'Deaths\''
110
+ else
111
+ results = results.sort_by { |k| k['Country'] }
112
+ end
57
113
 
58
- sorted = []
59
- processed.each do |_key, value|
60
- sorted << value
114
+ results.each do |value|
115
+ rows << [ value['Country'], commify(value['ConfirmedToday']), commify(value['RecoveredToday']), commify(value['DeathsToday']) ]
116
+ end
61
117
  end
62
118
 
63
- sorted = sorted.sort_by { |k| k['country'] }
64
- sorted = sorted.sort_by { |k| k['confirmed'] }.reverse if options[:confirmed]
65
- sorted = sorted.sort_by { |k| k['deaths'] }.reverse if options[:deaths]
66
- sorted = sorted.sort_by { |k| k['recovered'] }.reverse if options[:recovered]
67
- sorted = sorted.sort_by { |k| k['death_rate'] }.reverse if options[:percentage]
119
+ table = Terminal::Table.new :title => title.colorize(:light_green), :headings => headings, :rows => rows, :style => { :padding_left => 2, :padding_right => 2 }
120
+ table.align_column(1, :right)
121
+ table.align_column(2, :right)
122
+ table.align_column(3, :right)
123
+ puts table
124
+ end
68
125
 
69
- formatted = []
70
- sorted.each do |item|
71
- formatted << [item['country'], item['country_code'], item['id'], item['confirmed'], item['deaths'], item['recovered'], "%.2f" % item['death_rate']]
126
+ # -------------------------------------------------------------------------------- #
127
+ # Show Grand Totals By Country #
128
+ # -------------------------------------------------------------------------------- #
129
+ # Create a nice table view of the grand totals available per country. #
130
+ # -------------------------------------------------------------------------------- #
131
+
132
+ def show_grand_totals_by_country(results, options = {})
133
+ title = 'Grand Totals by Country'
134
+ headings = []
135
+ rows = []
136
+
137
+ headings << add_heading('Country', :left)
138
+ headings << add_heading('Confirmed')
139
+ headings << add_heading('Recovered')
140
+ headings << add_heading('Deaths')
141
+ headings << add_heading('Active')
142
+ headings << add_heading('Death Rate')
143
+ headings << add_heading('Recovery Rate')
144
+ headings << add_heading('Cases P/M')
145
+
146
+ unless results.nil? && results.length.positive?
147
+ case options[:order]
148
+ when $_confirmed
149
+ results = results.sort_by { |k| k['ConfirmedTotal'] }.reverse
150
+ title += ' Ordered by \'Confirmed\''
151
+ when $_recovered
152
+ results = results.sort_by { |k| k['RecoveredTotal'] }.reverse
153
+ title += ' Ordered by \'Recovered\''
154
+ when $_deaths
155
+ results = results.sort_by { |k| k['DeathsTotal'] }.reverse
156
+ title += ' Ordered by \'Deaths\''
157
+ when $_active
158
+ results = results.sort_by { |k| k['ActiveTotal'] }.reverse
159
+ title += ' Ordered by \'Active\''
160
+ when $_death_rate
161
+ results = results.sort_by { |k| k['DeathRate'] }.reverse
162
+ title += ' Ordered by \'Death Rate\''
163
+ when $_recovery_rate
164
+ results = results.sort_by { |k| k['RecoveryRate'] }.reverse
165
+ title += ' Ordered by \'Recovery Rate\''
166
+ when $_cases_pm
167
+ results = results.sort_by { |k| k['CasesPerMillion'] }.reverse
168
+ title += ' Ordered by \'Cases Per Million\''
169
+ else
170
+ results = results.sort_by { |k| k['Country'] }
171
+ end
172
+
173
+ results.each do |value|
174
+ rows << [ value['Country'], commify(value['ConfirmedTotal']), commify(value['RecoveredTotal']), commify(value['DeathsTotal']), commify(value['ActiveTotal']), "%.2f %%" % value['DeathRate'], "%.2f %%" % value['RecoveryRate'], commify(value['CasesPerMillion']) ]
175
+ end
72
176
  end
73
177
 
74
- table = Terminal::Table.new :title => 'Latest Statistics by Country', :headings => ['Country', 'Code', 'ID', 'Confirmed', 'Deaths', 'Recovered', 'Death Rate (%)'], :rows => formatted, :style => { :padding_left => 2, :padding_right => 2 }
75
- table.align_column(1, :center)
178
+ table = Terminal::Table.new :title => title.colorize(:light_green), :headings => headings, :rows => rows, :style => { :padding_left => 2, :padding_right => 2 }
179
+ table.align_column(1, :right)
76
180
  table.align_column(2, :right)
77
181
  table.align_column(3, :right)
78
182
  table.align_column(4, :right)
79
183
  table.align_column(5, :right)
80
184
  table.align_column(6, :right)
81
-
185
+ table.align_column(7, :right)
82
186
  puts table
83
187
  end
84
188
 
@@ -90,8 +194,9 @@ end
90
194
 
91
195
  def get_covid19_stats(options)
92
196
  begin
93
- format_latest_stats(Covid19.latest_stats) if options[:latest]
94
- format_latest_country_stats(Covid19.latest_stats_split_by_country, options) unless options[:latest]
197
+ show_summary_data(Covid19.latest_stats_global) if options[:summary]
198
+ show_todays_totals_by_country(Covid19.latest_stats_split_by_country, options) if options[:latest]
199
+ show_grand_totals_by_country(Covid19.latest_stats_split_by_country, options) if options[:totals]
95
200
  rescue StandardError => e
96
201
  puts "Error: #{e}"
97
202
  puts e.backtrace
@@ -109,7 +214,7 @@ end
109
214
  # -------------------------------------------------------------------------------- #
110
215
 
111
216
  def process_arguments
112
- options = { :latest => true }
217
+ options = { :summary => true }
113
218
  # Enforce the presence of
114
219
  mandatory = %I[]
115
220
 
@@ -120,37 +225,50 @@ def process_arguments
120
225
  puts opts
121
226
  exit(1)
122
227
  end
123
-
124
- opts.on('-s', '--split', 'View the latestest information for all locations') do
228
+ opts.on('-s', '--summary', 'Show a summary of the totals [Default]') do
229
+ options[:summary] = true
125
230
  options[:latest] = false
231
+ options[:totals] = false
126
232
  end
127
-
128
- opts.on('-c', '--confirmed', 'Order results by confirmed cases') do
129
- options[:confirmed] = true
130
- options[:deaths] = false
131
- options[:percentage] = false
132
- options[:recovered] = false
233
+ opts.on('-l', '--latest', 'Show the latest (todays) totals split by country (same as -t)') do
234
+ options[:summary] = false
235
+ options[:latest] = true
236
+ options[:totals] = false
133
237
  end
134
-
135
- opts.on('-d', '--deaths', 'Order results by deaths') do
136
- options[:confirmed] = false
137
- options[:deaths] = true
138
- options[:percentage] = false
139
- options[:recovered] = false
238
+ opts.on('-t', '--today', 'Show the latest (todays) totals split by country (same as -l)') do
239
+ options[:summary] = false
240
+ options[:latest] = true
241
+ options[:totals] = false
140
242
  end
141
-
142
- opts.on('-p', '--death-rate', 'Order results by percentage of deaths (death rate)') do
143
- options[:confirmed] = false
144
- options[:deaths] = false
145
- options[:percentage] = true
146
- options[:recovered] = false
243
+ opts.on('-T', '--totals', 'Show the full totals split by country') do
244
+ options[:summary] = false
245
+ options[:latest] = false
246
+ options[:totals] = true
147
247
  end
148
248
 
149
- opts.on('-r', '--recovered', 'Order results by recovered numbers') do
150
- options[:confirmed] = false
151
- options[:deaths] = false
152
- options[:percentage] = false
153
- options[:recovered] = true
249
+ opts.separator('')
250
+ opts.separator('Coloumn Sorting Options:')
251
+
252
+ opts.on('-c', '--confirmed', 'Order the output based on \'Confirmed\' column') do
253
+ options[:order] = $_confirmed
254
+ end
255
+ opts.on('-r', '--recovered', 'Order the output based on \'Recovered\' column') do
256
+ options[:order] = $_recovered
257
+ end
258
+ opts.on('-d', '--deaths', 'Order the output based on \'Deaths\' column') do
259
+ options[:order] = $_deaths
260
+ end
261
+ opts.on('-a', '--active', 'Order the output based on \'Active\' column') do
262
+ options[:order] = $_active
263
+ end
264
+ opts.on('-D', '--death-rate', 'Order the output based on \'Death Rate\' column') do
265
+ options[:order] = $_death_rate
266
+ end
267
+ opts.on('-R', '--recovery-rate', 'Order the output based on \'Recovery Rate\' column') do
268
+ options[:order] = $_recovery_rate
269
+ end
270
+ opts.on('-C', '--cases-per-million', 'Order the output based on \'Cases P/M\' column') do
271
+ options[:order] = $_cases_pm
154
272
  end
155
273
  end
156
274
 
@@ -1,40 +1,143 @@
1
1
  require 'covid19/version'
2
2
 
3
3
  require 'json'
4
+ require 'uri'
4
5
  require 'net/http'
5
6
 
6
7
  #
7
8
  # To Follow
8
9
  #
9
10
  class Covid19
10
- API_URL = 'https://coronavirus-tracker-api.herokuapp.com/v2/'.freeze
11
+ API_URL = 'https://corona-api.com/countries?include=timeline'.freeze
11
12
 
12
13
  class << self
13
- def latest_stats
14
- return retrieve_api_data('latest')
14
+ def latest_stats_all
15
+ return create_live_data_set
15
16
  end
16
17
 
17
- def latest_stats_split_by_country
18
- return retrieve_api_data('locations')
18
+ def latest_stats_global
19
+ return create_live_data_set['Global']
19
20
  end
20
21
 
21
- def latest_stats_by_country_code(country_code, with_timelines = false)
22
- timelines = with_timelines ? '&timelines=true' : ''
23
- return retrieve_api_data("locations?country_code=#{country_code.upcase}#{timelines}")
22
+ def latest_stats_split_by_country
23
+ return create_live_data_set['Countries']
24
24
  end
25
25
 
26
- def latest_stats_by_country_id(id)
27
- return retrieve_api_data("locations/#{id}")
26
+ private
27
+
28
+ def get_max(left, right)
29
+ return (left <=> right) >= 0 ? left : right
28
30
  end
29
31
 
30
- private
32
+ def create_live_data_set
33
+ global = { 'todays_confirmed' => 0, 'todays_recovered' => 0, 'todays_deaths' => 0, 'total_confirmed' => 0, 'total_recovered' => 0, 'total_deaths' => 0, 'total_active' => 0 }
34
+ country_list = []
35
+
36
+ data = retrieve_api_data['data']
37
+
38
+ data.each do |country|
39
+ todays_data = get_hash_value(country, 'today', [])
40
+ latest_data = get_hash_value(country, 'latest_data', [])
41
+ calculated_data = get_hash_value(latest_data, 'calculated', [])
42
+ timeline_data = get_hash_value(country, 'timeline', [])
43
+
44
+ #
45
+ # If we have timeline data then lets grab the most recent (current row)
46
+ #
47
+ current_data = if timeline_data.empty?
48
+ []
49
+ else
50
+ timeline_data.first
51
+ end
52
+
53
+ timeline_confirmed = get_hash_value(current_data, 'new_confirmed')
54
+ timeline_recovered = get_hash_value(current_data, 'new_recovered')
55
+ timeline_deaths = get_hash_value(current_data, 'new_deaths')
56
+ timeline_total_confirmed = get_hash_value(current_data, 'confirmed')
57
+ timeline_total_recovered = get_hash_value(current_data, 'recovered')
58
+ timeline_total_deaths = get_hash_value(current_data, 'deaths')
59
+ timeline_total_active = get_hash_value(current_data, 'active')
60
+
61
+ #
62
+ # Get todays data from the todays data set
63
+ #
64
+ todays_confirmed = get_hash_value(todays_data, 'confirmed')
65
+ todays_recovered = get_hash_value(todays_data, 'recovered')
66
+ todays_deaths = get_hash_value(todays_data, 'deaths')
31
67
 
32
- def retrieve_api_data(uri)
33
- url = URI("#{API_URL}#{uri}")
68
+ #
69
+ # Get the latest data from the latest dataset
70
+ #
71
+ total_confirmed = get_hash_value(latest_data, 'confirmed')
72
+ total_recovered = get_hash_value(latest_data, 'recovered')
73
+ total_deaths = get_hash_value(latest_data, 'deaths')
74
+ total_active = total_confirmed - (total_deaths + total_recovered)
75
+
76
+ #
77
+ # Use the biggest value - as timeline is updated at a different rate
78
+ #
79
+ todays_confirmed = get_max(timeline_confirmed, todays_confirmed)
80
+ todays_recovered = get_max(timeline_recovered, todays_recovered)
81
+ todays_deaths = get_max(timeline_deaths, todays_deaths)
82
+ total_confirmed = get_max(timeline_total_confirmed, total_confirmed)
83
+ total_recovered = get_max(timeline_total_recovered, total_recovered)
84
+ total_deaths = get_max(timeline_total_deaths, total_deaths)
85
+ total_active = get_max(timeline_total_active, total_active)
86
+
87
+ # This normally means the country has stopped given updates on recoveries
88
+ if get_hash_value(latest_data, 'recovered').zero?
89
+ todays_recovered = 0
90
+ total_recovered = 0
91
+ total_active = 0
92
+ end
93
+
94
+ global['todays_confirmed'] += todays_confirmed
95
+ global['todays_recovered'] += todays_recovered
96
+ global['todays_deaths'] += todays_deaths
97
+ global['total_confirmed'] += total_confirmed
98
+ global['total_recovered'] += total_recovered
99
+ global['total_deaths'] += total_deaths
100
+ global['total_active'] += total_active
101
+
102
+ row = {
103
+ 'Country' => country['name'],
104
+ 'Population' => get_hash_value(country, 'population', 'unknown'),
105
+ 'ConfirmedToday' => todays_confirmed,
106
+ 'RecoveredToday' => todays_recovered,
107
+ 'DeathsToday' => todays_deaths,
108
+ 'ConfirmedTotal' => total_confirmed,
109
+ 'RecoveredTotal' => total_recovered,
110
+ 'DeathsTotal' => total_deaths,
111
+ 'ActiveTotal' => total_active,
112
+ 'DeathRate' => get_hash_value(calculated_data, 'death_rate').to_f,
113
+ 'RecoveryRate' => get_hash_value(calculated_data, 'recovery_rate').to_f,
114
+ 'CasesPerMillion' => get_hash_value(calculated_data, 'cases_per_million_population').to_f
115
+ }
116
+
117
+ country_list << row
118
+ end
119
+
120
+ global = {
121
+ 'ConfirmedToday' => global['todays_confirmed'],
122
+ 'RecoveredToday' => global['todays_recovered'],
123
+ 'DeathsToday' => global['todays_deaths'],
124
+ 'ConfirmedTotal' => global['total_confirmed'],
125
+ 'RecoveredTotal' => global['total_recovered'],
126
+ 'DeathsTotal' => global['total_deaths'],
127
+ 'ActiveTotal' => global['total_active']
128
+ }
129
+
130
+ results = { 'Global' => global, 'Countries' => country_list }
131
+
132
+ return results
133
+ end
34
134
 
135
+ def retrieve_api_data
35
136
  begin
36
- response = Net::HTTP.get(url)
37
- return JSON.parse(response)
137
+ # encoded_url = URI.encode(API_URL)
138
+ uri = URI.parse(API_URL)
139
+ response = Net::HTTP.get_response(uri)
140
+ return JSON.parse(response.body)
38
141
  rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
39
142
  raise StandardError.new(e.to_s)
40
143
  end
@@ -1,3 +1,3 @@
1
1
  class Covid19
2
- VERSION = '0.1.3'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
@@ -3,83 +3,188 @@
3
3
  $LOAD_PATH.unshift('./lib')
4
4
 
5
5
  require 'bundler/setup'
6
+
7
+ require 'colorize'
6
8
  require 'optparse'
7
9
  require 'covid19'
8
10
  require 'terminal-table'
9
11
 
10
12
  # -------------------------------------------------------------------------------- #
11
- # Format Global Stats #
13
+ # Global Variables #
12
14
  # -------------------------------------------------------------------------------- #
13
- # Create a nice table view of the latest global stats. #
15
+ # A simple set of global variables to avoid having to do multiple string compares #
16
+ # as one typo can cause weird things to happen. #
14
17
  # -------------------------------------------------------------------------------- #
15
18
 
16
- def format_latest_stats(results)
17
- formatted = []
18
- results['latest'].each do |key, value|
19
- formatted << [key, value]
20
- end
19
+ $_confirmed = 1
20
+ $_recovered = 2
21
+ $_deaths = 3
22
+ $_active = 4
23
+ $_death_rate = 5
24
+ $_recovery_rate = 6
25
+ $_cases_pm = 7
21
26
 
22
- confirmed = results['latest']['confirmed']
23
- deaths = results['latest']['deaths']
27
+ # -------------------------------------------------------------------------------- #
28
+ # Get Hash Value #
29
+ # -------------------------------------------------------------------------------- #
30
+ # Simple wrapper to locate a value from a hash and handle error states. #
31
+ # -------------------------------------------------------------------------------- #
24
32
 
25
- percentage = ((deaths.to_f / confirmed) * 100)
33
+ def get_hash_value(hash, name, default_val = 0)
34
+ return default_val unless hash.is_a?(Hash)
26
35
 
27
- formatted << ['death rate', "%.2f%%" % percentage]
36
+ return hash.fetch(name, default_val)
37
+ end
28
38
 
29
- puts Terminal::Table.new :title => 'Latest Global Statistics', :rows => formatted, :style => { :width => 80 }
39
+ # -------------------------------------------------------------------------------- #
40
+ # Commify #
41
+ # -------------------------------------------------------------------------------- #
42
+ # Simple wrapper to make numbers a little more human readable. #
43
+ # -------------------------------------------------------------------------------- #
44
+
45
+ def commify(number)
46
+ return number.to_s.reverse.scan(/(?:\d*\.)?\d{1,3}-?/).join(',').reverse
30
47
  end
31
48
 
32
49
  # -------------------------------------------------------------------------------- #
33
- # Format Country Stats #
50
+ # Add Heading #
34
51
  # -------------------------------------------------------------------------------- #
35
- # Create a nice table view of the latest country stats. #
52
+ # Simple wrapper to generate a heading entry with optional alignment override. #
36
53
  # -------------------------------------------------------------------------------- #
37
- def format_latest_country_stats(results, options = {})
38
- processed = {}
39
54
 
40
- results['locations'].each do |item|
41
- country_code = item['country_code'].to_s
55
+ def add_heading(name, alignment = :center)
56
+ return { :value => name, :alignment => alignment }
57
+ end
42
58
 
43
- if processed[country_code]
44
- confirmed = processed[country_code]['confirmed'] + item['latest']['confirmed']
45
- deaths = processed[country_code]['deaths'] + item['latest']['deaths']
46
- recovered = processed[country_code]['recovered'] + item['latest']['recovered']
47
- else
48
- confirmed = item['latest']['confirmed']
49
- deaths = item['latest']['deaths']
50
- recovered = item['latest']['recovered']
51
- end
52
- percentage = ((deaths.to_f / confirmed) * 100)
59
+ # -------------------------------------------------------------------------------- #
60
+ # Show Summary Data #
61
+ # -------------------------------------------------------------------------------- #
62
+ # Create a nice table view of the latest global statistics. #
63
+ # -------------------------------------------------------------------------------- #
53
64
 
54
- percentage = 0 if percentage.nil?
65
+ def show_summary_data(results)
66
+ title = 'Global Statistics Summary'
67
+ headings = []
68
+ rows = []
55
69
 
56
- processed[country_code] = { 'country' => item['country'], 'country_code' => country_code, 'id' => item['id'], 'confirmed' => confirmed, 'deaths' => deaths, 'recovered' => recovered, 'death_rate' => percentage }
57
- end
70
+ headings << ''
71
+ headings << add_heading('Today')
72
+ headings << add_heading('Total')
73
+
74
+ rows << [ 'Confirmed Cases', commify(get_hash_value(results, 'ConfirmedToday')), commify(get_hash_value(results, 'ConfirmedTotal')) ]
75
+ rows << [ 'Recovered', commify(get_hash_value(results, 'RecoveredToday')), commify(get_hash_value(results, 'RecoveredTotal')) ]
76
+ rows << [ 'Deaths', commify(get_hash_value(results, 'DeathsToday')), commify(get_hash_value(results, 'DeathsTotal')) ]
77
+ rows << [ 'Active', '', commify(get_hash_value(results, 'ActiveTotal')) ]
78
+
79
+ table = Terminal::Table.new :title => title.colorize(:light_green), :headings => headings, :rows => rows, :style => { :padding_left => 2, :padding_right => 2 }
80
+ table.align_column(1, :right)
81
+ table.align_column(2, :right)
82
+ puts table
83
+ end
84
+
85
+ # -------------------------------------------------------------------------------- #
86
+ # Show Todays Totals By Country #
87
+ # -------------------------------------------------------------------------------- #
88
+ # Create a nice table view of todays totals available per country. #
89
+ # -------------------------------------------------------------------------------- #
90
+
91
+ def show_todays_totals_by_country(results, options = {})
92
+ title = 'Latest Totals by Country'
93
+ headings = []
94
+ rows = []
95
+
96
+ headings << add_heading('Country', :left)
97
+ headings << add_heading('Confirmed')
98
+ headings << add_heading('Recovered')
99
+ headings << add_heading('Deaths')
100
+
101
+ unless results.nil? && results.length.positive?
102
+ case options[:order]
103
+ when $_confirmed
104
+ results = results.sort_by { |k| k['ConfirmedToday'] }.reverse
105
+ title += ' Ordered by \'Confirmed\''
106
+ when $_recovered
107
+ results = results.sort_by { |k| k['RecoveredToday'] }.reverse
108
+ title += ' Ordered by \'Recovered\''
109
+ when $_deaths
110
+ results = results.sort_by { |k| k['DeathsToday'] }.reverse
111
+ title += ' Ordered by \'Deaths\''
112
+ else
113
+ results = results.sort_by { |k| k['Country'] }
114
+ end
58
115
 
59
- sorted = []
60
- processed.each do |_key, value|
61
- sorted << value
116
+ results.each do |value|
117
+ rows << [ value['Country'], commify(value['ConfirmedToday']), commify(value['RecoveredToday']), commify(value['DeathsToday']) ]
118
+ end
62
119
  end
63
120
 
64
- sorted = sorted.sort_by { |k| k['country'] }
65
- sorted = sorted.sort_by { |k| k['confirmed'] }.reverse if options[:confirmed]
66
- sorted = sorted.sort_by { |k| k['deaths'] }.reverse if options[:deaths]
67
- sorted = sorted.sort_by { |k| k['recovered'] }.reverse if options[:recovered]
68
- sorted = sorted.sort_by { |k| k['death_rate'] }.reverse if options[:percentage]
121
+ table = Terminal::Table.new :title => title.colorize(:light_green), :headings => headings, :rows => rows, :style => { :padding_left => 2, :padding_right => 2 }
122
+ table.align_column(1, :right)
123
+ table.align_column(2, :right)
124
+ table.align_column(3, :right)
125
+ puts table
126
+ end
69
127
 
70
- formatted = []
71
- sorted.each do |item|
72
- formatted << [item['country'], item['country_code'], item['id'], item['confirmed'], item['deaths'], item['recovered'], "%.2f" % item['death_rate']]
128
+ # -------------------------------------------------------------------------------- #
129
+ # Show Grand Totals By Country #
130
+ # -------------------------------------------------------------------------------- #
131
+ # Create a nice table view of the grand totals available per country. #
132
+ # -------------------------------------------------------------------------------- #
133
+
134
+ def show_grand_totals_by_country(results, options = {})
135
+ title = 'Grand Totals by Country'
136
+ headings = []
137
+ rows = []
138
+
139
+ headings << add_heading('Country', :left)
140
+ headings << add_heading('Confirmed')
141
+ headings << add_heading('Recovered')
142
+ headings << add_heading('Deaths')
143
+ headings << add_heading('Active')
144
+ headings << add_heading('Death Rate')
145
+ headings << add_heading('Recovery Rate')
146
+ headings << add_heading('Cases P/M')
147
+
148
+ unless results.nil? && results.length.positive?
149
+ case options[:order]
150
+ when $_confirmed
151
+ results = results.sort_by { |k| k['ConfirmedTotal'] }.reverse
152
+ title += ' Ordered by \'Confirmed\''
153
+ when $_recovered
154
+ results = results.sort_by { |k| k['RecoveredTotal'] }.reverse
155
+ title += ' Ordered by \'Recovered\''
156
+ when $_deaths
157
+ results = results.sort_by { |k| k['DeathsTotal'] }.reverse
158
+ title += ' Ordered by \'Deaths\''
159
+ when $_active
160
+ results = results.sort_by { |k| k['ActiveTotal'] }.reverse
161
+ title += ' Ordered by \'Active\''
162
+ when $_death_rate
163
+ results = results.sort_by { |k| k['DeathRate'] }.reverse
164
+ title += ' Ordered by \'Death Rate\''
165
+ when $_recovery_rate
166
+ results = results.sort_by { |k| k['RecoveryRate'] }.reverse
167
+ title += ' Ordered by \'Recovery Rate\''
168
+ when $_cases_pm
169
+ results = results.sort_by { |k| k['CasesPerMillion'] }.reverse
170
+ title += ' Ordered by \'Cases Per Million\''
171
+ else
172
+ results = results.sort_by { |k| k['Country'] }
173
+ end
174
+
175
+ results.each do |value|
176
+ rows << [ value['Country'], commify(value['ConfirmedTotal']), commify(value['RecoveredTotal']), commify(value['DeathsTotal']), commify(value['ActiveTotal']), "%.2f %%" % value['DeathRate'], "%.2f %%" % value['RecoveryRate'], commify(value['CasesPerMillion']) ]
177
+ end
73
178
  end
74
179
 
75
- table = Terminal::Table.new :title => 'Latest Statistics by Country', :headings => ['Country', 'Code', 'ID', 'Confirmed', 'Deaths', 'Recovered', 'Death Rate (%)'], :rows => formatted, :style => { :padding_left => 2, :padding_right => 2 }
76
- table.align_column(1, :center)
180
+ table = Terminal::Table.new :title => title.colorize(:light_green), :headings => headings, :rows => rows, :style => { :padding_left => 2, :padding_right => 2 }
181
+ table.align_column(1, :right)
77
182
  table.align_column(2, :right)
78
183
  table.align_column(3, :right)
79
184
  table.align_column(4, :right)
80
185
  table.align_column(5, :right)
81
186
  table.align_column(6, :right)
82
-
187
+ table.align_column(7, :right)
83
188
  puts table
84
189
  end
85
190
 
@@ -91,8 +196,9 @@ end
91
196
 
92
197
  def get_covid19_stats(options)
93
198
  begin
94
- format_latest_stats(Covid19.latest_stats) if options[:latest]
95
- format_latest_country_stats(Covid19.latest_stats_split_by_country, options) unless options[:latest]
199
+ show_summary_data(Covid19.latest_stats_global) if options[:summary]
200
+ show_todays_totals_by_country(Covid19.latest_stats_split_by_country, options) if options[:latest]
201
+ show_grand_totals_by_country(Covid19.latest_stats_split_by_country, options) if options[:totals]
96
202
  rescue StandardError => e
97
203
  puts "Error: #{e}"
98
204
  puts e.backtrace
@@ -110,7 +216,7 @@ end
110
216
  # -------------------------------------------------------------------------------- #
111
217
 
112
218
  def process_arguments
113
- options = { :latest => true }
219
+ options = { :summary => true }
114
220
  # Enforce the presence of
115
221
  mandatory = %I[]
116
222
 
@@ -121,37 +227,50 @@ def process_arguments
121
227
  puts opts
122
228
  exit(1)
123
229
  end
124
-
125
- opts.on('-s', '--split', 'View the latestest information for all locations') do
230
+ opts.on('-s', '--summary', 'Show a summary of the totals [Default]') do
231
+ options[:summary] = true
126
232
  options[:latest] = false
233
+ options[:totals] = false
127
234
  end
128
-
129
- opts.on('-c', '--confirmed', 'Order results by confirmed cases') do
130
- options[:confirmed] = true
131
- options[:deaths] = false
132
- options[:percentage] = false
133
- options[:recovered] = false
235
+ opts.on('-l', '--latest', 'Show the latest (todays) totals split by country (same as -t)') do
236
+ options[:summary] = false
237
+ options[:latest] = true
238
+ options[:totals] = false
134
239
  end
135
-
136
- opts.on('-d', '--deaths', 'Order results by deaths') do
137
- options[:confirmed] = false
138
- options[:deaths] = true
139
- options[:percentage] = false
140
- options[:recovered] = false
240
+ opts.on('-t', '--today', 'Show the latest (todays) totals split by country (same as -l)') do
241
+ options[:summary] = false
242
+ options[:latest] = true
243
+ options[:totals] = false
141
244
  end
142
-
143
- opts.on('-p', '--death-rate', 'Order results by percentage of deaths (death rate)') do
144
- options[:confirmed] = false
145
- options[:deaths] = false
146
- options[:percentage] = true
147
- options[:recovered] = false
245
+ opts.on('-T', '--totals', 'Show the full totals split by country') do
246
+ options[:summary] = false
247
+ options[:latest] = false
248
+ options[:totals] = true
148
249
  end
149
250
 
150
- opts.on('-r', '--recovered', 'Order results by recovered numbers') do
151
- options[:confirmed] = false
152
- options[:deaths] = false
153
- options[:percentage] = false
154
- options[:recovered] = true
251
+ opts.separator('')
252
+ opts.separator('Coloumn Sorting Options:')
253
+
254
+ opts.on('-c', '--confirmed', 'Order the output based on \'Confirmed\' column') do
255
+ options[:order] = $_confirmed
256
+ end
257
+ opts.on('-r', '--recovered', 'Order the output based on \'Recovered\' column') do
258
+ options[:order] = $_recovered
259
+ end
260
+ opts.on('-d', '--deaths', 'Order the output based on \'Deaths\' column') do
261
+ options[:order] = $_deaths
262
+ end
263
+ opts.on('-a', '--active', 'Order the output based on \'Active\' column') do
264
+ options[:order] = $_active
265
+ end
266
+ opts.on('-D', '--death-rate', 'Order the output based on \'Death Rate\' column') do
267
+ options[:order] = $_death_rate
268
+ end
269
+ opts.on('-R', '--recovery-rate', 'Order the output based on \'Recovery Rate\' column') do
270
+ options[:order] = $_recovery_rate
271
+ end
272
+ opts.on('-C', '--cases-per-million', 'Order the output based on \'Cases P/M\' column') do
273
+ options[:order] = $_cases_pm
155
274
  end
156
275
  end
157
276
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: covid19
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Gurney aka Wolf
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-26 00:00:00.000000000 Z
11
+ date: 2020-06-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: colorize
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.8'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.8'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,20 @@ dependencies:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '1.8'
83
+ - !ruby/object:Gem::Dependency
84
+ name: colorize
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.8'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.8'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: terminal-table
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -114,7 +142,7 @@ homepage: https://github.com/WolfAtheneum/covid19
114
142
  licenses:
115
143
  - MIT
116
144
  metadata: {}
117
- post_install_message:
145
+ post_install_message:
118
146
  rdoc_options: []
119
147
  require_paths:
120
148
  - lib
@@ -130,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
158
  version: '0'
131
159
  requirements: []
132
160
  rubygems_version: 3.0.3
133
- signing_key:
161
+ signing_key:
134
162
  specification_version: 4
135
163
  summary: A wrapper for the coronavirus API at https://github.com/ExpDev07/coronavirus-tracker-api.
136
164
  test_files: []