copilot 0.0.7 → 0.0.8
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.
- data/lib/copilot/array_to_csv_converter.rb +2 -21
- data/lib/copilot/requests/builds_for_bundle_id_request.rb +39 -24
- data/lib/copilot/requests/feedback_for_build_id_request.rb +31 -23
- data/lib/copilot/requests/internal/array_limiter.rb +29 -0
- data/lib/copilot/requests/internal/feedback_page.rb +0 -12
- data/lib/copilot/requests/internal/pager.rb +24 -0
- metadata +17 -6
- data/lib/copilot/requests/internal/build_table_row.rb +0 -61
- data/lib/copilot/requests/internal/builds_page.rb +0 -63
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'csv'
|
2
|
+
require 'hash_flattener'
|
2
3
|
|
3
4
|
module CoPilot
|
4
5
|
class ArrayToCsvConverter
|
@@ -19,7 +20,7 @@ module CoPilot
|
|
19
20
|
private
|
20
21
|
|
21
22
|
def rows
|
22
|
-
@rows ||= @array.map { |r| flatten r }
|
23
|
+
@rows ||= @array.map { |r| HashFlattener.flatten r }
|
23
24
|
end
|
24
25
|
|
25
26
|
def columns
|
@@ -47,25 +48,5 @@ module CoPilot
|
|
47
48
|
keys.sort
|
48
49
|
end
|
49
50
|
|
50
|
-
def flatten(enumerable)
|
51
|
-
res = {}
|
52
|
-
|
53
|
-
enumerable.each_with_index do |elem, i|
|
54
|
-
if elem.is_a?(Array)
|
55
|
-
k, v = elem
|
56
|
-
else
|
57
|
-
k, v = i, elem
|
58
|
-
end
|
59
|
-
|
60
|
-
if v.is_a? Enumerable
|
61
|
-
res.merge!(flatten k)
|
62
|
-
else
|
63
|
-
res[k] = v
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
res
|
68
|
-
end
|
69
|
-
|
70
51
|
end
|
71
52
|
end
|
@@ -1,5 +1,6 @@
|
|
1
|
-
|
2
|
-
require_relative 'internal/
|
1
|
+
require 'time'
|
2
|
+
require_relative 'internal/array_limiter'
|
3
|
+
require_relative 'internal/pager'
|
3
4
|
|
4
5
|
module CoPilot
|
5
6
|
class BuildsForBundleIdRequest
|
@@ -9,45 +10,59 @@ module CoPilot
|
|
9
10
|
end
|
10
11
|
|
11
12
|
def send(bundle_id, options={})
|
12
|
-
@
|
13
|
-
@since = options[:since]
|
14
|
-
@builds_page = BuildsPage.new @browser, bundle_id
|
15
|
-
@builds_page.goto
|
16
|
-
@builds_page.more_builds
|
13
|
+
@bundle_id = bundle_id
|
17
14
|
@builds = []
|
15
|
+
@builds_limiter = create_builds_limiter options
|
16
|
+
@pager = Pager.new @browser
|
17
|
+
go_to_builds_page
|
18
18
|
add_builds_for_each_page
|
19
|
-
remove_duplicate_builds
|
20
|
-
strip_excess_builds
|
21
19
|
@builds
|
22
20
|
end
|
23
21
|
|
24
22
|
private
|
25
23
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
24
|
+
def create_builds_limiter(options={})
|
25
|
+
l = ArrayLimiter.new @builds, max_number: options[:limit], max_time: options[:since]
|
26
|
+
l.time_lambda = lambda { |b| b[:added_date] }
|
27
|
+
l.limit_reached_lambda = lambda { @limit_reached = true }
|
28
|
+
l
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
|
33
|
-
@
|
31
|
+
def go_to_builds_page
|
32
|
+
@browser.goto 'https://www.testflightapp.com/dashboard/applications'
|
33
|
+
@browser.small(text: @bundle_id).click
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_builds_for_each_page
|
37
|
+
until limit_reached? || no_more_builds? do
|
38
|
+
add_builds
|
39
|
+
end
|
34
40
|
end
|
35
41
|
|
36
|
-
def
|
37
|
-
@
|
42
|
+
def limit_reached?
|
43
|
+
@limit_reached ||= false
|
38
44
|
end
|
39
45
|
|
40
|
-
def
|
41
|
-
|
42
|
-
@builds.select! { |b| b[:added_date] > @since } if @since
|
46
|
+
def no_more_builds?
|
47
|
+
!@pager.next_page
|
43
48
|
end
|
44
49
|
|
45
|
-
def
|
46
|
-
|
50
|
+
def add_builds
|
51
|
+
rows = @browser.trs(class: 'goversion pointer')
|
52
|
+
rows.each { |row| @builds_limiter.add create_build(row) }
|
47
53
|
end
|
48
54
|
|
49
|
-
def
|
50
|
-
|
55
|
+
def create_build(row)
|
56
|
+
{
|
57
|
+
id: row.id.split('/').last,
|
58
|
+
version: row[0].text,
|
59
|
+
added_date: Time.parse(row[1].text),
|
60
|
+
built_for: row[2].text,
|
61
|
+
sdk: row[3].text,
|
62
|
+
crashes: row[4].text.to_i,
|
63
|
+
feedback: row[5].text.to_i,
|
64
|
+
installs: row[6].text.to_i
|
65
|
+
}
|
51
66
|
end
|
52
67
|
|
53
68
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'internal/feedback_page'
|
2
|
+
require_relative 'internal/array_limiter'
|
2
3
|
|
3
4
|
module CoPilot
|
4
5
|
class FeedbackForBuildIdRequest
|
@@ -8,49 +9,56 @@ module CoPilot
|
|
8
9
|
end
|
9
10
|
|
10
11
|
def send(build_id, options={})
|
11
|
-
@limit = options[:limit]
|
12
|
-
@since = options[:since]
|
13
12
|
@feedback = []
|
13
|
+
@feedback_limiter = create_feedback_limiter options
|
14
14
|
@feedback_page = FeedbackPage.new @browser, build_id
|
15
|
+
@pager = Pager.new @browser
|
15
16
|
@feedback_page.goto
|
16
17
|
add_feedback_for_each_page
|
17
|
-
strip_excess_feedback
|
18
18
|
@feedback
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
+
def create_feedback_limiter(options={})
|
24
|
+
l = ArrayLimiter.new @feedback, max_number: options[:limit], max_time: options[:since]
|
25
|
+
l.time_lambda = lambda { |f| f[:date] }
|
26
|
+
l.limit_reached_lambda = lambda { @limit_reached = true }
|
27
|
+
l
|
28
|
+
end
|
29
|
+
|
23
30
|
def add_feedback_for_each_page
|
24
31
|
add_feedback
|
25
|
-
|
32
|
+
until limit_reached? || no_more_feedback? do
|
33
|
+
add_feedback
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def limit_reached?
|
38
|
+
@limit_reached ||= false
|
39
|
+
end
|
40
|
+
|
41
|
+
def no_more_feedback?
|
42
|
+
!@pager.next_page
|
26
43
|
end
|
27
44
|
|
28
45
|
def add_feedback
|
29
46
|
@feedback_page.expand_details
|
30
47
|
@feedback_page.each_summary_detail_pair do |summary, detail|
|
31
48
|
main_feedback_table = detail.table(class: 'mainfeedback')
|
32
|
-
@
|
33
|
-
user: summary.tds[0].text,
|
34
|
-
date: Time.parse(summary.tds[1].text),
|
35
|
-
subject: summary.tds[2].text,
|
36
|
-
replies: summary.tds[3].text.to_i,
|
37
|
-
via: main_feedback_table.ps[0].text,
|
38
|
-
message: main_feedback_table.ps[1].text
|
39
|
-
}
|
49
|
+
@feedback_limiter.add create_feedback(summary, main_feedback_table)
|
40
50
|
end
|
41
51
|
end
|
42
52
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
def age_reached
|
53
|
-
@since && @feedback.last[:date] > @since
|
53
|
+
def create_feedback(summary, main_feedback_table)
|
54
|
+
{
|
55
|
+
user: summary.tds[0].text,
|
56
|
+
date: Time.parse(summary.tds[1].text),
|
57
|
+
subject: summary.tds[2].text,
|
58
|
+
replies: summary.tds[3].text.to_i,
|
59
|
+
via: main_feedback_table.ps[0].text,
|
60
|
+
message: main_feedback_table.ps[1].text
|
61
|
+
}
|
54
62
|
end
|
55
63
|
|
56
64
|
def b
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CoPilot
|
2
|
+
class ArrayLimiter
|
3
|
+
|
4
|
+
attr_accessor :time_lambda, :limit_reached_lambda
|
5
|
+
|
6
|
+
def initialize(array, options={})
|
7
|
+
@array = array
|
8
|
+
@max_number = options[:max_number]
|
9
|
+
@max_time = options[:max_time]
|
10
|
+
@time_lambda = lambda { |v| v[:time] }
|
11
|
+
@limit_reached_lambda = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(value)
|
15
|
+
time = @time_lambda.call value
|
16
|
+
limit_reached = max_number_reached?(@array.length) || max_time_reached?(time)
|
17
|
+
limit_reached ? @limit_reached_lambda.call : @array << value
|
18
|
+
end
|
19
|
+
|
20
|
+
def max_number_reached?(number)
|
21
|
+
@max_number && number >= @max_number
|
22
|
+
end
|
23
|
+
|
24
|
+
def max_time_reached?(time)
|
25
|
+
@max_time && time > @max_time
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -19,20 +19,8 @@ module CoPilot
|
|
19
19
|
summary_rows.to_a.each_index { |i| yield [summary_rows[i], detail_rows[i]] }
|
20
20
|
end
|
21
21
|
|
22
|
-
def next_page
|
23
|
-
link = next_page_link
|
24
|
-
return false unless link
|
25
|
-
link.click
|
26
|
-
true
|
27
|
-
end
|
28
|
-
|
29
22
|
private
|
30
23
|
|
31
|
-
def next_page_link
|
32
|
-
links = b.as(title: 'Next Page')
|
33
|
-
links.length == 1 ? links[0] : nil
|
34
|
-
end
|
35
|
-
|
36
24
|
def summary_detail_row_pairs
|
37
25
|
pairs = []
|
38
26
|
summary_rows.to_a.each_index { |i| pairs << [summary_rows[i], detail_rows[i]] }
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module CoPilot
|
2
|
+
class Pager
|
3
|
+
|
4
|
+
def initialize(browser)
|
5
|
+
@browser = browser
|
6
|
+
end
|
7
|
+
|
8
|
+
def next_page
|
9
|
+
link = next_page_link
|
10
|
+
return false unless link
|
11
|
+
link.click
|
12
|
+
sleep 1
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def next_page_link
|
19
|
+
links = @browser.as(title: 'Next Page')
|
20
|
+
links.length == 1 ? links[0] : nil
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: copilot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: hash_flattener
|
16
|
+
requirement: &70291170643440 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70291170643440
|
14
25
|
- !ruby/object:Gem::Dependency
|
15
26
|
name: watir-webdriver
|
16
|
-
requirement: &
|
27
|
+
requirement: &70291170638840 !ruby/object:Gem::Requirement
|
17
28
|
none: false
|
18
29
|
requirements:
|
19
30
|
- - ! '>='
|
@@ -21,7 +32,7 @@ dependencies:
|
|
21
32
|
version: '0'
|
22
33
|
type: :runtime
|
23
34
|
prerelease: false
|
24
|
-
version_requirements: *
|
35
|
+
version_requirements: *70291170638840
|
25
36
|
description:
|
26
37
|
email: matthew-github@matthewriley.name
|
27
38
|
executables: []
|
@@ -33,9 +44,9 @@ files:
|
|
33
44
|
- lib/copilot/facade.rb
|
34
45
|
- lib/copilot/requests/builds_for_bundle_id_request.rb
|
35
46
|
- lib/copilot/requests/feedback_for_build_id_request.rb
|
36
|
-
- lib/copilot/requests/internal/
|
37
|
-
- lib/copilot/requests/internal/builds_page.rb
|
47
|
+
- lib/copilot/requests/internal/array_limiter.rb
|
38
48
|
- lib/copilot/requests/internal/feedback_page.rb
|
49
|
+
- lib/copilot/requests/internal/pager.rb
|
39
50
|
- lib/copilot/requests.rb
|
40
51
|
- lib/copilot.rb
|
41
52
|
homepage: http://rubygems.org/gems/copilot
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'time'
|
2
|
-
|
3
|
-
module CoPilot
|
4
|
-
class BuildTableRow
|
5
|
-
|
6
|
-
def initialize(tr)
|
7
|
-
@tr = tr
|
8
|
-
end
|
9
|
-
|
10
|
-
def build_id
|
11
|
-
@tr.id.split('/').last
|
12
|
-
end
|
13
|
-
|
14
|
-
def version
|
15
|
-
@tr[1].text
|
16
|
-
end
|
17
|
-
|
18
|
-
def added_date
|
19
|
-
Time.parse @tr[2].text
|
20
|
-
end
|
21
|
-
|
22
|
-
def built_for
|
23
|
-
@tr[3].text
|
24
|
-
end
|
25
|
-
|
26
|
-
def size
|
27
|
-
@tr[4].text
|
28
|
-
end
|
29
|
-
|
30
|
-
def sdk
|
31
|
-
@tr[5].text
|
32
|
-
end
|
33
|
-
|
34
|
-
def crashes
|
35
|
-
@tr[6].text.to_i
|
36
|
-
end
|
37
|
-
|
38
|
-
def feedback
|
39
|
-
@tr[7].text.to_i
|
40
|
-
end
|
41
|
-
|
42
|
-
def installs
|
43
|
-
@tr[8].text.to_i
|
44
|
-
end
|
45
|
-
|
46
|
-
def to_hash
|
47
|
-
{
|
48
|
-
id: build_id,
|
49
|
-
version: version,
|
50
|
-
added_date: added_date,
|
51
|
-
built_for: built_for,
|
52
|
-
size: size,
|
53
|
-
sdk: sdk,
|
54
|
-
crashes: crashes,
|
55
|
-
feedback: feedback,
|
56
|
-
installs: installs
|
57
|
-
}
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
module CoPilot
|
2
|
-
class BuildsPage
|
3
|
-
|
4
|
-
def initialize(browser, bundle_id)
|
5
|
-
@browser = browser
|
6
|
-
@bundle_id = bundle_id
|
7
|
-
end
|
8
|
-
|
9
|
-
def goto
|
10
|
-
b.goto 'https://www.testflightapp.com/dashboard/builds'
|
11
|
-
end
|
12
|
-
|
13
|
-
def header_div
|
14
|
-
@header_div ||= header_divs[bundle_index]
|
15
|
-
end
|
16
|
-
|
17
|
-
def builds_table
|
18
|
-
@builds_table ||= header_div.parent.tables[bundle_index]
|
19
|
-
end
|
20
|
-
|
21
|
-
def builds_table_rows
|
22
|
-
builds_table.trs(id: /\/dashboard\/builds\/report\/\d+\//)
|
23
|
-
end
|
24
|
-
|
25
|
-
def next_page
|
26
|
-
link = next_page_link
|
27
|
-
return false unless link
|
28
|
-
link.click
|
29
|
-
true
|
30
|
-
end
|
31
|
-
|
32
|
-
def more_builds_link
|
33
|
-
builds_table.as(text: 'more')[0]
|
34
|
-
end
|
35
|
-
|
36
|
-
def more_builds
|
37
|
-
link = more_builds_link
|
38
|
-
return unless link
|
39
|
-
link.click
|
40
|
-
sleep 2
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def next_page_link
|
46
|
-
links = builds_table.as(title: 'Next Page')
|
47
|
-
links.length == 1 ? links[0] : nil
|
48
|
-
end
|
49
|
-
|
50
|
-
def header_divs
|
51
|
-
@header_divs ||= b.divs(class: 'section-header-build')
|
52
|
-
end
|
53
|
-
|
54
|
-
def bundle_index
|
55
|
-
@bundle_index ||= header_divs.to_a.index { |div| div.li.text == @bundle_id }
|
56
|
-
end
|
57
|
-
|
58
|
-
def b
|
59
|
-
@browser
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
end
|