firebase-stats 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []