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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 4bf9e794073c670236e9601796efb0ac9260d2825f409034b3f246dbe65cb4b6
4
- data.tar.gz: 88bd820e0f1d5a07fccb6a75161c9d1e751d3d582ed3574ebaa5bdba983da17f
2
+ SHA1:
3
+ metadata.gz: 33922c3808312335aa817342c0057f110fa07d4f
4
+ data.tar.gz: cbe8daaf3b9811df06e7846fbbc0f879f0c8e573
5
5
  SHA512:
6
- metadata.gz: 0f053d6ec96f78f6214449425e7e89d6f22f35095af529ea499d04ad261498cf52301b721b9a37812aba42e4c2071c49753232fbfc2eba0862508baa308d0e71
7
- data.tar.gz: 7a9a11bc868752d1f3e50f59f0b5b99ec9b510f729710c85600f99b810cb490062e0bd2782d7828e32e8ad3f3ad0747c41d916a4aae2df2768de419f3c5d3d30
6
+ metadata.gz: 7c0e74d9d25e2cf64b288b6485213fb3eccd236e6db5073ab39c6e8ef57eb9040616431e359c331366ef8518bd032a2c4bd60a17abbe41f5b5232dbcb2288429
7
+ data.tar.gz: 83dfb63517c4597bed2a636e4cc53179f9d15cc34272cf2abfaf9fcbc02619797113c8eebed2a91a09aff7a5358951914ad3bc94d330b076251df413d4a18438
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 2.5.0
1
+ ruby 2.4.1
data/lib/logbook.rb CHANGED
@@ -1,15 +1,19 @@
1
1
  module Logbook
2
- VERSION = "0.1.4"
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/log_entry"
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
- Property = Struct.new(:name, :value)
14
- Tag = Struct.new(:value)
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
@@ -2,7 +2,7 @@ require "parslet"
2
2
 
3
3
  module Logbook
4
4
  class Builder < Parslet::Transform
5
- rule(tag: simple(:tag)) { Tag.new(tag.to_s) }
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].select { |item| item.instance_of?(Property) }.map { |property| [property.name, property.value] }.to_h
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, tags, note)
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].select { |item| item.instance_of?(Property) }.map { |property| [property.name, property.value] }.to_h
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, tags, note)
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.value
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
@@ -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 total_duration
18
- _, duration = entries.inject([nil, Duration.new(0)]) do |(previous_entry, duration), current_entry|
19
- if previous_entry && previous_entry.starts_clock?
20
- if current_entry.stops_clock?
21
- new_duration = Duration.new(duration.minutes + minutes_in_between(previous_entry, current_entry))
22
- [current_entry, new_duration]
23
- else
24
- [previous_entry, duration]
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
- duration
32
- end
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
@@ -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(0) >> newline }
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) >> str("\n") >>
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) >> str("\n") >>
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, :tags, :note)
3
- def merge_page_properties(properties)
4
- self.properties = properties.merge(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 starts_clock?
8
- false
8
+ def merge_page_properties(properties)
9
+ self.properties = properties.merge(self.properties)
9
10
  end
10
11
 
11
- def stops_clock?
12
- false
12
+ def task_id
13
+ self.properties[Task::TASK_ID_PROPERTY].value
13
14
  end
14
15
  end
15
16
  end
@@ -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, :tags, :note)
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 stops_clock?
24
- true
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
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-01-24 00:00:00.000000000 Z
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.7.3
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