fire_watch 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf637f8c3306b80859e71da69845909e8857fa5e
4
- data.tar.gz: b66b910931b402ba8415da646c12fe3974c86418
3
+ metadata.gz: 9318eee1f045e2414c745fad818fd230c62e1076
4
+ data.tar.gz: 2a0630f217ece84f8abf28b9c694998769885d71
5
5
  SHA512:
6
- metadata.gz: cc64f6f3f9f5f8bf5e210254f507be032645eb3cfd9cd48b918afe2f53817861485ae39c4ab16addb3a3a1e30c58b9f64141b7e75d0ecaa7fdd24532f839f2fb
7
- data.tar.gz: 792fa2e234b0192fa7e8843caee140c4d210a582a6c4fa081fdbb9729dbb4b52cea5d6e25d85876bd5998c036ed7191936bf9844c05bfa5e13b778469e67b812
6
+ metadata.gz: 17ace434edd54fbd83e7d7ce72f627d638fbd1d55e132f80e1b5e532764c1f867ed8af595b632241068fb8a425a190823db33107786be9367b69d804071ea0e7
7
+ data.tar.gz: 1f3bdbaeb09eed00c3c7286068ecf3310fe497240265eba4f2dcb3d35c35935d7d5a411197d7d0267db4c39426f191f9ce18d0dea09063aad462a8847ef836dd
@@ -29,10 +29,11 @@ Gem::Specification.new do |spec|
29
29
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
30
  spec.require_paths = ["lib"]
31
31
 
32
- spec.add_runtime_dependency "harvested", "~> 3.1"
32
+ spec.add_runtime_dependency "harvested", "~> 4.0"
33
33
  spec.add_runtime_dependency "highline", "~> 1.7"
34
34
  spec.add_runtime_dependency "github_api", "~> 0.17"
35
35
  spec.add_runtime_dependency "ruby-progressbar", "~>1.8"
36
+ spec.add_runtime_dependency 'parallel', '1.12.0'
36
37
 
37
38
  spec.add_development_dependency "bundler", "~> 1.15"
38
39
  spec.add_development_dependency "rake", "~> 10.0"
@@ -1,4 +1,5 @@
1
1
  require "fire_watch/version"
2
+ require "fire_watch/report_builder"
2
3
 
3
4
  module FireWatch
4
5
  # Your code goes here...
@@ -0,0 +1,93 @@
1
+ require 'parallel'
2
+ module FireWatch
3
+ class ReportBuilder
4
+ ISSUE_REGEX = /.?#(\d+)/
5
+ attr_reader :github_client, :harvest_client, :github_repos, :harvest_clients, :harvest_projects, :harvest_users, :time_entries
6
+
7
+ def initialize(github_client:, harvest_client:)
8
+ @github_client = github_client
9
+ @harvest_client = harvest_client
10
+ @github_repos, @harvest_clients, @harvest_projects, @harvest_users = Parallel.map([
11
+ @github_client.repos.list(org: 'wildland').sort_by{|r| r.name},
12
+ @harvest_client.clients.all,
13
+ @harvest_client.projects.all,
14
+ @harvest_client.users.all
15
+ ]) {|j| j}
16
+ end
17
+
18
+ def issue_pull_request_report(selected_github_repo:, selected_github_milestones:, selected_harvest_projects:)
19
+ @time_entries = Parallel.map(
20
+ selected_harvest_projects.select{|p| !p.starts_on.nil?}.map do |project|
21
+ @harvest_client.reports.time_by_project(
22
+ project,
23
+ Time.parse(project.starts_on),
24
+ project.ends_on.nil? ? Time.now : Time.parse(project.ends_on)
25
+ )
26
+ end
27
+ ){|j| j}
28
+ @time_entries.flatten!
29
+ github_report = []
30
+
31
+ selected_github_milestones.each do |selected_milestone|
32
+ issues, pull_request_issues = @github_client.issues.list(user: 'wildland', repo: selected_github_repo.name, milestone: selected_milestone.number, state: 'all').partition{|i| i.pull_request == nil}
33
+ pull_requests = pull_request_issues.map do |i|
34
+ @github_client.pull_requests.get(user: 'wildland', repo: selected_github_repo.name, number: i.number).body
35
+ end
36
+
37
+ issues.each do |issue|
38
+ github_report << report_line(
39
+ issue: issue,
40
+ pull_request: nil,
41
+ selected_github_repo: selected_github_repo,
42
+ selected_github_milestone: selected_milestone
43
+ )
44
+ end
45
+
46
+ pull_requests.each do |pr|
47
+ github_report << report_line(
48
+ issue: pr,
49
+ pull_request: pr,
50
+ selected_github_repo: selected_github_repo,
51
+ selected_github_milestone: selected_milestone
52
+ )
53
+ end
54
+ end
55
+
56
+ return github_report, @time_entries.map do |e|
57
+ {
58
+ time_entry: e,
59
+ harvest_user: @harvest_users.find{|u| u.id == e.user_id}
60
+ }
61
+ end # TODO format time_entries that are missing
62
+ end
63
+
64
+ def report_line(issue:, pull_request:, selected_github_repo:, selected_github_milestone:)
65
+ github_report_line = {
66
+ github_repo: selected_github_repo,
67
+ github_milestone: selected_github_milestone,
68
+ github_estimated_size_label: !issue.labels.nil? && !issue.labels.find{|l| l.name.include?"size:"}.nil? ? issue.labels.find{|l| l.name.include?"size:"} : nil,
69
+ issue: issue,
70
+ pull_request: pull_request,
71
+ harvest_time_entries: []
72
+ }
73
+ relevant_time_entries, @time_entries = @time_entries.partition do |entry|
74
+ entry.notes.nil? ? false : entry.notes.scan(ISSUE_REGEX).flatten.any?{|m| m.eql? github_report_line[:issue].number.to_s }
75
+ end
76
+ if !relevant_time_entries.empty?
77
+ relevant_time_entries.chunk{|i| i.user_id }.each do |user_id, user_time_entries|
78
+ user = harvest_users.find{|u| u.id == user_id}
79
+ github_report_line[:harvest_time_entries] << {
80
+ harvest_user: user,
81
+ time_entries: user_time_entries,
82
+ harvest_projects: @harvest_projects.select{|p| user_time_entries.map(&:project_id).uniq.include? p.id}
83
+ }
84
+ end
85
+
86
+ @time_entries.concat relevant_time_entries.select { |entry|
87
+ entry.notes.nil? ? false : entry.notes.scan(ISSUE_REGEX).flatten.count > 1
88
+ }
89
+ end
90
+ return github_report_line
91
+ end
92
+ end
93
+ end
@@ -5,36 +5,41 @@ require 'ruby-progressbar'
5
5
 
6
6
  module FireWatch
7
7
  class Runner
8
- ISSUE_REGEX = /.?#(\d+)/
9
8
  def self.invoke
10
9
 
11
10
  cli = HighLine.new
12
11
  # Github Client
13
12
  github_login = cli.ask('Github Login: ')
14
13
  github_password = cli.ask('Github Password: ') { |q| q.echo = 'x' }
15
- @github_client = Github.new(auto_pagination: true) do |config|
14
+ github_client = Github.new(auto_pagination: true) do |config|
16
15
  config.basic_auth = "#{github_login}:#{github_password}"
17
16
  if cli.agree("Do you use Two-Factor authentication (non-sms)?")
18
17
  config.connection_options = { headers: {"X-GitHub-OTP" => cli.ask('Two-Factor Code')} }
19
18
  end
20
19
  end
21
20
 
22
- puts "Fetching Repos from Github..."
23
- repos = @github_client.repos.list(org: 'wildland').sort_by{|r| r.name}
21
+ # Harvest Client
22
+ harvest_username = cli.ask('Harvest Username: ')
23
+ harvest_password = cli.ask('Harvest Password: ') { |q| q.echo = 'x' }
24
+ harvest_client = Harvest.client(username: harvest_username, password: harvest_password, subdomain: 'wildland')
25
+
26
+ @report_builder = ReportBuilder.new(
27
+ github_client: github_client,
28
+ harvest_client: harvest_client
29
+ )
24
30
 
25
31
 
26
32
  selected_repo = nil
27
33
  cli.choose do |menu|
28
34
  menu.prompt = "Select Github Repo:"
29
- repos.each do |r|
35
+ @report_builder.github_repos.each do |r|
30
36
  menu.choice(r.name) do
31
37
  selected_repo = r
32
38
  end
33
39
  end
34
40
  end
35
41
 
36
- puts "Fetching Milestones from Github..."
37
- milestones = @github_client.issues.milestones.list(user: 'wildland',repo: selected_repo.name, state: 'all').sort_by{|m| m.title}
42
+ milestones = @report_builder.github_client.issues.milestones.list(user: 'wildland',repo: selected_repo.name, state: 'all').sort_by{|m| m.title}
38
43
 
39
44
  selected_milestones = []
40
45
  done = false
@@ -55,15 +60,6 @@ module FireWatch
55
60
  break if done
56
61
  end
57
62
 
58
- # Harvest Client
59
- harvest_username = cli.ask('Harvest Username: ')
60
- harvest_password = cli.ask('Harvest Password: ') { |q| q.echo = 'x' }
61
- @harvest_client = Harvest.client(username: harvest_username, password: harvest_password, subdomain: 'wildland')
62
-
63
- puts "Fetching Clients and Projects from Harvest..."
64
- clients = @harvest_client.clients.all
65
- projects = @harvest_client.projects.all
66
-
67
63
  selected_projects = []
68
64
  done = false
69
65
  show_all = false
@@ -71,8 +67,8 @@ module FireWatch
71
67
  cli.choose do |menu|
72
68
  menu.prompt = "Add/Remove Project (#{selected_projects.map(&:name).join(', ')}): "
73
69
  menu.choice("Done Adding Projects") { done = true } if selected_projects.count > 0
74
- clients.sort_by(&:name).each do |client|
75
- projects.select{|p| p.client_id == client.id}.sort_by(&:name).each do |p|
70
+ @report_builder.harvest_clients.sort_by(&:name).each do |client|
71
+ @report_builder.harvest_projects.select{|p| p.client_id == client.id}.sort_by(&:name).each do |p|
76
72
  next unless show_all || p.active
77
73
  menu.choice("#{client.name} - #{p.name}") do
78
74
  if selected_projects.include? p
@@ -92,29 +88,16 @@ module FireWatch
92
88
  break if done
93
89
  end
94
90
 
95
- puts "Fetching Users from Harvest..."
96
- harvest_users = @harvest_client.users.all
97
-
98
- time_entries = []
99
-
100
- selected_projects.each do |project|
101
- if project.starts_on.nil?
102
- puts "No start date for #{project.name}"
103
- next
104
- end
105
- time_entries += @harvest_client.reports.time_by_project(
106
- project,
107
- Time.parse(project.starts_on),
108
- project.ends_on.nil? ? Time.now : Time.parse(project.ends_on)
109
- )
110
- end
111
-
112
91
  #Process Data
113
92
  csv_file_name = cli.ask('File name for report?').concat('.csv')
114
93
 
115
94
  puts "Generating Report with " + selected_milestones.map(&:title).join(', ') + " vs " + selected_projects.map(&:name).join(', ')
116
95
  puts "Saving to #{csv_file_name}"
117
- progressbar = ProgressBar.create(format: "%a %b\u{15E7}%i %p%% %t", progress_mark: ' ', remainder_mark: "\u{FF65}", starting_at: 10)
96
+ github_report, missing_harvest_time_entries = @report_builder.issue_pull_request_report(
97
+ selected_github_repo: selected_repo,
98
+ selected_github_milestones: selected_milestones,
99
+ selected_harvest_projects: selected_projects
100
+ )
118
101
  CSV.open(csv_file_name, "wb", force_quotes: true) do |csv|
119
102
  csv << [
120
103
  "Repo",
@@ -130,102 +113,42 @@ module FireWatch
130
113
  "Labels"
131
114
  ]
132
115
 
133
- selected_milestones.each do |selected_milestone|
134
- issues, pull_request_issues = @github_client.issues.list(user: 'wildland', repo: selected_repo.name, milestone: selected_milestone.number, state: 'all').partition{|i| i.pull_request == nil}
135
- pull_requests = pull_request_issues.map do |i|
136
- @github_client.pull_requests.get(user: 'wildland', repo: selected_repo.name, number: i.number).body
137
- end
138
-
139
- issues.each do |issue|
140
- relevant_time_entries, time_entries = time_entries.partition do |entry|
141
- entry.notes.nil? ? false : entry.notes.scan(ISSUE_REGEX).flatten.any?{|m| m.eql? issue.number.to_s }
142
- end
143
-
144
- if relevant_time_entries.empty?
145
- csv << [
146
- selected_repo.name,
147
- selected_milestone.title,
148
- issue.number,
149
- issue.title,
150
- "",
151
- "",
152
- !issue.labels.nil? && !issue.labels.find{|l| l.name.include?"size:"}.nil? ? issue.labels.find{|l| l.name.include?"size:"}.name : "",
153
- "",
154
- issue.state,
155
- "",
156
- issue.labels.nil? ? "" : issue.labels.compact.map{|l| l.name}.join(', ')
157
- ]
158
- else
159
- relevant_time_entries.chunk{|i| i.user_id }.each do |user_id, user_time_entries|
160
- user = harvest_users.find{|u| u.id == user_id}
161
- csv << [
162
- selected_repo.name,
163
- selected_milestone.title,
164
- issue.number,
165
- issue.title,
166
- projects.select{|p| user_time_entries.map(&:project_id).uniq.include? p.id}.map(&:name).join(', '),
167
- "#{user.first_name} #{user.last_name}", # Who
168
- !issue.labels.nil? && !issue.labels.find{|l| l.name.include?"size:"}.nil? ? issue.labels.find{|l| l.name.include?"size:"}.name : "",
169
- user_time_entries.inject(0){|sum, e| sum + (e.hours / e.notes.scan(ISSUE_REGEX).flatten.count) },
170
- issue.state,
171
- "",
172
- issue.labels.nil? ? "" : issue.labels.map{|l| l.name}.join(', ')
173
- ]
174
- end
175
- time_entries.concat relevant_time_entries.select { |entry|
176
- entry.notes.nil? ? false : entry.notes.scan(ISSUE_REGEX).flatten.count > 1
177
- }
178
- end
179
- progressbar.increment
180
- end
181
-
182
- pull_requests.each do |pr|
183
- relevant_time_entries, time_entries = time_entries.partition do |entry|
184
- entry.notes.nil? ? false : entry.notes.scan(ISSUE_REGEX).flatten.any?{|m| m.eql? pr.number.to_s }
185
- end
186
-
187
- if relevant_time_entries.empty?
188
- csv << [
189
- selected_repo.name,
190
- selected_milestone.title,
191
- pr.number,
192
- pr.title,
193
- "",
194
- "",
195
- !pr.labels.nil? && !pr.labels.find{|l| l.name.include?"size:"}.nil? ? pr.labels.find{|l| l.name.include?"size:"}.name : "",
196
- "",
197
- pr.state,
198
- pr.merged ? "Y" : "N",
199
- pr.labels.nil? ? "" : pr.labels.map{|l| l.name}.join(', ')
116
+ github_report.each do |row|
117
+ if !row[:harvest_time_entries].any?
118
+ csv << [
119
+ row[:github_repo].name,
120
+ row[:github_milestone].title,
121
+ row[:issue].number,
122
+ row[:issue].title,
123
+ '',
124
+ '',
125
+ (row[:github_estimated_size_label].nil? ? '' : row[:github_estimated_size_label].name),
126
+ '',
127
+ row[:issue].state,
128
+ (row[:pull_request].nil? ? '' : (row[:pull_request].merged ? 'Y' : 'N')),
129
+ (row[:issue].labels.nil? ? '' : row[:issue].labels.map{|label| label.name}.join(' , ')),
130
+ ]
131
+ else
132
+ row[:harvest_time_entries].each do |harvest_entries|
133
+ csv << [
134
+ row[:github_repo].name,
135
+ row[:github_milestone].title,
136
+ row[:issue].number,
137
+ row[:issue].title,
138
+ harvest_entries[:harvest_projects].map(&:name).join(', '),
139
+ "#{harvest_entries[:harvest_user].first_name} #{harvest_entries[:harvest_user].last_name}",
140
+ (row[:github_estimated_size_label].nil? ? '' : row[:github_estimated_size_label].name),
141
+ (harvest_entries[:time_entries].inject(0){|sum, e| sum + (e.hours / e.notes.scan(ReportBuilder::ISSUE_REGEX).flatten.count) }),
142
+ row[:issue].state,
143
+ (row[:pull_request].nil? ? '' : (row[:pull_request].merged ? 'Y' : 'N')),
144
+ (row[:issue].labels.nil? ? '' : row[:issue].labels.map{|label| label.name}.join(' , '))
200
145
  ]
201
- else
202
- relevant_time_entries.chunk{|i| i.user_id }.each do |user_id, user_time_entries|
203
- user = harvest_users.find{|u| u.id == user_id}
204
- csv << [
205
- selected_repo.name,
206
- selected_milestone.title,
207
- pr.number,
208
- pr.title,
209
- projects.select{|p| user_time_entries.map(&:project_id).uniq.include? p.id}.map(&:name).join(', '),
210
- "#{user.first_name} #{user.last_name}", # Who
211
- !pr.labels.nil? && !pr.labels.find{|l| l.name.include?"size:"}.nil? ? pr.labels.find{|l| l.name.include?"size:"}.name : "",
212
- user_time_entries.inject(0){|sum, e| sum + (e.hours / e.notes.scan(ISSUE_REGEX).flatten.count) },
213
- pr.state,
214
- pr.merged ? "Y" : "N",
215
- pr.labels.nil? ? "" : issue.labels.map{|l| l.name}.join(', ')
216
- ]
217
- end
218
- time_entries.concat relevant_time_entries.select { |entry|
219
- entry.notes.nil? ? false : entry.notes.scan(ISSUE_REGEX).flatten.count > 1
220
- }
221
146
  end
222
- progressbar.increment
223
147
  end
224
148
  end
225
149
  end
226
- progressbar.finish
227
150
 
228
- if time_entries.count > 0 && cli.agree("#{time_entries.count} Time entries not in this milestone. Create a list of them?")
151
+ if missing_harvest_time_entries.count > 0 && cli.agree("#{missing_harvest_time_entries.count} Time entries not in this milestone. Create a list of them?")
229
152
  time_entries_file_name = cli.ask('File name for report?').concat('.csv')
230
153
  CSV.open(time_entries_file_name, "wb", force_quotes: true) do |csv|
231
154
  csv << [
@@ -234,12 +157,12 @@ module FireWatch
234
157
  "Hours",
235
158
  "Notes"
236
159
  ]
237
- time_entries.each do |entry|
160
+ missing_harvest_time_entries.each do |l|
238
161
  csv << [
239
- entry.created_at,
240
- "#{harvest_users.find{|u| u.id == entry.user_id}.first_name} #{harvest_users.find{|u| u.id == entry.user_id}.last_name}",
241
- entry.hours,
242
- entry.notes
162
+ l[:time_entry].created_at,
163
+ "#{l[:harvest_user].first_name} #{l[:harvest_user].last_name}",
164
+ l[:time_entry].hours,
165
+ l[:time_entry].notes
243
166
  ]
244
167
  end
245
168
  end
@@ -1,3 +1,3 @@
1
1
  module FireWatch
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fire_watch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Weakley
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-31 00:00:00.000000000 Z
11
+ date: 2017-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: harvested
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '3.1'
19
+ version: '4.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '3.1'
26
+ version: '4.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: highline
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: parallel
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 1.12.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 1.12.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -127,6 +141,7 @@ files:
127
141
  - exe/fire_watch
128
142
  - fire_watch.gemspec
129
143
  - lib/fire_watch.rb
144
+ - lib/fire_watch/report_builder.rb
130
145
  - lib/fire_watch/runner.rb
131
146
  - lib/fire_watch/version.rb
132
147
  homepage: https://github.com/wildland/fire_watch