housekeeper-has_activity 0.4.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.
- data/MIT-LICENSE +20 -0
- data/README.textile +195 -0
- data/Rakefile +23 -0
- data/VERSION.yml +5 -0
- data/has_activity.gemspec +50 -0
- data/init.rb +3 -0
- data/install.rb +1 -0
- data/lib/core_ext.rb +53 -0
- data/lib/has_activity.rb +232 -0
- data/tasks/has_activity_tasks.rake +4 -0
- data/test/has_activity_test.rb +8 -0
- data/test/test_helper.rb +3 -0
- data/uninstall.rb +1 -0
- metadata +68 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 [name of plugin creator]
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
h1. HasActivity
|
2
|
+
---------------
|
3
|
+
|
4
|
+
* Originally created by Cary Dunn
|
5
|
+
* Modified functionality and additional features by HouseKeeper
|
6
|
+
|
7
|
+
A simple way to grab recent activity or a sum of values on a given model from a table grouped by day, hour,
|
8
|
+
or week with only 1 SQL query and giving the ability to pad the results for days/weeks/hours with no activity.
|
9
|
+
|
10
|
+
The plugin offers the ability to...
|
11
|
+
* Present as a Google Chart (line or bar)
|
12
|
+
* Scope your model to given :conditions
|
13
|
+
* Pad your results for days with no activity
|
14
|
+
* Present your data from a set date until present or between two dates
|
15
|
+
|
16
|
+
NOTE: MySQL only because of date functions use.
|
17
|
+
|
18
|
+
"Example Google Chart Generated":http://chart.apis.google.com/chart?cht=bvs&chs=200x50&chd=t:0,10,15,3,8,4,0,3,2,3,0,11,4,23,14,0,0,0,13,0,4,8,19,10,12,21,1&chco=336699&chbh=a,2&chds=0,23&chf=bg,s,EFEFEF&
|
19
|
+
|
20
|
+
h2. USAGE / INSTALLATION
|
21
|
+
------------------------
|
22
|
+
|
23
|
+
<pre><code>script/plugin install git://github.com/cdunn/has_activity.git</pre></code>
|
24
|
+
|
25
|
+
<pre><code>
|
26
|
+
class Feed < ActiveRecord::Base
|
27
|
+
# Defaults to created_at
|
28
|
+
has_activity
|
29
|
+
|
30
|
+
...
|
31
|
+
end
|
32
|
+
</code></pre>
|
33
|
+
|
34
|
+
<pre><code>
|
35
|
+
class Feed < ActiveRecord::Base
|
36
|
+
# Change the timestamp DB field to something custom
|
37
|
+
has_activity :by => "published_at"
|
38
|
+
|
39
|
+
...
|
40
|
+
end
|
41
|
+
</code></pre>
|
42
|
+
|
43
|
+
<pre><code>
|
44
|
+
# Get all activity since 1 week ago grouped by day (default)
|
45
|
+
Feed.activity_since(1.week.ago)
|
46
|
+
=> [{:activity=>0, :offset=>7, :created_at=>Mon Mar 09 10:27:34 -0700 2009}, {:activity=>0, :offset=>6, :created_at=>Tue Mar 10 10:27:34 -0700 2009}, {:activity=>8, :offset=>5, :created_at=>Wed Mar 11 01:19:09 -0700 2009}, {:activity=>2, :offset=>4, :created_at=>Thu Mar 12 04:26:09 -0700 2009}, {:activity=>0, :offset=>3, :created_at=>Fri Mar 13 10:27:34 -0700 2009}, {:activity=>0, :offset=>2, :created_at=>Sat Mar 14 10:27:34 -0700 2009}, {:activity=>0, :offset=>1, :created_at=>Sun Mar 15 10:27:34 -0700 2009}, {:activity=>0, :offset=>0, :created_at=>Mon Mar 16 10:27:34 -0700 2009}]
|
47
|
+
</code></pre>
|
48
|
+
|
49
|
+
SQL generated for previous statement...
|
50
|
+
<pre><code>
|
51
|
+
SELECT
|
52
|
+
published_at AS timestamp,
|
53
|
+
COUNT(*) AS activity_count,
|
54
|
+
DATEDIFF(now(), published_at) as days_ago
|
55
|
+
FROM feeds
|
56
|
+
WHERE 1=1 AND published_at > '2009-03-09 17:27:33'
|
57
|
+
GROUP BY days_ago
|
58
|
+
ORDER BY published_at ASC
|
59
|
+
</code></pre>
|
60
|
+
|
61
|
+
<pre><code>
|
62
|
+
# Get all activity since 1 day ago grouped by hour scoped to a certain user
|
63
|
+
Feed.activity_since(1.week.ago, :conditions => ["user_id = ?", current_user.id], :group_by => :hour)
|
64
|
+
=> [{:activity=>0, :offset=>24, :created_at=>Sun Mar 15 10:29:47 -0700 2009}, {:activity=>0, :offset=>23, :created_at=>Sun Mar 15 11:29:47 -0700 2009}, {:activity=>0, :offset=>22, :created_at=>Sun Mar 15 12:29:47 -0700 2009}, {:activity=>0, :offset=>21, :created_at=>Sun Mar 15 13:29:47 -0700 2009}, {:activity=>0, :offset=>20, :created_at=>Sun Mar 15 14:29:47 -0700 2009}, {:activity=>0, :offset=>19, :created_at=>Sun Mar 15 15:29:47 -0700 2009}, {:activity=>0, :offset=>18, :created_at=>Sun Mar 15 16:29:47 -0700 2009}, {:activity=>0, :offset=>17, :created_at=>Sun Mar 15 17:29:47 -0700 2009}, {:activity=>0, :offset=>16, :created_at=>Sun Mar 15 18:29:47 -0700 2009}, {:activity=>0, :offset=>15, :created_at=>Sun Mar 15 19:29:47 -0700 2009}, {:activity=>0, :offset=>14, :created_at=>Sun Mar 15 20:29:47 -0700 2009}, {:activity=>0, :offset=>13, :created_at=>Sun Mar 15 21:29:47 -0700 2009}, {:activity=>0, :offset=>12, :created_at=>Sun Mar 15 22:29:47 -0700 2009}, {:activity=>0, :offset=>11, :created_at=>Sun Mar 15 23:29:47 -0700 2009}, {:activity=>0, :offset=>10, :created_at=>Mon Mar 16 00:29:47 -0700 2009}, {:activity=>0, :offset=>9, :created_at=>Mon Mar 16 01:29:47 -0700 2009}, {:activity=>0, :offset=>8, :created_at=>Mon Mar 16 02:29:47 -0700 2009}, {:activity=>0, :offset=>7, :created_at=>Mon Mar 16 03:29:47 -0700 2009}, {:activity=>0, :offset=>6, :created_at=>Mon Mar 16 04:29:47 -0700 2009}, {:activity=>0, :offset=>5, :created_at=>Mon Mar 16 05:29:47 -0700 2009}, {:activity=>0, :offset=>4, :created_at=>Mon Mar 16 06:29:47 -0700 2009}, {:activity=>0, :offset=>3, :created_at=>Mon Mar 16 07:29:47 -0700 2009}, {:activity=>0, :offset=>2, :created_at=>Mon Mar 16 08:29:47 -0700 2009}, {:activity=>0, :offset=>1, :created_at=>Mon Mar 16 09:29:47 -0700 2009}, {:activity=>0, :offset=>0, :created_at=>Mon Mar 16 10:29:47 -0700 2009}]\
|
65
|
+
</code></pre>
|
66
|
+
|
67
|
+
SQL generated for previous statement...
|
68
|
+
<pre><code>
|
69
|
+
SELECT
|
70
|
+
published_at AS timestamp,
|
71
|
+
COUNT(*) AS activity_count,
|
72
|
+
((((YEAR(now()) - YEAR(published_at))*365)+(DAYOFYEAR(now())-DAYOFYEAR(published_at)))*24)+(HOUR(now())-HOUR(published_at)) as hours_ago,
|
73
|
+
CONCAT(YEAR(published_at), CONCAT(DAYOFYEAR(published_at), HOUR(published_at))) AS unique_hour
|
74
|
+
FROM feeds
|
75
|
+
WHERE user_id = 1 AND published_at > '2009-03-15 17:29:47'
|
76
|
+
GROUP BY unique_hour
|
77
|
+
ORDER BY published_at ASC
|
78
|
+
</code></pre>
|
79
|
+
|
80
|
+
<pre><code>
|
81
|
+
# Get all activity since 2 months ago grouped by week and order it from current week to oldest week
|
82
|
+
Feed.activity_since(2.months.ago, :group_by => :week, :order => :desc)
|
83
|
+
=> [{:activity=>0, :offset=>0, :created_at=>Mon Mar 16 10:34:28 -0700 2009}, {:activity=>0, :offset=>1, :created_at=>Mon Mar 09 10:34:28 -0700 2009}, {:activity=>0, :offset=>2, :created_at=>Mon Mar 02 10:34:28 -0800 2009}, {:activity=>0, :offset=>3, :created_at=>Mon Feb 23 10:34:28 -0800 2009}, {:activity=>0, :offset=>4, :created_at=>Mon Feb 16 10:34:28 -0800 2009}, {:activity=>0, :offset=>5, :created_at=>Mon Feb 09 10:34:28 -0800 2009}, {:activity=>0, :offset=>6, :created_at=>Mon Feb 02 10:34:28 -0800 2009}, {:activity=>0, :offset=>7, :created_at=>Mon Jan 26 10:34:28 -0800 2009}, {:activity=>0, :offset=>8, :created_at=>Mon Jan 19 10:34:28 -0800 2009}]
|
84
|
+
</code></pre>
|
85
|
+
|
86
|
+
SQL generated for previous statement...
|
87
|
+
<pre><code>
|
88
|
+
SELECT
|
89
|
+
published_at AS timestamp,
|
90
|
+
COUNT(*) AS activity_count,
|
91
|
+
((YEAR(now()) - YEAR(published_at))*52)+(WEEK(now())-WEEK(published_at)) as weeks_ago,
|
92
|
+
YEARWEEK(published_at) AS unique_week
|
93
|
+
FROM feeds
|
94
|
+
WHERE 1=1 AND published_at > '2009-01-16 17:34:28'
|
95
|
+
GROUP BY unique_week
|
96
|
+
ORDER BY published_at ASC
|
97
|
+
</code></pre>
|
98
|
+
|
99
|
+
<pre><code>
|
100
|
+
# Get Usd Net Commission in July ago grouped by day and order it in ascending order by the days commission was earnt on for account_id 1
|
101
|
+
AccountPnl.activity_between(july_month.beginning_of_month,
|
102
|
+
july_month.end_of_month,
|
103
|
+
:group_by => :day,
|
104
|
+
:padding => false,
|
105
|
+
:sum_on => "usd_net_commission",
|
106
|
+
:conditions => ["crm_account_id=?", 1],
|
107
|
+
:order => :asc)
|
108
|
+
</code></pre>
|
109
|
+
|
110
|
+
SQL generated for previous statement...
|
111
|
+
<pre><code>
|
112
|
+
SELECT
|
113
|
+
earnt_on AS timestamp,
|
114
|
+
COUNT(*) AS activity_count, SUM(usd_net_commission) as sum,
|
115
|
+
DATEDIFF('2009-07-31 23:59:59', earnt_on) as days_ago
|
116
|
+
FROM crm_daily_account_pnls
|
117
|
+
WHERE crm_account_id=16 AND earnt_on
|
118
|
+
BETWEEN '2009-07-01 00:00:00' AND '2009-07-31 23:59:59'
|
119
|
+
GROUP BY days_ago
|
120
|
+
ORDER BY earnt_on ASC
|
121
|
+
</code></pre>
|
122
|
+
|
123
|
+
|
124
|
+
h2. CONVERT DATA INTO GOOGLE CHART
|
125
|
+
|
126
|
+
<pre><code>
|
127
|
+
Feed.activity_since(6.months.ago, :group_by => :week, :order => :desc).to_activity_gchart
|
128
|
+
=> "http://chart.apis.google.com/chart?cht=bvs&chs=200x50&chd=t:0,10,15,3,8,4,0,3,2,3,0,11,4,23,14,0,0,0,13,0,4,8,19,10,12,21,1&chco=336699&chbh=a,2&chds=0,23&chf=bg,s,EFEFEF&"
|
129
|
+
</code></pre>
|
130
|
+
|
131
|
+
<pre><code>
|
132
|
+
Feed.activity_since(6.months.ago, :group_by => :week, :order => :desc).to_activity_gchart
|
133
|
+
AccountPnl.activity_between(july_month.beginning_of_month,
|
134
|
+
july_month.end_of_month,
|
135
|
+
:group_by => :day,
|
136
|
+
:padding => false,
|
137
|
+
:sum_on => "usd_net_commission",
|
138
|
+
:conditions => ["crm_account_id=?", 1],
|
139
|
+
:order => :asc).to_activity_gchart( :type => :line,
|
140
|
+
:column => :sum,
|
141
|
+
:size => "100x45",
|
142
|
+
:bgcolor => "f0ffdc",
|
143
|
+
:chart_color => "D1EAFF",
|
144
|
+
:area_color => "DFEBFF",
|
145
|
+
:line_color => "c7c7c7",
|
146
|
+
:line_width => "4"))
|
147
|
+
=> "http://chart.apis.google.com/chart?chs=100x45&cht=ls&chco=c7c7c7&chm=B,DFEBFF,0,0,0&chd=t:<data>&chds=0,0&chf=bg,s,f0ffdc&"
|
148
|
+
</code></pre>
|
149
|
+
|
150
|
+
h2. OTHER OPTIONS
|
151
|
+
|
152
|
+
<pre><code>
|
153
|
+
# Grabs a hash of the activity since <time ago> grouped by <hour/day/week>
|
154
|
+
#
|
155
|
+
# * :conditions
|
156
|
+
# same as the standard Rails finder. Used to scope your activity to a particular user, etc.
|
157
|
+
# * :padding
|
158
|
+
# true/false
|
159
|
+
# * :group_by
|
160
|
+
# :hour, :day, :week
|
161
|
+
#
|
162
|
+
def activity_since(since=1.week.ago, options={})
|
163
|
+
</code></pre>
|
164
|
+
|
165
|
+
<pre><code>
|
166
|
+
# Returns a URL to a simple google chart the represents the activity returned by the plugin
|
167
|
+
#
|
168
|
+
# * :type => :bar/:line
|
169
|
+
# * :size => "200x50"
|
170
|
+
# * :bgcolor => "EFEFEF"
|
171
|
+
# * :chart_color => "336699"
|
172
|
+
# * :area_color => "DFEBFF" (only for :line)
|
173
|
+
# * :line_color => "0077CC" (only for :line)
|
174
|
+
# * :line_width => "2" (only for :line)
|
175
|
+
#
|
176
|
+
def to_activity_gchart(options={})
|
177
|
+
</code></pre>
|
178
|
+
|
179
|
+
h2. INDEX THE TIMESTAMP YOU USE
|
180
|
+
|
181
|
+
Obviously there is not "one index fits all" but you should include an index on at least the timestamp that fits the conditions you are using.
|
182
|
+
The same applies when performing SUM on a column.
|
183
|
+
<pre><code>
|
184
|
+
add_index :model, [:created_at]
|
185
|
+
</code></pre>
|
186
|
+
|
187
|
+
h2. NOTES
|
188
|
+
|
189
|
+
* Using sum and creating Bar Charts is not fully tested - HouseKeeper
|
190
|
+
* Some work still needs to be done on the queries and a tidy up of has_activity.rb
|
191
|
+
|
192
|
+
Copyright (c) 2009 Cary Dunn <cary.dunn@gmail.com> "http://elctech.com":http://elctech.com, released under the MIT license
|
193
|
+
Modified by HouseKeeper
|
194
|
+
- Added the ability to sum a column - giving you a total ammount from a date until now or between 2 dates
|
195
|
+
- Added Between functionality so you can get activity or sums between 2 specified dates
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the has_activity plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.libs << 'test'
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate documentation for the has_activity plugin.'
|
17
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = 'HasActivity'
|
20
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
21
|
+
rdoc.rdoc_files.include('README')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{has_activity}
|
5
|
+
s.version = "0.4.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Cary Dunn", "Carl Burton"]
|
9
|
+
s.date = %q{2009-09-17}
|
10
|
+
s.description = %q{A simple way to grab recent activity or a columns sum on a given model from a table grouped by day, hour, or week with only 1 SQL query and giving the ability to pad the results for days/weeks/hours with no activity.}
|
11
|
+
s.email = %q{carl@house-keeping.com}
|
12
|
+
s.extra_rdoc_files = [
|
13
|
+
"README.textile"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
"VERSION.yml",
|
17
|
+
"uninstall.rb",
|
18
|
+
"README.textile",
|
19
|
+
"Rakefile",
|
20
|
+
"MIT-LICENSE",
|
21
|
+
"install.rb",
|
22
|
+
"init.rb",
|
23
|
+
"has_activity.gemspec",
|
24
|
+
"test/test_helper.rb",
|
25
|
+
"test/has_activity_test.rb",
|
26
|
+
"tasks/has_activity_tasks.rake",
|
27
|
+
"lib/has_activity.rb",
|
28
|
+
"lib/core_ext.rb"
|
29
|
+
]
|
30
|
+
s.has_rdoc = false
|
31
|
+
s.homepage = %q{http://github.com/housekeeper/has_activity}
|
32
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.rubygems_version = %q{1.3.1}
|
35
|
+
s.summary = %q{A simple way to grab recent activity or a columns sum on a given model from a table grouped by day, hour, or week with only 1 SQL query and giving the ability to pad the results for days/weeks/hours with no activity.}
|
36
|
+
s.test_files = [
|
37
|
+
"test/test_helper.rb",
|
38
|
+
"test/has_activity_test.rb"
|
39
|
+
]
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
43
|
+
s.specification_version = 2
|
44
|
+
|
45
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
46
|
+
else
|
47
|
+
end
|
48
|
+
else
|
49
|
+
end
|
50
|
+
end
|
data/init.rb
ADDED
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
data/lib/core_ext.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Original Author Cary Dunn <cary.dunn@gmail.com>
|
2
|
+
# Modified by HouseKeeper
|
3
|
+
|
4
|
+
unless Array.instance_methods.include? 'to_activity_gchart'
|
5
|
+
Array.class_eval do
|
6
|
+
# Returns a URL to a simple google chart the represents the activity returned by the plugin
|
7
|
+
#
|
8
|
+
# * :type => :bar/:line
|
9
|
+
# * :column => :activity/:sum
|
10
|
+
# * :size => "200x50"
|
11
|
+
# * :bgcolor => "EFEFEF"
|
12
|
+
# * :chart_color => "336699"
|
13
|
+
# * :area_color => "DFEBFF" (only for :line)
|
14
|
+
# * :line_color => "0077CC" (only for :line)
|
15
|
+
# * :line_width => "2" (only for :line)
|
16
|
+
#
|
17
|
+
def to_activity_gchart(options={})
|
18
|
+
options[:type] ||= :graph
|
19
|
+
options[:column] ||= :activity
|
20
|
+
options[:size] ||= "200x50"
|
21
|
+
options[:bgcolor] ||= "EFEFEF"
|
22
|
+
options[:chart_color] ||= "336699"
|
23
|
+
options[:area_color] ||= "DFEBFF"
|
24
|
+
options[:line_color] ||= "0077CC"
|
25
|
+
options[:line_width] ||= "2"
|
26
|
+
|
27
|
+
case options[:column]
|
28
|
+
when :activity
|
29
|
+
activity_min_data_point = self.min{|a,b| a[:activity] <=> b[:activity] }[:activity]
|
30
|
+
activity_max_data_point = self.max{|a,b| a[:activity] <=> b[:activity] }[:activity]
|
31
|
+
activity_str = self.map{|a| a[:activity]}.join(",")
|
32
|
+
return generate_url(activity_str, activity_min_data_point, activity_max_data_point, options)
|
33
|
+
when :sum
|
34
|
+
sum_min_data_point = self.min{|a,b| a[:sum] <=> b[:sum] }[:sum]
|
35
|
+
sum_max_data_point = self.max{|a,b| a[:sum] <=> b[:sum] }[:sum]
|
36
|
+
sum_str = self.map{|a| a[:sum]}.join(",")
|
37
|
+
return generate_url(sum_str, sum_min_data_point, sum_max_data_point, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def generate_url(data, min_data_point, max_data_point, options={})
|
45
|
+
if options[:type] == :line
|
46
|
+
return "http://chart.apis.google.com/chart?chs=#{options[:size]}&cht=ls&chco=#{options[:line_color]}&chm=B,#{options[:area_color]},0,0,0&chd=t:#{data}&chds=#{min_data_point},#{max_data_point}&chf=bg,s,#{options[:bgcolor]}&"
|
47
|
+
else
|
48
|
+
return "http://chart.apis.google.com/chart?cht=bvs&chs=#{options[:size]}&chd=t:#{data}&chco=#{options[:chart_color]}&chbh=a,#{options[:line_width]}&chds=#{min_data_point},#{max_data_point}&chf=bg,s,#{options[:bgcolor]}&"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/lib/has_activity.rb
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
#
|
2
|
+
# => has_activity
|
3
|
+
# => Cary Dunn <cary.dunn@gmail.com>
|
4
|
+
# => Modified by HouseKeeper: added between methods and sum option
|
5
|
+
|
6
|
+
# HasActivity
|
7
|
+
|
8
|
+
require 'core_ext'
|
9
|
+
|
10
|
+
module Elctech
|
11
|
+
module Has #:nodoc:
|
12
|
+
module Activity #:nodoc:
|
13
|
+
def self.included(base)
|
14
|
+
base.extend(ClassMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
|
19
|
+
def self.extended(base)
|
20
|
+
base.class_inheritable_accessor :activity_options
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_activity(options={})
|
24
|
+
options[:by] ||= "created_at"
|
25
|
+
include Elctech::Has::Activity::InstanceMethods
|
26
|
+
extend Elctech::Has::Activity::SingletonMethods
|
27
|
+
|
28
|
+
self.activity_options = options
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module SingletonMethods
|
33
|
+
|
34
|
+
# Grabs a hash of the activity between <time a> AND <time b>grouped by <hour/day/week>
|
35
|
+
#
|
36
|
+
# * :conditions
|
37
|
+
# same as the standard Rails finder. Used to scope your activity to a particular user, etc.
|
38
|
+
# * :padding
|
39
|
+
# true/false
|
40
|
+
# * :group_by
|
41
|
+
# :hour, :day, :week
|
42
|
+
#
|
43
|
+
def activity_between(between=1.week.ago, andbetween=Time.now.utc, options={})
|
44
|
+
activity_scope = (options.has_key?(:conditions) ? sanitize_sql(options[:conditions]) : "1=1")
|
45
|
+
options[:padding] ||= true
|
46
|
+
options[:order] ||= :asc
|
47
|
+
options[:group_by] ||= :day
|
48
|
+
sum = options[:sum_on] ? "SUM(#{options[:sum_on]}) as sum," : ""
|
49
|
+
|
50
|
+
case options[:group_by]
|
51
|
+
when :hour
|
52
|
+
sql_statement = sanitize_sql(
|
53
|
+
["SELECT
|
54
|
+
#{activity_options[:by]} AS timestamp,
|
55
|
+
COUNT(*) AS activity_count, #{sum}
|
56
|
+
((((YEAR(now()) - YEAR(#{activity_options[:by]}))*365)+(DAYOFYEAR(now())-DAYOFYEAR(#{activity_options[:by]})))*24)+(HOUR(now())-HOUR(#{activity_options[:by]})) as hours_ago,
|
57
|
+
CONCAT(YEAR(#{activity_options[:by]}), CONCAT(DAYOFYEAR(#{activity_options[:by]}), HOUR(#{activity_options[:by]}))) AS unique_hour
|
58
|
+
FROM #{self.table_name}
|
59
|
+
WHERE #{activity_scope} AND #{activity_options[:by]} > ?
|
60
|
+
GROUP BY unique_hour
|
61
|
+
ORDER BY #{activity_options[:by]} ASC",
|
62
|
+
between.to_s(:db)
|
63
|
+
]
|
64
|
+
)
|
65
|
+
unit = "hours_ago"
|
66
|
+
oldest_possible_unit = ((andbetween-between)/60)/60
|
67
|
+
when :week
|
68
|
+
sql_statement = sanitize_sql(
|
69
|
+
["SELECT
|
70
|
+
#{activity_options[:by]} AS timestamp,
|
71
|
+
COUNT(*) AS activity_count, #{sum}
|
72
|
+
((YEAR(now()) - YEAR(#{activity_options[:by]}))*52)+(WEEK(now())-WEEK(#{activity_options[:by]})) as weeks_ago,
|
73
|
+
YEARWEEK(#{activity_options[:by]}) AS unique_week
|
74
|
+
FROM #{self.table_name}
|
75
|
+
WHERE #{activity_scope} AND #{activity_options[:by]}
|
76
|
+
BETWEEN ? AND ?
|
77
|
+
GROUP BY unique_week
|
78
|
+
ORDER BY #{activity_options[:by]} ASC",
|
79
|
+
between.to_s(:db),andbetween.to_s(:db)
|
80
|
+
]
|
81
|
+
)
|
82
|
+
unit = "weeks_ago"
|
83
|
+
oldest_possible_unit = ((((andbetween-between)/60)/60)/24)/7
|
84
|
+
else
|
85
|
+
sql_statement = sanitize_sql(
|
86
|
+
["SELECT
|
87
|
+
#{activity_options[:by]} AS timestamp,
|
88
|
+
COUNT(*) AS activity_count, #{sum}
|
89
|
+
DATEDIFF('#{andbetween.to_s(:db)}', #{activity_options[:by]}) as days_ago
|
90
|
+
FROM #{self.table_name}
|
91
|
+
WHERE #{activity_scope} AND #{activity_options[:by]}
|
92
|
+
BETWEEN ? AND ?
|
93
|
+
GROUP BY days_ago
|
94
|
+
ORDER BY #{activity_options[:by]} ASC",
|
95
|
+
between.to_s(:db), andbetween.to_s(:db)
|
96
|
+
]
|
97
|
+
)
|
98
|
+
unit = "days_ago"
|
99
|
+
oldest_possible_unit = (((andbetween-between)/60)/60)/24
|
100
|
+
end
|
101
|
+
|
102
|
+
results = connection.select_all(sql_statement)
|
103
|
+
(options[:padding] ? pad_activity_results(results, unit, oldest_possible_unit.round, options[:order]) : format_activity_results(results, unit, order))
|
104
|
+
end
|
105
|
+
|
106
|
+
# Grabs a hash of the activity since <time ago> grouped by <hour/day/week>
|
107
|
+
#
|
108
|
+
# * :conditions
|
109
|
+
# same as the standard Rails finder. Used to scope your activity to a particular user, etc.
|
110
|
+
# * :padding
|
111
|
+
# true/false
|
112
|
+
# * :group_by
|
113
|
+
# :hour, :day, :week
|
114
|
+
#
|
115
|
+
def activity_since(since=1.week.ago, options={})
|
116
|
+
activity_scope = (options.has_key?(:conditions) ? sanitize_sql(options[:conditions]) : "1=1")
|
117
|
+
options[:padding] ||= true
|
118
|
+
options[:order] ||= :asc
|
119
|
+
options[:group_by] ||= :day
|
120
|
+
sum = options[:sum_on] ? "SUM(#{options[:sum_on]}) as sum," : ""
|
121
|
+
|
122
|
+
case options[:group_by]
|
123
|
+
when :hour
|
124
|
+
sql_statement = sanitize_sql(
|
125
|
+
["SELECT
|
126
|
+
#{activity_options[:by]} AS timestamp,
|
127
|
+
COUNT(*) AS activity_count, #{sum}
|
128
|
+
((((YEAR(now()) - YEAR(#{activity_options[:by]}))*365)+(DAYOFYEAR(now())-DAYOFYEAR(#{activity_options[:by]})))*24)+(HOUR(now())-HOUR(#{activity_options[:by]})) as hours_ago,
|
129
|
+
CONCAT(YEAR(#{activity_options[:by]}), CONCAT(DAYOFYEAR(#{activity_options[:by]}), HOUR(#{activity_options[:by]}))) AS unique_hour
|
130
|
+
FROM #{self.table_name}
|
131
|
+
WHERE #{activity_scope} AND #{activity_options[:by]} > ?
|
132
|
+
GROUP BY unique_hour
|
133
|
+
ORDER BY #{activity_options[:by]} ASC",
|
134
|
+
since.to_s(:db)
|
135
|
+
]
|
136
|
+
)
|
137
|
+
unit = "hours_ago"
|
138
|
+
oldest_possible_unit = ((Time.now-since)/60)/60
|
139
|
+
when :week
|
140
|
+
sql_statement = sanitize_sql(
|
141
|
+
["SELECT
|
142
|
+
#{activity_options[:by]} AS timestamp,
|
143
|
+
COUNT(*) AS activity_count, #{sum}
|
144
|
+
((YEAR(now()) - YEAR(#{activity_options[:by]}))*52)+(WEEK(now())-WEEK(#{activity_options[:by]})) as weeks_ago,
|
145
|
+
YEARWEEK(#{activity_options[:by]}) AS unique_week
|
146
|
+
FROM #{self.table_name}
|
147
|
+
WHERE #{activity_scope} AND #{activity_options[:by]} > ?
|
148
|
+
GROUP BY unique_week
|
149
|
+
ORDER BY #{activity_options[:by]} ASC",
|
150
|
+
since.to_s(:db)
|
151
|
+
]
|
152
|
+
)
|
153
|
+
unit = "weeks_ago"
|
154
|
+
oldest_possible_unit = ((((Time.now-since)/60)/60)/24)/7
|
155
|
+
else
|
156
|
+
sql_statement = sanitize_sql(
|
157
|
+
["SELECT
|
158
|
+
#{activity_options[:by]} AS timestamp,
|
159
|
+
COUNT(*) AS activity_count, #{sum}
|
160
|
+
DATEDIFF(now(), #{activity_options[:by]}) as days_ago
|
161
|
+
FROM #{self.table_name}
|
162
|
+
WHERE #{activity_scope} AND #{activity_options[:by]} > ?
|
163
|
+
GROUP BY days_ago
|
164
|
+
ORDER BY #{activity_options[:by]} ASC",
|
165
|
+
since.to_s(:db)
|
166
|
+
]
|
167
|
+
)
|
168
|
+
unit = "days_ago"
|
169
|
+
oldest_possible_unit = (((Time.now-since)/60)/60)/24
|
170
|
+
end
|
171
|
+
|
172
|
+
results = connection.select_all(sql_statement)
|
173
|
+
(options[:padding] ? pad_activity_results(results, unit, oldest_possible_unit.round, options[:order]) : format_activity_results(results, unit, order))
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
def format_activity_results(results, unit, order)
|
178
|
+
results.inject([]) do |rs,r|
|
179
|
+
entry = {
|
180
|
+
:offset => r[unit].to_i,
|
181
|
+
:activity => r["activity_count"].to_i,
|
182
|
+
:sum => r["sum"].to_f,
|
183
|
+
:date => Time.parse(r["timestamp"])
|
184
|
+
}
|
185
|
+
(order == :asc) ? rs.push(entry) : rs.unshift(entry)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def pad_activity_results(results, unit, oldest_possible_offset, order)
|
190
|
+
padded_results = []
|
191
|
+
|
192
|
+
current_unit_offset = oldest_possible_offset
|
193
|
+
current_result_index = 0
|
194
|
+
|
195
|
+
while current_unit_offset >= 0 do
|
196
|
+
if current_result_index < results.size && results[current_result_index][unit].to_i == current_unit_offset
|
197
|
+
entry = {
|
198
|
+
:offset => current_unit_offset,
|
199
|
+
:activity => results[current_result_index]["activity_count"].to_i,
|
200
|
+
:sum => results[current_result_index]["sum"].to_f,
|
201
|
+
:created_at => Time.parse(results[current_result_index]["timestamp"])
|
202
|
+
}
|
203
|
+
current_result_index = current_result_index+1
|
204
|
+
else
|
205
|
+
case unit
|
206
|
+
when "hours_ago"
|
207
|
+
created_at_given_offset = Time.now-current_unit_offset.hours
|
208
|
+
when "weeks_ago"
|
209
|
+
created_at_given_offset = Time.now-current_unit_offset.weeks
|
210
|
+
else
|
211
|
+
created_at_given_offset = Time.now-current_unit_offset.days
|
212
|
+
end
|
213
|
+
entry = {
|
214
|
+
:offset => current_unit_offset,
|
215
|
+
:activity => 0,
|
216
|
+
:sum => 0,
|
217
|
+
:created_at => created_at_given_offset
|
218
|
+
}
|
219
|
+
end
|
220
|
+
current_unit_offset = current_unit_offset-1
|
221
|
+
(order == :asc) ? padded_results.push(entry) : padded_results.unshift(entry)
|
222
|
+
end
|
223
|
+
|
224
|
+
padded_results
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
module InstanceMethods;end
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
data/test/test_helper.rb
ADDED
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: housekeeper-has_activity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cary Dunn
|
8
|
+
- Carl Burton
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-09-17 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: A simple way to grab recent activity or a columns sum on a given model from a table grouped by day, hour, or week with only 1 SQL query and giving the ability to pad the results for days/weeks/hours with no activity.
|
18
|
+
email: carl@house-keeping.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- README.textile
|
25
|
+
files:
|
26
|
+
- VERSION.yml
|
27
|
+
- uninstall.rb
|
28
|
+
- README.textile
|
29
|
+
- Rakefile
|
30
|
+
- MIT-LICENSE
|
31
|
+
- install.rb
|
32
|
+
- init.rb
|
33
|
+
- has_activity.gemspec
|
34
|
+
- test/test_helper.rb
|
35
|
+
- test/has_activity_test.rb
|
36
|
+
- tasks/has_activity_tasks.rake
|
37
|
+
- lib/has_activity.rb
|
38
|
+
- lib/core_ext.rb
|
39
|
+
has_rdoc: false
|
40
|
+
homepage: http://github.com/housekeeper/has_activity
|
41
|
+
licenses:
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options:
|
44
|
+
- --charset=UTF-8
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.3.5
|
63
|
+
signing_key:
|
64
|
+
specification_version: 2
|
65
|
+
summary: A simple way to grab recent activity or a columns sum on a given model from a table grouped by day, hour, or week with only 1 SQL query and giving the ability to pad the results for days/weeks/hours with no activity.
|
66
|
+
test_files:
|
67
|
+
- test/test_helper.rb
|
68
|
+
- test/has_activity_test.rb
|