firebase-stats 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1f9de921405769947da53d014425be7283f4417086404cf286aec2878ef76626
4
+ data.tar.gz: 1a3e5974d548846245ac4cf82514d6cd5032b539031b23bca24d48ea9cc4276d
5
+ SHA512:
6
+ metadata.gz: ddc6212d166cc9adde36d87cd1e683dd9595461d30832b7baddde6bbd5fa5cfbd3379f2cf4129c59196db6d7cf6df15a95907d5bd18364b56de9777c29b36653
7
+ data.tar.gz: 4a9557ea13c60f54c1c4015c8fd1b817b4b17fd1dbbcb652a74f02be52c7046a8187f818b1bf0cb7740b5aab29b0878a4c281bbe397edcaa8791228c07cdb6eb
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'rubygems'
5
+ require 'commander/import'
6
+ require 'firebase-stats'
7
+ require 'table_print'
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'
12
+
13
+ global_option('--platform STRING', String, 'Show only stats for this OS. Either ios, android or all (default)')
14
+
15
+ def map_platform(options)
16
+ raw_platform = options.platform ||= 'all'
17
+ platform = :all
18
+ platform = :ios if raw_platform.downcase.include?('ios')
19
+ platform = :android if raw_platform.downcase.include?('android')
20
+ platform
21
+ end
22
+
23
+ command :devices do |c|
24
+ c.syntax = 'firebase_stats_cli devices [options]'
25
+ c.summary = 'Prints out device stats'
26
+ c.description = ''
27
+ c.option '--friendly', 'Maps Android model numbers to friendly names'
28
+
29
+ c.action do |args, options|
30
+ stats = FirebaseStats::Reader.new
31
+ stats.parse_file(args[0])
32
+
33
+ platform = map_platform(options)
34
+
35
+ wrapper = FirebaseStats::Wrapper.new(stats, platform)
36
+ tp wrapper.devices(friendly: options.friendly)
37
+ end
38
+ end
39
+
40
+ 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'
45
+
46
+ c.action do |args, options|
47
+ stats = FirebaseStats::Reader.new
48
+ stats.parse_file(args[0])
49
+
50
+ platform = map_platform(options)
51
+
52
+ wrapper = FirebaseStats::Wrapper.new(stats, platform)
53
+
54
+ options.grouped.nil? ? tp(wrapper.os_version) : tp(wrapper.os_grouped)
55
+ end
56
+ end
data/lib/.DS_Store ADDED
Binary file
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'reader'
4
+ require 'wrapper'
data/lib/reader.rb ADDED
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FirebaseStats
4
+ require 'csv'
5
+
6
+ # Parses the Firebase CSV file into sections
7
+ class Reader
8
+ attr_reader :data
9
+
10
+ def initialize
11
+ super
12
+ @data = {}
13
+ end
14
+
15
+ # @param [String] filename
16
+ def parse_file(filename)
17
+ lines = File.readlines(filename)
18
+ parse(lines)
19
+ end
20
+
21
+ # @param [Array<String>] input
22
+ def parse(input)
23
+ curr_lines = []
24
+ input.each_with_index do |line, idx|
25
+ curr_lines.append(line) unless comment?(line) || line.strip.empty?
26
+
27
+ if (idx == input.length - 1) || line.strip.empty?
28
+ process_lines curr_lines unless curr_lines.empty?
29
+ curr_lines = []
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ # @param [Array<String>] lines
37
+ def process_lines(lines)
38
+ section = match_header lines[0]
39
+ return if section.nil?
40
+
41
+ parsed = CSV.parse(lines.join, headers: true)
42
+ @data[section] = parsed if @data[section].nil?
43
+ end
44
+
45
+ # @param [String] header
46
+ # @return [Symbol, nil]
47
+ # rubocop:disable Metrics/MethodLength
48
+ def match_header(header)
49
+ mappings = {
50
+ 'Day,28-Day,7-Day,1-Day' => :active_users,
51
+ 'Day,Average engagement time' => :daily_engagement,
52
+ 'Page path and screen class,User engagement,Screen views' => :screens,
53
+ 'Day,Total revenue' => :revenue,
54
+ 'App,Crash-free users' => :crash_free_users,
55
+ 'App,Version,Status' => :version_adoption,
56
+ 'Source,first_open conversions,LTV' => :acquisition,
57
+ 'Date,Week 0,Week 1,Week 2,Week 3,Week 4,Week 5' => :retention_cohorts,
58
+ 'Country ID,Sessions,% Total' => :audience_country,
59
+ 'Device model,Users' => :devices,
60
+ 'OS with version,Users' => :os_version,
61
+ 'Gender,Users' => :gender,
62
+ 'Category,Female,Male' => :gender_age,
63
+ 'Platform,Users' => :platform,
64
+ 'Platform,Users,% Total,User engagement,Total revenue' => :platform_engagement
65
+ }
66
+
67
+ mappings[header.strip]
68
+ end
69
+ # rubocop:enable Metrics/MethodLength
70
+
71
+ # @param [String] line
72
+ def comment?(line)
73
+ line.include?('#')
74
+ end
75
+ end
76
+ end
data/lib/wrapper.rb ADDED
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FirebaseStats
4
+ require 'csv'
5
+ # require 'android/devices'
6
+
7
+ # Transforms the parsed Firebase file into something more user friendly
8
+ class Wrapper
9
+ # @param [FirebaseStats::Reader] stats
10
+ # @param [Symbol] platform One of :all, :ios, :android
11
+ def initialize(stats, platform = :all)
12
+ super()
13
+ @stats = stats
14
+ @platform = platform
15
+ end
16
+
17
+ def os_version
18
+ filtered = filter_os(@stats.data[:os_version], @platform)
19
+
20
+ cleaned = []
21
+ 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)
26
+ }
27
+ end
28
+ cleaned
29
+ end
30
+
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) }
47
+ end
48
+ computed
49
+ end
50
+
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
56
+ end
57
+ total
58
+ end
59
+
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
67
+ }
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
+ end
73
+ cleaned
74
+ end
75
+
76
+ private
77
+
78
+ # @param [CSV::Table] os_data
79
+ # @param [Symbol] platform One of :all, :ios, :android
80
+ def filter_os(os_data, platform)
81
+ case platform
82
+ when :android
83
+ os_data.select { |row| row['OS with version'].downcase.include?('android') }
84
+ when :ios
85
+ os_data.select { |row| row['OS with version'].downcase.include?('ios') }
86
+ else
87
+ os_data
88
+ end
89
+ end
90
+
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
+ def as_percentage(total, value)
110
+ percentage = (value / total) * 100
111
+ if percentage < 0.01
112
+ percentage.round(4)
113
+ else
114
+ percentage.round(2)
115
+ end
116
+ end
117
+
118
+ 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] }
120
+ end
121
+
122
+ 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] }
124
+ end
125
+ end
126
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: firebase-stats
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - chedabob
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-03-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: commander
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: table_print
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ description: A simple h
42
+ email: matt.malone1@gmail.com
43
+ executables:
44
+ - firebase-stats
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - bin/firebase-stats
49
+ - lib/.DS_Store
50
+ - lib/firebase-stats.rb
51
+ - lib/reader.rb
52
+ - lib/wrapper.rb
53
+ homepage: https://rubygems.org/gems/firebase-stats
54
+ licenses:
55
+ - MIT
56
+ metadata: {}
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - "~>"
64
+ - !ruby/object:Gem::Version
65
+ version: '3'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubygems_version: 3.2.3
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: Firebase CSV Stats extractor
76
+ test_files: []