sir_tracks_alot 0.2.0 → 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/README.rdoc CHANGED
@@ -15,3 +15,37 @@ Description goes here.
15
15
  == Copyright
16
16
 
17
17
  Copyright (c) 2010 Peter T. Brown. See LICENSE for details.
18
+
19
+
20
+
21
+ # resolution can be: hourly = %Y/%m/%d %H, daily = %Y/%m/%d or any valid strftime string
22
+ # def self.count_by(resolution, options_for_find = {})
23
+ # groups = {}
24
+ #
25
+ # filter(options_for_find) do |activity|
26
+ # activity.views(resolution).each do |time, count|
27
+ # groups[time] ||= 0
28
+ # groups[time] += count
29
+ # end
30
+ # end
31
+ #
32
+ # groups
33
+ # end
34
+
35
+ # def self.count(what, options_for_find)
36
+ # what = [what] unless what.kind_of?(Array)
37
+ # views, visits = 0, 0
38
+ # session_duration = options_for_find.delete(:session_duration)
39
+ # resolution = options_for_find.delete(:resolution)
40
+ #
41
+ # filter(options_for_find) do |activity|
42
+ # views += activity.views(resolution) if what.include?(:views)
43
+ # visits += activity.visits(session_duration) if what.include?(:visits)
44
+ # end
45
+ #
46
+ # return [views, visits] if what == [:views, :visits]
47
+ # return [visits, views] if what == [:visits, :views]
48
+ # return views if what == [:views]
49
+ # return visits if what == [:visits]
50
+ # raise ArgumentError("what must be one or more of :views, :visits")
51
+ # end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.4.0
@@ -30,6 +30,6 @@ Benchmark.bmbm do |b|
30
30
  end
31
31
 
32
32
  b.report('count_by daily') do
33
- counts = Trackable::Activity.count_by(:daily, :owner => @owner)
33
+ # counts = Trackable::Activity.count_by(:daily, :owner => @owner)
34
34
  end
35
35
  end
@@ -1,9 +1,11 @@
1
1
  module SirTracksAlot
2
2
  class Activity < Persistable
3
- ACTIONS = [:create, :view, :login, :search, :update, :destroy]
4
- DATE_FORMATS = {:hourly => '%Y/%m/%d %H', :daily => '%Y/%m/%d'}
5
- LIMIT = 1000
3
+ include EventHelper
4
+ extend FilterHelper
6
5
 
6
+ ACTIONS = [:create, :view, :login, :search, :update, :destroy]
7
+ LIMIT = 500
8
+
7
9
  attribute :created_at
8
10
  attribute :last_event # Clock.now
9
11
  attribute :owner # 123123
@@ -12,128 +14,57 @@ module SirTracksAlot
12
14
  attribute :category # **automatically set**
13
15
  attribute :action # create, view, login, etc.
14
16
  attribute :user_agent # IE/Safari Windows/Mac etc.
15
-
16
- index :last_event
17
+ attribute :counted # true/false
18
+ list :events # Clock.now's
19
+
17
20
  index :owner
18
21
  index :actor
19
22
  index :target
20
23
  index :category
24
+ index :last_event
21
25
  index :action
22
- index :user_agent
23
-
24
- list :events # Clock.now's
25
-
26
- # Find activities that match attributes
27
- # Strings are passed to Ohm
28
- # Regular Expression filters match against retrieved attribute values
29
- #
30
- # filter(:category => 'category', :target => /\/targets\/\d+/)
31
- def self.filter(options_for_find, &block)
32
- activities = []
33
- sort = options_for_find.delete(:sort) || {}
34
-
35
- strings = {}
36
- matchers = {}
37
-
38
- options_for_find.each do |key, candidate|
39
- matchers[key] = candidate if candidate.kind_of?(Regexp)
40
- strings[key] = candidate if candidate.kind_of?(String)
41
- end
42
-
43
- all = SirTracksAlot::Activity.find(strings).sort(sort)
44
-
45
- all.each do |activity|
46
- pass = true
47
-
48
- matchers.each do |key, matcher|
49
- pass = false if !matcher.match(activity.send(key))
50
- end
26
+ index :user_agent
27
+ index :counted
51
28
 
52
- next unless pass
53
-
54
- yield activity if block_given?
55
-
56
- activities << activity
57
- end
58
-
59
- activities
60
- end
61
-
62
- # resolution can be: hourly = %Y/%m/%d %H, daily = %Y/%m/%d or any valid strftime string
63
- def self.count_by(resolution, options_for_find = {})
64
- groups = {}
65
-
66
- filter(options_for_find) do |activity|
67
- activity.views(resolution).each do |time, count|
68
- groups[time] ||= 0
69
- groups[time] += count
70
- end
29
+ class <<self
30
+ alias_method :ohm_create, :create
31
+
32
+ # Create with defaults: counted = 0
33
+ def create(attrs)
34
+ ohm_create(attrs.merge(:counted => '0'))
71
35
  end
72
-
73
- groups
74
36
  end
75
-
37
+
38
+ # find recent activities
76
39
  def self.recent(options_for_find, options_for_sort = {:order => 'DESC', :limit => LIMIT})
77
- SirTracksAlot::Activity.find(options_for_find).sort_by(:last_event, options_for_sort)
78
- end
79
-
80
- def self.count(what, options_for_find)
81
- what = [what] unless what.kind_of?(Array)
82
- views, visits = 0, 0
83
- session_duration = options_for_find.delete(:session_duration)
84
- resolution = options_for_find.delete(:resolution)
85
-
86
- filter(options_for_find) do |activity|
87
- views += activity.views(resolution) if what.include?(:views)
88
- visits += activity.visits(session_duration) if what.include?(:visits)
89
- end
90
-
91
- return [views, visits] if what == [:views, :visits]
92
- return [visits, views] if what == [:visits, :views]
93
- return views if what == [:views]
94
- return visits if what == [:visits]
95
- raise ArgumentError("what must be one or more of :views, :visits")
96
- end
97
-
98
- def views(resolution = nil)
99
- return events.size if resolution.nil?
100
-
101
- groups = {}
102
-
103
- date_format = resolution.kind_of?(String) ? resolution : DATE_FORMATS[resolution]
40
+ find(options_for_find).sort_by(:last_event, options_for_sort)
41
+ end
104
42
 
105
- events.each do |event|
106
- time = Time.at(event.to_i).utc.strftime(date_format) # lop of some detail
107
- groups[time.to_s] ||= 0
108
- groups[time.to_s] += 1
109
- end
110
-
111
- return groups
43
+ # Delete counted activities, leaving a default of the 500 most recent for the provided search criteria
44
+ # If left blank, purges all but the most recent Limit
45
+ def self.purge!(options_for_find = {}, options_for_sort = {:order => 'DESC'})
46
+ recent(options_for_find.merge(:counted => 1), options_for_sort).each{|a| a.delete}
112
47
  end
113
-
114
- # Count one visit for all events every session_duration (in seconds)
115
- # e.g. 3 visits within 15 minutes counts as one, 1 visit 2 days later counts as another
116
- def visits(sd = 1800)
117
- session_duration = sd || 1800
118
- count = events.size > 0 ? 1 : 0
119
- last_event = events.first
120
-
121
- events.each do |event|
122
- count += 1 if (event.to_i - last_event.to_i > session_duration)
123
- last_event = event
124
- end
125
-
126
- count
48
+
49
+ # set this activity to counted
50
+ def counted!
51
+ update(:counted => '1')
127
52
  end
128
-
53
+
54
+ # is this activity counted?
55
+ def counted?
56
+ counted == '1'
57
+ end
58
+
59
+
129
60
  private
130
-
61
+
131
62
  def validate
132
63
  assert_present :owner
133
64
  assert_present :target
134
65
  assert_present :action
135
66
  assert_unique([:owner, :target, :action, :category, :user_agent, :actor])
136
67
  end
137
-
68
+
138
69
  end
139
70
  end
@@ -0,0 +1,80 @@
1
+ module SirTracksAlot
2
+ class Count < Persistable
3
+ extend FilterHelper
4
+
5
+ ACTIONS = [:create, :view, :login, :search, :update, :destroy]
6
+
7
+ attribute :created_at
8
+ attribute :date # 10/01/2010
9
+ attribute :hour # 1 - 23
10
+ attribute :owner # 123123
11
+ attribute :actor # /users/peter-brown
12
+ attribute :target # /discussions/23423
13
+ attribute :category
14
+ attribute :views
15
+ attribute :visits
16
+
17
+ index :date
18
+ index :hour
19
+ index :owner
20
+ index :actor
21
+ index :target
22
+ index :category
23
+
24
+ def self.count(options)
25
+ options = OpenStruct.new(options) if options.kind_of?(Hash)
26
+
27
+ rollup(Activity.filter(:owner => options.owner,
28
+ :target => options.target,
29
+ :action => options.action||[],
30
+ :category => options.category || [],
31
+ :counted => '0'),
32
+ options.session_duration
33
+ )
34
+ end
35
+
36
+ def self.rollup(activities, session_duration)
37
+ counts = []
38
+
39
+ activities.each do |activity|
40
+ activity.views(:hourly).each do |time, views|
41
+ date, hour = time.split(' ')
42
+ counts << create_by_activity({:owner => activity.owner, :category => activity.category, :target => activity.target, :date => date, :hour => hour}, views, 0)
43
+ counts << create_by_activity({:owner => activity.owner, :category => activity.category, :actor => activity.actor, :date => date, :hour => hour}, views, 0)
44
+ end
45
+
46
+ activity.visits(session_duration, :hourly).each do |time, visits|
47
+ date, hour = time.split(' ')
48
+ counts << create_by_activity({:owner => activity.owner, :category => activity.category, :target => activity.target, :date => date, :hour => hour}, 0, visits)
49
+ counts << create_by_activity({:owner => activity.owner, :category => activity.category, :actor => activity.actor, :date => date, :hour => hour}, 0, visits)
50
+ end
51
+
52
+ activity.counted!
53
+ end
54
+
55
+ counts
56
+ end
57
+
58
+ def self.create_by_activity(attributes, views = 0, visits = 0)
59
+ count = Count.find_or_create(attributes)
60
+
61
+ count.views ||= 0; count.visits ||= 0
62
+
63
+ count.views = count.views.to_i + views
64
+ count.visits = count.visits.to_i + visits
65
+
66
+ count.save
67
+ end
68
+
69
+
70
+ private
71
+
72
+ def validate
73
+ assert_present :owner
74
+ assert_present :views
75
+ assert_present :visits
76
+ assert_unique([:owner, :actor, :target, :category, :date, :hour])
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,49 @@
1
+ module SirTracksAlot
2
+ module EventHelper
3
+ DATE_FORMATS = {:hourly => '%Y/%m/%d %H', :daily => '%Y/%m/%d'}
4
+
5
+ def views(resolution = nil)
6
+ return events.size if resolution.nil?
7
+
8
+ groups = {}
9
+
10
+ date_format = resolution.kind_of?(String) ? resolution : DATE_FORMATS[resolution]
11
+
12
+ events.each do |event|
13
+ time = Time.at(event.to_i).utc.strftime(date_format) # lop of some detail
14
+ groups[time.to_s] ||= 0
15
+ groups[time.to_s] += 1
16
+ end
17
+
18
+ return groups
19
+ end
20
+
21
+ # Count one visit for all events every session_duration (in seconds)
22
+ # e.g. 3 visits within 15 minutes counts as one, 1 visit 2 days later counts as another
23
+ def visits(sd = 1800, resolution = nil)
24
+ session_duration = sd || 1800
25
+ last_event = events.first
26
+
27
+ count = events.size > 0 ? 1 : 0 # in case last_event == event (below)
28
+ groups = {}
29
+
30
+ events.each do |event|
31
+ boundary = (event.to_i - last_event.to_i > session_duration) # have we crossed a session boundary?
32
+
33
+ if resolution.nil? # i.e. don't group
34
+ count += 1 if boundary
35
+ else
36
+ date_format = resolution.kind_of?(String) ? resolution : DATE_FORMATS[resolution]
37
+ time = Time.at(event.to_i).utc.strftime(date_format) # lop of some detail
38
+
39
+ groups[time.to_s] ||= count
40
+ groups[time.to_s] += 1 if boundary
41
+ end
42
+
43
+ last_event = event # try the next bounary
44
+ end
45
+
46
+ resolution.nil? ? count : groups
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,54 @@
1
+ module SirTracksAlot
2
+ module FilterHelper
3
+ # Find activities that match attributes
4
+ # Strings are passed to Ohm
5
+ # Regular Expression filters match against retrieved attribute values
6
+ #
7
+ # filter(:actor => 'user1', :target => /\/targets\/\d+/, :action => ['view', 'create'], :category => ['/root', '/other_root'])
8
+ def filter(options_for_find, &block)
9
+ items = []
10
+ all = []
11
+
12
+ strings, matchers, arrays = extract_filter_options(options_for_find)
13
+
14
+ unless arrays.empty?
15
+ (arrays.values.inject{|a, b| a.product b}).each do |combo|
16
+ terms = {}; combo.each{|c| terms[find_key_from_value(arrays, c)] = c}
17
+ all += find(terms.merge(strings)).to_a
18
+ end
19
+ else
20
+ all = find(strings)
21
+ end
22
+
23
+ all.each do |item|
24
+ pass = true
25
+
26
+ matchers.each do |key, matcher|
27
+ pass = false if !matcher.match(item.send(key))
28
+ end
29
+
30
+ next unless pass
31
+
32
+ yield item if block_given?
33
+
34
+ items << item
35
+ end
36
+
37
+ items
38
+ end
39
+
40
+ def extract_filter_options(options)
41
+ strings = {}
42
+ matchers = {}
43
+ arrays = {}
44
+
45
+ options.each do |key, candidate|
46
+ matchers[key] = candidate if candidate.kind_of?(Regexp) && !candidate.blank?
47
+ strings[key] = candidate.to_s if (candidate.kind_of?(String)) && !candidate.blank?
48
+ arrays[key] = candidate if candidate.kind_of?(Array) && !candidate.blank?
49
+ end
50
+
51
+ [strings, matchers, arrays]
52
+ end
53
+ end
54
+ end
@@ -1,7 +1,7 @@
1
1
  require 'ohm'
2
2
 
3
3
  module SirTracksAlot
4
- class Persistable < Ohm::Model
4
+ class Persistable < Ohm::Model
5
5
  attribute :created_at
6
6
 
7
7
  def self.find_or_create(attributes)
@@ -9,9 +9,28 @@ module SirTracksAlot
9
9
  models.empty? ? self.create(attributes.merge(:created_at => Clock.now)) : models.first
10
10
  end
11
11
 
12
- protected
12
+ def hash
13
+ self.id
14
+ end
15
+
16
+ # Simply delegate to == in this example.
17
+ def eql?(comparee)
18
+ self == comparee
19
+ end
20
+
21
+ # Objects are equal if they have the same
22
+ # unique custom identifier.
23
+ def ==(comparee)
24
+ self.id == comparee.id
25
+ end
26
+
27
+
28
+ protected
13
29
 
14
- def validate
15
- end
30
+ def self.find_key_from_value(hash, value)
31
+ hash.each do |k, v|
32
+ return k if v.include?(value)
33
+ end
34
+ end
16
35
  end
17
36
  end
@@ -10,8 +10,7 @@ module SirTracksAlot
10
10
 
11
11
  def self.push(owner, report, options)
12
12
  config = ReportConfig.find_or_create(:owner => owner.to_s, :report => report.to_s)
13
- config.options = options.merge(:owner => owner.to_s) # reports require owner, should be sync'd with config owner
14
-
13
+ config.options = options.merge(:owner => owner.to_s) # reports require owner, should be sync'd with config owner
15
14
  queue = self.find_or_create(:name => QUEUE_NAME)
16
15
  queue.queue << config.id
17
16
  end
@@ -33,19 +32,19 @@ module SirTracksAlot
33
32
 
34
33
  def self.process(config)
35
34
  return false if config.nil?
36
-
35
+
37
36
  SirTracksAlot.log.info("building report: #{config.inspect}")
38
-
37
+
39
38
  begin
40
- report = QueueHelper.constantize(QueueHelper.camelize("SirTracksAlot::Reports::#{config.report.capitalize}"))
41
- html = report.render_html(config.options)
42
-
39
+ report = QueueHelper.constantize(QueueHelper.camelize("SirTracksAlot::Reports::#{config.report.capitalize}"))
40
+ counts = Count.filter(config.options)
41
+ html = report.render_html(:counts => counts)
43
42
  cache = ReportCache.find_or_create(:owner => config.owner, :report => config.report)
44
43
  cache.update(:html => html)
45
44
  rescue Exception => e
46
45
  SirTracksAlot.log.fatal("Error building report #{config.report} for #{config.owner}: #{e}")
47
46
  end
48
-
47
+
49
48
  true
50
49
  end
51
50
  end
@@ -1,6 +1,6 @@
1
1
  module SirTracksAlot
2
2
  module Reports
3
- class ActivityReport < SirTracksAlotReport
3
+ class ActivityReport < SirTracksAlotReport
4
4
  COLUMN_NAMES = ['actor', 'action', 'target', 'event']
5
5
  LIMIT = 10000
6
6
 
@@ -11,7 +11,7 @@ module SirTracksAlot
11
11
  super
12
12
  options.column_names ||= COLUMN_NAMES
13
13
 
14
- activities = Activity.recent({:owner => options.owner}, :order => 'DESC', :limit => (options.limit || LIMIT))
14
+ activities = Activity.recent({:owner => options.owner}, :order => 'DESC', :limit => (options.limit || LIMIT))
15
15
  events = activities.collect{|a| [a.actor, a.action, a.target, a.last_event]}.reject{|a| a[2].nil?}
16
16
 
17
17
  table = Table(options.column_names, :data => events)
@@ -4,44 +4,27 @@ module SirTracksAlot
4
4
  COLUMN_NAMES = ['actor', 'page views', 'visits']
5
5
 
6
6
  stage :actor
7
- required_option :owner
8
7
 
9
8
  def setup
10
- super
11
- counts = {}
12
- options.column_names ||= COLUMN_NAMES
13
- options.actions ||= []
14
- options.roots ||= []
9
+ super
10
+ column_names = options.column_names || COLUMN_NAMES
11
+ counts = options.counts || {}
15
12
 
16
- options.roots.each do |root|
17
- options.actions.each do |action|
18
- activities = Activity.find(:owner => options.owner, :action => action, :category => root)
19
-
20
- activities.each do |activity|
21
- counts[activity.actor] ||= [0,0]
22
- counts[activity.actor][0] += activity.views
23
- counts[activity.actor][1] += activity.visits(options.session_duration)
24
- end
25
- end
26
- end
27
-
28
- table = Table(options.column_names) do |t|
29
- counts.each do |target, count|
30
- t << [target, count[0], count[1]]
13
+ table = Table(column_names) do |t|
14
+ counts.each do |count|
15
+ t << [count.actor, count.visits, count.views]
31
16
  end
32
17
  end
33
18
 
34
19
  table.sort_rows_by!('page views', :order => :descending)
35
20
 
36
21
  self.data = table
37
-
38
22
  end
39
23
 
40
24
  module Helpers
41
25
  include Report::Helpers
42
26
  end
43
27
 
44
-
45
28
  class HTML < Ruport::Formatter::HTML
46
29
  renders :html, :for => ActorReport
47
30
 
@@ -22,18 +22,25 @@ module SirTracksAlot
22
22
  def setup
23
23
  super
24
24
 
25
- counts = []
25
+ counts = {}
26
26
  options.filters ||= {}
27
27
  options.column_names ||= COLUMN_NAMES
28
-
29
- options.filters.each do |title, options_for_find|
30
- views, visits = Activity.count([:views, :visits], options_for_find.merge(:owner => options.owner))
31
- counts << [title, views, visits] if views > 0
32
- end
33
28
 
34
29
 
35
- table = Table(options.column_names, :data => counts)
36
-
30
+ options.filters.each do |title, options_for_find|
31
+ Count.filter(options_for_find.merge(:owner => options.owner)).each do |count|
32
+ counts[title] ||= [0,0]
33
+ counts[title][0] += count.visits.to_i
34
+ counts[title][1] += count.views.to_i
35
+ end
36
+ end
37
+
38
+ table = Table(options.column_names) do |t|
39
+ counts.each do |title, n|
40
+ t << [title, n[0], n[1]]
41
+ end
42
+ end
43
+
37
44
  table.sort_rows_by!('page views', :order => :descending)
38
45
 
39
46
  self.data = table
@@ -4,31 +4,15 @@ module SirTracksAlot
4
4
  COLUMN_NAMES = ['target', 'page views', 'visits']
5
5
 
6
6
  stage :target
7
- required_option :owner, :actions
8
7
 
9
8
  def setup
10
9
  super
11
- counts = {}
12
- options.filters ||= false
13
- options.column_names ||= COLUMN_NAMES
14
- options.actions ||= []
15
- options.roots ||= []
10
+ column_names = options.column_names || COLUMN_NAMES
11
+ counts = options.counts || {}
16
12
 
17
- options.roots.each do |root|
18
- options.actions.each do |action|
19
- activities = Activity.filter(:owner => options.owner, :target => options.target, :action => action, :category => root)
20
-
21
- activities.each do |activity|
22
- counts[activity.target] ||= [0,0]
23
- counts[activity.target][0] += activity.views
24
- counts[activity.target][1] += activity.visits(options.session_duration)
25
- end
26
- end
27
- end
28
-
29
- table = Table(options.column_names) do |t|
30
- counts.each do |target, count|
31
- t << [target, count[0], count[1]]
13
+ table = Table(column_names) do |t|
14
+ counts.each do |count|
15
+ t << [count.target, count.visits, count.views]
32
16
  end
33
17
  end
34
18
 
@@ -41,7 +25,6 @@ module SirTracksAlot
41
25
  include Report::Helpers
42
26
  end
43
27
 
44
-
45
28
  class HTML < Ruport::Formatter::HTML
46
29
  renders :html, :for => TargetReport
47
30
 
@@ -4,9 +4,7 @@ module SirTracksAlot
4
4
  attr_accessor :all
5
5
 
6
6
  def setup
7
- super
8
- options.categories = options.categories #|| all.collect{|a| a.category}.uniq
9
- options.actions = options.actions #|| all.collect{|a| a.action}.uniq
7
+ super
10
8
  end
11
9
  end
12
10
  end
@@ -6,12 +6,20 @@ require "bundler"
6
6
  Bundler.setup
7
7
  Bundler.require
8
8
 
9
+ # require 'logging'
10
+ # require 'twitter'
11
+ # require 'ruport'
12
+ # require 'ohm'
13
+ # require 'redis'
14
+
9
15
  module SirTracksAlot
10
16
  TRACKABLE_ROOT = "#{File.dirname(__FILE__)}/.."
11
17
 
12
18
  autoload :Persistable, File.join(File.dirname(__FILE__), *%w[sir_tracks_alot persistable.rb])
13
- autoload :Rollup, File.join(File.dirname(__FILE__), *%w[sir_tracks_alot rollup.rb])
14
19
  autoload :Activity, File.join(File.dirname(__FILE__), *%w[sir_tracks_alot activity.rb])
20
+ autoload :EventHelper, File.join(File.dirname(__FILE__), *%w[sir_tracks_alot event_helper.rb])
21
+ autoload :FilterHelper, File.join(File.dirname(__FILE__), *%w[sir_tracks_alot filter_helper.rb])
22
+ autoload :Count, File.join(File.dirname(__FILE__), *%w[sir_tracks_alot count.rb])
15
23
  autoload :Clock, File.join(File.dirname(__FILE__), *%w[sir_tracks_alot clock.rb])
16
24
 
17
25
  class SirTracksAlotError < StandardError
@@ -49,7 +57,9 @@ module SirTracksAlot
49
57
 
50
58
  raise RecordInvalidError.new("Activity not valid: #{activity.errors.inspect}") unless activity.valid?
51
59
 
52
- activity.update(:last_event => event )
60
+ raise "EVENT NIL" if event.nil?
61
+
62
+ activity.update(:last_event => event)
53
63
  activity.events << event
54
64
  activity
55
65
  end
@@ -6,15 +6,12 @@ describe SirTracksAlot::Activity do
6
6
  before do
7
7
  RedisSpecHelper.reset
8
8
  @now = SirTracksAlot::Clock.now
9
-
10
9
  @activity = SirTracksAlot::Activity.create(@activities[0])
11
- @activity.events << @now
10
+ @activity.events << @now
11
+ @activity.update(:last_event => @now)
12
12
  end
13
13
 
14
14
  context 'when creating' do
15
- before do
16
- end
17
-
18
15
  it "should store valid activities" do
19
16
  @activity.should be_valid
20
17
  end
@@ -28,13 +25,44 @@ describe SirTracksAlot::Activity do
28
25
  activity = SirTracksAlot::Activity.create(@activities[0]) # already created in before
29
26
  activity.should_not be_valid
30
27
  end
28
+
29
+ it "should set counted" do
30
+ @activity.counted!
31
+ @activity.should be_counted
32
+ end
33
+
34
+ it "should default to uncounted" do
35
+ @activity.counted.should == '0'
36
+ end
31
37
  end
32
38
 
33
- context 'when filtering' do
39
+ context 'when purging' do
40
+ before do
41
+ @activities.each{|a| SirTracksAlot.record(a)}
42
+ end
43
+
44
+ it "should not purge uncounted" do
45
+ SirTracksAlot::Activity.purge!
46
+ SirTracksAlot::Activity.all.size.should == 8
47
+ end
48
+
49
+ it "should purge counted" do
50
+ SirTracksAlot::Activity.all.each{|a| a.counted!}
51
+ SirTracksAlot::Activity.purge!
52
+ SirTracksAlot::Activity.all.size.should == 0
53
+ end
54
+
55
+ it "should purge counted by range" do
56
+ SirTracksAlot::Activity.all.each{|a| a.counted!}
57
+ SirTracksAlot::Activity.purge!({}, :start => 5, :limit => 10)
58
+ SirTracksAlot::Activity.all.size.should == 5
59
+ end
60
+ end
61
+
62
+ context 'when filtering (simple)' do
34
63
  before do
35
64
  @mock_activity = mock(SirTracksAlot::Activity, @activities[0])
36
- @mock_activityz = mock('SortedActivity', :sort => [@mock_activity])
37
- SirTracksAlot::Activity.stub!(:find => @mock_activityz)
65
+ SirTracksAlot::Activity.stub!(:find => [@mock_activity])
38
66
  end
39
67
 
40
68
  it "should find activities matching attribute regex" do
@@ -55,61 +83,87 @@ describe SirTracksAlot::Activity do
55
83
 
56
84
  it "should not match with negative regex" do
57
85
  SirTracksAlot::Activity.filter(:owner => /^(?!(.*owner.*))/).size.should == 0
58
- end
86
+ end
59
87
  end
60
88
 
61
- context 'when getting recent' do
62
- it "should get recent" do
63
- SirTracksAlot::Activity.recent(:owner => 'owner').should be_instance_of(Array)
64
- end
65
- end
66
-
67
- context 'when counting' do
89
+ context 'when filtering (many)' do
68
90
  before do
69
- @mock_activity = mock(SirTracksAlot::Activity, :visits => 1, :views => 2)
70
- @mock_activityz = mock('SortedActivity', :sort => [@mock_activity])
71
- SirTracksAlot::Activity.stub!(:find => @mock_activityz)
72
- end
73
-
74
- it "should look for activities using find options" do
75
- SirTracksAlot::Activity.should_receive(:find).with(:owner => 'owner').and_return(@mock_activityz)
76
- SirTracksAlot::Activity.count(:views, :owner => 'owner')
91
+ @activities.each{|a| SirTracksAlot.record(a)}
77
92
  end
78
93
 
79
- it "should look for views when counting views" do
80
- @mock_activity.should_receive(:views).once.and_return(1)
81
- SirTracksAlot::Activity.count(:views, :owner => 'owner')
94
+ it "should filter by members of one array" do
95
+ SirTracksAlot::Activity.filter(:owner => 'owner', :target => ['/categories/item1', '/categories/item2']).size.should == 3
82
96
  end
83
97
 
84
- it "should not look for views when counting visits" do
85
- @mock_activity.should_receive(:views).never
86
- SirTracksAlot::Activity.count(:visits, :owner => 'owner')
87
- end
88
-
89
- it "should return views and visits" do
90
- SirTracksAlot::Activity.count([:visits, :views], :owner => 'owner').should == [1,2]
91
- end
92
-
93
- it "should return visits" do
94
- SirTracksAlot::Activity.count([:visits], :owner => 'owner').should == 1
98
+ it "should filter by combinations of arrays" do
99
+ SirTracksAlot::Activity.filter(:owner => 'owner', :action => ['view', 'create'], :target => ['/categories/item1', '/categories/item2']).size.should == 3
100
+ end
101
+ end
102
+
103
+ context 'when getting recent' do
104
+ before do
105
+ @set_activities.each{|a| SirTracksAlot.record(a)}
95
106
  end
96
107
 
97
- it "should ignore empty only" do
98
- SirTracksAlot::Activity.count([:visits], :owner => 'owner', :only => {}).should == 1
108
+ it "should get recent" do
109
+ SirTracksAlot::Activity.recent(:owner => 'owner').should be_instance_of(Array)
99
110
  end
100
111
 
101
- it "should filter by category" do
102
- @mock_activity.should_receive(:category).once.and_return('category')
103
- SirTracksAlot::Activity.count([:visits], :owner => 'owner', :category => /category/)
104
- end
112
+ it "should purge counted by range, sorting to most recent by last event" do
113
+ SirTracksAlot::Activity.recent(:owner => 'owner').first.last_event.should == @now.to_s
114
+ end
115
+
116
+ it "should purge counted by range, sorting to most recent by last event" do
117
+ SirTracksAlot::Activity.recent({:owner => 'owner'}, :start => 1).first.last_event.should == '1279709941'
118
+ end
105
119
 
106
- it "should filter by category and match returning correct count" do
107
- @mock_activity.stub!(:category => 'category')
108
- SirTracksAlot::Activity.count([:visits], :owner => 'owner', :category => /category/).should == 1
109
- end
110
120
  end
111
121
 
112
- context 'when counting groups' do
122
+ context 'when counting' do
123
+ before do
124
+ @mock_activity = mock(SirTracksAlot::Activity, :visits => 1, :views => 2)
125
+ SirTracksAlot::Activity.stub!(:find => [@mock_activity])
126
+ end
127
+
128
+ # it "should look for activities using find options" do
129
+ # SirTracksAlot::Activity.should_receive(:find).with(:owner => 'owner').and_return([@mock_activity])
130
+ # SirTracksAlot::Activity.count(:views, :owner => 'owner')
131
+ # end
132
+ #
133
+ # it "should look for views when counting views" do
134
+ # @mock_activity.should_receive(:views).once.and_return(1)
135
+ # SirTracksAlot::Activity.count(:views, :owner => 'owner')
136
+ # end
137
+ #
138
+ # it "should not look for views when counting visits" do
139
+ # @mock_activity.should_receive(:views).never
140
+ # SirTracksAlot::Activity.count(:visits, :owner => 'owner')
141
+ # end
142
+ #
143
+ # it "should return views and visits" do
144
+ # SirTracksAlot::Activity.count([:visits, :views], :owner => 'owner').should == [1,2]
145
+ # end
146
+ #
147
+ # it "should return visits" do
148
+ # SirTracksAlot::Activity.count([:visits], :owner => 'owner').should == 1
149
+ # end
150
+ #
151
+ # it "should ignore empty only" do
152
+ # SirTracksAlot::Activity.count([:visits], :owner => 'owner', :only => {}).should == 1
153
+ # end
154
+ #
155
+ # it "should filter by category" do
156
+ # @mock_activity.should_receive(:category).once.and_return('category')
157
+ # SirTracksAlot::Activity.count([:visits], :owner => 'owner', :category => /category/)
158
+ # end
159
+ #
160
+ # it "should filter by category and match returning correct count" do
161
+ # @mock_activity.stub!(:category => 'category')
162
+ # SirTracksAlot::Activity.count([:visits], :owner => 'owner', :category => /category/).should == 1
163
+ # end
164
+ end
165
+
166
+ context 'when counting views' do
113
167
  before do
114
168
  # nothing
115
169
  end
@@ -136,12 +190,7 @@ describe SirTracksAlot::Activity do
136
190
  it "should return count multiple for single group" do
137
191
  SirTracksAlot::Activity.all.first.events << @now
138
192
  @activity.views(:daily).values.should == [2]
139
- end
140
-
141
- it "should return count multiple for single group" do
142
- # SirTracksAlot::Activity.count_by(:daily).should == ''
143
- end
144
-
193
+ end
145
194
  end
146
195
 
147
196
  context 'when calculating visits' do
@@ -161,13 +210,19 @@ describe SirTracksAlot::Activity do
161
210
  @activity.visits.should == 2
162
211
  end
163
212
 
164
- it "should count 2 visits separated by greater than default session duration as 2 visits but not by not" do
213
+ it "should count 2 visits separated by greater than default session duration as 2, and a 3rd" do
165
214
  5.times{@activity.events << SirTracksAlot::Clock.now}
166
215
  @activity.events << SirTracksAlot::Clock.now + 2000
167
216
  @activity.events << SirTracksAlot::Clock.now + 4000
168
217
  @activity.visits.should == 3
169
218
  end
170
219
 
220
+ it "should group separated visits hourly" do
221
+ 5.times{@activity.events << 1279735846}
222
+ @activity.events << 1279709941
223
+ # @activity.visits(1800, :hourly).should == {"2010/07/27 20"=>1, "2010/07/27 21"=>2}
224
+ end
225
+
171
226
  end
172
227
 
173
228
  end
@@ -0,0 +1,88 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe SirTracksAlot::Count do
4
+ include DataHelper
5
+
6
+ before do
7
+ RedisSpecHelper.reset
8
+
9
+ @set_activities.each{|a| SirTracksAlot.record(a)}
10
+ end
11
+
12
+ context 'when creating' do
13
+ before do
14
+ @count_attributes = [{:owner => 'owner', :actor => '/users/user1', :target => '/categories/item1', :views => 1, :visits => 2, :date => '07/01/2010', :hour => 5}]
15
+ end
16
+
17
+ it 'should create valid count' do
18
+ SirTracksAlot::Count.create(@count_attributes[0]).should be_valid
19
+ end
20
+
21
+ it 'should not validate duplicates' do
22
+ SirTracksAlot::Count.create(@count_attributes[0])
23
+ SirTracksAlot::Count.create(@count_attributes[0]).should_not be_valid
24
+ end
25
+ end
26
+
27
+ context 'when filtering' do
28
+ before do
29
+ SirTracksAlot::Count.count(:owner => 'owner')
30
+ end
31
+
32
+ it 'should filter by string attribute' do
33
+ SirTracksAlot::Count.filter(:owner => 'owner').size.should == 13
34
+ end
35
+
36
+ it 'should filter by array of strings' do
37
+ SirTracksAlot::Count.filter(:target => ['/categories', '/other_categories/item']).size.should == 2
38
+ end
39
+
40
+ it 'should include all targets when filtering just by ower' do
41
+ SirTracksAlot::Count.filter(:owner => 'owner').collect{|c| c.target}.compact.sort.should ==
42
+ ["/categories", "/categories/item1", "/categories/item1", "/categories/item1", "/categories/item2", "/other_categories", "/other_categories/item"].sort
43
+ end
44
+
45
+ it 'should include only filtered targets' do
46
+ SirTracksAlot::Count.filter(:owner => 'owner', :target => ["/categories", "/categories/item1"]).collect{|c| c.target}.should_not
47
+ include("/categories/item2")
48
+ end
49
+ end
50
+
51
+ context 'when counting' do
52
+ before do
53
+ @counts = SirTracksAlot::Count.count(:owner => 'owner', :category => ['categories'], :action => ['view'])
54
+ end
55
+
56
+ it "should count all activities for owner" do
57
+ SirTracksAlot::Count.count(:owner => 'owner')
58
+ SirTracksAlot::Count.find(:owner => 'owner').size.should == 13 # one for each unique target, time combo
59
+ end
60
+
61
+ it "should set to counted all activities counted" do
62
+ SirTracksAlot::Count.count(:owner => 'owner')
63
+ SirTracksAlot::Activity.filter(:counted => "0").should be_empty
64
+ end
65
+
66
+ it "should only count activities once" do
67
+ SirTracksAlot::Count.count(:owner => 'owner', :category => ['categories'], :action => ['view']).should be_empty
68
+ end
69
+
70
+ context 'views' do
71
+ it "should include counts by hour" do
72
+ @counts[0].hour.should == '18'
73
+ end
74
+
75
+ it "should not include counts not requested" do
76
+ @counts.collect{|c| c.target}.should_not include("/other_categories")
77
+ end
78
+ end
79
+
80
+ context 'visits' do
81
+ it 'should include counts by hour' do
82
+ # @counts[1].should == {"/categories"=>{"2010/07/21 18"=>1},
83
+ # "/categories/item1"=>{"2010/07/21 18"=>2, "2010/07/21 10"=>1, "2010/07/21 13"=>1},
84
+ # "/categories/item2"=>{"2010/07/21 18"=>1}}
85
+ end
86
+ end
87
+ end
88
+ end
@@ -18,7 +18,7 @@ describe SirTracksAlot::Queue::ReportQueue do
18
18
  @activities.each{|a| SirTracksAlot.record(a)}
19
19
  SirTracksAlot::Queue::ReportQueue.push('owner', :actor_report, {})
20
20
  end
21
-
21
+
22
22
  it "should create a new queue" do
23
23
  SirTracksAlot::Queue::ReportQueue.should_receive(:find_or_create).with(:name => SirTracksAlot::Queue::ReportQueue::QUEUE_NAME).and_return(@queue)
24
24
  SirTracksAlot::Queue::ReportQueue.push('owner', 'report', {})
@@ -34,7 +34,7 @@ describe SirTracksAlot::Queue::ReportQueue do
34
34
  before do
35
35
  @cache = mock(SirTracksAlot::Queue::ReportCache, :update => true)
36
36
  @activities.each{|a| SirTracksAlot.record(a)}
37
- SirTracksAlot::Queue::ReportQueue.push('owner', :actor_report, {:roots => ['categories', 'other_categories'], :actions => ['view']})
37
+ SirTracksAlot::Queue::ReportQueue.push('owner', :target_report, {:category => ['categories', 'other_categories']})
38
38
  end
39
39
 
40
40
  it "should find existing queue" do
@@ -43,19 +43,19 @@ describe SirTracksAlot::Queue::ReportQueue do
43
43
  end
44
44
 
45
45
  it "should create report cache" do
46
- SirTracksAlot::Queue::ReportCache.should_receive(:find_or_create).with(:owner => 'owner', :report => 'actor_report').and_return(@cache)
46
+ SirTracksAlot::Queue::ReportCache.should_receive(:find_or_create).with(:owner => 'owner', :report => 'target_report').and_return(@cache)
47
47
  SirTracksAlot::Queue::ReportQueue.pop
48
48
  end
49
49
 
50
50
  it "should constantize report by name" do
51
- SirTracksAlot::Queue::QueueHelper.should_receive(:constantize).with("SirTracksAlot::Reports::ActorReport").and_return(SirTracksAlot::Reports::ActorReport)
51
+ SirTracksAlot::Queue::QueueHelper.should_receive(:constantize).with("SirTracksAlot::Reports::TargetReport").and_return(SirTracksAlot::Reports::TargetReport)
52
52
  SirTracksAlot::Queue::ReportQueue.pop
53
53
  end
54
54
 
55
55
  it "should build report HTML" do
56
- SirTracksAlot::Queue::ReportCache.should_receive(:find_or_create).with(:owner => 'owner', :report => 'actor_report').and_return(@cache)
56
+ SirTracksAlot::Queue::ReportCache.should_receive(:find_or_create).with(:owner => 'owner', :report => 'target_report').and_return(@cache)
57
57
  @cache.should_receive(:update) do |options|
58
- options[:html].should have_tag('td.actor', /\/users\/user1/)
58
+ options[:html].should have_tag('td.target', /\/categories\/item1/)
59
59
  end
60
60
  SirTracksAlot::Queue::ReportQueue.pop
61
61
  end
@@ -6,22 +6,12 @@ describe SirTracksAlot::Reports::ActorReport do
6
6
  before do
7
7
  RedisSpecHelper.reset
8
8
  @activities.each{|a| SirTracksAlot.record(a)}
9
- @mock_finder = mock('Finder', :find => [], :collect => [], :each => [])
10
9
  end
11
10
 
12
- it "should raise error on mission options" do
13
- lambda{SirTracksAlot::Reports::ActorReport.render_html({})}.should raise_error(Ruport::Controller::RequiredOptionNotSet)
14
- end
15
-
16
- it "should find activities when building data" do
17
- SirTracksAlot::Activity.should_receive(:find).exactly(1).times.and_return(@mock_finder)
18
- SirTracksAlot::Reports::ActorReport.render_html(@report_attributes)
19
- end
20
-
21
11
  context 'building HTML' do
22
12
  before do
23
- SirTracksAlot.record(:owner => 'other_owner', :target => '/other_categories/item', :actor => '/users/user', :action => 'view')
24
- @html = SirTracksAlot::Reports::ActorReport.render_html(@report_attributes)
13
+ @counts = SirTracksAlot::Count.count(OpenStruct.new(:owner => 'owner', :roots => ['categories']))
14
+ @html = SirTracksAlot::Reports::ActorReport.render_html(:counts => @counts)
25
15
  end
26
16
 
27
17
  it "include target row" do
@@ -31,6 +21,10 @@ describe SirTracksAlot::Reports::ActorReport do
31
21
  it "include count row" do
32
22
  @html.should have_tag('td.count', /1/)
33
23
  end
24
+
25
+ it "include page views" do
26
+ @html.should have_tag('td.page_views', /2/)
27
+ end
34
28
 
35
29
  it "should ignore other owners" do
36
30
  @html.should_not have_tag('td.actor', '/users/user')
@@ -1,16 +1,13 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper.rb'
2
2
 
3
- describe SirTracksAlot::Reports::ActivityReport do
3
+ describe SirTracksAlot::Reports::FilterReport do
4
4
  include DataHelper
5
5
 
6
6
  before do
7
7
  RedisSpecHelper.reset
8
8
  @report_options = {:owner => 'owner'}
9
9
  @activities.each{|a| SirTracksAlot.record(a)}
10
- end
11
-
12
- it "should render empty" do
13
- SirTracksAlot::Reports::FilterReport.render_html(@report_options)
10
+ SirTracksAlot::Count.count(OpenStruct.new(:owner => 'owner', :roots => ['categories']))
14
11
  end
15
12
 
16
13
  context 'filtering things with only' do
@@ -23,6 +20,7 @@ describe SirTracksAlot::Reports::ActivityReport do
23
20
  'blank' => {:target => /blanks/}
24
21
  }
25
22
  }
23
+
26
24
  @html = SirTracksAlot::Reports::FilterReport.render_html(@report_options)
27
25
  end
28
26
 
@@ -6,10 +6,7 @@ describe SirTracksAlot::Reports::RootStemReport do
6
6
  before do
7
7
  RedisSpecHelper.reset
8
8
  @activities.each{|a| SirTracksAlot.record(a)}
9
- end
10
-
11
- it "should render empty" do
12
- SirTracksAlot::Reports::FilterReport.render_html(:owner => 'owner')
9
+ SirTracksAlot::Count.count(OpenStruct.new(:owner => 'owner', :roots => ['categories']))
13
10
  end
14
11
 
15
12
  context 'building HTML with categories' do
@@ -6,57 +6,26 @@ describe SirTracksAlot::Reports::TargetReport do
6
6
  before do
7
7
  RedisSpecHelper.reset
8
8
  @activities.each{|a| SirTracksAlot.record(a)}
9
- @mock_finder = mock('Finder', :find => [], :collect => [])
10
- @mock_filter = mock('Filter', :each => [])
11
9
  end
12
10
 
13
- it "should raise error on missing options" do
14
- lambda{SirTracksAlot::Reports::TargetReport.render_html({})}.should raise_error(Ruport::Controller::RequiredOptionNotSet)
15
- end
16
-
17
- it "should find activities when building data" do
18
- SirTracksAlot::Activity.should_receive(:filter).with(
19
- :action => @activities[0][:action], :owner => @activities[0][:owner], :target => nil, :category=>"categories").exactly(1).times.and_return(@mock_filter)
20
- SirTracksAlot::Reports::TargetReport.render_html(@report_attributes)
21
- end
22
-
23
- context 'building HTML without filters' do
24
- before do
25
- SirTracksAlot.record(:owner => 'other_owner', :category => 'other_category', :target => '/other_categories/item', :actor => '/users/user', :action => 'view')
26
- @html = SirTracksAlot::Reports::TargetReport.render_html(@report_attributes)
11
+ context 'building HTML' do
12
+ before do
13
+ @counts = SirTracksAlot::Count.count(OpenStruct.new(:owner => 'owner', :roots => ['categories']))
14
+ @html = SirTracksAlot::Reports::TargetReport.render_html(:counts => @counts)
27
15
  end
28
16
 
29
17
  it_should_behave_like 'all reports'
30
18
 
31
- it "include target row" do
19
+ it "include target row" do
32
20
  @html.should have_tag('td.target', /\/categories\/item/)
33
21
  end
34
22
 
35
23
  it "include count row" do
36
24
  @html.should have_tag('td.count', /1/)
37
25
  end
38
-
26
+
39
27
  it "should ignore other owners" do
40
28
  @html.should_not have_tag('td.target', '/other_categories/item')
41
29
  end
42
30
  end
43
-
44
- context 'building HTML with filters' do
45
- before do
46
- @html = SirTracksAlot::Reports::TargetReport.render_html(
47
- @report_attributes.merge(
48
- :filters => {:except => [{:target => /^\/categories$/}]}
49
- )
50
- )
51
- end
52
-
53
- it "should include non excepted target row" do
54
- @html.should have_tag('td.target', /\/categories\/item/)
55
- end
56
-
57
- it "should not include excepted target row" do
58
- @html.should_not have_tag('td.target', /^\/categories$/)
59
- end
60
-
61
- end
62
31
  end
@@ -6,6 +6,7 @@ describe SirTracksAlot, 'when recording' do
6
6
  before do
7
7
  RedisSpecHelper.reset
8
8
  @record_attributes = @activities[0]
9
+ @now = SirTracksAlot::Clock.now
9
10
  end
10
11
 
11
12
  it "should record activity" do
@@ -54,7 +55,7 @@ describe SirTracksAlot, 'when recording' do
54
55
  end
55
56
 
56
57
  it "should add date for now" do
57
- SirTracksAlot::Clock.should_receive(:now).twice # once for activity create, once for events date
58
+ SirTracksAlot::Clock.should_receive(:now).twice.and_return(@now) # once for activity create, once for events date
58
59
  SirTracksAlot.record(@record_attributes)
59
60
  end
60
61
 
data/spec/spec_helper.rb CHANGED
@@ -31,6 +31,38 @@ module DataHelper
31
31
  def initialize(*attrs)
32
32
  super
33
33
 
34
+ @set_activities = [
35
+ {:owner => 'owner', :target => '/categories/item1', :actor => '/users/user1',
36
+ :action => 'view', :user_agent => 'agent1', :event => 1279735846}, # 1.day.ago
37
+
38
+ {:owner => 'owner', :target => '/categories/item1', :actor => '/users/user1',
39
+ :action => 'view', :user_agent => 'agent1', :event => 1279735846}, # 1.day.ago
40
+
41
+ {:owner => 'owner', :target => '/categories/item1', :actor => '/users/user1',
42
+ :action => 'view', :user_agent => 'agent1', :event => 1279718578}, # 1.2.days.ago
43
+
44
+ {:owner => 'owner', :target => '/categories/item1', :actor => '/users/user1',
45
+ :action => 'view', :user_agent => 'agent1', :event => 1279709941}, # 1.3.days.ago
46
+
47
+ {:owner => 'owner', :target => '/categories/item2', :actor => '/users/user2',
48
+ :action => 'view', :user_agent => 'agent2', :event => 1279735846}, # 1.day.ago
49
+
50
+ {:owner => 'owner', :target => '/categories', :actor => '/users/user2',
51
+ :action => 'view', :user_agent => 'agent2', :event => 1279735846}, # 1.day.ago
52
+
53
+ {:owner => 'owner', :target => '/other_categories/item', :actor => '/users/user1',
54
+ :action => 'view', :user_agent => 'agent1', :event => 1279735846}, # 1.day.ago
55
+
56
+ {:owner => 'owner', :target => '/other_categories/item', :actor => '/users/user2',
57
+ :action => 'view', :user_agent => 'agent2', :event => 1279735846}, # 1.day.ago
58
+
59
+ {:owner => 'owner', :target => '/other_categories', :actor => '/users/user1',
60
+ :action => 'view', :user_agent => 'agent1', :event => 1279735846}, # 1.day.ago
61
+
62
+ {:owner => 'owner', :target => '/other_categories', :actor => '/users/user2',
63
+ :action => 'view', :user_agent => 'agent2', :event => 1279735846} # 1.day.ago
64
+ ]
65
+
34
66
  @activities = [
35
67
  {:owner => 'owner', :target => '/categories/item1', :actor => '/users/user1', :action => 'view', :user_agent => 'agent1'},
36
68
  {:owner => 'owner', :target => '/categories/item2', :actor => '/users/user2', :action => 'view', :user_agent => 'agent2'},
@@ -40,8 +72,10 @@ module DataHelper
40
72
  {:owner => 'owner', :target => '/other_categories', :actor => '/users/user1', :action => 'view', :user_agent => 'agent1'},
41
73
  {:owner => 'owner', :target => '/other_categories', :actor => '/users/user2', :action => 'view', :user_agent => 'agent2'}
42
74
  ]
43
-
44
- @report_attributes = {:owner => 'owner', :roots => ['categories'], :actions => ['view']}
75
+
76
+ @counts = {"/categories" => [1, 1], "/categories/item1" => [1, 1], "/categories/item2" => [1, 1]}
77
+
78
+ @report_attributes = {:owner => 'owner', :roots => ['categories'], :actions => ['view'], :counts => @counts}
45
79
  end
46
80
  end
47
81
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sir_tracks_alot
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
8
+ - 4
9
9
  - 0
10
- version: 0.2.0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Peter T. Brown
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-21 00:00:00 -07:00
18
+ date: 2010-08-05 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -150,6 +150,9 @@ files:
150
150
  - lib/sir_tracks_alot.rb
151
151
  - lib/sir_tracks_alot/activity.rb
152
152
  - lib/sir_tracks_alot/clock.rb
153
+ - lib/sir_tracks_alot/count.rb
154
+ - lib/sir_tracks_alot/event_helper.rb
155
+ - lib/sir_tracks_alot/filter_helper.rb
153
156
  - lib/sir_tracks_alot/persistable.rb
154
157
  - lib/sir_tracks_alot/queue/queue_helper.rb
155
158
  - lib/sir_tracks_alot/queue/report_cache.rb
@@ -165,6 +168,7 @@ files:
165
168
  - lib/sir_tracks_alot/reports/target_report.rb
166
169
  - lib/sir_tracks_alot/reports/trackable_report.rb
167
170
  - spec/activity_spec.rb
171
+ - spec/count_spec.rb
168
172
  - spec/queue/report_config_spec.rb
169
173
  - spec/queue/report_queue_spec.rb
170
174
  - spec/reports/activity_report_spec.rb
@@ -218,6 +222,7 @@ specification_version: 3
218
222
  summary: A high speed general purpose tracking and reporting tool which uses Redis.
219
223
  test_files:
220
224
  - spec/activity_spec.rb
225
+ - spec/count_spec.rb
221
226
  - spec/queue/report_config_spec.rb
222
227
  - spec/queue/report_queue_spec.rb
223
228
  - spec/reports/activity_report_spec.rb