firebase-stats 0.1.0 → 1.0.5

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: 1f9de921405769947da53d014425be7283f4417086404cf286aec2878ef76626
4
- data.tar.gz: 1a3e5974d548846245ac4cf82514d6cd5032b539031b23bca24d48ea9cc4276d
3
+ metadata.gz: 4c0052a1fd763bfd20e4561f5381098bc8436252f25ca478a51f152d9c9edc1f
4
+ data.tar.gz: 5057c8cf57c562d489d60ecf7d35fa4172ad025937da9b7fd3ddd72b09e8b86c
5
5
  SHA512:
6
- metadata.gz: ddc6212d166cc9adde36d87cd1e683dd9595461d30832b7baddde6bbd5fa5cfbd3379f2cf4129c59196db6d7cf6df15a95907d5bd18364b56de9777c29b36653
7
- data.tar.gz: 4a9557ea13c60f54c1c4015c8fd1b817b4b17fd1dbbcb652a74f02be52c7046a8187f818b1bf0cb7740b5aab29b0878a4c281bbe397edcaa8791228c07cdb6eb
6
+ metadata.gz: 2e36f05b2e942c81ebd86db0ddaff06aecca5e8167bf233591a32df06489b31b52985d6636d00dfcedd2c82ff914430c03d220297f3d93456b658c012b8240be
7
+ data.tar.gz: f74f39bd93fcfc0fc0c93ed6f11d9485d1635cbcbb64c0ca046cd91eb5f0828f8acaa9c3e3c2640cec4c62f00ef4cadf4f92dbe5e9b1dbe5d9c7ea07b5bba498
data/bin/firebase-stats CHANGED
@@ -6,11 +6,11 @@ require 'commander/import'
6
6
  require 'firebase-stats'
7
7
  require 'table_print'
8
8
 
9
- program :name, 'firebase_stats_cli'
10
- program :version, '0.1.0'
11
- program :description, 'CLI to view stuff from the Firebase Analytics CSV'
9
+ program :name, 'firebase-stats'
10
+ program :version, FirebaseStats::VERSION
11
+ program :description, 'A CLI tool to get different stats out of the huge Firebase Analytics CSV'
12
+ program :help_formatter, Commander::HelpFormatter::TerminalCompact
12
13
 
13
- global_option('--platform STRING', String, 'Show only stats for this OS. Either ios, android or all (default)')
14
14
 
15
15
  def map_platform(options)
16
16
  raw_platform = options.platform ||= 'all'
@@ -21,10 +21,12 @@ def map_platform(options)
21
21
  end
22
22
 
23
23
  command :devices do |c|
24
- c.syntax = 'firebase_stats_cli devices [options]'
24
+ c.syntax = 'devices <path to csv> [options]'
25
25
  c.summary = 'Prints out device stats'
26
- c.description = ''
27
- c.option '--friendly', 'Maps Android model numbers to friendly names'
26
+ c.description = 'Prints out a table with all of the devices, optionally filtered by OS'
27
+ c.option '--friendly', 'Prints out Android device models with their human readable name (this is slow)'
28
+ c.option '--limit NUMBER', Integer, 'Limits number of devices output'
29
+ c.option '--platform STRING', String, 'Show only stats for this OS. Either ios, android or all (default)'
28
30
 
29
31
  c.action do |args, options|
30
32
  stats = FirebaseStats::Reader.new
@@ -32,16 +34,18 @@ command :devices do |c|
32
34
 
33
35
  platform = map_platform(options)
34
36
 
35
- wrapper = FirebaseStats::Wrapper.new(stats, platform)
36
- tp wrapper.devices(friendly: options.friendly)
37
+ wrapper = FirebaseStats::Wrapper.new(stats)
38
+ tp wrapper.devices(friendly: options.friendly, limit: options.limit, platform: platform)
37
39
  end
38
40
  end
39
41
 
40
42
  command :os do |c|
41
- c.syntax = 'firebase_stats_cli os [options]'
42
- c.summary = 'Prints out all of the data'
43
- c.description = ''
44
- c.option '--grouped', 'Groups OS versions together'
43
+ c.syntax = 'os <path to csv> [options]'
44
+ c.summary = 'Prints out OS stats'
45
+ c.description = 'Prints out the number of devices per OS version, optionally filtered by OS and grouped by major version'
46
+ c.option '--grouped', 'Groups minor OS versions together'
47
+ c.option '--version-sorted', 'Sorts by OS version instead of percentage'
48
+ c.option '--platform STRING', String, 'Show only stats for this OS. Either ios, android or all (default)'
45
49
 
46
50
  c.action do |args, options|
47
51
  stats = FirebaseStats::Reader.new
@@ -49,8 +53,45 @@ command :os do |c|
49
53
 
50
54
  platform = map_platform(options)
51
55
 
52
- wrapper = FirebaseStats::Wrapper.new(stats, platform)
56
+ wrapper = FirebaseStats::Wrapper.new(stats)
53
57
 
54
- options.grouped.nil? ? tp(wrapper.os_version) : tp(wrapper.os_grouped)
58
+ grouped = options.grouped || false
59
+ major_order = options.version_sorted || false
60
+
61
+ data = wrapper.os(platform: platform, grouped: grouped, major_order: major_order)
62
+
63
+ tp data
64
+ end
65
+ end
66
+
67
+ command :gender do |c|
68
+ c.syntax = 'gender'
69
+ c.summary = 'Prints out gender stats (if available)'
70
+ c.description = 'Prints out a table with number of users of each gender'
71
+
72
+ c.action do |args, options|
73
+ stats = FirebaseStats::Reader.new
74
+ stats.parse_file(args[0])
75
+
76
+ platform = map_platform(options)
77
+
78
+ wrapper = FirebaseStats::Wrapper.new(stats)
79
+ tp wrapper.gender
80
+ end
81
+ end
82
+
83
+ command :gender_age do |c|
84
+ c.syntax = 'gender_age'
85
+ c.summary = 'Prints out age group stats (if available)'
86
+ c.description = 'Prints out a table with percentage of users of each gender grouped by age'
87
+
88
+ c.action do |args, options|
89
+ stats = FirebaseStats::Reader.new
90
+ stats.parse_file(args[0])
91
+
92
+ platform = map_platform(options)
93
+
94
+ wrapper = FirebaseStats::Wrapper.new(stats)
95
+ tp wrapper.gender_age
55
96
  end
56
97
  end
@@ -0,0 +1,24 @@
1
+ module FirebaseStats
2
+ # Parses the Firebase CSV file into sections
3
+ class DeviceUtils
4
+ # Is this device name an iOS device?
5
+ # @param [CSV::Row] device_name
6
+ def self.ios_device?(device_name)
7
+ device_name.downcase.include?('iphone') or device_name.downcase.include?('ipad') or device_name.downcase.include?('ipod')
8
+ end
9
+
10
+ # Filters a device list to only the requested platform
11
+ # @param [CSV::Table] device_data
12
+ # @param [Symbol] platform One of :all, :ios, :android
13
+ def self.filter_device(device_data, platform)
14
+ case platform
15
+ when :android
16
+ device_data.reject { |row| ios_device? row['Device model'] }
17
+ when :ios
18
+ device_data.select { |row| ios_device? row['Device model'] }
19
+ else
20
+ device_data
21
+ end
22
+ end
23
+ end
24
+ end
@@ -2,3 +2,5 @@
2
2
 
3
3
  require 'reader'
4
4
  require 'wrapper'
5
+ require 'version'
6
+ require 'device_utils'
data/lib/reader.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class FirebaseStats
3
+ module FirebaseStats
4
4
  require 'csv'
5
5
 
6
6
  # Parses the Firebase CSV file into sections
@@ -22,7 +22,7 @@ class FirebaseStats
22
22
  def parse(input)
23
23
  curr_lines = []
24
24
  input.each_with_index do |line, idx|
25
- curr_lines.append(line) unless comment?(line) || line.strip.empty?
25
+ curr_lines.push(line) unless comment?(line) || line.strip.empty?
26
26
 
27
27
  if (idx == input.length - 1) || line.strip.empty?
28
28
  process_lines curr_lines unless curr_lines.empty?
data/lib/version.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FirebaseStats
4
+ VERSION = '1.0.5'
5
+ end
data/lib/wrapper.rb CHANGED
@@ -1,76 +1,85 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class FirebaseStats
3
+ module FirebaseStats
4
4
  require 'csv'
5
- # require 'android/devices'
5
+ require 'android/devices'
6
+ require 'open-uri'
6
7
 
7
8
  # Transforms the parsed Firebase file into something more user friendly
8
9
  class Wrapper
9
10
  # @param [FirebaseStats::Reader] stats
10
- # @param [Symbol] platform One of :all, :ios, :android
11
- def initialize(stats, platform = :all)
11
+ def initialize(stats)
12
12
  super()
13
13
  @stats = stats
14
- @platform = platform
15
14
  end
16
15
 
17
- def os_version
18
- filtered = filter_os(@stats.data[:os_version], @platform)
16
+ # Get all OS versions, grouped by Major version
17
+ # @param [Symbol] platform One of :all, :ios, :android
18
+ # @param [Boolean] grouped Group by Major OS version
19
+ # @param [Boolean] major_order Order by Major OS version (instead of percentage)
20
+ def os(platform: :all, grouped: true, major_order: true)
21
+ os_data = all_os
22
+ filtered = filter_os(os_data, platform)
23
+
24
+ data = if grouped
25
+ make_group_stats(filtered, platform)
26
+ else
27
+ filtered
28
+ end
19
29
 
30
+ major_order ? major_version_sort(data) : data
31
+ end
32
+
33
+ # Gets all devices
34
+ # @param [Boolean] friendly Transform the Android model numbers into their human numaes
35
+ # @param [Integer] limit Number of devices to turn
36
+ # @param [Symbol] platform One of :all, :ios, :android
37
+ def devices(friendly: false, limit: 10, platform: :all)
38
+ filtered = DeviceUtils.filter_device(@stats.data[:devices], platform)
39
+ filtered = filtered.take(limit || 10)
20
40
  cleaned = []
21
41
  filtered.each do |row|
22
- cleaned << {
23
- 'version' => row['OS with version'],
24
- 'count' => row['Users'].to_i,
25
- 'percentage' => as_percentage(os_total, row['Users'].to_f)
42
+ device = {
43
+ 'model' => row['Device model']
26
44
  }
27
- end
28
- cleaned
29
- end
45
+ if friendly && ((platform == :all) || (platform == :android))
46
+ mapped = Android::Devices.search_by_model(row['Device model'])
47
+ device['friendly'] = if mapped.nil?
48
+ row['Device model']
49
+ else
50
+ mapped.name
51
+ end
52
+ end
53
+ device['count'] = row['Users'].to_i
30
54
 
31
- def os_grouped
32
- raw_os = @stats.data[:os_version]
33
-
34
- grouped = case @platform
35
- when :ios
36
- ios_os_group(raw_os)
37
- when :android
38
- android_os_group(raw_os)
39
- else
40
- android_os_group(raw_os).merge ios_os_group(raw_os)
41
- end
42
- computed = []
43
- grouped.each do |k, v|
44
- version_name = k
45
- total = v.map { |version| version['Users'].to_i }.reduce(0, :+)
46
- computed << { 'version' => version_name, 'total' => total, 'percentage' => as_percentage(os_total, total.to_f) }
55
+ cleaned << device
47
56
  end
48
- computed
57
+ cleaned
49
58
  end
50
59
 
51
- def os_total
52
- filtered = filter_os(@stats.data[:os_version], @platform)
53
- total = 0
54
- filtered.each do |row|
55
- total += row['Users'].to_i
60
+ def gender
61
+ raw = @stats.data[:gender]
62
+ data = []
63
+ raw.each do |row|
64
+ data << {
65
+ 'gender' => row['Gender'],
66
+ 'count' => row['Users']
67
+ }
56
68
  end
57
- total
69
+ data
58
70
  end
59
71
 
60
- def devices(friendly: false)
61
- filtered = filter_device(@stats.data[:devices], @platform)
62
- cleaned = []
63
- filtered.each do |row|
64
- device = {
65
- 'model' => row['Device model'],
66
- 'count' => row['Users'].to_i
72
+ def gender_age
73
+ raw = @stats.data[:gender_age]
74
+ data = []
75
+ raw.each do |row|
76
+ data << {
77
+ 'age' => row['Category'],
78
+ 'male' => (row['Male'].to_f * 100).round(2),
79
+ 'female' => (row['Female'].to_f * 100).round(2)
67
80
  }
68
- # if friendly && ((@platform == :all) || (@platform == :android))
69
- # device['friendly'] = Android::Devices.search_by_model(row['Device Model']).name
70
- # end
71
- cleaned << device
72
81
  end
73
- cleaned
82
+ data
74
83
  end
75
84
 
76
85
  private
@@ -80,32 +89,14 @@ class FirebaseStats
80
89
  def filter_os(os_data, platform)
81
90
  case platform
82
91
  when :android
83
- os_data.select { |row| row['OS with version'].downcase.include?('android') }
92
+ os_data.select { |row| row['version'].downcase.include?('android') }
84
93
  when :ios
85
- os_data.select { |row| row['OS with version'].downcase.include?('ios') }
94
+ os_data.select { |row| row['version'].downcase.include?('ios') }
86
95
  else
87
96
  os_data
88
97
  end
89
98
  end
90
99
 
91
- # @param [CSV::Table] device_data
92
- # @param [Symbol] platform One of :all, :ios, :android
93
- def filter_device(device_data, platform)
94
- case platform
95
- when :android
96
- device_data.reject { |row| ios_device? row['Device model'] }
97
- when :ios
98
- device_data.select { |row| ios_device? row['Device model'] }
99
- else
100
- device_data
101
- end
102
- end
103
-
104
- # @param [CSV::Row] device_name
105
- def ios_device?(device_name)
106
- device_name.downcase.include?('iphone') or device_name.downcase.include?('ipad')
107
- end
108
-
109
100
  def as_percentage(total, value)
110
101
  percentage = (value / total) * 100
111
102
  if percentage < 0.01
@@ -116,11 +107,59 @@ class FirebaseStats
116
107
  end
117
108
 
118
109
  def ios_os_group(os_details)
119
- filter_os(os_details, :ios).group_by { |row| row['OS with version'].match('(iOS [0-9]{1,2})').captures[0] }
110
+ filter_os(os_details, :ios).group_by { |row| row['version'].match('(iOS [0-9]{1,2})').captures[0] }
120
111
  end
121
112
 
122
113
  def android_os_group(os_details)
123
- filter_os(os_details, :android).group_by { |row| row['OS with version'].match('(Android [0-9]{1,2})').captures[0] }
114
+ filter_os(os_details, :android).group_by { |row| row['version'].match('(Android [0-9]{1,2})').captures[0] }
115
+ end
116
+
117
+ # Get all OS versions
118
+ def all_os
119
+ data = @stats.data[:os_version]
120
+
121
+ data.map do |row|
122
+ {
123
+ 'version' => row['OS with version'],
124
+ 'count' => row['Users'].to_i
125
+ }
126
+ end
127
+ end
128
+
129
+ def make_group_stats(os_data, platform)
130
+ data = make_os_groups(os_data, platform)
131
+
132
+ total_devices = os_total(os_data)
133
+ data.map do |k, v|
134
+ version_name = k
135
+ group_total = v.map { |version| version['count'].to_i }.reduce(0, :+)
136
+ { 'version' => version_name,
137
+ 'total' => group_total,
138
+ 'percentage' => as_percentage(total_devices.to_f, group_total) }
139
+ end
140
+ end
141
+
142
+ def make_os_groups(os_data, platform)
143
+ case platform
144
+ when :ios
145
+ ios_os_group(os_data)
146
+ when :android
147
+ android_os_group(os_data)
148
+ else
149
+ android_os_group(os_data).merge ios_os_group(os_data)
150
+ end
151
+ end
152
+
153
+ def os_total(os_data)
154
+ os_data.map { |row| row['count'] }.reduce(0, :+)
155
+ end
156
+
157
+ def major_version_sort(data)
158
+ data.sort_by do |row|
159
+ version = row['version']
160
+ number = version.match('([0-9.]+)').captures[0]
161
+ Gem::Version.new(number)
162
+ end.reverse
124
163
  end
125
164
  end
126
165
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: firebase-stats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - chedabob
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-16 00:00:00.000000000 Z
11
+ date: 2021-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: android-devices
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: commander
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,7 +52,50 @@ dependencies:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
54
  version: '1.5'
41
- description: A simple h
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '13'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '13'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.10'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.10'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.10'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.10'
97
+ description: A CLI tool to get different stats out of the huge Firebase Analytics
98
+ CSV
42
99
  email: matt.malone1@gmail.com
43
100
  executables:
44
101
  - firebase-stats
@@ -47,10 +104,12 @@ extra_rdoc_files: []
47
104
  files:
48
105
  - bin/firebase-stats
49
106
  - lib/.DS_Store
107
+ - lib/device_utils.rb
50
108
  - lib/firebase-stats.rb
51
109
  - lib/reader.rb
110
+ - lib/version.rb
52
111
  - lib/wrapper.rb
53
- homepage: https://rubygems.org/gems/firebase-stats
112
+ homepage: https://github.com/chedabob/firebase-stats
54
113
  licenses:
55
114
  - MIT
56
115
  metadata: {}
@@ -60,17 +119,17 @@ require_paths:
60
119
  - lib
61
120
  required_ruby_version: !ruby/object:Gem::Requirement
62
121
  requirements:
63
- - - "~>"
122
+ - - ">="
64
123
  - !ruby/object:Gem::Version
65
- version: '3'
124
+ version: '2.4'
66
125
  required_rubygems_version: !ruby/object:Gem::Requirement
67
126
  requirements:
68
127
  - - ">="
69
128
  - !ruby/object:Gem::Version
70
129
  version: '0'
71
130
  requirements: []
72
- rubygems_version: 3.2.3
131
+ rubygems_version: 3.2.14
73
132
  signing_key:
74
133
  specification_version: 4
75
- summary: Firebase CSV Stats extractor
134
+ summary: Firebase CSV Stats CLI
76
135
  test_files: []