tlog 0.0.1
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 +15 -0
- data/.gitignore +3 -0
- data/LICENSE +339 -0
- data/README.md +17 -0
- data/TODO +5 -0
- data/bin/tlog +22 -0
- data/lib/tlog/application.rb +81 -0
- data/lib/tlog/command/active.rb +45 -0
- data/lib/tlog/command/create.rb +32 -0
- data/lib/tlog/command/delete.rb +24 -0
- data/lib/tlog/command/display.rb +144 -0
- data/lib/tlog/command/init.rb +34 -0
- data/lib/tlog/command/start.rb +31 -0
- data/lib/tlog/command/stop.rb +25 -0
- data/lib/tlog/command/test.rb +32 -0
- data/lib/tlog/command.rb +14 -0
- data/lib/tlog/entity/active_log.rb +11 -0
- data/lib/tlog/entity/entry.rb +90 -0
- data/lib/tlog/entity/log.rb +102 -0
- data/lib/tlog/error.rb +6 -0
- data/lib/tlog/format/date_time.rb +7 -0
- data/lib/tlog/format/seconds.rb +13 -0
- data/lib/tlog/input.rb +10 -0
- data/lib/tlog/output.rb +29 -0
- data/lib/tlog/storage/disk.rb +277 -0
- data/lib/tlog/version.rb +3 -0
- data/lib/tlog.rb +47 -0
- data/tlog.gemspec +28 -0
- metadata +142 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
|
2
|
+
class Tlog::Command::Display < Tlog::Command
|
3
|
+
|
4
|
+
def name
|
5
|
+
"display"
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute(input, output)
|
9
|
+
raise Tlog::Error::CommandInvalid, "Logging invalid" unless display(input.args[0], input.options[:length], output)
|
10
|
+
end
|
11
|
+
|
12
|
+
def options(parser, options)
|
13
|
+
parser.banner = "usage: tlog log <log_name>"
|
14
|
+
|
15
|
+
parser.on("-l", "--length <length_threshold>") do |length|
|
16
|
+
options[:length] = length
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def display(log_name, length_threshold, output)
|
23
|
+
storage.in_branch do |wd|
|
24
|
+
if log_name
|
25
|
+
display_log(log_name, length_threshold, output)
|
26
|
+
else
|
27
|
+
display_all(length_threshold, output)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def display_log(log_name, length_threshold, output)
|
33
|
+
log = storage.require_log(log_name)
|
34
|
+
log_length = log.goal_length
|
35
|
+
entries = log.entries
|
36
|
+
if storage.start_time_string && is_current_log_name?(log_name)
|
37
|
+
start_time = Time.parse(storage.start_time_string)
|
38
|
+
end
|
39
|
+
return if length_exceeds_threshold?(log_length, length_threshold)
|
40
|
+
print_log_name(log_name, output)
|
41
|
+
print_header(output)
|
42
|
+
print_current(log_name, log_length, start_time, output)
|
43
|
+
display_entries(entries, output) if entries
|
44
|
+
print_footer(log, log_length, output)
|
45
|
+
end
|
46
|
+
|
47
|
+
def display_all(length_threshold, output)
|
48
|
+
storage.all_log_dirs.each do |log_path|
|
49
|
+
log_basename = log_path.basename.to_s
|
50
|
+
display_log(log_basename, length_threshold, output)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def display_entries(entries, output)
|
55
|
+
if entries.size > 0
|
56
|
+
entries.each do |entry|
|
57
|
+
out_str = "\t%-4s %16s %14s %14s %s" % [
|
58
|
+
date_time_format.timestamp(entry.time[:start]),
|
59
|
+
date_time_format.timestamp(entry.time[:end]),
|
60
|
+
seconds_format.duration(entry.length.to_s),
|
61
|
+
entry.owner,
|
62
|
+
entry.description,
|
63
|
+
]
|
64
|
+
output.line(out_str)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def print_footer(log, log_length, output)
|
70
|
+
output.line "-" * 100
|
71
|
+
print_total(log, output)
|
72
|
+
print_time_left(log, output)
|
73
|
+
end
|
74
|
+
|
75
|
+
def print_header(output)
|
76
|
+
output.line("\tStart End Duration Owner Description")
|
77
|
+
end
|
78
|
+
|
79
|
+
def print_total(log, output)
|
80
|
+
#output.line("-") * 52
|
81
|
+
duration = log.duration
|
82
|
+
if storage.current_log_name == log.name
|
83
|
+
duration += storage.time_since_start
|
84
|
+
end
|
85
|
+
output.line("\tTotal%45s " % seconds_format.duration(duration))
|
86
|
+
end
|
87
|
+
|
88
|
+
def print_log_name(log_name, output)
|
89
|
+
output.line_yellow("Log: #{log_name}")
|
90
|
+
end
|
91
|
+
|
92
|
+
def print_time_left(log, output)
|
93
|
+
if log.goal
|
94
|
+
log_goal = log.goal
|
95
|
+
if (storage.current_log_name == log.name)
|
96
|
+
current_time = Time.now - storage.cur_start_time
|
97
|
+
log_goal -= current_time.to_i
|
98
|
+
end
|
99
|
+
log_goal = 0 if log_goal < 0
|
100
|
+
output.line_red("\tTime left: %39s" % seconds_format.duration(log_goal))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
#should be added to entries array, not its own seperate thing
|
105
|
+
def print_current(log_name, log_length, current_start_time, output)
|
106
|
+
if is_current_log_name?(log_name)
|
107
|
+
formatted_length = seconds_format.duration storage.time_since_start
|
108
|
+
out_str = out_str = "\t%-4s %16s %14s %14s %s" % [
|
109
|
+
date_time_format.timestamp(current_start_time),
|
110
|
+
nil,
|
111
|
+
formatted_length,
|
112
|
+
storage.cur_entry_owner,
|
113
|
+
storage.cur_entry_description,
|
114
|
+
]
|
115
|
+
output.line(out_str)
|
116
|
+
storage.time_since_start
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def length_exceeds_threshold?(log_length, length_threshold)
|
121
|
+
if length_threshold and log_length
|
122
|
+
length_threshold = ChronicDuration.parse(length_threshold)
|
123
|
+
if log_length - length_threshold > 0
|
124
|
+
true
|
125
|
+
else
|
126
|
+
false
|
127
|
+
end
|
128
|
+
else
|
129
|
+
false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def update_log_length(log_length)
|
134
|
+
log_length - storage.time_since_start if log_length
|
135
|
+
end
|
136
|
+
|
137
|
+
def is_current_log_name?(log_name)
|
138
|
+
if storage.current_log_name == log_name
|
139
|
+
true
|
140
|
+
else
|
141
|
+
false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
class Tlog::Command::Init < Tlog::Command
|
3
|
+
|
4
|
+
def name
|
5
|
+
"init"
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute(input,output)
|
9
|
+
if input.args[0].nil?
|
10
|
+
raise Tlog::Error::CommandInvalid, "Project already initialized" unless @storage.init_project
|
11
|
+
else
|
12
|
+
raise Tlog::Error::CommandInvalid, "Command invalid"
|
13
|
+
end
|
14
|
+
#elsif input.args[1].nil?
|
15
|
+
# arg1 = input.args.shift
|
16
|
+
# #output.line("arg at 0 was #{arg1}")
|
17
|
+
#else
|
18
|
+
# arg1 = input.args.shift
|
19
|
+
# arg2 = input.args.shift
|
20
|
+
# #output.line("arg at 0 was #{arg1}")
|
21
|
+
# #output.line("arg at 1 was #{arg2}")
|
22
|
+
# raise Tlog::Error::CommandInvalid, "Command invalid"
|
23
|
+
#end
|
24
|
+
|
25
|
+
#@storage.init_project
|
26
|
+
end
|
27
|
+
|
28
|
+
def options(parser, options)
|
29
|
+
parser.banner = "usage: tlog init"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
class Tlog::Command::Start < Tlog::Command
|
3
|
+
|
4
|
+
def name
|
5
|
+
"start"
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute(input, output)
|
9
|
+
raise Tlog::Error::CommandInvalid, "Must specify log name" unless input.args[0]
|
10
|
+
start(input.args[0], input.options[:description])
|
11
|
+
end
|
12
|
+
|
13
|
+
def options(parser, options)
|
14
|
+
parser.banner = "usage: tlog start <log_name>"
|
15
|
+
|
16
|
+
parser.on("-d", "--description <description>") do |description|
|
17
|
+
options[:description] = description
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def start(log_name, entry_description)
|
24
|
+
storage.in_branch do |wd|
|
25
|
+
log = storage.require_log(log_name)
|
26
|
+
raise Tlog::Error::CommandNotFound, "Time log '#{log_name}' does not exist" unless log
|
27
|
+
current_owner = storage.cur_entry_owner
|
28
|
+
storage.start_log(log, entry_description)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Tlog::Command::Stop < Tlog::Command
|
2
|
+
|
3
|
+
def name
|
4
|
+
"stop"
|
5
|
+
end
|
6
|
+
|
7
|
+
def execute(input, output)
|
8
|
+
raise Tlog::Error::CommandInvalid, "Must specify log name" unless input.args[0]
|
9
|
+
stop(input.args[0])
|
10
|
+
end
|
11
|
+
|
12
|
+
def options(parser, options)
|
13
|
+
parser.banner = "usage: tlog stop <log_name>"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def stop(log_name)
|
19
|
+
storage.in_branch do |wd|
|
20
|
+
log = storage.require_log(log_name)
|
21
|
+
storage.stop_log(log)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
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
|
data/lib/tlog/command.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
|
2
|
+
class Tlog::Entity::Entry
|
3
|
+
|
4
|
+
attr_accessor :hex
|
5
|
+
attr_accessor :path
|
6
|
+
|
7
|
+
def initialize(path, hex)
|
8
|
+
@path = path
|
9
|
+
@hex = hex
|
10
|
+
end
|
11
|
+
|
12
|
+
def length
|
13
|
+
time_difference if time[:start] && time[:end]
|
14
|
+
end
|
15
|
+
|
16
|
+
def create(parent, current)
|
17
|
+
FileUtils.mkdir_p(path)
|
18
|
+
time_log = current[:start_time].to_s + " " + Time.now.to_s
|
19
|
+
write_file(parent_path, parent)
|
20
|
+
write_file(time_path, time_log.strip)
|
21
|
+
write_file(description_path, current[:description])
|
22
|
+
write_file(owner_path, current[:owner])
|
23
|
+
end
|
24
|
+
|
25
|
+
def parent_hex
|
26
|
+
read_file(parent_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def time
|
30
|
+
time_hash = {}
|
31
|
+
start_time_string = ""
|
32
|
+
end_time_string = ""
|
33
|
+
time_contents = read_file(time_path)
|
34
|
+
return time_hash unless time_contents
|
35
|
+
split_contents = time_contents.split(" ", 6)
|
36
|
+
for i in 0..2
|
37
|
+
start_time_string += split_contents[i] + " "
|
38
|
+
end
|
39
|
+
for i in 3..5
|
40
|
+
end_time_string += split_contents[i] + " "
|
41
|
+
end
|
42
|
+
time_hash[:start] = Time.parse(start_time_string)
|
43
|
+
time_hash[:end] = Time.parse(end_time_string)
|
44
|
+
return time_hash
|
45
|
+
end
|
46
|
+
|
47
|
+
def description
|
48
|
+
read_file(description_path)
|
49
|
+
end
|
50
|
+
|
51
|
+
def owner
|
52
|
+
read_file(owner_path)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def write_file(path, content)
|
58
|
+
File.open(path, 'w'){ |f| f.write(content)}
|
59
|
+
end
|
60
|
+
|
61
|
+
def read_file(path)
|
62
|
+
if File.exists?(path)
|
63
|
+
contents = File.read(path)
|
64
|
+
contents.strip
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def time_difference
|
69
|
+
time_hash = time
|
70
|
+
difference = time_hash[:end] - time_hash[:start]
|
71
|
+
difference.to_i
|
72
|
+
end
|
73
|
+
|
74
|
+
def parent_path
|
75
|
+
File.join(@path, 'PARENT')
|
76
|
+
end
|
77
|
+
|
78
|
+
def time_path
|
79
|
+
File.join(@path, 'TIME')
|
80
|
+
end
|
81
|
+
|
82
|
+
def description_path
|
83
|
+
File.join(@path, 'DESCRIPTION')
|
84
|
+
end
|
85
|
+
|
86
|
+
def owner_path
|
87
|
+
File.join(@path, 'OWNER')
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
|
2
|
+
class Tlog::Entity::Log
|
3
|
+
|
4
|
+
attr_accessor :name
|
5
|
+
attr_accessor :goal
|
6
|
+
attr_accessor :entries
|
7
|
+
attr_accessor :path
|
8
|
+
|
9
|
+
def initialize(log_path = nil)
|
10
|
+
@entries = []
|
11
|
+
if log_path
|
12
|
+
@name = log_path.basename.to_s
|
13
|
+
@path = log_path
|
14
|
+
@goal = goal_length
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def goal_length
|
19
|
+
if File.exists?(goal_path)
|
20
|
+
contents = File.read(goal_path)
|
21
|
+
contents.strip
|
22
|
+
contents.to_i
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def entries
|
28
|
+
log_entries = []
|
29
|
+
hex_value = head_hex_value
|
30
|
+
return log_entries unless hex_value
|
31
|
+
begin
|
32
|
+
entry = Tlog::Entity::Entry.new(entry_path(hex_value), hex_value)
|
33
|
+
hex_value = entry.parent_hex
|
34
|
+
log_entries.push(entry)
|
35
|
+
end until hex_value == "none"
|
36
|
+
return log_entries
|
37
|
+
end
|
38
|
+
|
39
|
+
def duration
|
40
|
+
dur = 0
|
41
|
+
entries.each do |entry|
|
42
|
+
dur += entry.length
|
43
|
+
end
|
44
|
+
dur
|
45
|
+
end
|
46
|
+
|
47
|
+
def create
|
48
|
+
unless Dir.exists?(@path)
|
49
|
+
FileUtils.mkdir_p(@path)
|
50
|
+
File.open(goal_path, 'w'){|f| f.write(@goal)} if @goal
|
51
|
+
true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_entry(current)
|
56
|
+
entry_hex = generate_random_hex
|
57
|
+
new_entry = Tlog::Entity::Entry.new(entry_path(entry_hex), entry_hex)
|
58
|
+
head_hex_value ? parent_hex = head_hex_value : parent_hex = "none"
|
59
|
+
|
60
|
+
update_head(entry_hex)
|
61
|
+
new_entry.create(parent_hex, current)
|
62
|
+
update_goal(new_entry.length) if goal_length
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_head(entry_hex)
|
66
|
+
File.open(head_path, 'w'){|f| f.write(entry_hex)}
|
67
|
+
end
|
68
|
+
|
69
|
+
def update_goal(entry_length)
|
70
|
+
new_length = goal_length - entry_length
|
71
|
+
File.open(goal_path, 'w'){|f| f.write(new_length)}
|
72
|
+
end
|
73
|
+
|
74
|
+
def delete
|
75
|
+
FileUtils.rm_rf(@path) if Dir.exists?(@path)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def head_hex_value
|
81
|
+
if File.exists?(head_path)
|
82
|
+
head_content = File.read(head_path)
|
83
|
+
head_content.strip if head_content
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def goal_path
|
88
|
+
File.join(@path, 'GOAL')
|
89
|
+
end
|
90
|
+
|
91
|
+
def head_path
|
92
|
+
File.join(@path, 'HEAD')
|
93
|
+
end
|
94
|
+
|
95
|
+
def entry_path(hex)
|
96
|
+
File.join(@path, hex)
|
97
|
+
end
|
98
|
+
|
99
|
+
def generate_random_hex
|
100
|
+
SecureRandom.hex(13)
|
101
|
+
end
|
102
|
+
end
|
data/lib/tlog/error.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
class Tlog::Format::Seconds
|
2
|
+
|
3
|
+
def self.duration(total_seconds)
|
4
|
+
output = ""
|
5
|
+
total_seconds ||= 0
|
6
|
+
total_seconds = total_seconds.to_i
|
7
|
+
mm, ss = total_seconds.divmod(60)
|
8
|
+
hh, mm = mm.divmod(60)
|
9
|
+
output = "%2s:%02d:%02d" % [hh, mm, ss]
|
10
|
+
return output
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
data/lib/tlog/input.rb
ADDED
data/lib/tlog/output.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
class Tlog::Output
|
3
|
+
|
4
|
+
attr_accessor :stdout
|
5
|
+
attr_accessor :stderr
|
6
|
+
|
7
|
+
def initialize(stdout,stderr)
|
8
|
+
@stdout = stdout
|
9
|
+
@stderr = stderr
|
10
|
+
end
|
11
|
+
|
12
|
+
def error(err)
|
13
|
+
@stderr.puts err
|
14
|
+
end
|
15
|
+
|
16
|
+
def line(out)
|
17
|
+
@stdout.puts out
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def line_yellow(out)
|
22
|
+
@stdout.puts out.yellow
|
23
|
+
end
|
24
|
+
|
25
|
+
def line_red(out)
|
26
|
+
@stdout.puts out.red
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|