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