pt 0.8.6 → 0.9.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 +4 -4
- data/Changelog.md +4 -0
- data/Gemfile +1 -0
- data/README.md +2 -45
- data/bin/pt +1 -1
- data/lib/pt.rb +3 -1
- data/lib/pt/action.rb +151 -0
- data/lib/pt/cli.rb +183 -0
- data/lib/pt/client.rb +60 -50
- data/lib/pt/data_row.rb +5 -1
- data/lib/pt/data_table.rb +12 -1
- data/lib/pt/helper.rb +271 -0
- data/lib/pt/version.rb +1 -1
- data/pt.gemspec +6 -1
- metadata +75 -3
- data/lib/pt/ui.rb +0 -771
data/lib/pt/ui.rb
DELETED
@@ -1,771 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'colored'
|
3
|
-
require 'highline'
|
4
|
-
require 'tempfile'
|
5
|
-
require 'uri'
|
6
|
-
module PT
|
7
|
-
class UI
|
8
|
-
|
9
|
-
GLOBAL_CONFIG_PATH = ENV['HOME'] + "/.pt"
|
10
|
-
LOCAL_CONFIG_PATH = Dir.pwd + '/.pt'
|
11
|
-
|
12
|
-
def initialize(args)
|
13
|
-
require 'pt/debugger' if ARGV.delete('--debug')
|
14
|
-
@io = HighLine.new
|
15
|
-
@global_config = load_global_config
|
16
|
-
@client = Client.new(@global_config[:api_number])
|
17
|
-
@local_config = load_local_config
|
18
|
-
@project = @client.get_project(@local_config[:project_id])
|
19
|
-
command = args[0].to_sym rescue :my_work
|
20
|
-
@params = args[1..-1]
|
21
|
-
commands.include?(command.to_sym) ? send(command.to_sym) : help
|
22
|
-
end
|
23
|
-
|
24
|
-
def my_work
|
25
|
-
title("My Work for #{user_s} in #{project_to_s}")
|
26
|
-
stories = @client.get_my_work(@project, @local_config[:user_name])
|
27
|
-
TasksTable.new(stories).print @global_config
|
28
|
-
end
|
29
|
-
|
30
|
-
def todo
|
31
|
-
title("My Work for #{user_s} in #{project_to_s}")
|
32
|
-
stories = @client.get_my_work(@project, @local_config[:user_name])
|
33
|
-
stories = stories.select { |story| story.current_state == "unscheduled" }
|
34
|
-
TasksTable.new(stories).print @global_config
|
35
|
-
end
|
36
|
-
|
37
|
-
%w[unscheduled started finished delivered accepted rejected].each do |state|
|
38
|
-
define_method(state.to_sym) do
|
39
|
-
if @params[0]
|
40
|
-
stories = @project.stories(filter: "owner:#{@params[0]} state:#{state}")
|
41
|
-
TasksTable.new(stories).print @global_config
|
42
|
-
else
|
43
|
-
# otherwise show them all
|
44
|
-
title("Stories #{state} for #{project_to_s}")
|
45
|
-
stories = @project.stories(filter:"state:#{state}")
|
46
|
-
TasksTable.new(stories).print @global_config
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def list
|
52
|
-
if @params[0]
|
53
|
-
if @params[0] == "all"
|
54
|
-
stories = @client.get_work(@project)
|
55
|
-
TasksTable.new(stories).print @global_config
|
56
|
-
else
|
57
|
-
stories = @client.get_my_work(@project, @params[0])
|
58
|
-
TasksTable.new(stories).print @global_config
|
59
|
-
end
|
60
|
-
else
|
61
|
-
members = @client.get_members(@project)
|
62
|
-
table = MembersTable.new(members)
|
63
|
-
user = select("Please select a member to see his tasks.", table).name
|
64
|
-
title("Work for #{user} in #{project_to_s}")
|
65
|
-
stories = @client.get_my_work(@project, user)
|
66
|
-
TasksTable.new(stories).print @global_config
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def recent
|
71
|
-
title("Your recent stories from #{project_to_s}")
|
72
|
-
stories = @project.stories( ids: @local_config[:recent_tasks].join(',') )
|
73
|
-
MultiUserTasksTable.new(stories).print @global_config
|
74
|
-
end
|
75
|
-
|
76
|
-
def label
|
77
|
-
|
78
|
-
task = get_task_from_params "Please select a story to show"
|
79
|
-
unless task
|
80
|
-
message("No matches found for '#{@params[0]}', please use a valid pivotal story Id")
|
81
|
-
return
|
82
|
-
end
|
83
|
-
|
84
|
-
if @params[1]
|
85
|
-
label = @params[1]
|
86
|
-
else
|
87
|
-
label = ask("Which label?")
|
88
|
-
end
|
89
|
-
|
90
|
-
@client.add_label( @project, task, label );
|
91
|
-
show_task(task_by_id_or_pt_id(task.id))
|
92
|
-
end
|
93
|
-
|
94
|
-
def create
|
95
|
-
if @params[0]
|
96
|
-
name = @params[0]
|
97
|
-
owner = @params[1] || @local_config[:user_name]
|
98
|
-
requester = @local_config[:user_name]
|
99
|
-
task_type = task_type_or_nil(@params[1]) || task_type_or_nil(@params[2]) || 'feature'
|
100
|
-
else
|
101
|
-
title("Let's create a new task:")
|
102
|
-
name = ask("Name for the new task:")
|
103
|
-
end
|
104
|
-
|
105
|
-
owner = @client.find_member(@project, owner).person.id if owner.kind_of?(String)
|
106
|
-
|
107
|
-
unless owner
|
108
|
-
if ask('Do you want to assign it now? (y/n)').downcase == 'y'
|
109
|
-
members = @client.get_members(@project)
|
110
|
-
table = PersonsTable.new(members.map(&:person))
|
111
|
-
owner = select("Please select a member to assign him the task.", table).id
|
112
|
-
else
|
113
|
-
owner = nil
|
114
|
-
end
|
115
|
-
requester = @local_config[:user_name]
|
116
|
-
task_type = ask('Type? (c)hore, (b)ug, anything else for feature)')
|
117
|
-
end
|
118
|
-
|
119
|
-
task_type = case task_type
|
120
|
-
when 'c', 'chore'
|
121
|
-
'chore'
|
122
|
-
when 'b', 'bug'
|
123
|
-
'bug'
|
124
|
-
else
|
125
|
-
'feature'
|
126
|
-
end
|
127
|
-
result = nil
|
128
|
-
|
129
|
-
owner_ids = [owner]
|
130
|
-
# did you do a -m so you can add a description?
|
131
|
-
if ARGV.include? "-m" or ARGV.include? "--m"
|
132
|
-
editor = ENV.fetch('EDITOR') { 'vi' }
|
133
|
-
temp_path = "/tmp/editor-#{ Process.pid }.txt"
|
134
|
-
system "#{ editor } #{ temp_path }"
|
135
|
-
|
136
|
-
description = File.read(temp_path)
|
137
|
-
story = @client.create_task_with_description(@project, name, owner_ids, task_type, description)
|
138
|
-
else
|
139
|
-
story = @client.create_task(@project, name, owner_ids, task_type)
|
140
|
-
end
|
141
|
-
# TODO need result
|
142
|
-
congrats("#{task_type} for #{owner} open #{story.url}")
|
143
|
-
end
|
144
|
-
|
145
|
-
def open
|
146
|
-
if @params[0]
|
147
|
-
if task = @client.get_task_by_id(@project, @params[0])
|
148
|
-
congrats("Opening #{task.name}")
|
149
|
-
`open #{task.url}`
|
150
|
-
else
|
151
|
-
message("Story ##{@params[0]} not found")
|
152
|
-
end
|
153
|
-
else
|
154
|
-
tasks = @client.get_my_open_tasks(@project, @local_config[:user_name])
|
155
|
-
table = TasksTable.new(tasks)
|
156
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
157
|
-
task = select("Please select a story to open it in the browser", table)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def comment
|
162
|
-
if @params[0]
|
163
|
-
task = task_by_id_or_pt_id @params[0].to_i
|
164
|
-
comment = @params[1]
|
165
|
-
title("Adding a comment to #{task.name}")
|
166
|
-
else
|
167
|
-
tasks = @client.get_my_work(@project, @local_config[:user_name])
|
168
|
-
table = TasksTable.new(tasks)
|
169
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
170
|
-
task = select("Please select a story to comment it", table)
|
171
|
-
comment = ask("Write your comment")
|
172
|
-
end
|
173
|
-
if @client.comment_task(@project, task, comment)
|
174
|
-
congrats("Comment sent, thanks!")
|
175
|
-
save_recent_task( task.id )
|
176
|
-
else
|
177
|
-
error("Ummm, something went wrong.")
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def assign
|
182
|
-
if @params[0]
|
183
|
-
task = task_by_id_or_pt_id @params[0].to_i
|
184
|
-
owner = find_owner @params[1]
|
185
|
-
else
|
186
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
187
|
-
tasks = @client.get_tasks_to_assign(@project)
|
188
|
-
table = TasksTable.new(tasks)
|
189
|
-
task = select("Please select a task to assign it an owner", table)
|
190
|
-
end
|
191
|
-
|
192
|
-
unless owner
|
193
|
-
members = @client.get_members(@project)
|
194
|
-
table = PersonsTable.new(members.map(&:person))
|
195
|
-
owner = select("Please select a member to assign him the task", table)
|
196
|
-
end
|
197
|
-
@client.assign_task(@project, task, owner)
|
198
|
-
|
199
|
-
congrats("Task assigned to #{owner.initials}, thanks!")
|
200
|
-
end
|
201
|
-
|
202
|
-
def estimate
|
203
|
-
if @params[0]
|
204
|
-
task = task_by_id_or_pt_id @params[0].to_i
|
205
|
-
title("Estimating '#{task.name}'")
|
206
|
-
|
207
|
-
if [0,1,2,3].include? @params[1].to_i
|
208
|
-
estimation = @params[1]
|
209
|
-
end
|
210
|
-
else
|
211
|
-
tasks = @client.get_my_tasks_to_estimate(@project, @local_config[:user_name])
|
212
|
-
table = TasksTable.new(tasks)
|
213
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
214
|
-
task = select("Please select a story to estimate it", table)
|
215
|
-
end
|
216
|
-
|
217
|
-
estimation ||= ask("How many points you estimate for it? (#{@project.point_scale})")
|
218
|
-
@client.estimate_task(@project, task, estimation)
|
219
|
-
congrats("Task estimated, thanks!")
|
220
|
-
end
|
221
|
-
|
222
|
-
def start
|
223
|
-
if @params[0]
|
224
|
-
task = task_by_id_or_pt_id @params[0].to_i
|
225
|
-
title("Starting '#{task.name}'")
|
226
|
-
else
|
227
|
-
tasks = @client.get_my_tasks_to_start(@project, @local_config[:user_name])
|
228
|
-
table = TasksTable.new(tasks)
|
229
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
230
|
-
task = select("Please select a story to mark it as started", table)
|
231
|
-
end
|
232
|
-
start_task task
|
233
|
-
end
|
234
|
-
|
235
|
-
def finish
|
236
|
-
if @params[0]
|
237
|
-
task = task_by_id_or_pt_id @params[0].to_i
|
238
|
-
title("Finishing '#{task.name}'")
|
239
|
-
else
|
240
|
-
tasks = @client.get_my_tasks_to_finish(@project, @local_config[:user_name])
|
241
|
-
table = TasksTable.new(tasks)
|
242
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
243
|
-
task = select("Please select a story to mark it as finished", table)
|
244
|
-
end
|
245
|
-
finish_task task
|
246
|
-
end
|
247
|
-
|
248
|
-
def deliver
|
249
|
-
if @params[0]
|
250
|
-
task = task_by_id_or_pt_id @params[0].to_i
|
251
|
-
title("Delivering '#{task.name}'")
|
252
|
-
else
|
253
|
-
tasks = @client.get_my_tasks_to_deliver(@project, @local_config[:user_name])
|
254
|
-
table = TasksTable.new(tasks)
|
255
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
256
|
-
task = select("Please select a story to mark it as delivered", table)
|
257
|
-
end
|
258
|
-
|
259
|
-
deliver_task task
|
260
|
-
end
|
261
|
-
|
262
|
-
def accept
|
263
|
-
if @params[0]
|
264
|
-
task = task_by_id_or_pt_id @params[0].to_i
|
265
|
-
title("Accepting '#{task.name}'")
|
266
|
-
else
|
267
|
-
tasks = @client.get_my_tasks_to_accept(@project, @local_config[:user_name])
|
268
|
-
table = TasksTable.new(tasks)
|
269
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
270
|
-
task = select("Please select a story to mark it as accepted", table)
|
271
|
-
end
|
272
|
-
@client.mark_task_as(@project, task, 'accepted')
|
273
|
-
congrats("Task accepted, hooray!")
|
274
|
-
end
|
275
|
-
|
276
|
-
def show
|
277
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
278
|
-
task = get_task_from_params "Please select a story to show"
|
279
|
-
unless task
|
280
|
-
message("No matches found for '#{@params[0]}', please use a valid pivotal story Id")
|
281
|
-
return
|
282
|
-
end
|
283
|
-
show_task(task)
|
284
|
-
end
|
285
|
-
|
286
|
-
def tasks
|
287
|
-
title("Open story tasks for #{user_s} in #{project_to_s}")
|
288
|
-
|
289
|
-
unless story = get_task_from_params( "Please select a story to show pending tasks" )
|
290
|
-
message("No matches found for '#{@params[0]}', please use a valid pivotal story Id")
|
291
|
-
return
|
292
|
-
end
|
293
|
-
|
294
|
-
story_task = get_open_story_task_from_params(story)
|
295
|
-
|
296
|
-
if story_task.position == -1
|
297
|
-
description = ask('Title for new task')
|
298
|
-
story.create_task(:description => description)
|
299
|
-
congrats("New todo task added to \"#{story.name}\"")
|
300
|
-
else
|
301
|
-
edit_story_task story_task
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
# takes a comma separated list of ids and prints the collection of tasks
|
306
|
-
def show_condensed
|
307
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
308
|
-
tasks = []
|
309
|
-
if @params[0]
|
310
|
-
@params[0].each_line(',') do |line|
|
311
|
-
tasks << @client.get_task_by_id(@project, line.to_i)
|
312
|
-
end
|
313
|
-
table = TasksTable.new(tasks)
|
314
|
-
table.print
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
# TODO implement story notes and comment
|
319
|
-
def reject
|
320
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
321
|
-
if @params[0]
|
322
|
-
task = @client.get_story(@project, @params[0])
|
323
|
-
title("Rejecting '#{task.name}'")
|
324
|
-
else
|
325
|
-
tasks = @client.get_my_tasks_to_reject(@project, @local_config[:user_name])
|
326
|
-
table = TasksTable.new(tasks)
|
327
|
-
title("Tasks for #{user_s} in #{project_to_s}")
|
328
|
-
task = select("Please select a story to mark it as rejected", table)
|
329
|
-
end
|
330
|
-
|
331
|
-
if @params[1]
|
332
|
-
comment = @params[1]
|
333
|
-
else
|
334
|
-
comment = ask("Please explain why are you rejecting the task.")
|
335
|
-
end
|
336
|
-
|
337
|
-
if @client.comment_task(@project, task, comment)
|
338
|
-
result = @client.mark_task_as(@project, task, 'rejected')
|
339
|
-
congrats("Task rejected, thanks!")
|
340
|
-
else
|
341
|
-
error("Ummm, something went wrong.")
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
def done
|
346
|
-
if @params[0]
|
347
|
-
task = task_by_id_or_pt_id @params[0].to_i
|
348
|
-
|
349
|
-
#we need this for finding again later
|
350
|
-
task_id = task.id
|
351
|
-
|
352
|
-
if !@params[1] && task.estimate == -1
|
353
|
-
error("You need to give an estimate for this task")
|
354
|
-
return
|
355
|
-
end
|
356
|
-
|
357
|
-
if @params[1] && task.estimate == -1
|
358
|
-
if [0,1,2,3].include? @params[1].to_i
|
359
|
-
estimate_task(task, @params[1].to_i)
|
360
|
-
end
|
361
|
-
if @params[2]
|
362
|
-
task = task_by_id_or_pt_id task_id
|
363
|
-
@client.comment_task(@project, task, @params[2])
|
364
|
-
end
|
365
|
-
else
|
366
|
-
@client.comment_task(@project, task, @params[1]) if @params[1]
|
367
|
-
end
|
368
|
-
|
369
|
-
task = task_by_id_or_pt_id task_id
|
370
|
-
start_task task
|
371
|
-
|
372
|
-
task = task_by_id_or_pt_id task_id
|
373
|
-
finish_task task
|
374
|
-
|
375
|
-
task = task_by_id_or_pt_id task_id
|
376
|
-
deliver_task task
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
def estimate_task task, difficulty
|
381
|
-
result = @client.estimate_task(@project, task, difficulty)
|
382
|
-
if result.errors.any?
|
383
|
-
error(result.errors.errors)
|
384
|
-
else
|
385
|
-
congrats("Task estimated, thanks!")
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
def start_task task
|
390
|
-
@client.mark_task_as(@project, task, 'started')
|
391
|
-
congrats("Task started, go for it!")
|
392
|
-
end
|
393
|
-
|
394
|
-
def finish_task task
|
395
|
-
if task.story_type == 'chore'
|
396
|
-
@client.mark_task_as(@project, task, 'accepted')
|
397
|
-
else
|
398
|
-
@client.mark_task_as(@project, task, 'finished')
|
399
|
-
end
|
400
|
-
congrats("Another task bites the dust, yeah!")
|
401
|
-
end
|
402
|
-
|
403
|
-
def deliver_task task
|
404
|
-
return if task.story_type == 'chore'
|
405
|
-
@client.mark_task_as(@project, task, 'delivered')
|
406
|
-
congrats("Task delivered, congrats!")
|
407
|
-
end
|
408
|
-
|
409
|
-
def find
|
410
|
-
if (story_id = @params[0].to_i).nonzero?
|
411
|
-
if task = task_by_id_or_pt_id(@params[0].to_i)
|
412
|
-
return show_task(task)
|
413
|
-
else
|
414
|
-
message("Task not found by id (#{story_id}), falling back to text search")
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
|
-
if @params[0]
|
419
|
-
tasks = @client.search_for_story(@project, @params[0])
|
420
|
-
tasks.each do |story_task|
|
421
|
-
title("--- [#{(tasks.index story_task) + 1 }] -----------------")
|
422
|
-
show_task(story_task)
|
423
|
-
end
|
424
|
-
message("No matches found for '#{@params[0]}'") if tasks.empty?
|
425
|
-
else
|
426
|
-
message("You need to provide a substring for a tasks title.")
|
427
|
-
end
|
428
|
-
end
|
429
|
-
|
430
|
-
def updates
|
431
|
-
activities = @client.get_activities(@project, @params[0])
|
432
|
-
tasks = @client.get_my_work(@project, @local_config[:user_name])
|
433
|
-
title("Recent Activity on #{project_to_s}")
|
434
|
-
activities.each do |activity|
|
435
|
-
show_activity(activity, tasks)
|
436
|
-
end
|
437
|
-
end
|
438
|
-
|
439
|
-
|
440
|
-
def help
|
441
|
-
if ARGV[0] && ARGV[0] != 'help'
|
442
|
-
message("Command #{ARGV[0]} not recognized. Showing help.")
|
443
|
-
end
|
444
|
-
|
445
|
-
title("Command line usage for pt #{VERSION}")
|
446
|
-
help = <<-HELP
|
447
|
-
pt # show all available stories
|
448
|
-
|
449
|
-
pt todo <owner> # show all unscheduled stories
|
450
|
-
|
451
|
-
pt (unscheduled,started,finished,delivered, accepted, rejected) <owner> # show all (unscheduled,started,finished,delivered, accepted, rejected) stories
|
452
|
-
|
453
|
-
pt create [title] <owner> <type> -m # create a new story (and include description ala git commit)
|
454
|
-
|
455
|
-
pt show [id] # shows detailed info about a story
|
456
|
-
|
457
|
-
pt tasks [id] # manage tasks of story
|
458
|
-
|
459
|
-
pt open [id] # open a story in the browser
|
460
|
-
|
461
|
-
pt assign [id] <owner> # assign owner
|
462
|
-
|
463
|
-
pt comment [id] [comment] # add a comment
|
464
|
-
|
465
|
-
pt label [id] [label] # add a label
|
466
|
-
|
467
|
-
pt estimate [id] [0-3] # estimate a story in points scale
|
468
|
-
|
469
|
-
pt (start,finish,deliver,accept) [id] # mark a story as started
|
470
|
-
|
471
|
-
pt reject [id] [reason] # mark a story as rejected, explaining why
|
472
|
-
|
473
|
-
pt done [id] <0-3> <comment> # lazy mans finish story, opens, assigns to you, estimates, finish & delivers
|
474
|
-
|
475
|
-
pt find [query] # looks in your stories by title and presents it
|
476
|
-
|
477
|
-
pt list [owner] # list all stories for another pt user
|
478
|
-
|
479
|
-
pt list all # list all stories for all users
|
480
|
-
|
481
|
-
pt updates # shows number recent activity from your current project
|
482
|
-
|
483
|
-
pt recent # shows stories you've recently shown or commented on with pt
|
484
|
-
|
485
|
-
All commands can be run entirely without arguments for a wizard based UI. Otherwise [required] <optional>.
|
486
|
-
Anything that takes an id will also take the num (index) from the pt command.
|
487
|
-
HELP
|
488
|
-
puts(help)
|
489
|
-
end
|
490
|
-
|
491
|
-
protected
|
492
|
-
|
493
|
-
def commands
|
494
|
-
(public_methods - Object.public_methods).map{ |c| c.to_sym}
|
495
|
-
end
|
496
|
-
|
497
|
-
# Config
|
498
|
-
|
499
|
-
def load_global_config
|
500
|
-
|
501
|
-
# skip global config if env vars are set
|
502
|
-
if ENV['PIVOTAL_EMAIL'] and ENV['PIVOTAL_API_KEY']
|
503
|
-
config = {
|
504
|
-
:email => ENV['PIVOTAL_EMAIL'],
|
505
|
-
:api_number => ENV['PIVOTAL_API_KEY']
|
506
|
-
}
|
507
|
-
return config
|
508
|
-
end
|
509
|
-
|
510
|
-
config = YAML.load(File.read(GLOBAL_CONFIG_PATH)) rescue {}
|
511
|
-
if config.empty?
|
512
|
-
message "I can't find info about your Pivotal Tracker account in #{GLOBAL_CONFIG_PATH}."
|
513
|
-
while !config[:api_number] do
|
514
|
-
config[:api_number] = ask "What is your token?"
|
515
|
-
end
|
516
|
-
congrats "Thanks!",
|
517
|
-
"Your API id is " + config[:api_number],
|
518
|
-
"I'm saving it in #{GLOBAL_CONFIG_PATH} so you don't have to log in again."
|
519
|
-
save_config(config, GLOBAL_CONFIG_PATH)
|
520
|
-
end
|
521
|
-
config
|
522
|
-
end
|
523
|
-
|
524
|
-
def get_local_config_path
|
525
|
-
# If the local config path does not exist, check to see if we're in a git repo
|
526
|
-
# And if so, try the top level of the checkout
|
527
|
-
if (!File.exist?(LOCAL_CONFIG_PATH) && system('git rev-parse 2> /dev/null'))
|
528
|
-
return `git rev-parse --show-toplevel`.chomp() + '/.pt'
|
529
|
-
else
|
530
|
-
return LOCAL_CONFIG_PATH
|
531
|
-
end
|
532
|
-
end
|
533
|
-
|
534
|
-
def load_local_config
|
535
|
-
check_local_config_path
|
536
|
-
config = YAML.load(File.read(get_local_config_path())) rescue {}
|
537
|
-
|
538
|
-
if ENV['PIVOTAL_PROJECT_ID']
|
539
|
-
|
540
|
-
config[:project_id] = ENV['PIVOTAL_PROJECT_ID']
|
541
|
-
|
542
|
-
project = @client.get_project(config[:project_id])
|
543
|
-
config[:project_name] = project.name
|
544
|
-
|
545
|
-
membership = @client.get_my_info
|
546
|
-
config[:user_name], config[:user_id], config[:user_initials] = membership.name, membership.id, membership.initials
|
547
|
-
save_config(config, get_local_config_path())
|
548
|
-
|
549
|
-
end
|
550
|
-
|
551
|
-
if config.empty?
|
552
|
-
message "I can't find info about this project in #{get_local_config_path()}"
|
553
|
-
projects = ProjectTable.new(@client.get_projects)
|
554
|
-
project = select("Please select the project for the current directory", projects)
|
555
|
-
config[:project_id], config[:project_name] = project.id, project.name
|
556
|
-
project = @client.get_project(project.id)
|
557
|
-
membership = @client.get_my_info
|
558
|
-
config[:user_name], config[:user_id], config[:user_initials] = membership.name, membership.id, membership.initials
|
559
|
-
congrats "Thanks! I'm saving this project's info",
|
560
|
-
"in #{get_local_config_path()}: remember to .gitignore it!"
|
561
|
-
save_config(config, get_local_config_path())
|
562
|
-
end
|
563
|
-
config
|
564
|
-
end
|
565
|
-
|
566
|
-
def check_local_config_path
|
567
|
-
if GLOBAL_CONFIG_PATH == get_local_config_path()
|
568
|
-
error("Please execute .pt inside your project directory and not in your home.")
|
569
|
-
exit
|
570
|
-
end
|
571
|
-
end
|
572
|
-
|
573
|
-
def save_config(config, path)
|
574
|
-
File.new(path, 'w') unless File.exists?(path)
|
575
|
-
File.open(path, 'w') {|f| f.write(config.to_yaml) }
|
576
|
-
end
|
577
|
-
|
578
|
-
# I/O
|
579
|
-
|
580
|
-
def split_lines(text)
|
581
|
-
text.respond_to?(:join) ? text.join("\n") : text
|
582
|
-
end
|
583
|
-
|
584
|
-
def title(*msg)
|
585
|
-
puts "\n#{split_lines(msg)}".bold
|
586
|
-
end
|
587
|
-
|
588
|
-
def congrats(*msg)
|
589
|
-
puts "\n#{split_lines(msg).green.bold}"
|
590
|
-
end
|
591
|
-
|
592
|
-
def message(*msg)
|
593
|
-
puts "\n#{split_lines(msg)}"
|
594
|
-
end
|
595
|
-
|
596
|
-
def compact_message(*msg)
|
597
|
-
puts "#{split_lines(msg)}"
|
598
|
-
end
|
599
|
-
|
600
|
-
def error(*msg)
|
601
|
-
puts "\n#{split_lines(msg).red.bold}"
|
602
|
-
end
|
603
|
-
|
604
|
-
def select(msg, table)
|
605
|
-
if table.length > 0
|
606
|
-
begin
|
607
|
-
table.print @global_config
|
608
|
-
row = ask "#{msg} (1-#{table.length}, 'q' to exit)"
|
609
|
-
quit if row == 'q'
|
610
|
-
selected = table[row]
|
611
|
-
error "Invalid selection, try again:" unless selected
|
612
|
-
end until selected
|
613
|
-
selected
|
614
|
-
else
|
615
|
-
table.print @global_config
|
616
|
-
message "Sorry, there are no options to select."
|
617
|
-
quit
|
618
|
-
end
|
619
|
-
end
|
620
|
-
|
621
|
-
def quit
|
622
|
-
message "bye!"
|
623
|
-
exit
|
624
|
-
end
|
625
|
-
|
626
|
-
def ask(msg)
|
627
|
-
@io.ask("#{msg.bold}")
|
628
|
-
end
|
629
|
-
|
630
|
-
def ask_secret(msg)
|
631
|
-
@io.ask("#{msg.bold}"){ |q| q.echo = '*' }
|
632
|
-
end
|
633
|
-
|
634
|
-
def user_s
|
635
|
-
"#{@local_config[:user_name]} (#{@local_config[:user_initials]})"
|
636
|
-
end
|
637
|
-
|
638
|
-
def project_to_s
|
639
|
-
"Project #{@local_config[:project_name].upcase}"
|
640
|
-
end
|
641
|
-
|
642
|
-
def task_type_or_nil query
|
643
|
-
if (["feature", "bug", "chore"].index query)
|
644
|
-
return query
|
645
|
-
end
|
646
|
-
nil
|
647
|
-
end
|
648
|
-
|
649
|
-
def task_by_id_or_pt_id id
|
650
|
-
if id < 1000
|
651
|
-
tasks = @client.get_my_work(@project, @local_config[:user_name])
|
652
|
-
table = TasksTable.new(tasks)
|
653
|
-
table[id]
|
654
|
-
else
|
655
|
-
@client.get_task_by_id @project, id
|
656
|
-
end
|
657
|
-
end
|
658
|
-
|
659
|
-
def find_task query
|
660
|
-
members = @client.get_members(@project)
|
661
|
-
members.each do | member |
|
662
|
-
if member.name.downcase.index query
|
663
|
-
return member.name
|
664
|
-
end
|
665
|
-
end
|
666
|
-
nil
|
667
|
-
end
|
668
|
-
|
669
|
-
def find_owner query
|
670
|
-
if query
|
671
|
-
member = @client.get_member(@project, query)
|
672
|
-
return member ? member.person : nil
|
673
|
-
end
|
674
|
-
nil
|
675
|
-
end
|
676
|
-
|
677
|
-
def show_task(task)
|
678
|
-
title task.name.green
|
679
|
-
estimation = [-1, nil].include?(task.estimate) ? "Unestimated" : "#{task.estimate} points"
|
680
|
-
message "#{task.current_state.capitalize} #{task.story_type} | #{estimation} | Req: #{task.requested_by.initials} |
|
681
|
-
Owners: #{task.owners.map(&:initials).join(',')} | Id: #{task.id}"
|
682
|
-
|
683
|
-
if (task.labels)
|
684
|
-
message "Labels: " + task.labels.map(&:name).join(', ')
|
685
|
-
end
|
686
|
-
message task.description unless task.description.nil? || task.description.empty?
|
687
|
-
message "View on pivotal: #{task.url}"
|
688
|
-
|
689
|
-
if task.tasks
|
690
|
-
title('tasks'.red)
|
691
|
-
task.tasks.each{ |t| compact_message "- #{t.complete ? "(v) " : "( )"} #{t.description}" }
|
692
|
-
end
|
693
|
-
|
694
|
-
|
695
|
-
task.comments.each do |n|
|
696
|
-
title('========================================='.red)
|
697
|
-
text = ">> #{n.person.initials}: #{n.text}"
|
698
|
-
text << "[#{n.file_attachment_ids.size}F]" if n.file_attachment_ids
|
699
|
-
message text
|
700
|
-
end
|
701
|
-
save_recent_task( task.id )
|
702
|
-
end
|
703
|
-
|
704
|
-
|
705
|
-
def show_activity(activity, tasks)
|
706
|
-
message("#{activity.message}")
|
707
|
-
end
|
708
|
-
|
709
|
-
def get_open_story_task_from_params(task)
|
710
|
-
title "Pending tasks for '#{task.name}'"
|
711
|
-
task_struct = Struct.new(:description, :position)
|
712
|
-
|
713
|
-
pending_tasks = [
|
714
|
-
task_struct.new('<< Add new task >>', -1)
|
715
|
-
]
|
716
|
-
|
717
|
-
task.tasks.each{ |t| pending_tasks << t unless t.complete }
|
718
|
-
table = TodoTaskTable.new(pending_tasks)
|
719
|
-
select("Pick task to edit, 1 to add new task", table)
|
720
|
-
end
|
721
|
-
|
722
|
-
def get_task_from_params(prompt)
|
723
|
-
if @params[0]
|
724
|
-
task = task_by_id_or_pt_id(@params[0].to_i)
|
725
|
-
else
|
726
|
-
tasks = @client.get_all_stories(@project, @local_config[:user_name])
|
727
|
-
table = TasksTable.new(tasks)
|
728
|
-
task = select(prompt, table)
|
729
|
-
end
|
730
|
-
end
|
731
|
-
|
732
|
-
def edit_story_task(story_task)
|
733
|
-
action_class = Struct.new(:action, :key)
|
734
|
-
|
735
|
-
table = ActionTable.new([
|
736
|
-
action_class.new('Complete', :complete),
|
737
|
-
action_class.new('Delete', :delete),
|
738
|
-
action_class.new('Edit', :edit)
|
739
|
-
# Move?
|
740
|
-
])
|
741
|
-
action_to_execute = select('What to do with todo?', table)
|
742
|
-
|
743
|
-
case action_to_execute.key
|
744
|
-
when :complete then
|
745
|
-
story_task.update(:complete => true)
|
746
|
-
congrats('Todo task completed!')
|
747
|
-
when :delete then
|
748
|
-
story_task.delete
|
749
|
-
congrats('Todo task removed')
|
750
|
-
when :edit then
|
751
|
-
new_description = ask('New task description')
|
752
|
-
story_task.update(:description => new_description)
|
753
|
-
congrats("Todo task changed to: \"#{story_task.description}\"")
|
754
|
-
end
|
755
|
-
end
|
756
|
-
|
757
|
-
def save_recent_task( task_id )
|
758
|
-
# save list of recently accessed tasks
|
759
|
-
unless (@local_config[:recent_tasks])
|
760
|
-
@local_config[:recent_tasks] = Array.new();
|
761
|
-
end
|
762
|
-
@local_config[:recent_tasks].unshift( task_id )
|
763
|
-
@local_config[:recent_tasks] = @local_config[:recent_tasks].uniq()
|
764
|
-
if @local_config[:recent_tasks].length > 10
|
765
|
-
@local_config[:recent_tasks].pop()
|
766
|
-
end
|
767
|
-
save_config( @local_config, get_local_config_path() )
|
768
|
-
end
|
769
|
-
|
770
|
-
end
|
771
|
-
end
|