kpi 0.5.1 → 0.5.2
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.
- data/README.rdoc +72 -26
- data/app/models/kpi/entry.rb +3 -2
- data/app/models/kpi/merged_report.rb +5 -2
- data/app/models/kpi/report.rb +2 -2
- data/test/unit/kpi/entry_tesr.rb +8 -0
- data/test/unit/kpi/merged_report_test.rb +16 -4
- metadata +29 -29
data/README.rdoc
CHANGED
@@ -22,62 +22,108 @@ Add to your Gemfile:
|
|
22
22
|
and run in console in project directory:
|
23
23
|
|
24
24
|
$ bundle
|
25
|
+
|
26
|
+
=== Rails 2.x
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
Create config/initializers/kpi.rb:
|
29
|
-
|
30
|
-
KPI.configure do |c|
|
31
|
-
c.app_name = "Your App Name"
|
32
|
-
c.mail_from = "Your App KPI Service <kpi@example.com>"
|
33
|
-
c.mail_to = "your@email.com"
|
34
|
-
end
|
28
|
+
This gem is tested with Rails 2.3.8.
|
35
29
|
|
36
30
|
== Defining report
|
37
31
|
|
38
32
|
Example report in app/models/daily_kpi_report.rb:
|
39
33
|
|
40
|
-
class DailyKpiReport < KPI::Report
|
34
|
+
class DailyKpiReport < KPI::Report
|
41
35
|
def users_count
|
42
|
-
|
36
|
+
result "Users count", User.count, :description => "Total users count"
|
43
37
|
end
|
44
38
|
|
45
39
|
def today_registrations_count
|
46
|
-
|
40
|
+
result "Today registrations count", User.where("created_at > ?", @time - 24.hours)
|
41
|
+
end
|
42
|
+
|
43
|
+
def total_income
|
44
|
+
result "Total income", Order.sum(:total), :unit => 'EUR'
|
45
|
+
end
|
46
|
+
|
47
|
+
def today_income
|
48
|
+
result "Total income", Order.where(:created_at => today).sum(:total), :unit => 'EUR'
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def today
|
54
|
+
((time - 24.hours)..time)
|
47
55
|
end
|
48
56
|
end
|
49
57
|
|
50
|
-
Each defined method should return
|
51
|
-
*
|
58
|
+
Each defined method should return a KPI::Entry object with following accessors:
|
59
|
+
* name
|
52
60
|
* value
|
53
|
-
*
|
61
|
+
* description
|
62
|
+
* unit
|
63
|
+
|
64
|
+
A collection of KPI::Entry is available through Enuberable: Report#entries
|
54
65
|
|
55
66
|
It is important, if your indicator depends on current time (last 24 hours, last week),
|
56
67
|
to rely on @time instance variable instead of Time.now. This lets you to generate report
|
57
68
|
for every moment in history:
|
58
69
|
|
59
|
-
report = DailyKpiReport.new(2.days.ago)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
70
|
+
report = DailyKpiReport.new(:time => 2.days.ago)
|
71
|
+
|
72
|
+
=== Merged reports
|
73
|
+
|
74
|
+
You can create merged report from two or more reports of the same type. Look at examples:
|
75
|
+
|
76
|
+
> today = DailyKpiReport.new
|
77
|
+
> today.users_count.value
|
78
|
+
# => 20
|
79
|
+
> today.today_income.value
|
80
|
+
# => 350
|
81
|
+
> yesterday = DailyKpiReport.new(:time => 1.day.ago)
|
82
|
+
> yesterday.users_count.value
|
83
|
+
# => 16
|
84
|
+
> yesterday.today_income.value
|
85
|
+
# => 250
|
86
|
+
> diff = KPI::MergedReport.new(today, yesterday) do |today, yesterday|
|
87
|
+
> KPI::Entry.new "$$ (change)", today - yesterday
|
88
|
+
> end
|
89
|
+
> avg = KPI::MergedReport.new(today, yesterday) do |*entries|
|
90
|
+
> KPI::Entry.new "$$ (avg)", entries
|
91
|
+
> end
|
92
|
+
> diff.users_count.value
|
93
|
+
# => 4
|
94
|
+
> avg.today_income.value
|
95
|
+
# => 300
|
96
|
+
> avg.today_income.unit
|
97
|
+
# => "EUR"
|
98
|
+
|
99
|
+
More reports can be passed to constructor of Merged report:
|
100
|
+
avg = KPI::MergedReport.new(week_now, week_last, week_next_to_last) do |*entries|
|
101
|
+
# ...
|
102
|
+
end
|
66
103
|
|
67
104
|
== Other
|
68
105
|
* You can override report title by overriding title method in report:
|
69
|
-
class DailyKpiReport < KPI::Report
|
106
|
+
class DailyKpiReport < KPI::Report
|
70
107
|
def title; "Your daily report"; end
|
71
108
|
# ...
|
72
109
|
end
|
73
110
|
* You can use Whenever gem to generate periodic reports
|
74
|
-
|
111
|
+
* Private methods are not considered as indicator methods and can be used as helper methods:
|
112
|
+
class WeeklyReport < KPI::Report
|
113
|
+
# ...
|
114
|
+
private
|
115
|
+
|
116
|
+
def week_range
|
117
|
+
t_prev_mon_begin = time - (time.wday-1)*24*60*60 - time.hour*60*60 - time.min*60 - time.sec
|
118
|
+
t_next_sun_end = t_prev_mon_begin + 7*24*60*60 - 1
|
119
|
+
(t_prev_mon_begin..t_next_sun_end)
|
120
|
+
end
|
121
|
+
end
|
75
122
|
== Todo
|
76
123
|
|
77
124
|
* Generators for configuration and reports
|
78
125
|
* Database persistence - storing reports in different databases: PostgreSQL & Mongo prioritized
|
79
|
-
*
|
80
|
-
* More tests ;)
|
126
|
+
* Support for "result" helper in block passed to constructor of MergedReport
|
81
127
|
|
82
128
|
|
83
129
|
== Contributing to KPI
|
data/app/models/kpi/entry.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
module KPI
|
2
2
|
class Entry
|
3
|
-
attr_reader :name, :value, :description
|
3
|
+
attr_reader :name, :value, :description, :unit
|
4
4
|
def initialize(*args)
|
5
5
|
options = args.extract_options!
|
6
6
|
raise ArgumentError, "Wrong number of arguments (#{args.count} of 2)" unless args.count == 2
|
7
7
|
@name, @value = args
|
8
8
|
@description = options[:description]
|
9
|
+
@unit = options[:unit]
|
9
10
|
end
|
10
11
|
|
11
12
|
def to_a
|
12
|
-
[@title, @value, @description].compact
|
13
|
+
[@title, @value, @description, @unit].compact
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|
@@ -28,9 +28,12 @@ module KPI
|
|
28
28
|
def method_missing(name, *args)
|
29
29
|
result = @compare.call(*@reports.map(&name.to_sym))
|
30
30
|
orginal = @reports.first.send(name.to_sym)
|
31
|
-
description = (orginal.description ? result.description.gsub("$$", orginal.description) : nil)
|
31
|
+
description = (orginal.description && result.description ? result.description.gsub("$$", orginal.description) : nil)
|
32
32
|
|
33
|
-
KPI::Entry.new(result.name.gsub("$$", orginal.name),
|
33
|
+
KPI::Entry.new(result.name.gsub("$$", orginal.name),
|
34
|
+
result.value,
|
35
|
+
:description => description,
|
36
|
+
:unit => (result.unit || orginal.unit))
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
data/app/models/kpi/report.rb
CHANGED
@@ -8,8 +8,8 @@ module KPI
|
|
8
8
|
blacklist :initialize, :collect!, :entries, :time, :title, :defined_kpis, :result
|
9
9
|
|
10
10
|
def initialize(*args)
|
11
|
-
options = args.extract_options!
|
12
|
-
@time = options[:time] || Time.now
|
11
|
+
@options = args.extract_options!
|
12
|
+
@time = @options[:time] || Time.now
|
13
13
|
end
|
14
14
|
attr_reader :time
|
15
15
|
|
data/test/unit/kpi/entry_tesr.rb
CHANGED
@@ -28,5 +28,13 @@ describe "KPI::Entry" do
|
|
28
28
|
assert_equal("desc", @entry.description)
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
describe "when unit given" do
|
33
|
+
before { @entry = KPI::Entry.new "Income", 1294.23, :description => "EUR" }
|
34
|
+
|
35
|
+
it "returns description" do
|
36
|
+
assert_equal("EUR", @entry.unit)
|
37
|
+
end
|
38
|
+
end
|
31
39
|
end
|
32
40
|
end
|
@@ -14,7 +14,7 @@ describe "KPI::MergedReport" do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def test_kpi_2
|
17
|
-
result "title 2 ", @return*2
|
17
|
+
result "title 2 ", @return*2, :unit => 'EUR'
|
18
18
|
end
|
19
19
|
end
|
20
20
|
class AnotherReport < KPI::Report
|
@@ -54,10 +54,10 @@ describe "KPI::MergedReport" do
|
|
54
54
|
|
55
55
|
describe "when two reports given for average" do
|
56
56
|
before do
|
57
|
-
report1 = TestKpi.new(2)
|
58
|
-
report2 = TestKpi.new(8)
|
57
|
+
@report1 = TestKpi.new(2)
|
58
|
+
@report2 = TestKpi.new(8)
|
59
59
|
|
60
|
-
@average = KPI::MergedReport.new(report1, report2) do |*entries|
|
60
|
+
@average = KPI::MergedReport.new(@report1, @report2) do |*entries|
|
61
61
|
average = entries.map(&:value).sum / entries.size
|
62
62
|
KPI::Entry.new "Average $$", average, :description => "$$ (average)"
|
63
63
|
end
|
@@ -74,6 +74,18 @@ describe "KPI::MergedReport" do
|
|
74
74
|
it "should change $$ in description to indicator descripiton" do
|
75
75
|
assert_equal "description (average)", @average.test_kpi.description
|
76
76
|
end
|
77
|
+
|
78
|
+
it "should have unit" do
|
79
|
+
assert_equal "EUR", @average.test_kpi_2.unit
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should allow to override unit" do
|
83
|
+
@merged = KPI::MergedReport.new(@report1, @report2) do |*entries|
|
84
|
+
KPI::Entry.new "merged $$", 1, :unit => "$"
|
85
|
+
end
|
86
|
+
assert_equal '$', @merged.test_kpi.unit
|
87
|
+
assert_equal '$', @merged.test_kpi_2.unit
|
88
|
+
end
|
77
89
|
|
78
90
|
it "should return nil description when no description" do
|
79
91
|
assert_nil @average.test_kpi_2.description
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kpi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 0.5.
|
9
|
+
- 2
|
10
|
+
version: 0.5.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Artur Roszczyk
|
@@ -15,12 +15,12 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-05-
|
18
|
+
date: 2011-05-14 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
|
23
|
-
|
22
|
+
name: actionmailer
|
23
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
25
25
|
requirements:
|
26
26
|
- - ">="
|
@@ -30,12 +30,12 @@ dependencies:
|
|
30
30
|
- 2
|
31
31
|
- 3
|
32
32
|
version: "2.3"
|
33
|
-
name: actionmailer
|
34
|
-
version_requirements: *id001
|
35
33
|
prerelease: false
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
34
|
type: :runtime
|
38
|
-
requirement:
|
35
|
+
requirement: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: activesupport
|
38
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
@@ -45,12 +45,12 @@ dependencies:
|
|
45
45
|
- 2
|
46
46
|
- 3
|
47
47
|
version: "2.3"
|
48
|
-
name: activesupport
|
49
|
-
version_requirements: *id002
|
50
48
|
prerelease: false
|
49
|
+
type: :runtime
|
50
|
+
requirement: *id002
|
51
51
|
- !ruby/object:Gem::Dependency
|
52
|
-
|
53
|
-
|
52
|
+
name: minitest
|
53
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
54
54
|
none: false
|
55
55
|
requirements:
|
56
56
|
- - ">="
|
@@ -59,12 +59,12 @@ dependencies:
|
|
59
59
|
segments:
|
60
60
|
- 0
|
61
61
|
version: "0"
|
62
|
-
name: minitest
|
63
|
-
version_requirements: *id003
|
64
62
|
prerelease: false
|
65
|
-
- !ruby/object:Gem::Dependency
|
66
63
|
type: :development
|
67
|
-
requirement:
|
64
|
+
requirement: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: bundler
|
67
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
68
68
|
none: false
|
69
69
|
requirements:
|
70
70
|
- - ~>
|
@@ -74,12 +74,12 @@ dependencies:
|
|
74
74
|
- 1
|
75
75
|
- 0
|
76
76
|
version: "1.0"
|
77
|
-
name: bundler
|
78
|
-
version_requirements: *id004
|
79
77
|
prerelease: false
|
80
|
-
- !ruby/object:Gem::Dependency
|
81
78
|
type: :development
|
82
|
-
requirement:
|
79
|
+
requirement: *id004
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: jeweler
|
82
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -90,12 +90,12 @@ dependencies:
|
|
90
90
|
- 5
|
91
91
|
- 2
|
92
92
|
version: 1.5.2
|
93
|
-
name: jeweler
|
94
|
-
version_requirements: *id005
|
95
93
|
prerelease: false
|
96
|
-
- !ruby/object:Gem::Dependency
|
97
94
|
type: :development
|
98
|
-
requirement:
|
95
|
+
requirement: *id005
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rcov
|
98
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
99
99
|
none: false
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -104,9 +104,9 @@ dependencies:
|
|
104
104
|
segments:
|
105
105
|
- 0
|
106
106
|
version: "0"
|
107
|
-
name: rcov
|
108
|
-
version_requirements: *id006
|
109
107
|
prerelease: false
|
108
|
+
type: :development
|
109
|
+
requirement: *id006
|
110
110
|
description: This gem helps you to track key indicators in your Rails app.
|
111
111
|
email: artur.roszczyk@gmail.com
|
112
112
|
executables: []
|
@@ -165,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
165
|
requirements: []
|
166
166
|
|
167
167
|
rubyforge_project:
|
168
|
-
rubygems_version: 1.
|
168
|
+
rubygems_version: 1.5.2
|
169
169
|
signing_key:
|
170
170
|
specification_version: 3
|
171
171
|
summary: Key Performance Indicators for Rails 3.x
|