tlog 0.0.8 → 0.0.9
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 +8 -8
- data/README.md +75 -4
- data/lib/tlog.rb +7 -1
- data/lib/tlog/application.rb +11 -29
- data/lib/tlog/command/active.rb +12 -4
- data/lib/tlog/command/checkout.rb +31 -0
- data/lib/tlog/command/create.rb +6 -2
- data/lib/tlog/command/delete.rb +6 -1
- data/lib/tlog/command/display.rb +12 -8
- data/lib/tlog/command/help.rb +38 -0
- data/lib/tlog/command/init.rb +4 -0
- data/lib/tlog/command/owner.rb +32 -0
- data/lib/tlog/command/points.rb +32 -0
- data/lib/tlog/command/start.rb +14 -8
- data/lib/tlog/command/state.rb +32 -0
- data/lib/tlog/command/stop.rb +14 -6
- data/lib/tlog/command_suite.rb +34 -0
- data/lib/tlog/entity/active_log.rb +3 -1
- data/lib/tlog/entity/entry.rb +1 -9
- data/lib/tlog/entity/log.rb +63 -8
- data/lib/tlog/error.rb +6 -0
- data/lib/tlog/output.rb +4 -0
- data/lib/tlog/storage/disk.rb +58 -23
- data/tlog.gemspec +3 -3
- metadata +9 -5
- data/lib/tlog/command/test.rb +0 -32
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MGExZGJhMGE1OTc4ZTRkNTVjMGZhNjhjNDEzYjdhZmQ4MGMwMTRmYQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NzIwMGE5NDQ5ZmFkY2RhOTkxNGEzNDA0NmNkZjdiN2Y3N2RhNDg5Zg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NjE1MTg1N2FmNzY3Y2QwZTIxOWFhNzk4MDgwZmRhMDlhNTRiZWVlYTZlMDg5
|
10
|
+
MDhiOWFlNjQyMDc0ZWIzOGQ0NjdlNjY4MmIyNWVlZDQ4ZmNhZDEzMjliMzFj
|
11
|
+
YTE4NWExYWI0NDNjMzk5M2JjNTZhNjEzNmZhMGJhMTc4NThhMTc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
M2RmMDI5NGNkZDFlNTIwMTUyN2EyZDAyNjkyYWYwZjRhNWMzMjBjOWIyMGZi
|
14
|
+
YjcwMTBiZDE5ZjcyYzc1OWFiN2JkOWE5ODY4YjdkOTdiOGRhNjcwNDBiMTYw
|
15
|
+
MTBjNjlkZDgwMjUxNzdlNTllMjA2NzcyN2Q2MDUyNzMwOGVmYWU=
|
data/README.md
CHANGED
@@ -1,9 +1,80 @@
|
|
1
1
|
tlog
|
2
|
-
|
2
|
+
============================================
|
3
3
|
|
4
|
-
CLI time tracking
|
4
|
+
A git-based CLI to help you with time tracking on your projects.
|
5
5
|
|
6
|
-
##
|
7
|
-
|
6
|
+
## Installing
|
7
|
+
```bash
|
8
|
+
$ sudo gem install tlog
|
9
|
+
```
|
8
10
|
|
11
|
+
## Usage
|
12
|
+
* Navigate to a directory that has a git repo
|
9
13
|
|
14
|
+
### Create a time log
|
15
|
+
```bash
|
16
|
+
$ tlog create example
|
17
|
+
```
|
18
|
+
|
19
|
+
### Check out a time log
|
20
|
+
```bash
|
21
|
+
$ tlog checkout example
|
22
|
+
```
|
23
|
+
|
24
|
+
### Create a time log with a time goal
|
25
|
+
```bash
|
26
|
+
$ tlog create example --goal 4hr
|
27
|
+
```
|
28
|
+
|
29
|
+
### Start a new task on a time log
|
30
|
+
```bash
|
31
|
+
$ tlog start example -d "My task description"
|
32
|
+
```
|
33
|
+
|
34
|
+
### Stop the current task
|
35
|
+
```bash
|
36
|
+
$ tlog stop example
|
37
|
+
```
|
38
|
+
|
39
|
+
### Show active time logs and label the current one, if it exists
|
40
|
+
```bash
|
41
|
+
$ tlog active
|
42
|
+
All Time Logs:
|
43
|
+
testing
|
44
|
+
feature1(current)
|
45
|
+
bug fix
|
46
|
+
feature2
|
47
|
+
```
|
48
|
+
|
49
|
+
### Display all the current time logs and their tasks, total time logged and time left.
|
50
|
+
```bash
|
51
|
+
$ tlog display
|
52
|
+
Log: example1
|
53
|
+
Start End Duration Owner Description
|
54
|
+
May 29, 11:57PM May 29, 11:58PM 0:01:13 chriwend My Description
|
55
|
+
----------------------------------------------------------------------------------------------------
|
56
|
+
Total 0:01:13
|
57
|
+
Log: example2
|
58
|
+
Start End Duration Owner Description
|
59
|
+
May 30, 12:00AM 0:02:26 chriwend Fixing bug
|
60
|
+
May 30, 12:00AM May 30, 12:00AM 0:00:10 chriwend (no description)
|
61
|
+
----------------------------------------------------------------------------------------------------
|
62
|
+
Total 0:02:36
|
63
|
+
Time left: 3:57:24
|
64
|
+
```
|
65
|
+
|
66
|
+
### Delete a time log
|
67
|
+
```bash
|
68
|
+
$ tlog delete example
|
69
|
+
```
|
70
|
+
|
71
|
+
## Collaboration
|
72
|
+
More to come on this after I test it...
|
73
|
+
|
74
|
+
## Contributing
|
75
|
+
|
76
|
+
Please look at the TODO for possible additional features. Use [Github issues](https://github.com/cewendel/tlog/issues) to track bugs and feature requests.
|
77
|
+
|
78
|
+
## Licence
|
79
|
+
|
80
|
+
GNU GENERAL PUBLIC LICENCE Version 2
|
data/lib/tlog.rb
CHANGED
@@ -24,14 +24,20 @@ require 'chronic'
|
|
24
24
|
require "optparse"
|
25
25
|
require "colorize"
|
26
26
|
|
27
|
+
require 'tlog/command_suite'
|
28
|
+
|
27
29
|
require 'tlog/command'
|
28
|
-
require 'tlog/command/init'
|
29
30
|
require 'tlog/command/start'
|
30
31
|
require 'tlog/command/stop'
|
31
32
|
require 'tlog/command/active'
|
32
33
|
require 'tlog/command/delete'
|
33
34
|
require 'tlog/command/display'
|
34
35
|
require 'tlog/command/create'
|
36
|
+
require 'tlog/command/help'
|
37
|
+
require 'tlog/command/checkout'
|
38
|
+
require 'tlog/command/state'
|
39
|
+
require 'tlog/command/owner'
|
40
|
+
require 'tlog/command/points'
|
35
41
|
|
36
42
|
require 'tlog/storage/disk'
|
37
43
|
|
data/lib/tlog/application.rb
CHANGED
@@ -18,44 +18,26 @@ class Tlog::Application
|
|
18
18
|
@output.error($!)
|
19
19
|
@output.error(@optparse.to_s)
|
20
20
|
rescue Tlog::Error::CommandInvalid
|
21
|
-
@output.error(command_name + "
|
21
|
+
@output.error(command_name + " command invalid: " + $!.message)
|
22
|
+
@output.error(@optparse.to_s)
|
23
|
+
rescue Tlog::Error::CommandNotFound, OptionParser::MissingArgument
|
24
|
+
@output.error($!)
|
22
25
|
@output.error(@optparse.to_s)
|
23
|
-
rescue Tlog::Error::CommandNotFound
|
24
|
-
@output.error(command_name +": " + $!.message) # format class?
|
25
26
|
rescue
|
26
27
|
@output.error($!)
|
27
28
|
end
|
28
29
|
return outcome
|
29
30
|
end
|
30
31
|
|
31
|
-
def all_commands
|
32
|
-
storage = working_dir_storage
|
33
|
-
commands = [
|
34
|
-
Tlog::Command::Init.new,
|
35
|
-
Tlog::Command::Start.new,
|
36
|
-
Tlog::Command::Stop.new,
|
37
|
-
Tlog::Command::Active.new,
|
38
|
-
Tlog::Command::Delete.new,
|
39
|
-
Tlog::Command::Display.new,
|
40
|
-
Tlog::Command::Create.new,
|
41
|
-
]
|
42
|
-
commands.each do |command|
|
43
|
-
command.storage = storage
|
44
|
-
command.seconds_format = Tlog::Format::Seconds
|
45
|
-
command.date_time_format = Tlog::Format::DateTime
|
46
|
-
end
|
47
|
-
return commands
|
48
|
-
end
|
49
|
-
|
50
|
-
|
51
32
|
private
|
52
33
|
|
53
|
-
def working_dir_storage
|
54
|
-
Tlog::Storage::Disk.new('.')
|
55
|
-
end
|
56
|
-
|
57
34
|
def find(command_name)
|
58
|
-
|
35
|
+
commands = Tlog::Command_Suite.commands
|
36
|
+
command = nil
|
37
|
+
commands.each do |cmd|
|
38
|
+
return cmd if cmd.name == command_name
|
39
|
+
end
|
40
|
+
command
|
59
41
|
end
|
60
42
|
|
61
43
|
def prepare_command(command)
|
@@ -72,7 +54,7 @@ class Tlog::Application
|
|
72
54
|
command.execute(@input, @output)
|
73
55
|
true
|
74
56
|
else
|
75
|
-
raise Tlog::Error::CommandNotFound, "Command not found"
|
57
|
+
raise Tlog::Error::CommandNotFound, "Command not found, use 'tlog help' for list of commands"
|
76
58
|
end
|
77
59
|
end
|
78
60
|
|
data/lib/tlog/command/active.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
+
|
1
2
|
class Tlog::Command::Active < Tlog::Command
|
2
3
|
|
3
4
|
def name
|
4
5
|
"active"
|
5
6
|
end
|
6
7
|
|
8
|
+
def description
|
9
|
+
"prints out all active time logs, the time log in-progress if there is one. Or the currently checked-out time log"
|
10
|
+
end
|
11
|
+
|
7
12
|
def execute(input, output)
|
8
|
-
output.line("execute on active command") #change to out
|
9
13
|
print_time_entry(output)
|
10
14
|
end
|
11
15
|
|
@@ -23,6 +27,7 @@ class Tlog::Command::Active < Tlog::Command
|
|
23
27
|
log_name = log.basename.to_s
|
24
28
|
active_log = Tlog::Entity::Active_Log.new(log_name)
|
25
29
|
active_log.current = true if storage.current_log_name == log_name
|
30
|
+
active_log.checked_out = true if storage.checkout_value == log_name
|
26
31
|
active_logs.push(active_log)
|
27
32
|
end
|
28
33
|
output.line_yellow("All Time Logs:")
|
@@ -32,12 +37,15 @@ class Tlog::Command::Active < Tlog::Command
|
|
32
37
|
|
33
38
|
def print_logs(active_logs, output)
|
34
39
|
active_logs.each do |active_log|
|
40
|
+
out_line = active_log.name
|
35
41
|
if active_log.current
|
36
|
-
out_line
|
37
|
-
out_line << " (current)"
|
42
|
+
out_line << " (in-progress)"
|
38
43
|
output.line_red(out_line);
|
44
|
+
elsif active_log.checked_out
|
45
|
+
out_line << " (checked_out)"
|
46
|
+
output.line_blue(out_line)
|
39
47
|
else
|
40
|
-
output.line(
|
48
|
+
output.line(out_line)
|
41
49
|
end
|
42
50
|
end
|
43
51
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
class Tlog::Command::Checkout < Tlog::Command
|
3
|
+
|
4
|
+
def name
|
5
|
+
"checkout"
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
"checkouts a time log in order to start tasks on"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(input, output)
|
13
|
+
raise Tlog::Error::CommandInvalid, "Must specify log name" unless input.args[0]
|
14
|
+
checkout(input.args[0])
|
15
|
+
end
|
16
|
+
|
17
|
+
def options(parser, options)
|
18
|
+
parser.banner = "usage: tlog checkout <log_name>"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def checkout(log_name)
|
24
|
+
storage.in_branch do |wd|
|
25
|
+
log = storage.require_log(log_name)
|
26
|
+
raise Tlog::Error::TimeLogNotFound, "Time log '#{log_name}' does not exist" unless log
|
27
|
+
storage.checkout_log(log)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/tlog/command/create.rb
CHANGED
@@ -5,13 +5,17 @@ class Tlog::Command::Create < Tlog::Command
|
|
5
5
|
"create"
|
6
6
|
end
|
7
7
|
|
8
|
+
def description
|
9
|
+
"creates a new time log either with no goal or with a goal"
|
10
|
+
end
|
11
|
+
|
8
12
|
def execute(input, output)
|
9
13
|
raise Tlog::Error::CommandInvalid, "Must specify log name" unless input.args[0]
|
10
14
|
|
11
15
|
log = Tlog::Entity::Log.new
|
12
16
|
log.name = input.args[0];
|
13
17
|
log.goal = ChronicDuration.parse(input.options[:goal]) if input.options[:goal]
|
14
|
-
raise Tlog::Error::
|
18
|
+
raise Tlog::Error::CommandInvalid, "Could create log: Log already exists" unless create_log(log)
|
15
19
|
end
|
16
20
|
|
17
21
|
def options(parser, options)
|
@@ -26,7 +30,7 @@ class Tlog::Command::Create < Tlog::Command
|
|
26
30
|
|
27
31
|
def create_log(log)
|
28
32
|
storage.in_branch do |wd|
|
29
|
-
storage.create_log(log)
|
33
|
+
raise Tlog::Error::CommandInvalid, "Time log '#{log.name}' already exists" unless storage.create_log(log)
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
data/lib/tlog/command/delete.rb
CHANGED
@@ -4,6 +4,10 @@ class Tlog::Command::Delete < Tlog::Command
|
|
4
4
|
"delete"
|
5
5
|
end
|
6
6
|
|
7
|
+
def description
|
8
|
+
"deletes a time log"
|
9
|
+
end
|
10
|
+
|
7
11
|
def execute(input, output)
|
8
12
|
raise Tlog::Error::CommandInvalid, "Task does not exist" unless delete(input.args[0])
|
9
13
|
end
|
@@ -17,7 +21,8 @@ class Tlog::Command::Delete < Tlog::Command
|
|
17
21
|
def delete(log_name)
|
18
22
|
storage.in_branch do |wd|
|
19
23
|
log = storage.require_log(log_name)
|
20
|
-
|
24
|
+
raise Tlog::Error::TimeLogNotFound, "Time log '#{log_name}' does not exist" unless log
|
25
|
+
storage.delete_log(log)
|
21
26
|
end
|
22
27
|
end
|
23
28
|
|
data/lib/tlog/command/display.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
|
2
|
+
# I will forever love whoever re-writes this class (badly needed)
|
2
3
|
class Tlog::Command::Display < Tlog::Command
|
3
4
|
|
4
5
|
def name
|
5
6
|
"display"
|
6
7
|
end
|
7
8
|
|
9
|
+
def description
|
10
|
+
"displays shit"
|
11
|
+
end
|
12
|
+
|
8
13
|
def execute(input, output)
|
9
14
|
raise Tlog::Error::CommandInvalid, "Logging invalid" unless display(input.args[0], input.options[:length], output)
|
10
15
|
end
|
@@ -37,7 +42,7 @@ class Tlog::Command::Display < Tlog::Command
|
|
37
42
|
start_time = Time.parse(storage.start_time_string)
|
38
43
|
end
|
39
44
|
return if length_exceeds_threshold?(log_length, length_threshold)
|
40
|
-
|
45
|
+
print_log_info(log, output)
|
41
46
|
print_header(output)
|
42
47
|
print_current(log_name, log_length, start_time, output)
|
43
48
|
display_entries(entries, output) if entries
|
@@ -54,11 +59,10 @@ class Tlog::Command::Display < Tlog::Command
|
|
54
59
|
def display_entries(entries, output)
|
55
60
|
if entries.size > 0
|
56
61
|
entries.each do |entry|
|
57
|
-
out_str = "\t%-4s %16s
|
62
|
+
out_str = "\t%-4s %16s %14s %s" % [
|
58
63
|
date_time_format.timestamp(entry.time[:start]),
|
59
64
|
date_time_format.timestamp(entry.time[:end]),
|
60
65
|
seconds_format.duration(entry.length.to_s),
|
61
|
-
entry.owner,
|
62
66
|
entry.description,
|
63
67
|
]
|
64
68
|
output.line(out_str)
|
@@ -73,7 +77,7 @@ class Tlog::Command::Display < Tlog::Command
|
|
73
77
|
end
|
74
78
|
|
75
79
|
def print_header(output)
|
76
|
-
output.line("\tStart End Duration
|
80
|
+
output.line("\tStart End Duration Description")
|
77
81
|
end
|
78
82
|
|
79
83
|
def print_total(log, output)
|
@@ -85,8 +89,9 @@ class Tlog::Command::Display < Tlog::Command
|
|
85
89
|
output.line("\tTotal%45s " % seconds_format.duration(duration))
|
86
90
|
end
|
87
91
|
|
88
|
-
def
|
89
|
-
|
92
|
+
def print_log_info(log, output)
|
93
|
+
out_str = "Log: #{log.name}\nState: #{log.state}\nPoints: #{log.points}\nOwner: #{log.owner}"
|
94
|
+
output.line_yellow(out_str)
|
90
95
|
end
|
91
96
|
|
92
97
|
def print_time_left(log, output)
|
@@ -105,11 +110,10 @@ class Tlog::Command::Display < Tlog::Command
|
|
105
110
|
def print_current(log_name, log_length, current_start_time, output)
|
106
111
|
if is_current_log_name?(log_name)
|
107
112
|
formatted_length = seconds_format.duration storage.time_since_start
|
108
|
-
out_str = out_str = "\t%-4s %16s
|
113
|
+
out_str = out_str = "\t%-4s %16s %14s %s" % [
|
109
114
|
date_time_format.timestamp(current_start_time),
|
110
115
|
nil,
|
111
116
|
formatted_length,
|
112
|
-
storage.cur_entry_owner,
|
113
117
|
storage.cur_entry_description,
|
114
118
|
]
|
115
119
|
output.line(out_str)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
class Tlog::Command::Help < Tlog::Command
|
3
|
+
|
4
|
+
def name
|
5
|
+
"help"
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
"outputs lists of commands and their descriptions"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(input, output)
|
13
|
+
commands = Tlog::Command_Suite.commands
|
14
|
+
commands.sort! {|a,b| a.name <=> b.name}
|
15
|
+
max_name_length = 0
|
16
|
+
|
17
|
+
commands.each do |command|
|
18
|
+
name_length = command.name.length
|
19
|
+
max_name_length = name_length if name_length > max_name_length
|
20
|
+
end
|
21
|
+
|
22
|
+
output.line("usage: tlog <command>")
|
23
|
+
output.line(nil)
|
24
|
+
|
25
|
+
commands.each do |command|
|
26
|
+
line = sprintf("%-#{max_name_length}s %s", command.name, command.description)
|
27
|
+
output.line(line)
|
28
|
+
end
|
29
|
+
|
30
|
+
output.line(nil)
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
|
34
|
+
def options(parser, options)
|
35
|
+
parser.banner = "usage: tlog help"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/tlog/command/init.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
class Tlog::Command::Owner < Tlog::Command
|
3
|
+
|
4
|
+
def name
|
5
|
+
"owner"
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
"changes the owner of the checked-out time log"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(input, output)
|
13
|
+
new_owner = input.args[0]
|
14
|
+
change_owner(new_owner)
|
15
|
+
end
|
16
|
+
|
17
|
+
def options(parser, options)
|
18
|
+
parser.banner = "usage: tlog owner <new_owner>"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def change_owner(new_owner)
|
24
|
+
storage.in_branch do |wd|
|
25
|
+
checked_out_log = storage.checkout_value
|
26
|
+
raise Tlog::Error::CheckoutInvalid, "No time log is checked out" unless checked_out_log
|
27
|
+
log = storage.require_log(checked_out_log)
|
28
|
+
raise Tlog::Error::TimeLogNotFound, "Time log '#{checked_out_log}' does not exist" unless log
|
29
|
+
storage.change_log_owner(log, new_owner)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
class Tlog::Command::Points < Tlog::Command
|
3
|
+
|
4
|
+
def name
|
5
|
+
"points"
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
"changes the point value of the checked-out time log"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(input, output)
|
13
|
+
new_points_value = input.args[0]
|
14
|
+
change_state(new_points_value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def options(parser, options)
|
18
|
+
parser.banner = "usage: tlog points <new_points_value>"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def change_state(points)
|
24
|
+
storage.in_branch do |wd|
|
25
|
+
checked_out_log = storage.checkout_value
|
26
|
+
raise Tlog::Error::CheckoutInvalid, "No time log is checked out" unless checked_out_log
|
27
|
+
log = storage.require_log(checked_out_log)
|
28
|
+
raise Tlog::Error::TimeLogNotFound, "Time log '#{checked_out_log}' does not exist" unless log
|
29
|
+
storage.change_log_points(log, points)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/tlog/command/start.rb
CHANGED
@@ -5,13 +5,16 @@ class Tlog::Command::Start < Tlog::Command
|
|
5
5
|
"start"
|
6
6
|
end
|
7
7
|
|
8
|
+
def description
|
9
|
+
"starts a new task for a time log"
|
10
|
+
end
|
11
|
+
|
8
12
|
def execute(input, output)
|
9
|
-
|
10
|
-
start(input.args[0], input.options[:description])
|
13
|
+
start(input.options[:description])
|
11
14
|
end
|
12
15
|
|
13
16
|
def options(parser, options)
|
14
|
-
parser.banner = "usage: tlog start
|
17
|
+
parser.banner = "usage: tlog start"
|
15
18
|
|
16
19
|
parser.on("-d", "--description <description>") do |description|
|
17
20
|
options[:description] = description
|
@@ -20,12 +23,15 @@ class Tlog::Command::Start < Tlog::Command
|
|
20
23
|
|
21
24
|
private
|
22
25
|
|
23
|
-
def start(
|
26
|
+
def start(entry_description)
|
24
27
|
storage.in_branch do |wd|
|
25
|
-
|
26
|
-
raise Tlog::Error::
|
27
|
-
|
28
|
-
|
28
|
+
checked_out_log = storage.checkout_value
|
29
|
+
raise Tlog::Error::CheckoutInvalid, "No time log is checked out" unless checked_out_log
|
30
|
+
log = storage.require_log(checked_out_log)
|
31
|
+
raise Tlog::Error::TimeLogNotFound, "Time log '#{checked_out_log}' does not exist" unless log
|
32
|
+
unless storage.start_log(log, entry_description)
|
33
|
+
raise Tlog::Error::CommandInvalid, "Time log '#{checked_out_log}' is already in progress"
|
34
|
+
end
|
29
35
|
end
|
30
36
|
end
|
31
37
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
class Tlog::Command::State < Tlog::Command
|
3
|
+
|
4
|
+
def name
|
5
|
+
"state"
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
"changes the state of the checked-out time log"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(input, output)
|
13
|
+
new_state = input.args[0]
|
14
|
+
change_state(new_state)
|
15
|
+
end
|
16
|
+
|
17
|
+
def options(parser, options)
|
18
|
+
parser.banner = "usage: tlog state <new_state>"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def change_state(new_state)
|
24
|
+
storage.in_branch do |wd|
|
25
|
+
checked_out_log = storage.checkout_value
|
26
|
+
raise Tlog::Error::CheckoutInvalid, "No time log is checked out" unless checked_out_log
|
27
|
+
log = storage.require_log(checked_out_log)
|
28
|
+
raise Tlog::Error::TimeLogNotFound, "Time log '#{checked_out_log}' does not exist" unless log
|
29
|
+
storage.change_log_state(log, new_state)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/tlog/command/stop.rb
CHANGED
@@ -4,21 +4,29 @@ class Tlog::Command::Stop < Tlog::Command
|
|
4
4
|
"stop"
|
5
5
|
end
|
6
6
|
|
7
|
+
def description
|
8
|
+
"ends a task for a time log"
|
9
|
+
end
|
10
|
+
|
7
11
|
def execute(input, output)
|
8
|
-
|
9
|
-
stop(input.args[0])
|
12
|
+
stop
|
10
13
|
end
|
11
14
|
|
12
15
|
def options(parser, options)
|
13
|
-
parser.banner = "usage: tlog stop
|
16
|
+
parser.banner = "usage: tlog stop"
|
14
17
|
end
|
15
18
|
|
16
19
|
private
|
17
20
|
|
18
|
-
def stop
|
21
|
+
def stop
|
19
22
|
storage.in_branch do |wd|
|
20
|
-
|
21
|
-
|
23
|
+
checked_out_log = storage.checkout_value
|
24
|
+
raise Tlog::Error::CheckoutInvalid, "No time log is checked out" unless checked_out_log
|
25
|
+
log = storage.require_log(checked_out_log)
|
26
|
+
raise Tlog::Error::TimeLogNotFound, "Time log '#{checked_out_log}' does not exist" unless log
|
27
|
+
unless storage.stop_log(log)
|
28
|
+
raise Tlog::Error::CommandInvalid, "Failed to stop log '#{checked_out_log}': This time log is not in progress"
|
29
|
+
end
|
22
30
|
end
|
23
31
|
end
|
24
32
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
# Simple helper class that handles an array of commands
|
3
|
+
class Tlog::Command_Suite
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def commands
|
7
|
+
storage = self.working_dir_storage
|
8
|
+
commands = [
|
9
|
+
Tlog::Command::Start.new,
|
10
|
+
Tlog::Command::Stop.new,
|
11
|
+
Tlog::Command::Help.new,
|
12
|
+
Tlog::Command::Active.new,
|
13
|
+
Tlog::Command::Delete.new,
|
14
|
+
Tlog::Command::Display.new,
|
15
|
+
Tlog::Command::Create.new,
|
16
|
+
Tlog::Command::Checkout.new,
|
17
|
+
Tlog::Command::State.new,
|
18
|
+
Tlog::Command::Points.new,
|
19
|
+
Tlog::Command::Owner.new
|
20
|
+
]
|
21
|
+
commands.each do |command|
|
22
|
+
command.storage = storage
|
23
|
+
command.seconds_format = Tlog::Format::Seconds
|
24
|
+
command.date_time_format = Tlog::Format::DateTime
|
25
|
+
end
|
26
|
+
commands
|
27
|
+
end
|
28
|
+
|
29
|
+
def working_dir_storage
|
30
|
+
Tlog::Storage::Disk.new('.')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
|
2
2
|
# Helper class for printing out active time logs
|
3
3
|
class Tlog::Entity::Active_Log
|
4
|
-
|
4
|
+
attr_reader :name
|
5
5
|
attr_accessor :current
|
6
|
+
attr_accessor :checked_out
|
6
7
|
|
7
8
|
def initialize(name)
|
8
9
|
@name = name
|
9
10
|
@current = false
|
11
|
+
@checked_out = false
|
10
12
|
end
|
11
13
|
end
|
data/lib/tlog/entity/entry.rb
CHANGED
@@ -19,7 +19,7 @@ class Tlog::Entity::Entry
|
|
19
19
|
write_file(parent_path, parent)
|
20
20
|
write_file(time_path, time_log.strip)
|
21
21
|
write_file(description_path, current[:description])
|
22
|
-
write_file(owner_path, current[:owner])
|
22
|
+
#write_file(owner_path, current[:owner])
|
23
23
|
end
|
24
24
|
|
25
25
|
def parent_hex
|
@@ -48,10 +48,6 @@ class Tlog::Entity::Entry
|
|
48
48
|
read_file(description_path)
|
49
49
|
end
|
50
50
|
|
51
|
-
def owner
|
52
|
-
read_file(owner_path)
|
53
|
-
end
|
54
|
-
|
55
51
|
private
|
56
52
|
|
57
53
|
def write_file(path, content)
|
@@ -83,8 +79,4 @@ class Tlog::Entity::Entry
|
|
83
79
|
File.join(@path, 'DESCRIPTION')
|
84
80
|
end
|
85
81
|
|
86
|
-
def owner_path
|
87
|
-
File.join(@path, 'OWNER')
|
88
|
-
end
|
89
|
-
|
90
82
|
end
|
data/lib/tlog/entity/log.rb
CHANGED
@@ -15,6 +15,20 @@ class Tlog::Entity::Log
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def create(options)
|
19
|
+
unless Dir.exists?(@path)
|
20
|
+
FileUtils.mkdir_p(@path)
|
21
|
+
state = 'open'
|
22
|
+
points = 0
|
23
|
+
owner = 'none'
|
24
|
+
state = options[:state] if options[:state]
|
25
|
+
points = options[:points] if options[:point]
|
26
|
+
owner = options[:owner] if options[:owner]
|
27
|
+
write_log(state, points, owner)
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
18
32
|
def goal_length
|
19
33
|
if File.exists?(goal_path)
|
20
34
|
contents = File.read(goal_path)
|
@@ -22,7 +36,6 @@ class Tlog::Entity::Log
|
|
22
36
|
contents.to_i
|
23
37
|
end
|
24
38
|
end
|
25
|
-
|
26
39
|
|
27
40
|
def entries
|
28
41
|
log_entries = []
|
@@ -44,15 +57,30 @@ class Tlog::Entity::Log
|
|
44
57
|
dur
|
45
58
|
end
|
46
59
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
60
|
+
def owner
|
61
|
+
read_file(owner_path) if File.exists?(owner_path)
|
62
|
+
end
|
63
|
+
|
64
|
+
def state
|
65
|
+
read_file(state_path) if File.exists?(state_path)
|
66
|
+
end
|
67
|
+
|
68
|
+
def points
|
69
|
+
read_file(points_path) if File.exists?(points_path)
|
70
|
+
end
|
71
|
+
|
72
|
+
def update_state(state)
|
73
|
+
File.open(state_path, 'w'){|f| f.write(state)}
|
54
74
|
end
|
55
75
|
|
76
|
+
def update_points(points)
|
77
|
+
File.open(points_path, 'w'){|f| f.write(points)}
|
78
|
+
end
|
79
|
+
|
80
|
+
def update_owner(owner)
|
81
|
+
File.open(owner_path, 'w'){|f| f.write(owner)}
|
82
|
+
end
|
83
|
+
|
56
84
|
def add_entry(current)
|
57
85
|
entry_hex = generate_random_hex
|
58
86
|
new_entry = Tlog::Entity::Entry.new(entry_path(entry_hex), entry_hex)
|
@@ -78,6 +106,21 @@ class Tlog::Entity::Log
|
|
78
106
|
|
79
107
|
private
|
80
108
|
|
109
|
+
def write_log(state, points, owner)
|
110
|
+
File.open(points_path, 'w'){|f| f.write(points)}
|
111
|
+
File.open(state_path, 'w'){|f| f.write(state)}
|
112
|
+
File.open(owner_path, 'w'){|f| f.write(owner)}
|
113
|
+
File.open(hold_path, 'w+'){|f| f.write('hold')}
|
114
|
+
File.open(goal_path, 'w'){|f| f.write(@goal)} if @goal
|
115
|
+
end
|
116
|
+
|
117
|
+
def read_file(path)
|
118
|
+
if File.exists?(path)
|
119
|
+
contents = File.read(path)
|
120
|
+
contents.strip
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
81
124
|
def head_hex_value
|
82
125
|
if File.exists?(head_path)
|
83
126
|
head_content = File.read(head_path)
|
@@ -85,6 +128,18 @@ class Tlog::Entity::Log
|
|
85
128
|
end
|
86
129
|
end
|
87
130
|
|
131
|
+
def points_path
|
132
|
+
File.join(@path, 'POINTS')
|
133
|
+
end
|
134
|
+
|
135
|
+
def state_path
|
136
|
+
File.join(@path, 'STATE')
|
137
|
+
end
|
138
|
+
|
139
|
+
def owner_path
|
140
|
+
File.join(@path, 'OWNER')
|
141
|
+
end
|
142
|
+
|
88
143
|
def goal_path
|
89
144
|
File.join(@path, 'GOAL')
|
90
145
|
end
|
data/lib/tlog/error.rb
CHANGED
data/lib/tlog/output.rb
CHANGED
data/lib/tlog/storage/disk.rb
CHANGED
@@ -7,8 +7,6 @@ class Tlog::Storage::Disk
|
|
7
7
|
attr_accessor :tlog_index
|
8
8
|
attr_accessor :working_dir
|
9
9
|
|
10
|
-
# Class methods 'create_repo' 'all_logs', also 'create' command
|
11
|
-
|
12
10
|
def initialize(git_dir)
|
13
11
|
@git = Git.open(find_repo(git_dir))
|
14
12
|
# Format class?
|
@@ -25,11 +23,21 @@ class Tlog::Storage::Disk
|
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
28
|
-
def
|
26
|
+
def checkout_log(log)
|
27
|
+
File.open(checkout_path, 'w'){|f| f.write(log.name)}
|
28
|
+
git.add
|
29
|
+
git.commit("Checking out time log '#{log.name}'")
|
30
|
+
end
|
31
|
+
|
32
|
+
def checkout_value
|
33
|
+
read_file(checkout_path) if File.exists?(checkout_path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_log(log, options = {})
|
29
37
|
log.path = log_path(log.name)
|
30
|
-
if log.create
|
38
|
+
if log.create(options)
|
31
39
|
git.add
|
32
|
-
git.commit("Created log #{log.name}")
|
40
|
+
git.commit("Created log '#{log.name}'")
|
33
41
|
true
|
34
42
|
else
|
35
43
|
false
|
@@ -38,14 +46,13 @@ class Tlog::Storage::Disk
|
|
38
46
|
|
39
47
|
def delete_log(log)
|
40
48
|
log.path = log_path(log.name)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
49
|
+
log.delete
|
50
|
+
delete_current(log.name)
|
51
|
+
delete_checkout(log.name)
|
52
|
+
|
53
|
+
# Recursively removes the directory that stores the time log
|
54
|
+
git.remove(log.path, {:recursive => "-r"})
|
55
|
+
git.commit("Deleted log '#{log.name}'")
|
49
56
|
end
|
50
57
|
|
51
58
|
def require_log(log_name)
|
@@ -57,7 +64,7 @@ class Tlog::Storage::Disk
|
|
57
64
|
if update_current(log.name, entry_description)
|
58
65
|
create_log(log) # Creates directory if it has not already been created
|
59
66
|
git.add
|
60
|
-
git.commit("Started log #{log.name}")
|
67
|
+
git.commit("Started log '#{log.name}'")
|
61
68
|
true
|
62
69
|
else
|
63
70
|
false
|
@@ -65,23 +72,37 @@ class Tlog::Storage::Disk
|
|
65
72
|
end
|
66
73
|
|
67
74
|
def stop_log(log)
|
68
|
-
if Dir.exists?(current_path)
|
75
|
+
if Dir.exists?(current_path) and log.name == checkout_value
|
69
76
|
current_hash = {
|
70
77
|
:name => current_log_name,
|
71
78
|
:start_time => current_start_time,
|
72
79
|
:description => current_entry_description,
|
73
|
-
:owner => cur_entry_owner
|
74
80
|
}
|
75
81
|
delete_current(current_hash[:name])
|
76
82
|
log.add_entry(current_hash)
|
77
83
|
git.add
|
78
|
-
git.commit("Stopped log #{log.name}")
|
84
|
+
git.commit("Stopped log '#{log.name}'")
|
79
85
|
true
|
80
86
|
else
|
81
87
|
false
|
82
88
|
end
|
83
89
|
end
|
84
90
|
|
91
|
+
def change_log_state(log, new_state)
|
92
|
+
log.path = log_path(log.name)
|
93
|
+
log.update_state(new_state)
|
94
|
+
end
|
95
|
+
|
96
|
+
def change_log_points(log, new_points_value)
|
97
|
+
log.path = log_path(log.name)
|
98
|
+
log.update_points(new_points_value)
|
99
|
+
end
|
100
|
+
|
101
|
+
def change_log_owner(log, new_owner)
|
102
|
+
log.path = log_path(log.name)
|
103
|
+
log.update_owner(new_owner)
|
104
|
+
end
|
105
|
+
|
85
106
|
def log_duration(log_name)
|
86
107
|
duration = 0
|
87
108
|
if current_log_name == log_name
|
@@ -105,6 +126,10 @@ class Tlog::Storage::Disk
|
|
105
126
|
current_start_time
|
106
127
|
end
|
107
128
|
|
129
|
+
def cur_user
|
130
|
+
git.config["user.email"].split('@').first rescue ''
|
131
|
+
end
|
132
|
+
|
108
133
|
def time_since_start
|
109
134
|
if Dir.exists?(current_path)
|
110
135
|
difference = Time.now - Time.parse(current_start_time)
|
@@ -118,10 +143,6 @@ class Tlog::Storage::Disk
|
|
118
143
|
Time.parse(current_start_time) if current_start_path
|
119
144
|
end
|
120
145
|
|
121
|
-
def cur_entry_owner
|
122
|
-
git.config["user.email"].split('@').first rescue ''
|
123
|
-
end
|
124
|
-
|
125
146
|
def cur_entry_description
|
126
147
|
current_entry_description
|
127
148
|
end
|
@@ -190,17 +211,27 @@ class Tlog::Storage::Disk
|
|
190
211
|
end
|
191
212
|
end
|
192
213
|
|
193
|
-
def delete_current(log_name)
|
214
|
+
def delete_current(log_name)
|
194
215
|
if Dir.exists?(current_path)
|
195
216
|
if current_log_name == log_name
|
196
217
|
FileUtils.rm_rf(current_path)
|
197
|
-
git.remove(current_path, {:recursive => 'r'})
|
218
|
+
#git.remove(current_path, {:recursive => 'r'})
|
198
219
|
end
|
199
220
|
else
|
200
221
|
false
|
201
222
|
end
|
202
223
|
end
|
203
224
|
|
225
|
+
def delete_checkout(log_name)
|
226
|
+
if File.exists?(checkout_path)
|
227
|
+
if checkout_value == log_name
|
228
|
+
FileUtils.rm(checkout_path)
|
229
|
+
end
|
230
|
+
else
|
231
|
+
fals
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
204
235
|
def write_to_current(log_name, entry_description)
|
205
236
|
# Create a current object, with a "read" method
|
206
237
|
File.open(current_name_path, 'w'){ |f| f.write(log_name)}
|
@@ -255,6 +286,10 @@ class Tlog::Storage::Disk
|
|
255
286
|
File.expand_path(File.join('tasks'))
|
256
287
|
end
|
257
288
|
|
289
|
+
def checkout_path
|
290
|
+
File.join(logs_path, 'CHECKOUT');
|
291
|
+
end
|
292
|
+
|
258
293
|
def current_path
|
259
294
|
File.expand_path(File.join('current'))
|
260
295
|
end
|
data/tlog.gemspec
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
Gem::Specification.new do |spec|
|
3
3
|
|
4
4
|
spec.name = "tlog"
|
5
|
-
spec.version = "0.0.
|
6
|
-
spec.date = "2013-
|
5
|
+
spec.version = "0.0.9"
|
6
|
+
spec.date = "2013-06-02"
|
7
7
|
|
8
8
|
spec.required_ruby_version = ">=1.9.3"
|
9
9
|
|
10
10
|
spec.summary = "CLI Project Time Logger"
|
11
|
-
spec.description = "tlog is a
|
11
|
+
spec.description = "tlog is a distributed project time and ticket tracker"
|
12
12
|
spec.license = "GPL-2"
|
13
13
|
|
14
14
|
spec.add_dependency("commander", "4.0")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tlog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Wendel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: commander
|
@@ -80,8 +80,7 @@ dependencies:
|
|
80
80
|
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.5.8
|
83
|
-
description: tlog is a
|
84
|
-
you've spend on different parts of a project
|
83
|
+
description: tlog is a distributed project time and ticket tracker
|
85
84
|
email: chriwend@umich.edu
|
86
85
|
executables:
|
87
86
|
- tlog
|
@@ -97,13 +96,18 @@ files:
|
|
97
96
|
- lib/tlog/application.rb
|
98
97
|
- lib/tlog/command.rb
|
99
98
|
- lib/tlog/command/active.rb
|
99
|
+
- lib/tlog/command/checkout.rb
|
100
100
|
- lib/tlog/command/create.rb
|
101
101
|
- lib/tlog/command/delete.rb
|
102
102
|
- lib/tlog/command/display.rb
|
103
|
+
- lib/tlog/command/help.rb
|
103
104
|
- lib/tlog/command/init.rb
|
105
|
+
- lib/tlog/command/owner.rb
|
106
|
+
- lib/tlog/command/points.rb
|
104
107
|
- lib/tlog/command/start.rb
|
108
|
+
- lib/tlog/command/state.rb
|
105
109
|
- lib/tlog/command/stop.rb
|
106
|
-
- lib/tlog/
|
110
|
+
- lib/tlog/command_suite.rb
|
107
111
|
- lib/tlog/entity/active_log.rb
|
108
112
|
- lib/tlog/entity/entry.rb
|
109
113
|
- lib/tlog/entity/log.rb
|
data/lib/tlog/command/test.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
|
2
|
-
class Tlog::Command::Test < Tlog::Command
|
3
|
-
|
4
|
-
def name
|
5
|
-
"test"
|
6
|
-
end
|
7
|
-
|
8
|
-
def execute(input,output)
|
9
|
-
output.line("execute on test called")
|
10
|
-
if input.args[0].nil?
|
11
|
-
output.line("args at 0 was nil")
|
12
|
-
elsif input.args[1].nil?
|
13
|
-
arg1 = input.args.shift
|
14
|
-
output.line("arg at 0 was #{arg1}")
|
15
|
-
else
|
16
|
-
arg1 = input.args.shift
|
17
|
-
arg2 = input.args.shift
|
18
|
-
output.line("arg at 0 was #{arg1}")
|
19
|
-
output.line("arg at 1 was #{arg2}")
|
20
|
-
raise Tlog::Error::CommandInvalid, "Command invalid"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def options(parser, options)
|
25
|
-
parser.banner = "usage: tlog test <project>"
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|