sir_tracks_alot 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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