Commit 8eb914c0811e0235b9ae926f712061785ca40c06

Authored by Rodrigo Souto
1 parent 5fba93dd

action_tracker: register and use activities_count to fetch most_active profile

(ActionItem3039)
app/models/organization.rb
... ... @@ -32,13 +32,6 @@ class Organization < Profile
32 32 :joins => "LEFT OUTER JOIN role_assignments ON profiles.id = role_assignments.resource_id",
33 33 :order => "total DESC"
34 34  
35   - named_scope :more_active,
36   - :select => "#{Profile.qualified_column_names}, count(action_tracker.id) as total",
37   - :joins => "LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.target_id",
38   - :group => Profile.qualified_column_names,
39   - :order => 'total DESC',
40   - :conditions => ['action_tracker.created_at >= ? OR action_tracker.id IS NULL', ActionTracker::Record::RECENT_DELAY.days.ago]
41   -
42 35 def validation_methodology
43 36 self.validation_info ? self.validation_info.validation_methodology : nil
44 37 end
... ...
app/models/person.rb
... ... @@ -68,13 +68,6 @@ class Person < Profile
68 68  
69 69 named_scope :more_popular, :order => 'friends_count DESC'
70 70  
71   - named_scope :more_active,
72   - :select => "#{Profile.qualified_column_names}, count(action_tracker.id) as total",
73   - :joins => "LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.user_id",
74   - :group => Profile.qualified_column_names,
75   - :order => 'total DESC',
76   - :conditions => ['action_tracker.created_at >= ? OR action_tracker.id IS NULL', ActionTracker::Record::RECENT_DELAY.days.ago]
77   -
78 71 named_scope :abusers, :joins => :abuse_complaints, :conditions => ['tasks.status = 3'], :select => 'DISTINCT profiles.*'
79 72 named_scope :non_abusers, :joins => "LEFT JOIN tasks ON profiles.id = tasks.requestor_id AND tasks.type='AbuseComplaint'", :conditions => ["tasks.status != 3 OR tasks.id is NULL"], :select => "DISTINCT profiles.*"
80 73  
... ...
app/models/profile.rb
... ... @@ -113,10 +113,11 @@ class Profile < ActiveRecord::Base
113 113  
114 114 named_scope :visible, :conditions => { :visible => true }
115 115 named_scope :public, :conditions => { :visible => true, :public_profile => true }
116   - # Subclasses must override these methods
  116 +
  117 + # Subclasses must override this method
117 118 named_scope :more_popular
118   - named_scope :more_active
119 119  
  120 + named_scope :more_active, :order => 'activities_count DESC'
120 121 named_scope :more_recent, :order => "created_at DESC"
121 122  
122 123 acts_as_trackable :dependent => :destroy
... ...
config/initializers/activities_counter_cache.rb 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +job = Delayed::Backend::ActiveRecord::Job.all :conditions => ['handler LIKE ?', "%ActivitiesCounterCacheJob%"]
  2 +if job.blank?
  3 + Delayed::Backend::ActiveRecord::Job.enqueue(ActivitiesCounterCacheJob.new, -3)
  4 +end
... ...
db/migrate/20140312151857_define_initial_value_for_profiles_activities_count.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +class DefineInitialValueForProfilesActivitiesCount < ActiveRecord::Migration
  2 + def self.up
  3 + person_activities_counts = execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.user_id WHERE (action_tracker.created_at >= '#{30.days.ago.to_s(:db)}') AND ( (profiles.type = 'Person' ) ) GROUP BY profiles.id;")
  4 + organization_activities_counts = execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.target_id WHERE (action_tracker.created_at >= '#{30.days.ago.to_s(:db)}') AND ( (profiles.type = 'Community' OR profiles.type = 'Enterprise' OR profiles.type = 'Organization' ) ) GROUP BY profiles.id;")
  5 + activities_counts = person_activities_counts.entries + organization_activities_counts.entries
  6 + activities_counts.each do |count|
  7 + execute("UPDATE profiles SET activities_count=#{count['count'].to_i} WHERE profiles.id=#{count['id']};")
  8 + end
  9 + end
  10 +
  11 + def self.down
  12 + execute("UPDATE profiles SET activities_count=0;")
  13 + end
  14 +end
... ...
lib/activities_counter_cache_job.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +class ActivitiesCounterCacheJob
  2 + def perform
  3 + person_activities_counts = ActiveRecord::Base.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.user_id WHERE (action_tracker.created_at >= '#{ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db)}') AND ( (profiles.type = 'Person' ) ) GROUP BY profiles.id;")
  4 + organization_activities_counts = ActiveRecord::Base.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.target_id WHERE (action_tracker.created_at >= '#{ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db)}') AND ( (profiles.type = 'Community' OR profiles.type = 'Enterprise' OR profiles.type = 'Organization' ) ) GROUP BY profiles.id;")
  5 + activities_counts = person_activities_counts.entries + organization_activities_counts.entries
  6 + activities_counts.each do |count|
  7 + ActiveRecord::Base.connection.execute("UPDATE profiles SET activities_count=#{count['count'].to_i} WHERE profiles.id=#{count['id']};")
  8 + end
  9 + Delayed::Job.enqueue(ActivitiesCounterCacheJob.new, -3, 1.day.from_now)
  10 + end
  11 +end
... ...
test/unit/activities_counter_cache_job_test.rb 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class ActivitiesCounterCacheJobTest < ActiveSupport::TestCase
  4 +
  5 + should 'correctly update the person activities counter cache' do
  6 + person = create_user('person').person
  7 + ActionTracker::Record.create!(:user => person, :verb => 'create_article')
  8 + ActionTracker::Record.create!(:user => person, :verb => 'create_article')
  9 + assert_equal 2, person.activities_count
  10 +
  11 + person.activities_count = 0
  12 + person.save!
  13 + job = ActivitiesCounterCacheJob.new
  14 + job.perform
  15 + person.reload
  16 + assert_equal 2, person.activities_count
  17 + end
  18 +
  19 + should 'correctly update the organization activities counter cache' do
  20 + person = create_user('person').person
  21 + organization = Organization.create!(:name => 'Organization1', :identifier => 'organization1')
  22 + ActionTracker::Record.create!(:user => person, :verb => 'create_article', :target => organization)
  23 + ActionTracker::Record.create!(:user => person, :verb => 'create_article', :target => organization)
  24 + assert_equal 2, organization.activities_count
  25 +
  26 + organization.activities_count = 0
  27 + organization.save!
  28 + job = ActivitiesCounterCacheJob.new
  29 + job.perform
  30 + organization.reload
  31 + assert_equal 2, organization.activities_count
  32 + end
  33 +
  34 +end
... ...
test/unit/organization_test.rb
... ... @@ -364,25 +364,7 @@ class OrganizationTest &lt; ActiveSupport::TestCase
364 364 fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => Time.now, :target_id => p3.id)
365 365 fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => Time.now, :target_id => p3.id)
366 366  
367   - assert_equal [p3,p2,p1] , Organization.more_active
368   - end
369   -
370   - should 'more active profile take in consideration only actions created only in the recent delay interval' do
371   - ActionTracker::Record.destroy_all
372   - recent_delay = ActionTracker::Record::RECENT_DELAY.days.ago
373   -
374   - person = fast_create(Person)
375   - Organization.destroy_all
376   - p1 = fast_create(Organization)
377   - p2 = fast_create(Organization)
378   -
379   - ActionTracker::Record.destroy_all
380   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => recent_delay, :target_id => p1.id)
381   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => recent_delay, :target_id => p1.id)
382   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => recent_delay, :target_id => p2.id)
383   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => recent_delay - 1.day, :target_id => p2.id)
384   -
385   - assert_equal [p1,p2] , Organization.more_active
  367 + assert_order [p3,p2,p1] , Organization.more_active
386 368 end
387 369  
388 370 should 'list profiles that have no actions in more active list' do
... ... @@ -422,4 +404,34 @@ class OrganizationTest &lt; ActiveSupport::TestCase
422 404 assert !organization.visible
423 405 end
424 406  
  407 + should 'increase activities_count on new activity' do
  408 + person = fast_create(Person)
  409 + organization = fast_create(Organization)
  410 + assert_difference organization, :activities_count, 1 do
  411 + ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => organization
  412 + organization.reload
  413 + end
  414 + end
  415 +
  416 + should 'decrease activities_count on activity removal' do
  417 + person = fast_create(Person)
  418 + organization = fast_create(Organization)
  419 + record = ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => organization
  420 + assert_difference organization, :activities_count, -1 do
  421 + record.destroy
  422 + organization.reload
  423 + end
  424 + end
  425 +
  426 + should 'not decrease activities_count on activity removal after the recent delay' do
  427 + person = fast_create(Person)
  428 + organization = fast_create(Organization)
  429 + record = ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => organization, :created_at => (ActionTracker::Record::RECENT_DELAY + 1).days.ago
  430 + assert_no_difference organization, :activities_count do
  431 + record.destroy
  432 + organization.reload
  433 + end
  434 + end
  435 +
  436 +
425 437 end
... ...
test/unit/person_test.rb
... ... @@ -904,14 +904,15 @@ class PersonTest &lt; ActiveSupport::TestCase
904 904  
905 905 action_tracker = fast_create(ActionTracker::Record, :verb => 'create_article')
906 906 action_tracker.target = community
  907 + action_tracker.user = p4
907 908 action_tracker.save!
908 909 ActionTrackerNotification.delete_all
909   - assert_difference(ActionTrackerNotification, :count, 3) do
  910 + assert_difference(ActionTrackerNotification, :count, 4) do
910 911 Person.notify_activity(action_tracker)
911 912 process_delayed_job_queue
912 913 end
913 914 ActionTrackerNotification.all.map{|a|a.profile}.map do |profile|
914   - assert [community,p1,p3].include?(profile)
  915 + assert [community,p1,p3,p4].include?(profile)
915 916 end
916 917 end
917 918  
... ... @@ -1014,9 +1015,9 @@ class PersonTest &lt; ActiveSupport::TestCase
1014 1015  
1015 1016 should 'the community specific notification created when a member joins community could not be propagated to members' do
1016 1017 ActionTracker::Record.delete_all
1017   - p1 = create_user('test_user').person
1018   - p2 = create_user('test_user').person
1019   - p3 = create_user('test_user').person
  1018 + p1 = create_user('p1').person
  1019 + p2 = create_user('p2').person
  1020 + p3 = create_user('p3').person
1020 1021 c = fast_create(Community, :name => "Foo")
1021 1022 c.add_member(p1)
1022 1023 process_delayed_job_queue
... ... @@ -1136,30 +1137,14 @@ class PersonTest &lt; ActiveSupport::TestCase
1136 1137 p3 = fast_create(Person)
1137 1138  
1138 1139 ActionTracker::Record.destroy_all
1139   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p1, :created_at => Time.now)
1140   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2, :created_at => Time.now)
1141   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2, :created_at => Time.now)
1142   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3, :created_at => Time.now)
1143   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3, :created_at => Time.now)
1144   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3, :created_at => Time.now)
1145   -
1146   - assert_equal [p3,p2,p1] , Person.more_active
1147   - end
1148   -
1149   - should 'more active profile take in consideration only actions created only in the recent delay interval' do
1150   - Person.delete_all
1151   - ActionTracker::Record.destroy_all
1152   - recent_delay = ActionTracker::Record::RECENT_DELAY.days.ago
  1140 + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p1)
  1141 + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2)
  1142 + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2)
  1143 + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3)
  1144 + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3)
  1145 + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3)
1153 1146  
1154   - p1 = fast_create(Person)
1155   - p2 = fast_create(Person)
1156   -
1157   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p1, :created_at => recent_delay)
1158   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p1, :created_at => recent_delay)
1159   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2, :created_at => recent_delay)
1160   - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2, :created_at => recent_delay - 1.day)
1161   -
1162   - assert_equal [p1,p2], Person.more_active
  1147 + assert_order [p3,p2,p1] , Person.more_active
1163 1148 end
1164 1149  
1165 1150 should 'list profiles that have no actions in more active list' do
... ... @@ -1429,7 +1414,7 @@ class PersonTest &lt; ActiveSupport::TestCase
1429 1414 end
1430 1415 end
1431 1416  
1432   - should 'decrease friends_count on new friendship' do
  1417 + should 'decrease friends_count on friendship removal' do
1433 1418 person = create_user('person').person
1434 1419 friend = create_user('friend').person
1435 1420 person.add_friend(friend)
... ... @@ -1443,4 +1428,33 @@ class PersonTest &lt; ActiveSupport::TestCase
1443 1428 person.reload
1444 1429 end
1445 1430 end
  1431 +
  1432 + should 'increase activities_count on new activity' do
  1433 + person = fast_create(Person)
  1434 + assert_difference person, :activities_count, 1 do
  1435 + ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => fast_create(Profile)
  1436 + person.reload
  1437 + end
  1438 + end
  1439 +
  1440 + should 'decrease activities_count on activity removal' do
  1441 + person = fast_create(Person)
  1442 + record = ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => fast_create(Profile)
  1443 + assert_difference person, :activities_count, -1 do
  1444 + record.destroy
  1445 + person.reload
  1446 + end
  1447 + end
  1448 +
  1449 + should 'not decrease activities_count on activity removal after the recent delay' do
  1450 + person = fast_create(Person)
  1451 + record = ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => fast_create(Profile)
  1452 + record.created_at = record.created_at - ActionTracker::Record::RECENT_DELAY.days - 1.day
  1453 + record.save!
  1454 + assert_no_difference person, :activities_count do
  1455 + record.destroy
  1456 + person.reload
  1457 + end
  1458 + end
  1459 +
1446 1460 end
... ...
vendor/plugins/action_tracker/lib/action_tracker_model.rb
1 1 module ActionTracker
2 2 class Record < ActiveRecord::Base
3 3  
  4 + extend CacheCounterHelper
  5 +
4 6 set_table_name 'action_tracker'
5 7  
6 8 belongs_to :user, :polymorphic => true
... ... @@ -14,6 +16,22 @@ module ActionTracker
14 16 validates_presence_of :user
15 17 validate :user_existence
16 18  
  19 + after_create do |record|
  20 + update_cache_counter(:activities_count, record.user, 1)
  21 + if record.target.kind_of?(Organization)
  22 + update_cache_counter(:activities_count, record.target, 1)
  23 + end
  24 + end
  25 +
  26 + after_destroy do |record|
  27 + if record.created_at >= RECENT_DELAY.days.ago
  28 + update_cache_counter(:activities_count, record.user, -1)
  29 + if record.target.kind_of?(Organization)
  30 + update_cache_counter(:activities_count, record.target, -1)
  31 + end
  32 + end
  33 + end
  34 +
17 35 def user_existence
18 36 errors.add(:user, "user doesn't exists") if user && !user.class.exists?(user)
19 37 end
... ...