timr 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 +4 -4
- data/README.md +8 -0
- data/bin/.gitignore +1 -0
- data/bin/build_man.sh +1 -0
- data/bin/dev_data.sh +57 -0
- data/bin/dev_setup.sh +2 -0
- data/bin/test.sh +3 -1
- data/bin/test_report.sh +82 -0
- data/bin/timr_bash_completion.sh +13 -1
- data/lib/timr.rb +1 -0
- data/lib/timr/command/basic_command.rb +2 -0
- data/lib/timr/command/log_command.rb +3 -3
- data/lib/timr/command/report_command.rb +109 -71
- data/lib/timr/command/reset_command.rb +75 -0
- data/lib/timr/duration.rb +5 -1
- data/lib/timr/exception/timr_error.rb +4 -0
- data/lib/timr/model/stack.rb +8 -0
- data/lib/timr/model/task.rb +41 -9
- data/lib/timr/model/track.rb +47 -15
- data/lib/timr/timr.rb +45 -3
- data/lib/timr/version.rb +2 -2
- data/man/index.txt +1 -0
- data/man/timr-report.1 +66 -21
- data/man/timr-report.1.ronn +30 -1
- data/man/timr-reset.1 +41 -0
- data/man/timr-reset.1.ronn +33 -0
- data/man/timr.1 +3 -0
- data/man/timr.1.ronn +1 -0
- metadata +7 -2
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module TheFox
|
5
|
+
module Timr
|
6
|
+
module Command
|
7
|
+
|
8
|
+
# Remove current running Track. Paused commands will not be deleted.
|
9
|
+
#
|
10
|
+
# Man page: [timr-reset(1)](../../../../man/timr-reset.1.html)
|
11
|
+
class ResetCommand < BasicCommand
|
12
|
+
|
13
|
+
include TheFox::Timr::Error
|
14
|
+
|
15
|
+
# Path to man page.
|
16
|
+
MAN_PATH = 'man/timr-reset.1'
|
17
|
+
|
18
|
+
def initialize(argv = Array.new)
|
19
|
+
super()
|
20
|
+
|
21
|
+
@help_opt = false
|
22
|
+
@stack_opt = false
|
23
|
+
|
24
|
+
loop_c = 0 # Limit the loop.
|
25
|
+
while loop_c < 1024 && argv.length > 0
|
26
|
+
loop_c += 1
|
27
|
+
arg = argv.shift
|
28
|
+
|
29
|
+
case arg
|
30
|
+
when '-h', '--help'
|
31
|
+
@help_opt = true
|
32
|
+
when '-s', '--stack'
|
33
|
+
@stack_opt = true
|
34
|
+
else
|
35
|
+
raise ResetCommandError, "Unknown argument '#{arg}'. See 'timr report --help'."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# See BasicCommand#run.
|
41
|
+
def run
|
42
|
+
if @help_opt
|
43
|
+
help
|
44
|
+
return
|
45
|
+
end
|
46
|
+
|
47
|
+
@timr = Timr.new(@cwd)
|
48
|
+
|
49
|
+
track = @timr.stack.current_track
|
50
|
+
if track && track.running?
|
51
|
+
puts '--- RESET ---'
|
52
|
+
puts track.to_compact_str
|
53
|
+
puts
|
54
|
+
end
|
55
|
+
|
56
|
+
@timr.reset({:stack => @stack_opt})
|
57
|
+
|
58
|
+
puts @timr.stack
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def help
|
64
|
+
puts 'usage: timr reset [-s|--stack]'
|
65
|
+
puts
|
66
|
+
puts 'Options'
|
67
|
+
puts ' -s, --stack Clean the Stack.'
|
68
|
+
puts
|
69
|
+
end
|
70
|
+
|
71
|
+
end # class TrackCommand
|
72
|
+
|
73
|
+
end # module Command
|
74
|
+
end # module Timr
|
75
|
+
end # module TheFox
|
data/lib/timr/duration.rb
CHANGED
@@ -22,6 +22,7 @@ module TheFox
|
|
22
22
|
# - PopCommandError
|
23
23
|
# - PushCommandError
|
24
24
|
# - ReportCommandError
|
25
|
+
# - ResetCommandError
|
25
26
|
# - StartCommandError
|
26
27
|
# - StatusCommandError
|
27
28
|
# - StopCommandError
|
@@ -82,6 +83,9 @@ module TheFox
|
|
82
83
|
class ReportCommandError < CommandError
|
83
84
|
end
|
84
85
|
|
86
|
+
class ResetCommandError < CommandError
|
87
|
+
end
|
88
|
+
|
85
89
|
class StartCommandError < CommandError
|
86
90
|
end
|
87
91
|
|
data/lib/timr/model/stack.rb
CHANGED
data/lib/timr/model/task.rb
CHANGED
@@ -124,6 +124,15 @@ module TheFox
|
|
124
124
|
true
|
125
125
|
end
|
126
126
|
|
127
|
+
def reset
|
128
|
+
if @current_track
|
129
|
+
@current_track = nil
|
130
|
+
|
131
|
+
# Mark Task as changed.
|
132
|
+
changed
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
127
136
|
# Select Track by Time Range and/or Status.
|
128
137
|
#
|
129
138
|
# Options:
|
@@ -464,7 +473,7 @@ module TheFox
|
|
464
473
|
# Get estimation as String.
|
465
474
|
def estimation_s
|
466
475
|
if @estimation
|
467
|
-
@estimation.
|
476
|
+
@estimation.to_human_s
|
468
477
|
else
|
469
478
|
'---'
|
470
479
|
end
|
@@ -630,9 +639,8 @@ module TheFox
|
|
630
639
|
#
|
631
640
|
# Options:
|
632
641
|
#
|
633
|
-
# - `:billed`
|
642
|
+
# - `:billed` (Boolean)
|
634
643
|
def duration(options = Hash.new)
|
635
|
-
|
636
644
|
billed_opt = options.fetch(:billed, nil)
|
637
645
|
|
638
646
|
duration = Duration.new
|
@@ -678,7 +686,7 @@ module TheFox
|
|
678
686
|
def remaining_time_s
|
679
687
|
rmt = remaining_time
|
680
688
|
if rmt
|
681
|
-
rmt.
|
689
|
+
rmt.to_human_s
|
682
690
|
else
|
683
691
|
'---'
|
684
692
|
end
|
@@ -804,7 +812,7 @@ module TheFox
|
|
804
812
|
to_ax << 'Description: %s' % [self.description]
|
805
813
|
end
|
806
814
|
if self.estimation
|
807
|
-
to_ax << 'Estimation: %s' % [self.estimation.
|
815
|
+
to_ax << 'Estimation: %s' % [self.estimation.to_human_s]
|
808
816
|
end
|
809
817
|
to_ax
|
810
818
|
end
|
@@ -839,7 +847,7 @@ module TheFox
|
|
839
847
|
end
|
840
848
|
|
841
849
|
# Duration
|
842
|
-
duration_human = self.duration.
|
850
|
+
duration_human = self.duration.to_human_s
|
843
851
|
to_ax << 'Duration: %s' % [duration_human]
|
844
852
|
|
845
853
|
duration_man_days = self.duration.to_man_days
|
@@ -937,18 +945,42 @@ module TheFox
|
|
937
945
|
|
938
946
|
# Return formatted String.
|
939
947
|
#
|
948
|
+
# Options:
|
949
|
+
#
|
950
|
+
# - `:format`
|
951
|
+
# - `:prefix` - Default: `%`
|
952
|
+
#
|
953
|
+
# Format:
|
954
|
+
#
|
940
955
|
# - `%id` - ID
|
941
956
|
# - `%sid` - Short ID
|
942
957
|
# - `%fid` - Foreign ID
|
943
958
|
# - `%n` - Name
|
944
959
|
# - `%d` - Description
|
945
|
-
|
946
|
-
|
960
|
+
# - `%ds` - Duration Seconds
|
961
|
+
# - `%dh` - Duration Human Format
|
962
|
+
def formatted(options = Hash.new)
|
963
|
+
format = options.fetch(:format, '')
|
964
|
+
prefix = options.fetch(:prefix, '%')
|
965
|
+
|
966
|
+
formatted_s = format
|
947
967
|
.gsub("#{prefix}id", self.id)
|
948
968
|
.gsub("#{prefix}sid", self.short_id ? self.short_id : '')
|
949
969
|
.gsub("#{prefix}fid", self.foreign_id ? self.foreign_id : '')
|
950
970
|
.gsub("#{prefix}n", self.name ? self.name : '')
|
951
|
-
.gsub("#{prefix}
|
971
|
+
.gsub("#{prefix}ds", self.duration(options).to_s)
|
972
|
+
|
973
|
+
duration_human = self.duration(options).to_human
|
974
|
+
if duration_human
|
975
|
+
formatted_s.gsub!('%dh', duration_human)
|
976
|
+
else
|
977
|
+
formatted_s.gsub!('%dh', '')
|
978
|
+
end
|
979
|
+
|
980
|
+
# Must not before %dh and %ds.
|
981
|
+
formatted_s.gsub!("#{prefix}d", self.description ? self.description : '')
|
982
|
+
|
983
|
+
formatted_s
|
952
984
|
end
|
953
985
|
|
954
986
|
def inspect
|
data/lib/timr/model/track.rb
CHANGED
@@ -223,9 +223,17 @@ module TheFox
|
|
223
223
|
#
|
224
224
|
# - `:from` (Time), `:to` (Time)
|
225
225
|
# Limit the begin and end datetimes to a specific range.
|
226
|
+
# - `:billed` (Boolean)
|
226
227
|
def duration(options = Hash.new)
|
227
228
|
from_opt = options.fetch(:from, nil)
|
228
229
|
to_opt = options.fetch(:to, nil)
|
230
|
+
billed_opt = options.fetch(:billed, nil)
|
231
|
+
|
232
|
+
unless billed_opt.nil?
|
233
|
+
if @is_billed != billed_opt
|
234
|
+
return Duration.new(0)
|
235
|
+
end
|
236
|
+
end
|
229
237
|
|
230
238
|
if @begin_datetime
|
231
239
|
bdt = @begin_datetime.utc
|
@@ -258,20 +266,12 @@ module TheFox
|
|
258
266
|
|
259
267
|
# Alias method.
|
260
268
|
def billed_duration(options = Hash.new)
|
261
|
-
|
262
|
-
duration(options)
|
263
|
-
else
|
264
|
-
Duration.new(0)
|
265
|
-
end
|
269
|
+
duration(options.merge({:billed => true}))
|
266
270
|
end
|
267
271
|
|
268
272
|
# Alias method.
|
269
273
|
def unbilled_duration(options = Hash.new)
|
270
|
-
|
271
|
-
duration(options)
|
272
|
-
else
|
273
|
-
Duration.new(0)
|
274
|
-
end
|
274
|
+
duration(options.merge({:billed => false}))
|
275
275
|
end
|
276
276
|
|
277
277
|
# When begin_datetime is `2017-01-01 01:15`
|
@@ -312,6 +312,11 @@ module TheFox
|
|
312
312
|
Status.new(short_status)
|
313
313
|
end
|
314
314
|
|
315
|
+
# Is the Track running?
|
316
|
+
def running?
|
317
|
+
status.short_status == 'R' # running
|
318
|
+
end
|
319
|
+
|
315
320
|
# Is the Track stopped?
|
316
321
|
def stopped?
|
317
322
|
status.short_status == 'S' # stopped
|
@@ -434,7 +439,7 @@ module TheFox
|
|
434
439
|
end
|
435
440
|
|
436
441
|
if self.duration && self.duration.to_i > 0
|
437
|
-
to_ax << 'Duration: %s' % [self.duration.
|
442
|
+
to_ax << 'Duration: %s' % [self.duration.to_human_s]
|
438
443
|
end
|
439
444
|
|
440
445
|
to_ax << 'Status: %s' % [self.status.colorized]
|
@@ -479,7 +484,7 @@ module TheFox
|
|
479
484
|
end
|
480
485
|
|
481
486
|
if self.duration && self.duration.to_i > 0
|
482
|
-
duration_human = self.duration.
|
487
|
+
duration_human = self.duration.to_human_s
|
483
488
|
to_ax << 'Duration: %s' % [duration_human]
|
484
489
|
|
485
490
|
duration_man_days = self.duration.to_man_days
|
@@ -511,6 +516,12 @@ module TheFox
|
|
511
516
|
|
512
517
|
# Return formatted String.
|
513
518
|
#
|
519
|
+
# Options:
|
520
|
+
#
|
521
|
+
# - `:format`
|
522
|
+
#
|
523
|
+
# Format:
|
524
|
+
#
|
514
525
|
# - `%id` - ID
|
515
526
|
# - `%sid` - Short ID
|
516
527
|
# - `%t` - Title generated from message.
|
@@ -521,7 +532,13 @@ module TheFox
|
|
521
532
|
# - `%edt` - End DateTime
|
522
533
|
# - `%ed` - End Date
|
523
534
|
# - `%et` - End Time
|
524
|
-
|
535
|
+
# - `%ds` - Duration Seconds
|
536
|
+
# - `%dh` - Duration Human Format
|
537
|
+
# - `%bi` - Billed Integer
|
538
|
+
# - `%bh` - Billed Human Format (YES, NO)
|
539
|
+
def formatted(options = Hash.new)
|
540
|
+
format = options.fetch(:format, '')
|
541
|
+
|
525
542
|
formatted_s = format
|
526
543
|
.gsub('%id', self.id)
|
527
544
|
.gsub('%sid', self.short_id)
|
@@ -533,14 +550,29 @@ module TheFox
|
|
533
550
|
.gsub('%edt', self.end_datetime ? self.end_datetime.strftime('%F %H:%M') : '')
|
534
551
|
.gsub('%ed', self.end_datetime ? self.end_datetime.strftime('%F') : '')
|
535
552
|
.gsub('%et', self.end_datetime ? self.end_datetime.strftime('%H:%M') : '')
|
553
|
+
.gsub('%ds', self.duration.to_s)
|
554
|
+
.gsub('%bi', self.is_billed.to_i.to_s)
|
555
|
+
.gsub('%bh', self.is_billed ? 'YES' : 'NO')
|
556
|
+
|
557
|
+
duration_human = self.duration.to_human
|
558
|
+
if duration_human
|
559
|
+
formatted_s.gsub!('%dh', self.duration.to_human)
|
560
|
+
else
|
561
|
+
formatted_s.gsub!('%dh', '')
|
562
|
+
end
|
563
|
+
|
564
|
+
task_formating_options = {
|
565
|
+
:format => formatted_s,
|
566
|
+
:prefix => '%T',
|
567
|
+
}
|
536
568
|
|
537
569
|
if @task
|
538
|
-
formatted_s = @task.formatted(
|
570
|
+
formatted_s = @task.formatted(task_formating_options)
|
539
571
|
else
|
540
572
|
tmp_task = Task.new
|
541
573
|
tmp_task.id = ''
|
542
574
|
|
543
|
-
formatted_s = tmp_task.formatted(
|
575
|
+
formatted_s = tmp_task.formatted(task_formating_options)
|
544
576
|
end
|
545
577
|
|
546
578
|
formatted_s
|
data/lib/timr/timr.rb
CHANGED
@@ -157,7 +157,7 @@ module TheFox
|
|
157
157
|
# Task Path
|
158
158
|
task_file_path = BasicModel.create_path_by_id(@tasks_path, task.id)
|
159
159
|
|
160
|
-
# Save
|
160
|
+
# Save Task to file.
|
161
161
|
task.save_to_file(task_file_path)
|
162
162
|
|
163
163
|
@stack.start(track)
|
@@ -341,7 +341,7 @@ module TheFox
|
|
341
341
|
# Task Path
|
342
342
|
task_file_path = BasicModel.create_path_by_id(@tasks_path, task.id)
|
343
343
|
|
344
|
-
# Save
|
344
|
+
# Save Task to file.
|
345
345
|
task.save_to_file(task_file_path)
|
346
346
|
|
347
347
|
@stack.push(track)
|
@@ -359,6 +359,48 @@ module TheFox
|
|
359
359
|
continue(options)
|
360
360
|
end
|
361
361
|
|
362
|
+
# Remove current running Track.
|
363
|
+
#
|
364
|
+
# Options:
|
365
|
+
#
|
366
|
+
# - `:stack` (Boolean) Reset the Stack.
|
367
|
+
def reset(options = Hash.new)
|
368
|
+
stack_opt = options.fetch(:stack, false)
|
369
|
+
|
370
|
+
track = @stack.current_track
|
371
|
+
if track && track.running?
|
372
|
+
task = track.task
|
373
|
+
if task
|
374
|
+
task.reset
|
375
|
+
end
|
376
|
+
|
377
|
+
track.remove
|
378
|
+
|
379
|
+
# Save Task to file.
|
380
|
+
task.save_to_file
|
381
|
+
|
382
|
+
@stack.remove(track)
|
383
|
+
end
|
384
|
+
|
385
|
+
if stack_opt
|
386
|
+
@stack.tracks.each do |track|
|
387
|
+
task = track.task
|
388
|
+
if task
|
389
|
+
task.reset
|
390
|
+
|
391
|
+
# Save Task to file.
|
392
|
+
task.save_to_file
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
@stack.reset
|
397
|
+
end
|
398
|
+
|
399
|
+
@stack.save_to_file
|
400
|
+
|
401
|
+
nil
|
402
|
+
end
|
403
|
+
|
362
404
|
# Create a new [Task](rdoc-ref:TheFox::Timr::Model::Task) based on the `options` Hash. Will not be started or something else.
|
363
405
|
#
|
364
406
|
# Uses [Task#create_task_from_hash](rdoc-ref:TheFox::Timr::Model::Task::create_task_from_hash) to create a new Task instance and [BasicModel#create_path_by_id](rdoc-ref:TheFox::Timr::Model::BasicModel.create_path_by_id) to create a new file path.
|
@@ -383,7 +425,7 @@ module TheFox
|
|
383
425
|
# Task Path
|
384
426
|
task_file_path = BasicModel.create_path_by_id(@tasks_path, task.id)
|
385
427
|
|
386
|
-
# Save
|
428
|
+
# Save Task to file.
|
387
429
|
task.save_to_file(task_file_path)
|
388
430
|
|
389
431
|
# Leave Stack untouched.
|
data/lib/timr/version.rb
CHANGED
data/man/index.txt
CHANGED
@@ -6,6 +6,7 @@ timr-pause(1) timr-pause.1.ronn
|
|
6
6
|
timr-pop(1) timr-pop.1.ronn
|
7
7
|
timr-push(1) timr-push.1.ronn
|
8
8
|
timr-report(1) timr-report.1.ronn
|
9
|
+
timr-reset(1) timr-reset.1.ronn
|
9
10
|
timr-start(1) timr-start.1.ronn
|
10
11
|
timr-status(1) timr-status.1.ronn
|
11
12
|
timr-stop(1) timr-stop.1.ronn
|