copilot 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|