tricle 0.1.0 → 0.2.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/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/Appraisals +9 -0
- data/CONTRIBUTING.md +2 -3
- data/Gemfile +13 -1
- data/Guardfile +1 -1
- data/README.md +33 -12
- data/gemfiles/rails_3.gemfile +21 -0
- data/gemfiles/rails_4.gemfile +21 -0
- data/lib/tricle/aggregation.rb +46 -0
- data/lib/tricle/email_helper.rb +32 -7
- data/lib/tricle/metric.rb +10 -3
- data/lib/tricle/presenters/group.rb +3 -5
- data/lib/tricle/templates/email.css +2 -2
- data/lib/tricle/templates/email.html.erb +7 -5
- data/lib/tricle/version.rb +1 -1
- data/spec/app/no_total_test_mailer.rb +11 -0
- data/spec/app/test_metric.rb +2 -34
- data/spec/app/test_metric_with_no_total.rb +35 -0
- data/spec/config/timecop.rb +1 -1
- data/spec/presenters/group_spec.rb +3 -4
- data/spec/presenters/report_spec.rb +5 -5
- data/spec/spec_helper.rb +0 -1
- data/spec/unit/aggregation_spec.rb +41 -0
- data/spec/unit/email_helper_spec.rb +87 -7
- data/spec/unit/mail_preview_spec.rb +2 -2
- data/spec/unit/mailer_spec.rb +22 -15
- data/spec/unit/metric_spec.rb +2 -2
- data/spec/unit/range_data_spec.rb +2 -2
- data/spec/unit/test_metric_spec.rb +3 -4
- data/tricle.gemspec +3 -13
- metadata +36 -155
- data/lib/tricle/presenters/metric.rb +0 -51
- data/spec/presenters/metric_spec.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e1bf9319ba25a271eaae97c379e62f2f6a6b68e
|
4
|
+
data.tar.gz: 4a515d52060d25dcae9a4f6c00c41786bf930b10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc6c048e313464575809c42d0dfd56857893be868834de9b8fc3c411c05dcfe813401cd60589ba4f9a65c0d7a7cf810486a5b542a2ece34857f102cec507d040
|
7
|
+
data.tar.gz: 204151436a1410c2b7138dd1480f929e733a8260b4c6b1a97dc893ad6c3f3dd46f8a62ebbd7e15fa3743b608bab95c56c34e9566be0a858802205684229bdc98
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Appraisals
ADDED
data/CONTRIBUTING.md
CHANGED
data/Gemfile
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
# Specify your gem's dependencies in tricle.gemspec
|
4
3
|
gemspec
|
4
|
+
|
5
|
+
group :development, :test do
|
6
|
+
gem 'appraisal'
|
7
|
+
gem 'bundler'
|
8
|
+
gem 'byebug', platforms: :mri_20
|
9
|
+
gem 'guard', '~> 2.6'
|
10
|
+
gem 'guard-rspec'
|
11
|
+
gem 'rake'
|
12
|
+
gem 'rspec', '~> 3.0'
|
13
|
+
gem 'ruby_gntp'
|
14
|
+
gem 'shotgun'
|
15
|
+
gem 'timecop', '~> 0.7'
|
16
|
+
end
|
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Tricle [](https://travis-ci.org/afeld/tricle) [](https://codeclimate.com/github/afeld/tricle)
|
2
2
|
|
3
3
|
Automated metrics reporting via email. It's datastore-agnostic, so you can query SQL, MongoDB, external APIs, etc. to generate the stats you need. See [here](https://github.com/afeld/tricle-afeld) for an example implementation ([live demo](http://tricle.afeld.me/weekly_metrics)).
|
4
4
|
|
@@ -12,7 +12,7 @@ This gem can be used within an existing project (e.g. a Rails app), or standalon
|
|
12
12
|
|
13
13
|
```ruby
|
14
14
|
# Gemfile
|
15
|
-
gem 'tricle', '~> 0.
|
15
|
+
gem 'tricle', '~> 0.2.0'
|
16
16
|
|
17
17
|
# Rakefile
|
18
18
|
require 'tricle/tasks'
|
@@ -51,14 +51,15 @@ class MyMetric < Tricle::Metric
|
|
51
51
|
# ...
|
52
52
|
end
|
53
53
|
|
54
|
-
# Retrieve the cumulative value for this metric.
|
54
|
+
# Optional: Retrieve the cumulative value for this metric. If not defined,
|
55
|
+
# the total won't be displayed in the mailer.
|
55
56
|
#
|
56
57
|
# @return [Fixnum] the grand total
|
57
58
|
def total
|
58
59
|
# ...
|
59
60
|
end
|
60
61
|
|
61
|
-
# Optional:
|
62
|
+
# Optional: Only necessary if using `list` for this Metric within your Mailer.
|
62
63
|
#
|
63
64
|
# @param start_at [Time]
|
64
65
|
# @param end_at [Time] non-inclusive
|
@@ -73,11 +74,11 @@ end
|
|
73
74
|
ActiveRecord example:
|
74
75
|
|
75
76
|
```ruby
|
76
|
-
# metrics/new_users.rb
|
77
|
+
# app/metrics/new_users.rb
|
77
78
|
class NewUsers < Tricle::Metric
|
78
79
|
|
79
80
|
def size_for_range(start_at, end_at)
|
80
|
-
self.
|
81
|
+
self.items_for_range(start_at, end_at).count
|
81
82
|
end
|
82
83
|
|
83
84
|
def total
|
@@ -85,16 +86,12 @@ class NewUsers < Tricle::Metric
|
|
85
86
|
end
|
86
87
|
|
87
88
|
def items_for_range(start_at, end_at)
|
88
|
-
self.
|
89
|
+
self.users.where('created_at >= ? AND created_at < ?', start_at, end_at)
|
89
90
|
end
|
90
91
|
|
91
92
|
|
92
93
|
private
|
93
94
|
|
94
|
-
def size_for_range(start_at, end_at)
|
95
|
-
self.users.where('created_at >= ? AND created_at < ?', start_at, end_at)
|
96
|
-
end
|
97
|
-
|
98
95
|
# You can add whatever helper methods in that class that you need.
|
99
96
|
def users
|
100
97
|
# non-deleted Users
|
@@ -104,6 +101,24 @@ class NewUsers < Tricle::Metric
|
|
104
101
|
end
|
105
102
|
```
|
106
103
|
|
104
|
+
If you would like finer-grain optimization, the methods included from the [`Aggregation`](lib/tricle/arregation.rb) mixin can be overridden.
|
105
|
+
|
106
|
+
#### "Lower is better" metrics
|
107
|
+
|
108
|
+
By default, Tricle highlights numbers that increased in green, and those that decreased in red. If you have a metric where a *lower* number is considered better, you'll want to override the `#better` method so Tricle highlights your cells properly:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
class LowerIsBetterMetric < Tricle::Metric
|
112
|
+
def better
|
113
|
+
:lower
|
114
|
+
end
|
115
|
+
|
116
|
+
...
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
You can also return `:none`, and none of your cells for that metric will be highlighted green or red.
|
121
|
+
|
107
122
|
### Mailers
|
108
123
|
|
109
124
|
Mailers specify how a particular set of Metrics should be sent. You can define one or multiple, to send different metrics to different groups of people.
|
@@ -143,7 +158,7 @@ end
|
|
143
158
|
e.g.
|
144
159
|
|
145
160
|
```ruby
|
146
|
-
# mailers/weekly_insights.rb
|
161
|
+
# app/mailers/weekly_insights.rb
|
147
162
|
class WeeklyInsights < Tricle::Mailer
|
148
163
|
|
149
164
|
default(
|
@@ -199,6 +214,12 @@ To send all Tricle emails, run
|
|
199
214
|
rake tricle:emails:send
|
200
215
|
```
|
201
216
|
|
217
|
+
To set a speficic time zone, use the `TZ` environment variable (see the list [here](http://en.wikipedia.org/wiki/List_of_tz_database_time_zones)).
|
218
|
+
|
219
|
+
```bash
|
220
|
+
TZ=UTC rake tricle:emails:send
|
221
|
+
```
|
222
|
+
|
202
223
|
### Cron Setup
|
203
224
|
|
204
225
|
TODO
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "actionmailer", "3.2.19"
|
6
|
+
gem "activesupport", "3.2.19"
|
7
|
+
|
8
|
+
group :development, :test do
|
9
|
+
gem "appraisal"
|
10
|
+
gem "bundler"
|
11
|
+
gem "byebug", :platforms => :mri_20
|
12
|
+
gem "guard", "~> 2.6"
|
13
|
+
gem "guard-rspec"
|
14
|
+
gem "rake"
|
15
|
+
gem "rspec", "~> 3.0"
|
16
|
+
gem "ruby_gntp"
|
17
|
+
gem "shotgun"
|
18
|
+
gem "timecop", "~> 0.7"
|
19
|
+
end
|
20
|
+
|
21
|
+
gemspec :path => "../"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "actionmailer", "4.1.5"
|
6
|
+
gem "activesupport", "4.1.5"
|
7
|
+
|
8
|
+
group :development, :test do
|
9
|
+
gem "appraisal"
|
10
|
+
gem "bundler"
|
11
|
+
gem "byebug", :platforms => :mri_20
|
12
|
+
gem "guard", "~> 2.6"
|
13
|
+
gem "guard-rspec"
|
14
|
+
gem "rake"
|
15
|
+
gem "rspec", "~> 3.0"
|
16
|
+
gem "ruby_gntp"
|
17
|
+
gem "shotgun"
|
18
|
+
gem "timecop", "~> 0.7"
|
19
|
+
end
|
20
|
+
|
21
|
+
gemspec :path => "../"
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Tricle
|
4
|
+
module Aggregation
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
attr_reader :now
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
# TODO allow Time to be passed in so it can be frozen
|
12
|
+
@now = Time.now
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def days_ago(n)
|
17
|
+
start_at = self.now.beginning_of_day.ago(n.days)
|
18
|
+
end_at = start_at + 1.day
|
19
|
+
self.size_for_range(start_at, end_at)
|
20
|
+
end
|
21
|
+
|
22
|
+
def yesterday
|
23
|
+
self.days_ago(1)
|
24
|
+
end
|
25
|
+
|
26
|
+
def weeks_ago(n)
|
27
|
+
start_at = self.now.beginning_of_week.weeks_ago(n)
|
28
|
+
end_at = start_at + 7.days
|
29
|
+
self.size_for_range(start_at, end_at)
|
30
|
+
end
|
31
|
+
|
32
|
+
def last_week
|
33
|
+
self.weeks_ago(1)
|
34
|
+
end
|
35
|
+
|
36
|
+
def weeks_average(past_num_weeks)
|
37
|
+
weeks_range = 1..past_num_weeks
|
38
|
+
total = weeks_range.reduce(0){|sum, n| sum + self.weeks_ago(n) }
|
39
|
+
total.to_f / past_num_weeks
|
40
|
+
end
|
41
|
+
|
42
|
+
def week_average_this_quarter
|
43
|
+
self.weeks_average(13)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/tricle/email_helper.rb
CHANGED
@@ -4,31 +4,56 @@ require 'active_support/core_ext/numeric/time'
|
|
4
4
|
module Tricle
|
5
5
|
module EmailHelper
|
6
6
|
def weeks_ago(n)
|
7
|
-
Date.today.beginning_of_week.
|
7
|
+
Date.today.beginning_of_week.ago(n.weeks)
|
8
8
|
end
|
9
9
|
|
10
10
|
def format_date(date)
|
11
11
|
date.strftime('%-m/%-d/%y')
|
12
12
|
end
|
13
13
|
|
14
|
+
def format_number(number)
|
15
|
+
number_with_delimiter((number.abs >= 100 ? number.round : sig_figs(number)))
|
16
|
+
end
|
17
|
+
|
14
18
|
def number_with_delimiter(number)
|
15
19
|
# from http://stackoverflow.com/a/11466770/358804
|
16
|
-
number.to_s.
|
20
|
+
integer, decimal = number.to_s.split(".")
|
21
|
+
[integer.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse, decimal].compact.join('.')
|
22
|
+
end
|
23
|
+
|
24
|
+
def sig_figs(number, num_sig_figs = 3)
|
25
|
+
# http://six-impossible.blogspot.com/2011/05/significant-digits-in-ruby-float.html
|
26
|
+
f = sprintf("%.#{num_sig_figs - 1}e", number).to_f
|
27
|
+
i = f.to_i # avoid
|
28
|
+
i == f && i.to_s.size > num_sig_figs ? i : f
|
17
29
|
end
|
18
30
|
|
19
31
|
def percent_change(new_val, old_val)
|
20
|
-
if old_val ==
|
32
|
+
if old_val == new_val
|
33
|
+
'No change'
|
34
|
+
elsif old_val == 0
|
21
35
|
new_val >= 0 ? '+' : '-'
|
22
36
|
else
|
23
37
|
fraction = (new_val - old_val) / old_val.to_f
|
24
|
-
|
38
|
+
(fraction >= 0 ? '+' : '').concat("#{sig_figs(fraction * 100.0)}%")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def percent_change_class(new_val, old_val, better)
|
43
|
+
case better
|
44
|
+
when :higher
|
45
|
+
(new_val >= old_val) ? 'good' : 'bad'
|
46
|
+
when :lower
|
47
|
+
(new_val >= old_val) ? 'bad' : 'good'
|
48
|
+
else
|
49
|
+
''
|
25
50
|
end
|
26
51
|
end
|
27
52
|
|
28
|
-
def percent_change_cell(new_val, old_val)
|
29
|
-
cls = (new_val
|
53
|
+
def percent_change_cell(new_val, old_val, better)
|
54
|
+
cls = self.percent_change_class(new_val, old_val, better)
|
30
55
|
pct_str = percent_change(new_val, old_val)
|
31
|
-
old_val_str =
|
56
|
+
old_val_str = format_number(old_val)
|
32
57
|
%[<td class="#{cls}"><div>#{pct_str}</div><div>#{old_val_str}</div></td>].html_safe
|
33
58
|
end
|
34
59
|
|
data/lib/tricle/metric.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
require 'active_support/core_ext/date/calculations'
|
2
2
|
require 'active_support/core_ext/numeric/time'
|
3
3
|
require_relative 'abstract_method_error'
|
4
|
+
require_relative 'aggregation'
|
4
5
|
|
5
6
|
module Tricle
|
6
7
|
class Metric
|
8
|
+
include Aggregation
|
9
|
+
|
10
|
+
def better
|
11
|
+
:higher
|
12
|
+
end
|
13
|
+
|
7
14
|
def title
|
8
15
|
self.class.name.titleize
|
9
16
|
end
|
@@ -12,12 +19,12 @@ module Tricle
|
|
12
19
|
self.items_for_range(start_at, end_at).size
|
13
20
|
end
|
14
21
|
|
15
|
-
def
|
22
|
+
def items_for_range(start_at, end_at)
|
16
23
|
raise Tricle::AbstractMethodError.new
|
17
24
|
end
|
18
25
|
|
19
|
-
def
|
20
|
-
|
26
|
+
def total?
|
27
|
+
self.respond_to?(:total)
|
21
28
|
end
|
22
29
|
end
|
23
30
|
end
|
@@ -1,19 +1,17 @@
|
|
1
|
-
require_relative 'metric'
|
2
1
|
require_relative 'section'
|
3
2
|
|
4
3
|
module Tricle
|
5
4
|
module Presenters
|
6
5
|
class Group < Section
|
7
|
-
attr_reader :
|
6
|
+
attr_reader :metrics, :title
|
8
7
|
|
9
8
|
def initialize(title=nil)
|
10
9
|
@title = title
|
11
|
-
@
|
10
|
+
@metrics = []
|
12
11
|
end
|
13
12
|
|
14
13
|
def add_metric(klass)
|
15
|
-
|
16
|
-
self.metric_presenters << presenter
|
14
|
+
self.metrics << klass.new
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|
@@ -27,15 +27,17 @@
|
|
27
27
|
<%= quarter_dates_cell %>
|
28
28
|
</th>
|
29
29
|
</tr>
|
30
|
-
<% section.
|
30
|
+
<% section.metrics.each do |metric| %>
|
31
31
|
<tr>
|
32
32
|
<th class="metric-title"><%= metric.title %></th>
|
33
33
|
<td>
|
34
|
-
<div><%=
|
35
|
-
|
34
|
+
<div><%= format_number(metric.last_week) %></div>
|
35
|
+
<% if metric.total? %>
|
36
|
+
<div><%= format_number(metric.total) %> (total)</div>
|
37
|
+
<% end %>
|
36
38
|
</td>
|
37
|
-
<%= percent_change_cell(metric.last_week, metric.weeks_ago(2)) %>
|
38
|
-
<%= percent_change_cell(metric.last_week, metric.week_average_this_quarter) %>
|
39
|
+
<%= percent_change_cell(metric.last_week, metric.weeks_ago(2), metric.better) %>
|
40
|
+
<%= percent_change_cell(metric.last_week, metric.week_average_this_quarter, metric.better) %>
|
39
41
|
</tr>
|
40
42
|
<% end %>
|
41
43
|
<% end %>
|
data/lib/tricle/version.rb
CHANGED