goal 0.0.3 → 0.1.0
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 +4 -4
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +1 -1
- data/README.md +28 -11
- data/bin/goal +17 -77
- data/lib/goal/report.rb +47 -0
- data/lib/goal/version.rb +1 -1
- data/lib/goal.rb +64 -1
- data/spec/calculator_spec.rb +72 -0
- data/spec/report_spec.rb +43 -0
- metadata +6 -6
- data/lib/goal/base.rb +0 -32
- data/spec/base_spec.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a273a57258b55e11c3d0f61514950efb5026ffa
|
4
|
+
data.tar.gz: 9e98c87c2cee48f8617f609acdf7388d6f9ede4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2674f99af748b9e286bf9b04316816e6561c7f3b5599ccf466d8ecfcf7723901f4abb7ed9bd3d2634ea69991827f74523463f78f75d1e90b2e8ac0807fe26bb0
|
7
|
+
data.tar.gz: 964867d39e49b6d1a4a31ed4aa87fd62f9eaeedc34becd4e6ae54cc36d5110f822ed9ed86417f8f77d1409f33ed575701468b063e14a01555539ff81e0d44e1a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
### 0.1.0
|
2
|
+
|
3
|
+
* bug fix
|
4
|
+
* Fix typo "Left days" => "Days left" on report
|
5
|
+
* Now -h or -u and -t are required options for the binary file
|
6
|
+
|
7
|
+
* new
|
8
|
+
* Removes the Base class, now there is a Calculator class reponsible to make
|
9
|
+
the main calculations and generate data to the report
|
10
|
+
* Add a report class to print the report
|
11
|
+
* Add a new option -h(--hours)
|
12
|
+
|
13
|
+
|
1
14
|
### 0.0.3
|
2
15
|
|
3
16
|
* bug fix
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -8,28 +8,44 @@ Goal
|
|
8
8
|
the month that have 23 business days.
|
9
9
|
|
10
10
|
```ruby
|
11
|
-
|
11
|
+
calculator = Goal::HourCalculator.new
|
12
12
|
|
13
|
-
|
13
|
+
calculator.estimated_for(160) # => 97.39130434782608
|
14
14
|
```
|
15
15
|
|
16
16
|
So I need to have 97.39 hours to be within my goal.
|
17
17
|
|
18
18
|
|
19
|
-
## Using the
|
19
|
+
## Using the binary file
|
20
20
|
|
21
21
|
```bash
|
22
22
|
$ goal -u freshbooks_user -t freshbooks_token
|
23
23
|
|
24
|
-
| Goals |
|
25
|
-
| 160 | 97 | 4.93
|
26
|
-
| 200 | 121 | 9.37
|
27
|
-
| 250 | 152 | 14.93
|
28
|
-
| 300 | 182 | 20.48
|
29
|
-
|
|
24
|
+
| Goals | Expected | Average |
|
25
|
+
| 160 | 97 | 4.93 |
|
26
|
+
| 200 | 121 | 9.37 |
|
27
|
+
| 250 | 152 | 14.93 |
|
28
|
+
| 300 | 182 | 20.48 |
|
29
|
+
|____________________________|
|
30
30
|
| Current: 115.66h
|
31
31
|
| Average: 8.26h
|
32
|
-
|
|
32
|
+
| Days left: 9
|
33
|
+
```
|
34
|
+
|
35
|
+
or
|
36
|
+
|
37
|
+
```bash
|
38
|
+
$ goal -h 115.66
|
39
|
+
|
40
|
+
| Goals | Expected | Average |
|
41
|
+
| 160 | 97 | 4.93 |
|
42
|
+
| 200 | 121 | 9.37 |
|
43
|
+
| 250 | 152 | 14.93 |
|
44
|
+
| 300 | 182 | 20.48 |
|
45
|
+
|____________________________|
|
46
|
+
| Current: 115.66h
|
47
|
+
| Average: 8.26h
|
48
|
+
| Days left: 9
|
33
49
|
```
|
34
50
|
|
35
51
|
### Available options
|
@@ -39,9 +55,10 @@ Usage: goal [options]
|
|
39
55
|
-u, --username=USERNAME Freshbooks username
|
40
56
|
-t, --token=TOKEN Freshbooks token
|
41
57
|
-g, --goals=[GOALS] Your goal list. Ex 160:200:300
|
58
|
+
-h, --hours=HOURS Worked time in hours.
|
42
59
|
```
|
43
60
|
|
44
61
|
Options `-u` and `-t` are required and specify the user and token to access the
|
45
|
-
Freshbooks hours.
|
62
|
+
Freshbooks hours. But you can use the -h option to specify manually the hours.
|
46
63
|
|
47
64
|
The `-g` is optional, by default it has the following value `160:200:250:300`
|
data/bin/goal
CHANGED
@@ -1,85 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'goal'
|
4
|
-
require 'colorize'
|
5
4
|
require 'optparse'
|
6
5
|
|
7
|
-
class MyGoals
|
8
|
-
def initialize(options = {})
|
9
|
-
@base = Goal::Base.new
|
10
|
-
@calculator = Goal::HourCalculator.new
|
11
|
-
@goal_list = options.fetch(:goal_list, [160, 200, 250, 300])
|
12
|
-
@username = options[:username]
|
13
|
-
@token = options[:token]
|
14
|
-
end
|
15
|
-
|
16
|
-
def print
|
17
|
-
puts
|
18
|
-
print_header
|
19
|
-
print_data
|
20
|
-
print_footer
|
21
|
-
puts
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
attr_reader :base, :calculator, :goal_list, :username, :token
|
27
|
-
|
28
|
-
def print_header
|
29
|
-
puts '| Goals | Estimate | Avg. to Goal |'
|
30
|
-
end
|
31
|
-
|
32
|
-
def print_data
|
33
|
-
goals.each do |goal|
|
34
|
-
avg = calculator.rate_to_goal(goal[:goal], current_time).round(2)
|
35
|
-
|
36
|
-
puts '| ' + goal[:goal].to_s.ljust(5) + ' | ' + goal[:expected].to_i.to_s.ljust(8) + ' | ' + avg.to_s.ljust(12).colorize(average_color(avg)) + ' |'
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def print_footer
|
41
|
-
puts '|_________________________________|'
|
42
|
-
puts "| Current: " + "#{current_time}h".colorize(current_color)
|
43
|
-
puts "| Average: " + "#{rate}h".colorize(current_color)
|
44
|
-
puts "| Left days: #{calculator.days_left}"
|
45
|
-
end
|
46
|
-
|
47
|
-
def current_time
|
48
|
-
@current_time ||= base.worked_time(username, token).round(2)
|
49
|
-
end
|
50
|
-
|
51
|
-
def rate
|
52
|
-
@rate ||= calculator.hour_rate(current_time).round(2)
|
53
|
-
end
|
54
|
-
|
55
|
-
def goals
|
56
|
-
base.calculate_goals goal_list
|
57
|
-
end
|
58
|
-
|
59
|
-
def current_color
|
60
|
-
if rate < 8
|
61
|
-
:red
|
62
|
-
elsif rate >= 8 && rate < 9
|
63
|
-
:yellow
|
64
|
-
else
|
65
|
-
:green
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def average_color(avg)
|
70
|
-
if avg < 8
|
71
|
-
:green
|
72
|
-
elsif avg >= 8 && avg < 9
|
73
|
-
:yellow
|
74
|
-
else
|
75
|
-
:red
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
6
|
options = {}
|
81
7
|
|
82
|
-
OptionParser.new do |opts|
|
8
|
+
optparse = OptionParser.new do |opts|
|
83
9
|
opts.banner = "Usage: goal [options]"
|
84
10
|
|
85
11
|
opts.on("-u", "--username=USERNAME", "Freshbooks username") do |u|
|
@@ -93,6 +19,20 @@ OptionParser.new do |opts|
|
|
93
19
|
opts.on("-g", "--goals=[GOALS]", "Your goal list. Ex 160:200:300") do |g|
|
94
20
|
options[:goal_list] = g.split(':').map(&:to_i)
|
95
21
|
end
|
96
|
-
end.parse!
|
97
22
|
|
98
|
-
|
23
|
+
opts.on("-h", "--hours=HOURS", "Worked time in hours.") do |h|
|
24
|
+
options[:hours] = h.to_f
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
optparse.parse!
|
29
|
+
|
30
|
+
unless options[:hours] || (options[:username] && options[:token])
|
31
|
+
puts 'Arguments -h or -u and -t are required.'
|
32
|
+
puts optparse
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
|
36
|
+
calculator = Goal::Calculator.new(options)
|
37
|
+
puts Goal::Report.new(calculator.generate_data)
|
38
|
+
|
data/lib/goal/report.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Goal
|
2
|
+
class Report
|
3
|
+
def initialize(data, options = {})
|
4
|
+
@data = data
|
5
|
+
@column_width = options.fetch(:column_width, 8)
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
output = row(header) + "\n"
|
10
|
+
|
11
|
+
data[:rows].each do |r|
|
12
|
+
output += row([r[:goal], r[:expected], r[:average]]) + "\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
output += footer
|
16
|
+
|
17
|
+
output
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :data, :column_width
|
23
|
+
|
24
|
+
def header
|
25
|
+
%w(Goals Expected Average)
|
26
|
+
end
|
27
|
+
|
28
|
+
def footer
|
29
|
+
"|" + ('-' * ((column_width * 3) + 8)) + "|\n" +
|
30
|
+
"| Current time: #{data[:summary][:total_time]}\n" +
|
31
|
+
"| Current rate: #{data[:summary][:rate]}\n" +
|
32
|
+
"| Days left: #{data[:summary][:days_left]}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def row(columns)
|
36
|
+
prepared_columns = prepare_columns(columns)
|
37
|
+
|
38
|
+
"| #{prepared_columns.join(' | ')} |"
|
39
|
+
end
|
40
|
+
|
41
|
+
def prepare_columns(columns)
|
42
|
+
columns.map do |c|
|
43
|
+
c.to_s.ljust(column_width)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/goal/version.rb
CHANGED
data/lib/goal.rb
CHANGED
@@ -1,6 +1,69 @@
|
|
1
1
|
require 'goal/hour_calculator'
|
2
2
|
require 'goal/freshbooks_calculator'
|
3
|
-
require 'goal/
|
3
|
+
require 'goal/report'
|
4
4
|
|
5
5
|
module Goal
|
6
|
+
class Calculator
|
7
|
+
def initialize(options = {})
|
8
|
+
@calculator = Goal::HourCalculator.new
|
9
|
+
@goal_list = options.fetch(:goal_list, [160, 200, 250, 300])
|
10
|
+
@username = options[:username]
|
11
|
+
@token = options[:token]
|
12
|
+
@hours = options[:hours]
|
13
|
+
end
|
14
|
+
|
15
|
+
def worked_time
|
16
|
+
if hours
|
17
|
+
hours
|
18
|
+
elsif username && token
|
19
|
+
freshbooks.hours
|
20
|
+
else
|
21
|
+
0
|
22
|
+
end.round(2)
|
23
|
+
end
|
24
|
+
|
25
|
+
# It calculates the current hour rate
|
26
|
+
def rate
|
27
|
+
calculator.hour_rate(worked_time).round(2)
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_data
|
31
|
+
data = {}
|
32
|
+
|
33
|
+
data[:rows] = goals
|
34
|
+
data[:summary] = summary
|
35
|
+
|
36
|
+
data
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :base, :calculator, :goal_list, :username, :token, :hours
|
42
|
+
|
43
|
+
def freshbooks
|
44
|
+
Goal::FreshbooksCalculator.new(username, token)
|
45
|
+
end
|
46
|
+
|
47
|
+
def goals
|
48
|
+
calculated_goals = []
|
49
|
+
|
50
|
+
goal_list.each do |goal|
|
51
|
+
calculated_goals.push({
|
52
|
+
goal: goal,
|
53
|
+
expected: calculator.estimated_for(goal).round(2),
|
54
|
+
average: calculator.rate_to_goal(goal, worked_time).round(2)
|
55
|
+
})
|
56
|
+
end
|
57
|
+
|
58
|
+
calculated_goals
|
59
|
+
end
|
60
|
+
|
61
|
+
def summary
|
62
|
+
{
|
63
|
+
total_time: worked_time,
|
64
|
+
rate: rate,
|
65
|
+
days_left: calculator.days_left
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
6
69
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Goal::Calculator do
|
4
|
+
describe '#worked_time' do
|
5
|
+
context 'with hours' do
|
6
|
+
subject do
|
7
|
+
described_class.new(hours: 90)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should return hours' do
|
11
|
+
expect(subject.worked_time).to eq 90
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with hours' do
|
16
|
+
subject do
|
17
|
+
described_class.new(username: 'username', token: 'token')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should return the freshbooks hours' do
|
21
|
+
instance = double(:instance)
|
22
|
+
|
23
|
+
Goal::FreshbooksCalculator.should_receive(:new).with('username', 'token').and_return(instance)
|
24
|
+
instance.should_receive(:hours).and_return 55.55
|
25
|
+
|
26
|
+
expect(subject.worked_time).to eq 55.55
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#rate' do
|
32
|
+
subject do
|
33
|
+
described_class.new(hours: 90)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should return calculate the rate' do
|
37
|
+
Date.stub(today: Date.new(2013, 10, 20))
|
38
|
+
|
39
|
+
expect(subject.rate).to eq 6.43
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#generate_data' do
|
44
|
+
subject do
|
45
|
+
described_class.new(hours: 90, goal_list: [160, 200])
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should return the generated data' do
|
49
|
+
Date.stub(today: Date.new(2013, 10, 20))
|
50
|
+
|
51
|
+
expect(subject.generate_data).to eq({
|
52
|
+
rows: [
|
53
|
+
{
|
54
|
+
goal: 160,
|
55
|
+
expected: 97.39,
|
56
|
+
average: 7.78
|
57
|
+
},
|
58
|
+
{
|
59
|
+
goal: 200,
|
60
|
+
expected: 121.74,
|
61
|
+
average: 12.22
|
62
|
+
}
|
63
|
+
],
|
64
|
+
summary: {
|
65
|
+
total_time: 90.0,
|
66
|
+
rate: 6.43,
|
67
|
+
days_left: 9
|
68
|
+
}
|
69
|
+
})
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/report_spec.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Goal::Report do
|
4
|
+
let(:data) do
|
5
|
+
{
|
6
|
+
rows: [
|
7
|
+
{
|
8
|
+
goal: 160,
|
9
|
+
expected: 97.39,
|
10
|
+
average: 7.78
|
11
|
+
},
|
12
|
+
{
|
13
|
+
goal: 200,
|
14
|
+
expected: 121.74,
|
15
|
+
average: 12.22
|
16
|
+
}
|
17
|
+
],
|
18
|
+
summary: {
|
19
|
+
total_time: 90.0,
|
20
|
+
rate: 6.43,
|
21
|
+
days_left: 9
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
subject do
|
27
|
+
described_class.new(data)
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#to_s' do
|
31
|
+
it 'should build the report' do
|
32
|
+
report = "| Goals | Expected | Average |\n"
|
33
|
+
report += "| 160 | 97.39 | 7.78 |\n"
|
34
|
+
report += "| 200 | 121.74 | 12.22 |\n"
|
35
|
+
report += "|--------------------------------|\n"
|
36
|
+
report += "| Current time: 90.0\n"
|
37
|
+
report += "| Current rate: 6.43\n"
|
38
|
+
report += "| Days left: 9"
|
39
|
+
|
40
|
+
expect(subject.to_s).to eq report
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: goal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mateus Lorandi dos Santos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-freshbooks
|
@@ -83,13 +83,14 @@ files:
|
|
83
83
|
- bin/goal
|
84
84
|
- goal.gemspec
|
85
85
|
- lib/goal.rb
|
86
|
-
- lib/goal/base.rb
|
87
86
|
- lib/goal/freshbooks_calculator.rb
|
88
87
|
- lib/goal/hour_calculator.rb
|
88
|
+
- lib/goal/report.rb
|
89
89
|
- lib/goal/version.rb
|
90
|
-
- spec/
|
90
|
+
- spec/calculator_spec.rb
|
91
91
|
- spec/freshbooks_calculator_spec.rb
|
92
92
|
- spec/hour_calculator_spec.rb
|
93
|
+
- spec/report_spec.rb
|
93
94
|
- spec/spec_helper.rb
|
94
95
|
homepage: http://github.com/comogo/goal
|
95
96
|
licenses:
|
@@ -111,9 +112,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
112
|
version: '0'
|
112
113
|
requirements: []
|
113
114
|
rubyforge_project:
|
114
|
-
rubygems_version: 2.1.
|
115
|
+
rubygems_version: 2.1.9
|
115
116
|
signing_key:
|
116
117
|
specification_version: 4
|
117
118
|
summary: Keep track of your hour goals
|
118
119
|
test_files: []
|
119
|
-
has_rdoc:
|
data/lib/goal/base.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
module Goal
|
2
|
-
class Base
|
3
|
-
def initialize
|
4
|
-
@calculator = HourCalculator.new
|
5
|
-
end
|
6
|
-
|
7
|
-
def worked_time(username, token)
|
8
|
-
FreshbooksCalculator.new(username, token).hours
|
9
|
-
end
|
10
|
-
|
11
|
-
def calculate_goal(goal)
|
12
|
-
calculator.estimated_for(goal)
|
13
|
-
end
|
14
|
-
|
15
|
-
def calculate_goals(goals)
|
16
|
-
calculated_goals = []
|
17
|
-
|
18
|
-
goals.each do |goal|
|
19
|
-
calculated_goals.push({
|
20
|
-
goal: goal,
|
21
|
-
expected: calculate_goal(goal)
|
22
|
-
})
|
23
|
-
end
|
24
|
-
|
25
|
-
calculated_goals
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
attr_reader :calculator
|
31
|
-
end
|
32
|
-
end
|
data/spec/base_spec.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Goal::Base do
|
4
|
-
describe '#worked_time' do
|
5
|
-
it 'should get the worked_time from freshbooks' do
|
6
|
-
freshbooks_instance = double(:freshbooks_instance)
|
7
|
-
|
8
|
-
Goal::FreshbooksCalculator.should_receive(:new).with('username', 'token').and_return(freshbooks_instance)
|
9
|
-
freshbooks_instance.should_receive(:hours).and_return(30)
|
10
|
-
|
11
|
-
expect(subject.worked_time('username', 'token')).to eq 30
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
describe '#calculate_goal' do
|
16
|
-
it 'should calculate goal using the hour calculator' do
|
17
|
-
Goal::HourCalculator.any_instance.should_receive(:estimated_for).with(200).and_return 10
|
18
|
-
|
19
|
-
expect(subject.calculate_goal(200)).to eq 10
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
describe '#calculate_goals' do
|
24
|
-
it 'should calculate goals using the hour calculator' do
|
25
|
-
Goal::HourCalculator.any_instance.should_receive(:estimated_for).with(160).and_return 10
|
26
|
-
Goal::HourCalculator.any_instance.should_receive(:estimated_for).with(200).and_return 20
|
27
|
-
|
28
|
-
expect(subject.calculate_goals([160, 200])).to eq([
|
29
|
-
{
|
30
|
-
goal: 160,
|
31
|
-
expected: 10
|
32
|
-
},
|
33
|
-
{
|
34
|
-
goal: 200,
|
35
|
-
expected: 20
|
36
|
-
}
|
37
|
-
])
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|