logbook-ruby 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.tool-versions +1 -1
- data/lib/logbook.rb +9 -5
- data/lib/logbook/builder.rb +6 -8
- data/lib/logbook/clock.rb +43 -0
- data/lib/logbook/log_entry.rb +1 -9
- data/lib/logbook/page.rb +22 -17
- data/lib/logbook/parser.rb +6 -5
- data/lib/logbook/property.rb +20 -0
- data/lib/logbook/task.rb +22 -0
- data/lib/logbook/task_definition.rb +8 -7
- data/lib/logbook/task_entry.rb +9 -4
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 33922c3808312335aa817342c0057f110fa07d4f
|
4
|
+
data.tar.gz: cbe8daaf3b9811df06e7846fbbc0f879f0c8e573
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c0e74d9d25e2cf64b288b6485213fb3eccd236e6db5073ab39c6e8ef57eb9040616431e359c331366ef8518bd032a2c4bd60a17abbe41f5b5232dbcb2288429
|
7
|
+
data.tar.gz: 83dfb63517c4597bed2a636e4cc53179f9d15cc34272cf2abfaf9fcbc02619797113c8eebed2a91a09aff7a5358951914ad3bc94d330b076251df413d4a18438
|
data/.tool-versions
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby 2.
|
1
|
+
ruby 2.4.1
|
data/lib/logbook.rb
CHANGED
@@ -1,15 +1,19 @@
|
|
1
1
|
module Logbook
|
2
|
-
VERSION = "0.1.
|
2
|
+
VERSION = "0.1.5"
|
3
3
|
|
4
4
|
require "logbook/builder"
|
5
|
+
require "logbook/clock"
|
6
|
+
require "logbook/log_entry"
|
5
7
|
require "logbook/page"
|
6
8
|
require "logbook/parser"
|
7
|
-
require "logbook/
|
9
|
+
require "logbook/property"
|
8
10
|
require "logbook/task"
|
9
11
|
require "logbook/task_definition"
|
10
12
|
require "logbook/task_entry"
|
11
13
|
|
12
|
-
Duration = Struct.new(:minutes)
|
13
|
-
|
14
|
-
|
14
|
+
Duration = Struct.new(:minutes) do
|
15
|
+
def +(other_duration)
|
16
|
+
Duration.new(self.minutes + other_duration.minutes)
|
17
|
+
end
|
18
|
+
end
|
15
19
|
end
|
data/lib/logbook/builder.rb
CHANGED
@@ -2,7 +2,7 @@ require "parslet"
|
|
2
2
|
|
3
3
|
module Logbook
|
4
4
|
class Builder < Parslet::Transform
|
5
|
-
rule(tag: simple(:tag)) {
|
5
|
+
rule(tag: simple(:tag)) { Property.new(tag.to_s) }
|
6
6
|
|
7
7
|
rule(property: {name: simple(:name), value: simple(:value)}) do
|
8
8
|
Property.new(name.to_s, value.to_s)
|
@@ -16,21 +16,19 @@ module Logbook
|
|
16
16
|
end
|
17
17
|
|
18
18
|
rule(task_definition: subtree(:task)) do
|
19
|
-
properties = task[:properties].
|
20
|
-
tags = task[:properties].select { |item| item.instance_of?(Tag) }
|
19
|
+
properties = task[:properties].map { |property| [property.name, property] }.to_h
|
21
20
|
line_number, _ = task[:status].line_and_column
|
22
21
|
note = task[:note].to_s.strip.chomp
|
23
22
|
|
24
|
-
TaskDefinition.new(line_number, task[:title].to_s, task[:status].to_s, properties,
|
23
|
+
TaskDefinition.new(line_number, task[:title].to_s, task[:status].to_s, properties, note)
|
25
24
|
end
|
26
25
|
|
27
26
|
rule(task_entry: subtree(:task)) do
|
28
|
-
properties = task[:properties].
|
29
|
-
tags = task[:properties].select { |item| item.instance_of?(Tag) }
|
27
|
+
properties = task[:properties].map { |property| [property.name, property] }.to_h
|
30
28
|
line_number, _ = task[:status].line_and_column
|
31
29
|
note = task[:note].to_s.strip.chomp
|
32
30
|
|
33
|
-
TaskEntry.new(line_number, task[:time].to_s, task[:title].to_s, task[:status].to_s, properties,
|
31
|
+
TaskEntry.new(line_number, task[:time].to_s, task[:title].to_s, task[:status].to_s, properties, note)
|
34
32
|
end
|
35
33
|
|
36
34
|
def self.build(contents)
|
@@ -40,7 +38,7 @@ module Logbook
|
|
40
38
|
logbook_page = entries_and_properties.inject(Page.new) do |page, entry_or_property|
|
41
39
|
case entry_or_property
|
42
40
|
when Property
|
43
|
-
current_properties[entry_or_property.name] = entry_or_property
|
41
|
+
current_properties[entry_or_property.name] = entry_or_property
|
44
42
|
page
|
45
43
|
when TaskDefinition, TaskEntry
|
46
44
|
entry_or_property.merge_page_properties(current_properties)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Logbook
|
2
|
+
class Clock
|
3
|
+
def tick(entry)
|
4
|
+
case entry
|
5
|
+
when LogEntry
|
6
|
+
if running?
|
7
|
+
yield(tracked_entry, minutes_in_between(tracked_entry, entry))
|
8
|
+
reset
|
9
|
+
end
|
10
|
+
when TaskEntry
|
11
|
+
if running?
|
12
|
+
yield(tracked_entry, minutes_in_between(tracked_entry, entry))
|
13
|
+
reset
|
14
|
+
end
|
15
|
+
|
16
|
+
if entry.starts_clock?
|
17
|
+
track(entry)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
# ignore
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
attr_accessor :tracked_entry
|
26
|
+
|
27
|
+
def reset
|
28
|
+
@tracked_entry = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def running?
|
32
|
+
!tracked_entry.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def track(entry)
|
36
|
+
@tracked_entry = entry
|
37
|
+
end
|
38
|
+
|
39
|
+
def minutes_in_between(previous_entry, current_entry)
|
40
|
+
Duration.new((current_entry.recorded_at.to_time - previous_entry.recorded_at.to_time) / 60)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/logbook/log_entry.rb
CHANGED
@@ -5,19 +5,11 @@ module Logbook
|
|
5
5
|
attr_accessor :properties
|
6
6
|
|
7
7
|
def recorded_at
|
8
|
-
date = self.properties[DATE_PROPERTY_NAME]
|
8
|
+
date = self.properties[DATE_PROPERTY_NAME].value
|
9
9
|
time = self.time
|
10
10
|
|
11
11
|
DateTime.parse(date + " " + time)
|
12
12
|
rescue ArgumentError
|
13
13
|
end
|
14
|
-
|
15
|
-
def starts_clock?
|
16
|
-
false
|
17
|
-
end
|
18
|
-
|
19
|
-
def stops_clock?
|
20
|
-
true
|
21
|
-
end
|
22
14
|
end
|
23
15
|
end
|
data/lib/logbook/page.rb
CHANGED
@@ -1,39 +1,44 @@
|
|
1
1
|
module Logbook
|
2
2
|
class Page
|
3
|
-
attr_accessor :entries, :properties
|
3
|
+
attr_accessor :clock, :entries, :logged_work, :properties
|
4
4
|
|
5
5
|
def initialize
|
6
|
+
@clock = Clock.new
|
6
7
|
@entries = []
|
8
|
+
@logged_work = {}
|
7
9
|
end
|
8
10
|
|
9
11
|
def add(entry)
|
10
12
|
entries << entry
|
13
|
+
clock.tick(entry) { |tracked_entry, duration| logged_work.store(tracked_entry, duration) }
|
11
14
|
end
|
12
15
|
|
13
16
|
def entry_at(line_number)
|
14
17
|
entries.reverse.find { |entry| entry.line_number <= line_number }
|
15
18
|
end
|
16
19
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
def logged_time
|
21
|
+
logged_work.map { |entry, duration| duration }.reduce(Duration.new(0), &:+)
|
22
|
+
end
|
23
|
+
|
24
|
+
def tasks
|
25
|
+
entries.reduce({}) do |tasks, entry|
|
26
|
+
case entry
|
27
|
+
when TaskEntry, TaskDefinition
|
28
|
+
if entry.belongs_to_task?
|
29
|
+
task = tasks[entry.task_id] ||= Task.new(entry.task_id)
|
30
|
+
|
31
|
+
if logged_work.has_key?(entry)
|
32
|
+
task.log_work(entry, logged_work[entry])
|
33
|
+
else
|
34
|
+
task.add_entry(entry)
|
35
|
+
end
|
25
36
|
end
|
26
37
|
else
|
27
|
-
[current_entry, duration]
|
28
38
|
end
|
29
|
-
end
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
private
|
35
|
-
def minutes_in_between(previous_entry, current_entry)
|
36
|
-
(current_entry.recorded_at.to_time - previous_entry.recorded_at.to_time) / 60
|
40
|
+
tasks
|
41
|
+
end
|
37
42
|
end
|
38
43
|
end
|
39
44
|
end
|
data/lib/logbook/parser.rb
CHANGED
@@ -7,7 +7,8 @@ module Logbook
|
|
7
7
|
rule(:newline) { match("\n") }
|
8
8
|
|
9
9
|
rule(:text) { match("[^\n]") }
|
10
|
-
rule(:text_line) { text.repeat(
|
10
|
+
rule(:text_line) { text.repeat(1) }
|
11
|
+
rule(:text_line_with_newline) { text.repeat(0) >> newline }
|
11
12
|
rule(:label) { match["a-zA-Z"] >> match["a-zA-Z0-9_-"].repeat }
|
12
13
|
|
13
14
|
rule(:property_value) { match("[^\\n\\]\\[]").repeat(1) }
|
@@ -27,19 +28,19 @@ module Logbook
|
|
27
28
|
rule(:status) { str("[") >> label.as(:status) >> str("]") }
|
28
29
|
rule(:title) { text.repeat }
|
29
30
|
|
30
|
-
rule(:note_line) { time.absent? >> status.absent? >> property.absent? >> text_line
|
31
|
+
rule(:note_line) { time.absent? >> status.absent? >> property.absent? >> (text_line_with_newline | text_line)}
|
31
32
|
rule(:note) { note_line.repeat }
|
32
33
|
|
33
34
|
rule(:log_entry) { time >> whitespace >> newline.maybe >> note.as(:note) }
|
34
35
|
|
35
36
|
rule(:task_definition) do
|
36
|
-
status >> whitespace >> title.as(:title) >>
|
37
|
+
status >> whitespace >> title.as(:title) >> newline >>
|
37
38
|
newline.repeat >> property_or_tag_list.as(:properties) >>
|
38
39
|
newline.repeat >> note.as(:note)
|
39
40
|
end
|
40
41
|
|
41
42
|
rule(:task_entry) do
|
42
|
-
time >> whitespace >> status >> whitespace >> title.as(:title) >>
|
43
|
+
time >> whitespace >> status >> whitespace >> title.as(:title) >> newline >>
|
43
44
|
newline.repeat >> property_or_tag_list.as(:properties) >>
|
44
45
|
newline.repeat >> note.as(:note)
|
45
46
|
end
|
@@ -50,7 +51,7 @@ module Logbook
|
|
50
51
|
task_entry.as(:task_entry) |
|
51
52
|
log_entry.as(:log_entry) |
|
52
53
|
property.as(:property) |
|
53
|
-
text_line
|
54
|
+
(text_line_with_newline | text_line)
|
54
55
|
).repeat
|
55
56
|
end
|
56
57
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Logbook
|
2
|
+
class Property
|
3
|
+
attr_accessor :name, :value
|
4
|
+
|
5
|
+
TAG_VALUE = nil
|
6
|
+
|
7
|
+
def initialize(name, value = TAG_VALUE)
|
8
|
+
@name = name
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_value?
|
13
|
+
!is_tag? && !value.strip.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def is_tag?
|
17
|
+
self.value === TAG_VALUE
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/logbook/task.rb
CHANGED
@@ -1,9 +1,31 @@
|
|
1
1
|
module Logbook
|
2
2
|
class Task
|
3
|
+
attr_accessor :entries, :id, :properties, :status, :title, :time_logged
|
4
|
+
|
3
5
|
DONE = "Done"
|
4
6
|
PAUSE = "Pause"
|
5
7
|
REOPEN = "Reopen"
|
6
8
|
RESUME = "Resume"
|
7
9
|
START = "Start"
|
10
|
+
TASK_ID_PROPERTY = "ID"
|
11
|
+
|
12
|
+
def initialize(id)
|
13
|
+
@entries = []
|
14
|
+
@id = id
|
15
|
+
@properties = {}
|
16
|
+
@time_logged = Duration.new(0)
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_entry(entry)
|
20
|
+
self.properties.merge!(entry.properties)
|
21
|
+
self.status = entry.status
|
22
|
+
self.title = entry.title
|
23
|
+
end
|
24
|
+
|
25
|
+
def log_work(entry, duration)
|
26
|
+
add_entry(entry)
|
27
|
+
|
28
|
+
self.time_logged += duration
|
29
|
+
end
|
8
30
|
end
|
9
31
|
end
|
@@ -1,15 +1,16 @@
|
|
1
1
|
module Logbook
|
2
|
-
class TaskDefinition < Struct.new(:line_number, :title, :status, :properties, :
|
3
|
-
def
|
4
|
-
self.properties
|
2
|
+
class TaskDefinition < Struct.new(:line_number, :title, :status, :properties, :note)
|
3
|
+
def belongs_to_task?
|
4
|
+
self.properties.has_key?(Task::TASK_ID_PROPERTY) &&
|
5
|
+
self.properties[Task::TASK_ID_PROPERTY].has_value?
|
5
6
|
end
|
6
7
|
|
7
|
-
def
|
8
|
-
|
8
|
+
def merge_page_properties(properties)
|
9
|
+
self.properties = properties.merge(self.properties)
|
9
10
|
end
|
10
11
|
|
11
|
-
def
|
12
|
-
|
12
|
+
def task_id
|
13
|
+
self.properties[Task::TASK_ID_PROPERTY].value
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|
data/lib/logbook/task_entry.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
1
|
require "date"
|
2
2
|
|
3
3
|
module Logbook
|
4
|
-
class TaskEntry < Struct.new(:line_number, :time, :title, :status, :properties, :
|
4
|
+
class TaskEntry < Struct.new(:line_number, :time, :title, :status, :properties, :note)
|
5
5
|
DATE_PROPERTY_NAME = "Date"
|
6
6
|
|
7
|
+
def belongs_to_task?
|
8
|
+
self.properties.has_key?(Task::TASK_ID_PROPERTY) &&
|
9
|
+
self.properties[Task::TASK_ID_PROPERTY].has_value?
|
10
|
+
end
|
11
|
+
|
7
12
|
def merge_page_properties(properties)
|
8
13
|
self.properties = properties.merge(self.properties)
|
9
14
|
end
|
10
15
|
|
11
16
|
def recorded_at
|
12
|
-
date = self.properties[DATE_PROPERTY_NAME]
|
17
|
+
date = self.properties[DATE_PROPERTY_NAME].value
|
13
18
|
time = self.time
|
14
19
|
|
15
20
|
DateTime.parse(date + " " + time)
|
@@ -20,8 +25,8 @@ module Logbook
|
|
20
25
|
[Task::START, Task::RESUME, Task::REOPEN].include?(self.status)
|
21
26
|
end
|
22
27
|
|
23
|
-
def
|
24
|
-
|
28
|
+
def task_id
|
29
|
+
self.properties[Task::TASK_ID_PROPERTY].value
|
25
30
|
end
|
26
31
|
end
|
27
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logbook-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriel Malkas
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-03-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -114,9 +114,11 @@ files:
|
|
114
114
|
- bin/setup
|
115
115
|
- lib/logbook.rb
|
116
116
|
- lib/logbook/builder.rb
|
117
|
+
- lib/logbook/clock.rb
|
117
118
|
- lib/logbook/log_entry.rb
|
118
119
|
- lib/logbook/page.rb
|
119
120
|
- lib/logbook/parser.rb
|
121
|
+
- lib/logbook/property.rb
|
120
122
|
- lib/logbook/task.rb
|
121
123
|
- lib/logbook/task_definition.rb
|
122
124
|
- lib/logbook/task_entry.rb
|
@@ -140,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
142
|
version: '0'
|
141
143
|
requirements: []
|
142
144
|
rubyforge_project:
|
143
|
-
rubygems_version: 2.
|
145
|
+
rubygems_version: 2.6.11
|
144
146
|
signing_key:
|
145
147
|
specification_version: 4
|
146
148
|
summary: Ruby library for the Logbook file format
|