haora 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/haora +43 -0
- data/lib/haora/day.rb +42 -0
- data/lib/haora/file.rb +11 -0
- data/lib/haora/io.rb +56 -0
- data/lib/haora/project.rb +31 -0
- data/lib/haora/renderer.rb +42 -0
- data/lib/haora/task.rb +19 -0
- data/lib/haora/timestamp.rb +78 -0
- data/lib/haora/workbook.rb +35 -0
- data/lib/haora.rb +60 -0
- metadata +125 -0
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
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: []
|