tlog 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|