xc_metrics_aggregator 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 +7 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +36 -0
- data/LICENSE +22 -0
- data/README.md +244 -0
- data/Rakefile +2 -0
- data/assets/XCMetricsAggregator.png +0 -0
- data/bin/bundle +114 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/xcmagg +5 -0
- data/lib/xc_metrics_aggregator/crawler.rb +19 -0
- data/lib/xc_metrics_aggregator/formatter/formatter.rb +105 -0
- data/lib/xc_metrics_aggregator/formatter/output_format.rb +10 -0
- data/lib/xc_metrics_aggregator/product.rb +113 -0
- data/lib/xc_metrics_aggregator/service/categories_service.rb +146 -0
- data/lib/xc_metrics_aggregator/service/devices_service.rb +85 -0
- data/lib/xc_metrics_aggregator/service/latest_service.rb +91 -0
- data/lib/xc_metrics_aggregator/service/metrics_service.rb +51 -0
- data/lib/xc_metrics_aggregator/service/percentiles_service.rb +52 -0
- data/lib/xc_metrics_aggregator/structure/structure.rb +17 -0
- data/lib/xc_metrics_aggregator/version.rb +3 -0
- data/lib/xc_metrics_aggregator.rb +125 -0
- data/lib/xcode_metrics_automation.applescript +210 -0
- data/run.sh +5 -0
- data/xc_metrics_aggregator.gemspec +32 -0
- metadata +171 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'etc'
|
2
|
+
require 'json'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
|
6
|
+
module XcMetricsAggregator
|
7
|
+
PRODUCT_PATH = File.join('/', 'Users', Etc.getlogin, 'Library', 'Developer' , 'Xcode', 'Products')
|
8
|
+
|
9
|
+
class Product
|
10
|
+
def metrics_dir
|
11
|
+
Pathname.new(File.join(@path, 'Metrics'))
|
12
|
+
end
|
13
|
+
|
14
|
+
def metrics_file
|
15
|
+
Pathname.new(File.join(metrics_dir, 'AppStore/Metrics.xcmetricsdata'))
|
16
|
+
end
|
17
|
+
|
18
|
+
def bundle_id
|
19
|
+
Pathname.new(@path).basename.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_metrics?
|
23
|
+
File.exists?(metrics_dir)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(path)
|
27
|
+
@path = path
|
28
|
+
end
|
29
|
+
|
30
|
+
def open
|
31
|
+
FileNotFoundException.new("File not Found: #{metrics_file}") unless has_metrics?
|
32
|
+
File.open(metrics_file) do |file|
|
33
|
+
yield JSON.load(file, symbolize_names: true)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def try_to_open
|
39
|
+
return unless has_metrics?
|
40
|
+
File.open(metrics_file) do |file|
|
41
|
+
yield JSON.load(file, symbolize_names: true)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class ProductsService
|
47
|
+
attr_reader :products
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
@products = Dir.glob(PRODUCT_PATH + "/*").map { |dir_path| Product.new dir_path }
|
51
|
+
end
|
52
|
+
|
53
|
+
def targets(bundle_ids=[])
|
54
|
+
if bundle_ids.empty?
|
55
|
+
products
|
56
|
+
else
|
57
|
+
products.select do |product|
|
58
|
+
bundle_ids.include? product.bundle_id.to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def target(bundle_id)
|
64
|
+
if bundle_id.nil?
|
65
|
+
raise StandardError.new("needs bundle id")
|
66
|
+
end
|
67
|
+
|
68
|
+
products.select { |product| bundle_id == product.bundle_id.to_s }.first
|
69
|
+
end
|
70
|
+
|
71
|
+
def each_product(bundle_ids=[])
|
72
|
+
targets(bundle_ids).each do |product|
|
73
|
+
yield product
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def structure(available_path)
|
78
|
+
structure = XcMetricsAggregator::TableStructure.new
|
79
|
+
structure.title = "available app list"
|
80
|
+
structure.headings = headings(available_path)
|
81
|
+
structure.rows = rows(available_path)
|
82
|
+
structure
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
def headings(available_path)
|
87
|
+
if available_path
|
88
|
+
['bundle id', 'status', 'raw data path']
|
89
|
+
else
|
90
|
+
['bundle id', 'status']
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
def rows(available_path)
|
96
|
+
rows = []
|
97
|
+
products.each do |product|
|
98
|
+
status =
|
99
|
+
if product.has_metrics?
|
100
|
+
"has metrics"
|
101
|
+
else
|
102
|
+
"fail to get metrics"
|
103
|
+
end
|
104
|
+
if available_path
|
105
|
+
rows << [product.bundle_id, status, product.metrics_file.to_s]
|
106
|
+
else
|
107
|
+
rows << [product.bundle_id, status]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
return rows
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'xc_metrics_aggregator/structure/structure'
|
2
|
+
|
3
|
+
module XcMetricsAggregator
|
4
|
+
class CategoriesService
|
5
|
+
attr_reader :device_service, :percentile_service
|
6
|
+
|
7
|
+
def initialize(bundle_id, json)
|
8
|
+
@json = json
|
9
|
+
@bundle_id = bundle_id
|
10
|
+
@device_service = DevicesService.new bundle_id, json
|
11
|
+
@percentile_service = PercentilesService.new bundle_id, json
|
12
|
+
end
|
13
|
+
|
14
|
+
def structure
|
15
|
+
structure = XcMetricsAggregator::TableStructure.new
|
16
|
+
structure.title = @bundle_id
|
17
|
+
structure.headings = headings
|
18
|
+
structure.rows = rows
|
19
|
+
structure
|
20
|
+
end
|
21
|
+
|
22
|
+
def categories
|
23
|
+
@json["categories"].map { |json| Category.new json }
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_dataset(section_name, device, percentile)
|
27
|
+
section = categories.map { |category| category.sections.find { |section| section.display_name == section_name } }.first
|
28
|
+
dataset = section.datasets.find do |dataset|
|
29
|
+
dataset.filter_criteria.device == device.identifier \
|
30
|
+
&& dataset.filter_criteria.percentile == percentile.identifier
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def get_available_dataset(section_name, device, percentile)
|
36
|
+
section = categories.map { |category|
|
37
|
+
category.sections.find { |section|
|
38
|
+
section.display_name == section_name
|
39
|
+
}
|
40
|
+
}.flatten.compact.first
|
41
|
+
if section.nil?
|
42
|
+
return nil
|
43
|
+
end
|
44
|
+
|
45
|
+
datasets = section.datasets.select do |dataset|
|
46
|
+
available_device = device ? dataset.filter_criteria.device == device.identifier : true
|
47
|
+
available_percentile = percentile ? dataset.filter_criteria.percentile == percentile.identifier : true
|
48
|
+
available_device && available_percentile && !dataset.points.empty?
|
49
|
+
end.last
|
50
|
+
datasets
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_section(section_name)
|
54
|
+
section = categories.map { |category|
|
55
|
+
category.sections.find { |section|
|
56
|
+
section.display_name == section_name
|
57
|
+
}
|
58
|
+
}.flatten.compact.first
|
59
|
+
section
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def rows
|
64
|
+
categories.map do |category|
|
65
|
+
[
|
66
|
+
category.display_name,
|
67
|
+
category.sections.map{ |s| s.display_name }.join("\n"),
|
68
|
+
category.sections.map{ |s| s.unit.display_name }.join("\n")
|
69
|
+
]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def headings
|
74
|
+
["category", "section", "unit"]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
module XcMetricsAggregator
|
81
|
+
class Category
|
82
|
+
attr_accessor :sections, :display_name, :identifier
|
83
|
+
|
84
|
+
def initialize(json)
|
85
|
+
@sections = json["sections"].map { |section| Section.new section }
|
86
|
+
@display_name = json["displayName"]
|
87
|
+
@identifier = json["identifier"]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Section
|
92
|
+
attr_accessor :datasets, :display_name, :identifier, :unit
|
93
|
+
|
94
|
+
def initialize(json)
|
95
|
+
@datasets = json["datasets"].map { |d| DataSet.new d }
|
96
|
+
@unit = Unit.new json["unit"]
|
97
|
+
@display_name = json["displayName"]
|
98
|
+
@identifier = json["identifier"]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Unit
|
103
|
+
attr_accessor :display_name, :identifier
|
104
|
+
|
105
|
+
def initialize(json)
|
106
|
+
@display_name = json["displayName"]
|
107
|
+
@identifier = json["identifier"]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class FilterCriteria
|
112
|
+
attr_accessor :device, :percentile
|
113
|
+
|
114
|
+
def initialize(json)
|
115
|
+
@device = json["device"]
|
116
|
+
@percentile = json["percentile"]
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.new_from_prop(device, percentile)
|
120
|
+
self.new({"device": device.identifier, "percentile": percentile.identifier})
|
121
|
+
end
|
122
|
+
|
123
|
+
def ==(other)
|
124
|
+
device == other.device && percentile == other.percentile
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class Point
|
129
|
+
attr_accessor :summary, :version, :percentage_breakdown
|
130
|
+
|
131
|
+
def initialize(json)
|
132
|
+
@summary = json["summary"]
|
133
|
+
@version = json["version"]
|
134
|
+
@percentage_breakdown = json["percentageBreakdown"]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class DataSet
|
139
|
+
attr_accessor :points, :filter_criteria
|
140
|
+
|
141
|
+
def initialize(json)
|
142
|
+
@points = json["points"].map { |p| Point.new p }
|
143
|
+
@filter_criteria = FilterCriteria.new json["filterCriteria"]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'xc_metrics_aggregator/structure/structure'
|
2
|
+
|
3
|
+
module XcMetricsAggregator
|
4
|
+
class Device
|
5
|
+
attr_accessor :is_represented, :display_name, :identifier
|
6
|
+
|
7
|
+
def initialize(json)
|
8
|
+
@is_represented = json["isRepresented"]
|
9
|
+
@display_name = json["displayName"]
|
10
|
+
@identifier = json["identifier"]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class DeviceFamily
|
15
|
+
attr_accessor :is_represented, :display_name, :identifier, :devices
|
16
|
+
|
17
|
+
def initialize(json)
|
18
|
+
@is_represented = json["isRepresented"]
|
19
|
+
@display_name = json["displayName"]
|
20
|
+
@identifier = json["identifier"]
|
21
|
+
@devices = json["devices"].map { |json| Device.new json }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
class DevicesService
|
27
|
+
def initialize(bundle_id, json)
|
28
|
+
@json = json
|
29
|
+
@bundle_id = bundle_id
|
30
|
+
end
|
31
|
+
|
32
|
+
def devicefamilies
|
33
|
+
device_families_json = @json["filterCriteriaSets"]["deviceFamilies"]
|
34
|
+
device_families_json.map do |device_family_json|
|
35
|
+
DeviceFamily.new device_family_json
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def structure
|
40
|
+
structure = XcMetricsAggregator::TableStructure.new
|
41
|
+
structure.title = @bundle_id
|
42
|
+
structure.headings = headings()
|
43
|
+
structure.rows = rows()
|
44
|
+
structure
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_device(identifier)
|
48
|
+
if identifier.nil?
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
device = devicefamilies.map do |devicefamily|
|
53
|
+
if devicefamily.identifier == identifier
|
54
|
+
return devicefamily
|
55
|
+
end
|
56
|
+
|
57
|
+
devicefamily.devices.select do |device|
|
58
|
+
device.identifier == identifier
|
59
|
+
end
|
60
|
+
end.flatten.first
|
61
|
+
|
62
|
+
device
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def rows
|
67
|
+
rows = []
|
68
|
+
devicefamilies.each_with_index do |devicefamily, idx|
|
69
|
+
device_display_names = devicefamily.devices.map{ |d| d.display_name }.join("\n")
|
70
|
+
device_identifiers = devicefamily.devices.map{ |d| d.identifier }.join("\n")
|
71
|
+
row = [devicefamily.display_name, device_display_names, device_identifiers]
|
72
|
+
rows += if idx == devicefamilies.count - 1
|
73
|
+
[row]
|
74
|
+
else
|
75
|
+
[row] + [:separator]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
return rows
|
79
|
+
end
|
80
|
+
|
81
|
+
def headings
|
82
|
+
["kind", "device", "id"]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'xc_metrics_aggregator/structure/structure'
|
2
|
+
|
3
|
+
module XcMetricsAggregator
|
4
|
+
class LatestService
|
5
|
+
def initialize(section, deviceid, percentileid)
|
6
|
+
target_datasets = {}
|
7
|
+
unit_label = ""
|
8
|
+
each_product do |product|
|
9
|
+
product.try_to_open do |json|
|
10
|
+
device = get_device(product, json, deviceid)
|
11
|
+
percentile = get_percentile(product, json, percentileid)
|
12
|
+
|
13
|
+
if device && percentile
|
14
|
+
dataset = get_dataset(product, json, section, device, percentile)
|
15
|
+
else
|
16
|
+
dataset = get_available_dataset(product, json, section, device, percentile)
|
17
|
+
end
|
18
|
+
|
19
|
+
unless dataset.nil? || dataset.points.empty?
|
20
|
+
unit_label = get_unit_label(product, json, section)
|
21
|
+
target_datasets[product.bundle_id] = dataset
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
@unit_label = unit_label
|
26
|
+
@target_datasets = target_datasets
|
27
|
+
end
|
28
|
+
|
29
|
+
def structure
|
30
|
+
structure = XcMetricsAggregator::ChartStructure.new
|
31
|
+
structure.series = series
|
32
|
+
structure.samples = samples
|
33
|
+
structure.unit = @unit_label
|
34
|
+
return structure
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def samples
|
39
|
+
@target_datasets.keys.each_with_index.reduce([]) do |sum, (bundle_id, idx)|
|
40
|
+
dataset = @target_datasets[bundle_id]
|
41
|
+
if dataset.points.empty?
|
42
|
+
sum
|
43
|
+
else
|
44
|
+
latest_point = dataset.points.last
|
45
|
+
sum + [[idx, latest_point.summary]]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def series
|
51
|
+
structure = XcMetricsAggregator::TableStructure.new
|
52
|
+
structure.headings = ["Label", "Bundle ID", "Version"]
|
53
|
+
structure.rows = @target_datasets.keys.each_with_index.map do |bundle_id, idx|
|
54
|
+
latest_point = @target_datasets[bundle_id].points.last
|
55
|
+
[idx, bundle_id, latest_point.version]
|
56
|
+
end
|
57
|
+
structure
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def each_product
|
62
|
+
XcMetricsAggregator::ProductsService.new.each_product do |product|
|
63
|
+
yield product
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_device(product, json, deviceid)
|
68
|
+
DevicesService.new(product.bundle_id, json).get_device deviceid
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_percentile(product, json, percentileid)
|
72
|
+
PercentilesService.new(product.bundle_id, json).get_percentile percentileid
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_category_service(product, json)
|
76
|
+
CategoriesService.new(product.bundle_id, json)
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_dataset(product, json, section, device, percentile)
|
80
|
+
get_category_service(product, json).get_dataset section, device, percentile
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_available_dataset(product, json, section, device, percentile)
|
84
|
+
get_category_service(product, json).get_available_dataset section, device, percentile
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_unit_label(product, json, section)
|
88
|
+
get_category_service(product, json).get_section(section).unit.display_name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'xc_metrics_aggregator/service/categories_service'
|
2
|
+
require 'xc_metrics_aggregator/structure/structure'
|
3
|
+
|
4
|
+
module XcMetricsAggregator
|
5
|
+
class MetricsService
|
6
|
+
def initialize(bundle_id, json)
|
7
|
+
@bundle_id = bundle_id
|
8
|
+
@category_service = CategoriesService.new bundle_id, json
|
9
|
+
end
|
10
|
+
|
11
|
+
def structures(section_name, device_id, percentile_id, version)
|
12
|
+
rows = []
|
13
|
+
samples = []
|
14
|
+
index = 0
|
15
|
+
datasets(section_name).each do |dataset|
|
16
|
+
device_identifier = dataset.filter_criteria.device
|
17
|
+
validated_device = !device_id || device_identifier == device_id
|
18
|
+
percentile_identifier = dataset.filter_criteria.percentile
|
19
|
+
validated_percentile = !percentile_id || percentile_identifier == percentile_id
|
20
|
+
|
21
|
+
unless validated_device && validated_percentile
|
22
|
+
next
|
23
|
+
end
|
24
|
+
percentile = @category_service.percentile_service.get_percentile dataset.filter_criteria.percentile
|
25
|
+
device = @category_service.device_service.get_device dataset.filter_criteria.device
|
26
|
+
rows += dataset.points.map.with_index(index) { |p, i| [i, p.version, device.display_name, percentile.display_name] }
|
27
|
+
samples += dataset.points.map.with_index(index) { |p, i| [i, p.summary] }
|
28
|
+
index += dataset.points.count
|
29
|
+
end
|
30
|
+
|
31
|
+
table_structure = XcMetricsAggregator::TableStructure.new
|
32
|
+
table_structure.headings = ["Label", "Version", "Device", "Percentile"]
|
33
|
+
table_structure.title = section_name
|
34
|
+
table_structure.rows = rows
|
35
|
+
structure = XcMetricsAggregator::ChartStructure.new
|
36
|
+
structure.series = table_structure
|
37
|
+
structure.unit = @category_service.get_section(section_name).unit.display_name
|
38
|
+
structure.samples = samples
|
39
|
+
|
40
|
+
structure
|
41
|
+
end
|
42
|
+
|
43
|
+
def datasets(section_name)
|
44
|
+
section = @category_service.get_section(section_name)
|
45
|
+
unless section
|
46
|
+
raise StandardError.new("wrong section name")
|
47
|
+
end
|
48
|
+
section.datasets
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'xc_metrics_aggregator/structure/structure'
|
2
|
+
|
3
|
+
module XcMetricsAggregator
|
4
|
+
class Percentile
|
5
|
+
attr_accessor :display_name, :is_represented, :identifier
|
6
|
+
|
7
|
+
def initialize(json)
|
8
|
+
@is_represented = json["isRepresented"]
|
9
|
+
@display_name = json["displayName"]
|
10
|
+
@identifier = json["identifier"]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class PercentilesService
|
15
|
+
def initialize(bundle_id, json)
|
16
|
+
@json = json
|
17
|
+
@bundle_id = bundle_id
|
18
|
+
end
|
19
|
+
|
20
|
+
def percentiles
|
21
|
+
percentiles_json = @json["filterCriteriaSets"]["percentiles"]
|
22
|
+
percentiles_json.map { |percentile_json| Percentile.new percentile_json }
|
23
|
+
end
|
24
|
+
|
25
|
+
def structure
|
26
|
+
structure = XcMetricsAggregator::TableStructure.new
|
27
|
+
structure.title = @bundle_id
|
28
|
+
structure.headings = headings()
|
29
|
+
structure.rows = rows()
|
30
|
+
structure
|
31
|
+
end
|
32
|
+
|
33
|
+
def rows
|
34
|
+
percentiles.map { |percentile| [percentile.display_name, percentile.identifier] }
|
35
|
+
end
|
36
|
+
|
37
|
+
def headings
|
38
|
+
["percentile", "id"]
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_percentile(identifier)
|
42
|
+
if identifier.nil?
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
res = percentiles.find do |percentile|
|
48
|
+
percentile.identifier == identifier
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'xc_metrics_aggregator/formatter/formatter'
|
2
|
+
|
3
|
+
module XcMetricsAggregator
|
4
|
+
class Structure
|
5
|
+
def format(formatter)
|
6
|
+
formatter.format(self)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class TableStructure < Structure
|
11
|
+
attr_accessor :title, :headings, :rows
|
12
|
+
end
|
13
|
+
|
14
|
+
class ChartStructure < Structure
|
15
|
+
attr_accessor :series, :samples, :unit, :dscription
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require "xc_metrics_aggregator/version"
|
2
|
+
require 'xc_metrics_aggregator/product'
|
3
|
+
require 'xc_metrics_aggregator/crawler'
|
4
|
+
require 'xc_metrics_aggregator/service/devices_service'
|
5
|
+
require 'xc_metrics_aggregator/service/percentiles_service'
|
6
|
+
require 'xc_metrics_aggregator/service/categories_service'
|
7
|
+
require 'xc_metrics_aggregator/service/metrics_service'
|
8
|
+
require 'xc_metrics_aggregator/service/latest_service'
|
9
|
+
require 'xc_metrics_aggregator/formatter/formatter'
|
10
|
+
require 'xc_metrics_aggregator/formatter/output_format'
|
11
|
+
require 'thor'
|
12
|
+
require 'ascii_charts'
|
13
|
+
require 'pp'
|
14
|
+
|
15
|
+
module XcMetricsAggregator
|
16
|
+
|
17
|
+
class Error < StandardError; end
|
18
|
+
class CLI < Thor
|
19
|
+
|
20
|
+
desc "crowl", "Aggregate raw data of Xcode Organizer Metrics"
|
21
|
+
def crowl
|
22
|
+
Crawler.execute()
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "apps", "Shows available bundle ids on Xcode Organizer Metrics"
|
26
|
+
option :available_path, type: :boolean, aliases: "-a", default: false
|
27
|
+
option :format, aliases: "-f", type: :string, default: "ascii"
|
28
|
+
def apps
|
29
|
+
service = ProductsService.new
|
30
|
+
puts service.structure(options[:available_path]).format Formatter.get_formatter(format)
|
31
|
+
end
|
32
|
+
|
33
|
+
option :bundle_ids, :aliases => "-b", type: :array, default: []
|
34
|
+
option :format, :aliases => "-f", type: :string, default: "ascii"
|
35
|
+
desc "devices [-b <bundle id 1> <bundle id 2> ...] [-f <format>]", "Show available devices by builde id"
|
36
|
+
def devices
|
37
|
+
each_product do |product|
|
38
|
+
product.try_to_open do |json|
|
39
|
+
puts DevicesService
|
40
|
+
.new(product.bundle_id, json)
|
41
|
+
.structure
|
42
|
+
.format Formatter.get_formatter(format)
|
43
|
+
puts "\n\n"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
option :bundle_ids, :aliases => "-b", type: :array, default: []
|
49
|
+
option :format, :aliases => "-f", type: :string, default: "ascii"
|
50
|
+
desc "percentiles [-b <bundle id>,...] [-f <format>]", "Show available percentiles by builde id"
|
51
|
+
def percentiles
|
52
|
+
each_product do |product|
|
53
|
+
product.try_to_open do |json|
|
54
|
+
puts PercentilesService
|
55
|
+
.new(product.bundle_id, json)
|
56
|
+
.structure
|
57
|
+
.format Formatter.get_formatter(format)
|
58
|
+
puts "\n\n"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
option :bundle_id, :aliases => "-b", require: true
|
65
|
+
option :format, :aliases => "-f", type: :string, default: "ascii"
|
66
|
+
desc "categories -b <bundle id> [-f <format>]", "Show available categories by builde id"
|
67
|
+
def sections
|
68
|
+
product.try_to_open do |json|
|
69
|
+
puts CategoriesService
|
70
|
+
.new(product.bundle_id, json)
|
71
|
+
.structure
|
72
|
+
.format Formatter.get_formatter(format)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
option :section, :aliases => "-s", require: true
|
77
|
+
option :bundle_id, :aliases => "-b", require: true
|
78
|
+
option :format, :aliases => "-f", type: :string, default: "ascii"
|
79
|
+
option :version, :aliases => "-v", require: false
|
80
|
+
option :device, :aliases => "-d", require: false
|
81
|
+
option :percentile, :aliases => "-p", require: false
|
82
|
+
desc "metrics -b <bundle id> -s <section> [-f <format>] [-d <device id>] [-p <percentile id>] [-v <version>]", "Show metrics data to a builde id"
|
83
|
+
def metrics
|
84
|
+
product.try_to_open do |json|
|
85
|
+
structure = MetricsService
|
86
|
+
.new(product.bundle_id, json)
|
87
|
+
.structures(options[:section], options[:device], options[:percentile], options[:version])
|
88
|
+
|
89
|
+
puts structure.format Formatter.get_formatter(format)
|
90
|
+
puts "\n\n"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
option :section, :aliases => "-s", require: true
|
95
|
+
option :device, :aliases => "-d", type: :string
|
96
|
+
option :percentile, :aliases => "-p", type: :string
|
97
|
+
option :format, :aliases => "-f", type: :string, default: "ascii"
|
98
|
+
desc "latest -s <section> [-p <percentile id>] [-d <device id>] [-f <format>]", "Compare a latest version's metrics between available builde ids"
|
99
|
+
def latest
|
100
|
+
deviceid = options[:device]
|
101
|
+
percentileid = options[:percentile]
|
102
|
+
section = options[:section]
|
103
|
+
|
104
|
+
puts LatestService
|
105
|
+
.new(section, deviceid, percentileid)
|
106
|
+
.structure
|
107
|
+
.format Formatter.get_formatter(format)
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
def format
|
112
|
+
OutputFormat.all.find { |v| v == options[:format] }
|
113
|
+
end
|
114
|
+
|
115
|
+
def each_product
|
116
|
+
ProductsService.new.each_product(options[:bundle_ids]) { |product|
|
117
|
+
yield product
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
def product
|
122
|
+
ProductsService.new.target options[:bundle_id]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|