logbook-ruby 0.1.4 → 0.1.5
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 +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
|