timr 0.3.0 → 0.4.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/.ackrc +9 -0
- data/.editorconfig +1 -0
- data/.env.example +7 -0
- data/.github/CONTRIBUTING.md +32 -0
- data/.github/ISSUE_TEMPLATE.md +13 -0
- data/.gitignore +8 -2
- data/.rdoc_options +21 -0
- data/.travis.yml +10 -7
- data/Gemfile +8 -0
- data/README.md +216 -3
- data/bin/.gitignore +2 -0
- data/bin/README.md +17 -0
- data/bin/build.sh +14 -0
- data/bin/build_api.sh +14 -0
- data/bin/build_coverage.sh +23 -0
- data/bin/build_info.sh +27 -0
- data/bin/build_man.sh +41 -0
- data/bin/clean.sh +14 -0
- data/bin/dev_setup.sh +19 -0
- data/bin/install.sh +49 -0
- data/bin/publish +38 -0
- data/bin/release.sh +35 -0
- data/bin/test.sh +19 -0
- data/bin/timr +20 -40
- data/bin/timr_bash_completion.sh +337 -0
- data/bin/uninstall.sh +24 -0
- data/lib/timr.rb +36 -8
- data/lib/timr/command/basic_command.rb +170 -0
- data/lib/timr/command/continue_command.rb +86 -0
- data/lib/timr/command/help_command.rb +137 -0
- data/lib/timr/command/log_command.rb +297 -0
- data/lib/timr/command/pause_command.rb +89 -0
- data/lib/timr/command/pop_command.rb +176 -0
- data/lib/timr/command/push_command.rb +141 -0
- data/lib/timr/command/report_command.rb +689 -0
- data/lib/timr/command/start_command.rb +172 -0
- data/lib/timr/command/status_command.rb +198 -0
- data/lib/timr/command/stop_command.rb +127 -0
- data/lib/timr/command/task_command.rb +318 -0
- data/lib/timr/command/track_command.rb +381 -0
- data/lib/timr/command/version_command.rb +18 -0
- data/lib/timr/duration.rb +159 -0
- data/lib/timr/exception/timr_error.rb +113 -0
- data/lib/timr/ext/time.rb +12 -0
- data/lib/timr/helper/datetime_helper.rb +128 -0
- data/lib/timr/helper/terminal_helper.rb +58 -0
- data/lib/timr/helper/translation_helper.rb +45 -0
- data/lib/timr/model/basic_model.rb +287 -0
- data/lib/timr/model/config.rb +48 -0
- data/lib/timr/model/foreign_id_db.rb +84 -0
- data/lib/timr/model/stack.rb +161 -0
- data/lib/timr/model/task.rb +1039 -0
- data/lib/timr/model/track.rb +589 -0
- data/lib/timr/progressbar.rb +41 -0
- data/lib/timr/simple_opt_parser.rb +230 -0
- data/lib/timr/status.rb +70 -0
- data/lib/timr/table.rb +88 -0
- data/lib/timr/timr.rb +500 -558
- data/lib/timr/version.rb +4 -15
- data/man/.gitignore +2 -0
- data/man/_footer +3 -0
- data/man/timr-continue.1 +48 -0
- data/man/timr-continue.1.ronn +39 -0
- data/man/timr-ftime.7 +77 -0
- data/man/timr-ftime.7.ronn +57 -0
- data/man/timr-log.1 +109 -0
- data/man/timr-log.1.ronn +87 -0
- data/man/timr-pause.1 +56 -0
- data/man/timr-pause.1.ronn +45 -0
- data/man/timr-pop.1 +66 -0
- data/man/timr-pop.1.ronn +53 -0
- data/man/timr-push.1 +25 -0
- data/man/timr-push.1.ronn +20 -0
- data/man/timr-report.1 +228 -0
- data/man/timr-report.1.ronn +193 -0
- data/man/timr-start.1 +100 -0
- data/man/timr-start.1.ronn +82 -0
- data/man/timr-status.1 +53 -0
- data/man/timr-status.1.ronn +42 -0
- data/man/timr-stop.1 +75 -0
- data/man/timr-stop.1.ronn +60 -0
- data/man/timr-task.1 +147 -0
- data/man/timr-task.1.ronn +115 -0
- data/man/timr-track.1 +109 -0
- data/man/timr-track.1.ronn +89 -0
- data/man/timr.1 +119 -0
- data/man/timr.1.ronn +68 -0
- data/timr.gemspec +18 -3
- data/timr.sublime-project +20 -1
- metadata +142 -23
- data/Makefile +0 -12
- data/Makefile.common +0 -56
- data/lib/timr/stack.rb +0 -81
- data/lib/timr/task.rb +0 -258
- data/lib/timr/track.rb +0 -167
- data/lib/timr/window.rb +0 -259
- data/lib/timr/window_help.rb +0 -41
- data/lib/timr/window_tasks.rb +0 -30
- data/lib/timr/window_test.rb +0 -20
- data/lib/timr/window_timeline.rb +0 -33
- data/tests/tc_stack.rb +0 -121
- data/tests/tc_task.rb +0 -190
- data/tests/tc_track.rb +0 -144
- data/tests/tc_window.rb +0 -428
- data/tests/ts_all.rb +0 -6
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module TheFox
|
3
|
+
module Timr
|
4
|
+
module Command
|
5
|
+
|
6
|
+
class VersionCommand < BasicCommand
|
7
|
+
|
8
|
+
# See BasicCommand#run.
|
9
|
+
def run
|
10
|
+
puts "#{NAME} #{VERSION} (#{DATE})"
|
11
|
+
puts "#{HOMEPAGE}"
|
12
|
+
end
|
13
|
+
|
14
|
+
end # class VersionCommand
|
15
|
+
|
16
|
+
end # module Command
|
17
|
+
end # module Timr
|
18
|
+
end # module TheFox
|
@@ -0,0 +1,159 @@
|
|
1
|
+
|
2
|
+
require 'chronic_duration'
|
3
|
+
|
4
|
+
module TheFox
|
5
|
+
module Timr
|
6
|
+
|
7
|
+
# Uses seconds as basis.
|
8
|
+
class Duration
|
9
|
+
|
10
|
+
include Error
|
11
|
+
|
12
|
+
# Basis Data (Integer)
|
13
|
+
#
|
14
|
+
# A Duration is stored as seconds.
|
15
|
+
attr_reader :seconds
|
16
|
+
|
17
|
+
def initialize(seconds = 0)
|
18
|
+
@seconds = seconds.to_i
|
19
|
+
|
20
|
+
@smh_seconds = nil
|
21
|
+
@smh_minutes = nil
|
22
|
+
@smh_hours = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# Seconds, Minutes, Hours as Array
|
26
|
+
def to_smh
|
27
|
+
@smh_seconds = @seconds
|
28
|
+
|
29
|
+
@smh_hours = @smh_seconds / 3600
|
30
|
+
|
31
|
+
@smh_seconds -= @smh_hours * 3600
|
32
|
+
@smh_minutes = @smh_seconds / 60
|
33
|
+
|
34
|
+
@smh_seconds -= @smh_minutes * 60
|
35
|
+
|
36
|
+
[@smh_seconds, @smh_minutes, @smh_hours]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Converts seconds to `nh nm ns` format. Where `n` is a number.
|
40
|
+
def to_human
|
41
|
+
dur_opt = {
|
42
|
+
:format => :short,
|
43
|
+
:limit_to_hours => true,
|
44
|
+
:keep_zero => false,
|
45
|
+
:units => 2,
|
46
|
+
}
|
47
|
+
h = ChronicDuration.output(@seconds, dur_opt)
|
48
|
+
|
49
|
+
h ? h : '---'
|
50
|
+
end
|
51
|
+
|
52
|
+
# Man-days, Man-hours
|
53
|
+
#
|
54
|
+
# Man Unit:
|
55
|
+
#
|
56
|
+
# - 8 hours are 1 man-day.
|
57
|
+
# - 5 man-days are 1 man-week, and so on.
|
58
|
+
def to_man_days
|
59
|
+
if @seconds < 28800 # 8 * 3600 = 1 man-day
|
60
|
+
to_human
|
61
|
+
elsif @seconds < 144000 # 5 * 28800 = 1 man-week
|
62
|
+
seconds = @seconds
|
63
|
+
|
64
|
+
man_days = seconds / 28800
|
65
|
+
|
66
|
+
seconds -= man_days * 28800
|
67
|
+
man_hours = seconds / 3600
|
68
|
+
|
69
|
+
'%dd %dh' % [man_days, man_hours]
|
70
|
+
else
|
71
|
+
seconds = @seconds
|
72
|
+
|
73
|
+
man_weeks = seconds / 144000
|
74
|
+
|
75
|
+
seconds -= man_weeks * 144000
|
76
|
+
man_days = seconds / 28800
|
77
|
+
|
78
|
+
seconds -= man_days * 28800
|
79
|
+
man_hours = seconds / 3600
|
80
|
+
|
81
|
+
'%dw %dd %dh' % [man_weeks, man_days, man_hours]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Adds two Durations.
|
86
|
+
def +(duration)
|
87
|
+
case duration
|
88
|
+
when Integer
|
89
|
+
Duration.new(@seconds + duration)
|
90
|
+
when Duration
|
91
|
+
Duration.new(@seconds + duration.seconds)
|
92
|
+
when nil
|
93
|
+
Duration.new(@seconds)
|
94
|
+
else
|
95
|
+
raise DurationError, "Wrong type #{duration.class} for '+' function."
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Subtract two Durations.
|
100
|
+
def -(duration)
|
101
|
+
case duration
|
102
|
+
when Integer
|
103
|
+
diff = @seconds - duration
|
104
|
+
when Duration
|
105
|
+
diff = @seconds - duration.seconds
|
106
|
+
else
|
107
|
+
raise DurationError, "Wrong type #{duration.class} for '+' function."
|
108
|
+
end
|
109
|
+
|
110
|
+
if diff < 0
|
111
|
+
diff = 0
|
112
|
+
end
|
113
|
+
Duration.new(diff)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Lesser
|
117
|
+
def <(obj)
|
118
|
+
case obj
|
119
|
+
when Integer
|
120
|
+
@seconds < obj
|
121
|
+
when Duration
|
122
|
+
@seconds < obj.to_i
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Greater
|
127
|
+
def >(obj)
|
128
|
+
case obj
|
129
|
+
when Integer
|
130
|
+
@seconds > obj
|
131
|
+
when Duration
|
132
|
+
@seconds > obj.to_i
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# String
|
137
|
+
def to_s
|
138
|
+
@seconds.to_s
|
139
|
+
end
|
140
|
+
|
141
|
+
# Use this instead of `Duration.seconds`.
|
142
|
+
def to_i
|
143
|
+
@seconds
|
144
|
+
end
|
145
|
+
|
146
|
+
# All methods in this block are static.
|
147
|
+
class << self
|
148
|
+
|
149
|
+
# Parse a String using [ChronicDuration](https://rubygems.org/gems/chronic_duration) and create a new Duration instance.
|
150
|
+
def parse(str)
|
151
|
+
Duration.new(ChronicDuration.parse(str))
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
end # class Duration
|
157
|
+
|
158
|
+
end # module Timr
|
159
|
+
end # module TheFox
|
@@ -0,0 +1,113 @@
|
|
1
|
+
|
2
|
+
module TheFox
|
3
|
+
module Timr
|
4
|
+
|
5
|
+
# # Errors Inheritance Tree
|
6
|
+
#
|
7
|
+
# TimrError is the basic class for all Timr Error classes. It subclasses from [Rubys StandardError](http://ruby-doc.org/core-2.4.1/Exception.html).
|
8
|
+
#
|
9
|
+
# - TimrError
|
10
|
+
# - ModelError
|
11
|
+
# - IdError
|
12
|
+
# - ForeignIdError
|
13
|
+
# - TaskError
|
14
|
+
# - TrackError
|
15
|
+
# - StackError
|
16
|
+
# - DateTimeHelperError
|
17
|
+
# - TerminalHelperError
|
18
|
+
# - CommandError
|
19
|
+
# - ContinueCommandError
|
20
|
+
# - LogCommandError
|
21
|
+
# - PauseCommandError
|
22
|
+
# - PopCommandError
|
23
|
+
# - PushCommandError
|
24
|
+
# - ReportCommandError
|
25
|
+
# - StartCommandError
|
26
|
+
# - StatusCommandError
|
27
|
+
# - StopCommandError
|
28
|
+
# - TaskCommandError
|
29
|
+
# - TrackCommandError
|
30
|
+
# - HelpCommandError
|
31
|
+
# - DurationError
|
32
|
+
# - ThisShouldNeverHappenError
|
33
|
+
module Error
|
34
|
+
|
35
|
+
class TimrError < StandardError
|
36
|
+
end
|
37
|
+
|
38
|
+
class ModelError < TimrError
|
39
|
+
end
|
40
|
+
|
41
|
+
class IdError < ModelError
|
42
|
+
end
|
43
|
+
|
44
|
+
class ForeignIdError < ModelError
|
45
|
+
end
|
46
|
+
|
47
|
+
class TaskError < ModelError
|
48
|
+
end
|
49
|
+
|
50
|
+
class TrackError < ModelError
|
51
|
+
end
|
52
|
+
|
53
|
+
class StackError < ModelError
|
54
|
+
end
|
55
|
+
|
56
|
+
# Use by TheFox::Timr::Helper::DateTimeHelper.
|
57
|
+
class DateTimeHelperError < TimrError
|
58
|
+
end
|
59
|
+
|
60
|
+
# Use by TheFox::Timr::Helper::TerminalHelper.
|
61
|
+
class TerminalHelperError < TimrError
|
62
|
+
end
|
63
|
+
|
64
|
+
class CommandError < TimrError
|
65
|
+
end
|
66
|
+
|
67
|
+
class ContinueCommandError < CommandError
|
68
|
+
end
|
69
|
+
|
70
|
+
class LogCommandError < CommandError
|
71
|
+
end
|
72
|
+
|
73
|
+
class PauseCommandError < CommandError
|
74
|
+
end
|
75
|
+
|
76
|
+
class PopCommandError < CommandError
|
77
|
+
end
|
78
|
+
|
79
|
+
class PushCommandError < CommandError
|
80
|
+
end
|
81
|
+
|
82
|
+
class ReportCommandError < CommandError
|
83
|
+
end
|
84
|
+
|
85
|
+
class StartCommandError < CommandError
|
86
|
+
end
|
87
|
+
|
88
|
+
class StatusCommandError < CommandError
|
89
|
+
end
|
90
|
+
|
91
|
+
class StopCommandError < CommandError
|
92
|
+
end
|
93
|
+
|
94
|
+
class TaskCommandError < CommandError
|
95
|
+
end
|
96
|
+
|
97
|
+
class TrackCommandError < CommandError
|
98
|
+
end
|
99
|
+
|
100
|
+
class HelpCommandError < CommandError
|
101
|
+
end
|
102
|
+
|
103
|
+
class DurationError < TimrError
|
104
|
+
end
|
105
|
+
|
106
|
+
# This should really never happen. But you never know. ;)
|
107
|
+
class ThisShouldNeverHappenError < TimrError
|
108
|
+
end
|
109
|
+
|
110
|
+
end # module Error
|
111
|
+
|
112
|
+
end # module Timr
|
113
|
+
end # module TheFox
|
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
# Extend core [Time](http://ruby-doc.org/stdlib-2.4.0/libdoc/time/rdoc/Time.html) class.
|
3
|
+
class Time
|
4
|
+
|
5
|
+
# See [How do I get elapsed time in milliseconds in Ruby?](http://stackoverflow.com/questions/1414951/how-do-i-get-elapsed-time-in-milliseconds-in-ruby)
|
6
|
+
#
|
7
|
+
# Not really used. Only to measure the execution from start to end. See `bin/timr`.
|
8
|
+
def to_ms
|
9
|
+
(self.to_f * 1000.0).to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
|
2
|
+
require 'date'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module TheFox
|
6
|
+
module Timr
|
7
|
+
module Helper
|
8
|
+
|
9
|
+
# This class should *NOT* replace [Time](http://ruby-doc.org/core-2.4.1/Time.html), [Date](http://ruby-doc.org/stdlib-2.4.1/libdoc/date/rdoc/Date.html) or [DateTime](http://ruby-doc.org/stdlib-2.4.1/libdoc/date/rdoc/DateTime.html).
|
10
|
+
class DateTimeHelper
|
11
|
+
|
12
|
+
# All methods in this block are static.
|
13
|
+
class << self
|
14
|
+
|
15
|
+
include TheFox::Timr::Error
|
16
|
+
|
17
|
+
# Convert String to Date.
|
18
|
+
def convert_date(date)
|
19
|
+
case date
|
20
|
+
when String
|
21
|
+
Time.parse(date).utc.to_date
|
22
|
+
when nil
|
23
|
+
Time.now.utc.to_date
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Convert String to Time.
|
28
|
+
def convert_time(time)
|
29
|
+
case time
|
30
|
+
when String
|
31
|
+
Time.parse(time).utc
|
32
|
+
when nil
|
33
|
+
Time.now.utc
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Parse an argv Array.
|
38
|
+
#
|
39
|
+
# Create a `from` and `to` for a day.
|
40
|
+
def parse_day_argv(argv)
|
41
|
+
day = argv.shift
|
42
|
+
from = Time.parse("#{day} 00:00:00")
|
43
|
+
to = Time.parse("#{day} 23:59:59")
|
44
|
+
[from, to]
|
45
|
+
end
|
46
|
+
|
47
|
+
# Parse an argv Array.
|
48
|
+
#
|
49
|
+
# Create a `from` and `to` for a month.
|
50
|
+
def parse_month_argv(argv)
|
51
|
+
parts = argv.shift.split('-').map{ |s| s.to_i }
|
52
|
+
if parts.count == 1
|
53
|
+
y = Date.today.year
|
54
|
+
m = parts.first
|
55
|
+
else
|
56
|
+
y, m = parts
|
57
|
+
end
|
58
|
+
if y < 2000 # shit
|
59
|
+
y += 2000
|
60
|
+
end
|
61
|
+
|
62
|
+
start_date = Date.new(y, m, 1)
|
63
|
+
end_date = Date.new(y, m, -1)
|
64
|
+
|
65
|
+
from = Time.parse("#{start_date.strftime('%F')} 00:00:00")
|
66
|
+
to = Time.parse("#{end_date.strftime('%F')} 23:59:59")
|
67
|
+
[from, to]
|
68
|
+
end
|
69
|
+
|
70
|
+
# Parse an argv Array.
|
71
|
+
#
|
72
|
+
# Create a `from` and `to` for a year.
|
73
|
+
def parse_year_argv(argv)
|
74
|
+
y = argv.shift
|
75
|
+
if y
|
76
|
+
y = y.to_i
|
77
|
+
else
|
78
|
+
y = Date.today.year
|
79
|
+
end
|
80
|
+
if y < 2000 # shit
|
81
|
+
y += 2000
|
82
|
+
end
|
83
|
+
|
84
|
+
start_date = Date.new(y, 1, 1)
|
85
|
+
end_date = Date.new(y, 12, -1)
|
86
|
+
|
87
|
+
from = Time.parse("#{start_date.strftime('%F')} 00:00:00")
|
88
|
+
to = Time.parse("#{end_date.strftime('%F')} 23:59:59")
|
89
|
+
[from, to]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Create a Time instance from a Hash.
|
93
|
+
#
|
94
|
+
# Options:
|
95
|
+
#
|
96
|
+
# - `:date` String
|
97
|
+
# - `:time` String
|
98
|
+
def get_datetime_from_options(options = Hash.new)
|
99
|
+
date_opt = options.fetch(:date, nil)
|
100
|
+
time_opt = options.fetch(:time, nil)
|
101
|
+
|
102
|
+
if date_opt && !time_opt
|
103
|
+
raise DateTimeHelperError, 'You also need to set a time when giving a date.'
|
104
|
+
end
|
105
|
+
|
106
|
+
datetime_a = []
|
107
|
+
if date_opt
|
108
|
+
datetime_a << date_opt
|
109
|
+
end
|
110
|
+
if time_opt
|
111
|
+
datetime_a << time_opt
|
112
|
+
end
|
113
|
+
|
114
|
+
if datetime_a.count > 0
|
115
|
+
datetime_s = datetime_a.join(' ')
|
116
|
+
Time.parse(datetime_s).utc
|
117
|
+
else
|
118
|
+
Time.now.utc
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end # class DateTimeHelper
|
125
|
+
|
126
|
+
end # module Helper
|
127
|
+
end # module Timr
|
128
|
+
end # module TheFox
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
module TheFox
|
3
|
+
module Timr
|
4
|
+
module Helper
|
5
|
+
|
6
|
+
# This class helps with Terminal operations.
|
7
|
+
class TerminalHelper
|
8
|
+
|
9
|
+
# All methods in this block are static.
|
10
|
+
class << self
|
11
|
+
|
12
|
+
include TheFox::Timr::Error
|
13
|
+
|
14
|
+
# Run external editor via EDITOR environment variable.
|
15
|
+
def run_external_editor(text = nil)
|
16
|
+
case text
|
17
|
+
when Array
|
18
|
+
text = text.join("\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
if !ENV['EDITOR'] || ENV['EDITOR'].length == 0
|
22
|
+
raise TerminalHelperError, 'EDITOR environment variable not set.'
|
23
|
+
end
|
24
|
+
|
25
|
+
tmpfile = Tempfile.new('timr_message')
|
26
|
+
if text
|
27
|
+
tmpfile.write(text)
|
28
|
+
end
|
29
|
+
tmpfile.close
|
30
|
+
|
31
|
+
system_s = '%s %s' % [ENV['EDITOR'], tmpfile.path]
|
32
|
+
system(system_s)
|
33
|
+
|
34
|
+
tmpfile.open
|
35
|
+
tmpfile_lines = tmpfile.read
|
36
|
+
tmpfile.close
|
37
|
+
|
38
|
+
tmpfile_lines
|
39
|
+
.split("\n")
|
40
|
+
.select{ |row| row[0] != '#' }
|
41
|
+
.join("\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
# Print help to external editor.
|
45
|
+
def external_editor_help(edit_text)
|
46
|
+
edit_text << '# This is a comment.'
|
47
|
+
edit_text << '# The first line should be a sentence. Sentence have dots at the end.'
|
48
|
+
edit_text << '# The second line should be empty, if you provide a more detailed'
|
49
|
+
edit_text << '# description from on the third line. Like on Git.'
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end # class TerminalHelper
|
55
|
+
|
56
|
+
end # module Helper
|
57
|
+
end # module Timr
|
58
|
+
end # module TheFox
|