haora 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dfa559ad454e9144340aa59e327854c6bd4dc187
4
+ data.tar.gz: ecfabc805841a3352dbef5923bd2bd4fbf0a16d4
5
+ SHA512:
6
+ metadata.gz: 47689c4dadc88d8aaba74bc8a40afd287542b737e078f0f416ef850b890b5ce747b5a30b64438fdb96a425041025b7dd78bad1b06c12364df18a9b8fd19a383a
7
+ data.tar.gz: 6caba7632ef466873407ec2d299d3c84c8087268d3d44802c5e9085edfdcfbfb752422eedecaf6388481bee388849ea437e744506d4823b585b3160bbae0b85d
data/bin/haora ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'gli'
4
+ include GLI::App
5
+
6
+ require 'haora'
7
+
8
+ desc 'Path to file containing working times'
9
+ arg_name 'file_name'
10
+ default_value File.join(ENV['HOME'], '.haora', 'workbook')
11
+ flag :f, :file
12
+
13
+ desc 'Date to be used'
14
+ arg_name 'date'
15
+ default_value Date.today.strftime '%d.%m.%Y'
16
+ flag :d, :date
17
+
18
+ desc 'Adds a task to the current or another day'
19
+ command :add do |c|
20
+ c.flag :p, :project
21
+ c.flag :b, :begin
22
+ c.flag :e, :end
23
+ c.action do |global_actions, options, args|
24
+ file_name = global_actions[:file]
25
+ date = global_actions[:date]
26
+ start = options[:begin]
27
+ stop = options[:end]
28
+ project = options[:project]
29
+ text = args[0]
30
+ Haora::Service.for(file_name).add_task(date: date, start: start, stop: stop, project: project, text: text)
31
+ end
32
+ end
33
+
34
+ desc 'Lists tasks of a day'
35
+ command :list do |c|
36
+ c.action do |global_actions, options, args|
37
+ file_name = global_actions[:file]
38
+ date = global_actions[:date]
39
+ puts Haora::Service.for(file_name).list(date)
40
+ end
41
+ end
42
+
43
+ exit run(ARGV)
data/lib/haora/day.rb ADDED
@@ -0,0 +1,42 @@
1
+ require 'date'
2
+
3
+ module Haora
4
+
5
+ class Day
6
+ attr_reader :date
7
+
8
+ def initialize(date = nil)
9
+ @date = date.nil? ? Date.today : date
10
+ end
11
+
12
+ def tasks
13
+ @tasks ||= []
14
+ end
15
+
16
+ def <<(task)
17
+ tasks << task
18
+ end
19
+
20
+ def ==(other)
21
+ date == other.date
22
+ end
23
+
24
+ def <=>(other)
25
+ date <=> other.date
26
+ end
27
+
28
+ def to_s
29
+ @date.strftime '%F'
30
+ end
31
+
32
+ end
33
+
34
+ class DateParser
35
+
36
+ def self.parse(date)
37
+ return Date.strptime(date, '%d.%m.%Y')
38
+ end
39
+
40
+ end
41
+
42
+ end
data/lib/haora/file.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'fileutils'
2
+
3
+ class File
4
+
5
+ def self.ensure_exist(path)
6
+ dir = File.dirname path
7
+ FileUtils.makedirs dir unless Dir.exist? dir
8
+ FileUtils.touch path unless File.exist? path
9
+ end
10
+
11
+ end
data/lib/haora/io.rb ADDED
@@ -0,0 +1,56 @@
1
+ module Haora
2
+
3
+ module TextIOExporter
4
+
5
+ def export(workbook, io)
6
+ io.puts(workbook.days.sort.map { |d| export_day(d) })
7
+ end
8
+
9
+ private
10
+
11
+ def export_day(day)
12
+ d = 'DAY ' << day.date.strftime('%F') << $/
13
+ d << day.tasks
14
+ .map { |t| t.start.to_s << '-' << t.stop.to_s << '|' << t.project.name << '|' << t.text << $/ }
15
+ .join
16
+ end
17
+
18
+ end
19
+
20
+ module TextIOImporter
21
+
22
+ private
23
+
24
+ DAY_RE = /DAY (\d\d\d\d-\d\d-\d\d)/
25
+ TASK_RE = /(\d?\d:\d\d)-(\d\d:\d\d)?\|(.*)\|(.*)?/
26
+
27
+ public
28
+
29
+ def import(io)
30
+ @workbook = Workbook.new
31
+ io.each_line { |line|
32
+ DAY_RE.match(line) { |date_match| new_day(date_match) }
33
+ TASK_RE.match(line) { |task_match| new_task(task_match) }
34
+ }
35
+ @workbook
36
+ end
37
+
38
+ private
39
+
40
+ def new_task(task_match)
41
+ @day << Task.new(start: task_match[1], stop: task_match[2], project: task_match[3], text: task_match[4])
42
+ end
43
+
44
+ def new_day(date_match)
45
+ @day = Day.new(Date.parse(date_match[1]))
46
+ @workbook << @day
47
+ end
48
+
49
+ end
50
+
51
+ module TextIO
52
+ include TextIOExporter
53
+ include TextIOImporter
54
+ end
55
+
56
+ end
@@ -0,0 +1,31 @@
1
+ module Haora
2
+
3
+ class Project
4
+ attr_reader :name
5
+
6
+ @projects = Hash.new
7
+ @projects.default_proc = proc do |hash, name|
8
+ hash[name] = Project.new(name)
9
+ end
10
+
11
+ def self.by(params)
12
+ @projects[params[:name]]
13
+ end
14
+
15
+ def self.all
16
+ @projects
17
+ end
18
+
19
+ def self.clear
20
+ @projects.clear
21
+ end
22
+
23
+ private
24
+
25
+ def initialize(name)
26
+ @name = name
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,42 @@
1
+ module Haora
2
+
3
+ class StandardDayRenderer
4
+
5
+ def render(day)
6
+ 'Tasks of ' << rendered_date_of(day) << "\n" \
7
+ << rendered(day.tasks)
8
+ end
9
+
10
+ private
11
+
12
+ def rendered_date_of(day)
13
+ if day.date == Date.today
14
+ 'today'
15
+ else
16
+ day.date.strftime('%d.%m.%Y')
17
+ end
18
+ end
19
+
20
+ def rendered(tasks)
21
+ tasks.map { |task| rendered_task(task) }.join("\n")
22
+ end
23
+
24
+ def rendered_task(task)
25
+ timespan = rendered_timespan(task.start, task.stop)
26
+ duration = rendered_duration(task.start, task.stop)
27
+ '%s = %s on %s: %s' % [timespan, duration, task.project.name, task.text]
28
+ end
29
+
30
+ def rendered_timespan(start, stop)
31
+ timespan = start.to_s << ' - ' << stop.to_s
32
+ timespan.prepend(' ') if timespan.length < 13
33
+ timespan
34
+ end
35
+
36
+ def rendered_duration(start, stop)
37
+ '%5.2f' % (stop - start).hours
38
+ end
39
+
40
+ end
41
+
42
+ end
data/lib/haora/task.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Haora
2
+
3
+ class Task
4
+ attr_reader :start, :stop, :project, :text
5
+
6
+ def initialize(params)
7
+ @start = params[:start] ? Timestamp.new(params[:start]) : Timestamp.now
8
+ @stop = params[:stop] ? Timestamp.new(params[:stop]) : nil
9
+ @project = Project.by(name: params[:project])
10
+ @text = params[:text] || ''
11
+ end
12
+
13
+ def to_s
14
+ '%s - %s : %s : %s' % [@start, @stop, @project.name, @text]
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,78 @@
1
+ module Haora
2
+
3
+ class Timestamp
4
+ attr_reader :hour, :minute
5
+
6
+ def initialize(*params)
7
+ if params.size == 1 && params.first.is_a?(String)
8
+ @hour, @minute = parse_timestamp_literal(params.first)
9
+ elsif params.size == 2 && (params.all? { |p| p.is_a?(Integer) })
10
+ @hour, @minute = params.first(2)
11
+ end
12
+ raise ArgumentError if invalid?
13
+ end
14
+
15
+ def self.now
16
+ now = Time.now
17
+ Timestamp.new(now.hour, now.min)
18
+ end
19
+
20
+ private
21
+
22
+ def parse_timestamp_literal(timestamp)
23
+ /(\d?\d):(\d\d)/.match(timestamp) do |m|
24
+ return [m[1].to_i, m[2].to_i]
25
+ end
26
+ end
27
+
28
+ def invalid?
29
+ raise ArgumentError if !@hour.is_a?(Integer) || !@minute.is_a?(Integer)
30
+ raise ArgumentError if @hour < 0 || @minute < 0
31
+ raise ArgumentError if @hour > 23 || @minute > 59
32
+ end
33
+
34
+ public
35
+
36
+ def to_s
37
+ '%d:%02d' % [hour, minute]
38
+ end
39
+
40
+ def to_minutes
41
+ hour * 60 + minute
42
+ end
43
+
44
+ def -(other)
45
+ Duration.of_minutes(self.to_minutes - other.to_minutes)
46
+ end
47
+
48
+ end
49
+
50
+ class Duration
51
+ attr_reader :minutes
52
+
53
+ def self.of_minutes(minutes)
54
+ Duration.new(minutes)
55
+ end
56
+
57
+ def parts
58
+ {:hours => @minutes / 60,
59
+ :minutes => @minutes % 60}
60
+ end
61
+
62
+ def hours
63
+ minutes / 60.0
64
+ end
65
+
66
+ def to_s
67
+ '%d h %d m' % [parts[:hours], parts[:minutes]]
68
+ end
69
+
70
+ private
71
+
72
+ def initialize(minutes)
73
+ @minutes = minutes
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,35 @@
1
+ module Haora
2
+
3
+ class Workbook
4
+ attr_accessor :path
5
+
6
+ def days
7
+ @days ||= []
8
+ end
9
+
10
+ def <<(day)
11
+ days << day
12
+ end
13
+
14
+ def [](param)
15
+ date = parse_date_param(param)
16
+ day = days.select { |d| d.date == date }.first
17
+ days << (day = Day.new(date)) if day.nil?
18
+ day
19
+ end
20
+
21
+ private
22
+
23
+ def parse_date_param(param)
24
+ date = Date.today
25
+ if param.is_a? String
26
+ date = DateParser.parse param
27
+ elsif param.is_a? Date
28
+ date = param
29
+ end
30
+ date
31
+ end
32
+
33
+ end
34
+
35
+ end
data/lib/haora.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'haora/timestamp'
2
+ require 'haora/task'
3
+ require 'haora/project'
4
+ require 'haora/day'
5
+ require 'haora/renderer'
6
+ require 'haora/workbook'
7
+ require 'haora/io'
8
+ require 'haora/file'
9
+
10
+ module Haora
11
+
12
+ class Service
13
+ attr_reader :workbook
14
+
15
+ def self.for(file_name)
16
+ Service.new(file_name)
17
+ end
18
+
19
+ def add_task(params)
20
+ apply {
21
+ @workbook[params[:date]] << Task.new(start: params[:start], stop: params[:stop], project: params[:project], text: params[:text])
22
+ }
23
+ end
24
+
25
+ def list(date = nil)
26
+ day = @workbook[date]
27
+ StandardDayRenderer.new.render(day)
28
+ end
29
+
30
+ private
31
+
32
+ def apply
33
+ yield
34
+ close
35
+ end
36
+
37
+ include TextIO
38
+
39
+ def initialize(file_name)
40
+ @workbook = open_workbook(file_name)
41
+ end
42
+
43
+ def open_workbook(file_name)
44
+ File.ensure_exist file_name
45
+ File.open(file_name, 'r') do |file|
46
+ workbook = import(file)
47
+ workbook.path = file.path
48
+ workbook
49
+ end
50
+ end
51
+
52
+ def close
53
+ File.open(@workbook.path, 'w') do |file|
54
+ export(@workbook, file)
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: haora
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Dirk Rademann
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-02-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gli
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-reporters
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: aruba
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.14'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.14'
83
+ description: Command line application to keep track of daily work hours.
84
+ email: dirk.rademann@icloud.com
85
+ executables:
86
+ - haora
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - bin/haora
91
+ - lib/haora.rb
92
+ - lib/haora/day.rb
93
+ - lib/haora/file.rb
94
+ - lib/haora/io.rb
95
+ - lib/haora/project.rb
96
+ - lib/haora/renderer.rb
97
+ - lib/haora/task.rb
98
+ - lib/haora/timestamp.rb
99
+ - lib/haora/workbook.rb
100
+ homepage: http://github.com/drademann/haora-cli
101
+ licenses:
102
+ - MIT
103
+ metadata: {}
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.6.10
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Work Hour Diary
125
+ test_files: []