From 8eb914c0811e0235b9ae926f712061785ca40c06 Mon Sep 17 00:00:00 2001 From: Rodrigo Souto Date: Tue, 18 Mar 2014 20:18:44 +0000 Subject: [PATCH] action_tracker: register and use activities_count to fetch most_active profile --- app/models/organization.rb | 7 ------- app/models/person.rb | 7 ------- app/models/profile.rb | 5 +++-- config/initializers/activities_counter_cache.rb | 4 ++++ db/migrate/20140312151857_define_initial_value_for_profiles_activities_count.rb | 14 ++++++++++++++ lib/activities_counter_cache_job.rb | 11 +++++++++++ test/unit/activities_counter_cache_job_test.rb | 34 ++++++++++++++++++++++++++++++++++ test/unit/organization_test.rb | 50 +++++++++++++++++++++++++++++++------------------- test/unit/person_test.rb | 72 +++++++++++++++++++++++++++++++++++++++++++----------------------------- vendor/plugins/action_tracker/lib/action_tracker_model.rb | 18 ++++++++++++++++++ 10 files changed, 158 insertions(+), 64 deletions(-) create mode 100644 config/initializers/activities_counter_cache.rb create mode 100644 db/migrate/20140312151857_define_initial_value_for_profiles_activities_count.rb create mode 100644 lib/activities_counter_cache_job.rb create mode 100644 test/unit/activities_counter_cache_job_test.rb diff --git a/app/models/organization.rb b/app/models/organization.rb index b00f295..9e602c6 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -32,13 +32,6 @@ class Organization < Profile :joins => "LEFT OUTER JOIN role_assignments ON profiles.id = role_assignments.resource_id", :order => "total DESC" - named_scope :more_active, - :select => "#{Profile.qualified_column_names}, count(action_tracker.id) as total", - :joins => "LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.target_id", - :group => Profile.qualified_column_names, - :order => 'total DESC', - :conditions => ['action_tracker.created_at >= ? OR action_tracker.id IS NULL', ActionTracker::Record::RECENT_DELAY.days.ago] - def validation_methodology self.validation_info ? self.validation_info.validation_methodology : nil end diff --git a/app/models/person.rb b/app/models/person.rb index 202a23b..5483bcc 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -68,13 +68,6 @@ class Person < Profile named_scope :more_popular, :order => 'friends_count DESC' - named_scope :more_active, - :select => "#{Profile.qualified_column_names}, count(action_tracker.id) as total", - :joins => "LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.user_id", - :group => Profile.qualified_column_names, - :order => 'total DESC', - :conditions => ['action_tracker.created_at >= ? OR action_tracker.id IS NULL', ActionTracker::Record::RECENT_DELAY.days.ago] - named_scope :abusers, :joins => :abuse_complaints, :conditions => ['tasks.status = 3'], :select => 'DISTINCT profiles.*' 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.*" diff --git a/app/models/profile.rb b/app/models/profile.rb index 37deb3d..45290f9 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -113,10 +113,11 @@ class Profile < ActiveRecord::Base named_scope :visible, :conditions => { :visible => true } named_scope :public, :conditions => { :visible => true, :public_profile => true } - # Subclasses must override these methods + + # Subclasses must override this method named_scope :more_popular - named_scope :more_active + named_scope :more_active, :order => 'activities_count DESC' named_scope :more_recent, :order => "created_at DESC" acts_as_trackable :dependent => :destroy diff --git a/config/initializers/activities_counter_cache.rb b/config/initializers/activities_counter_cache.rb new file mode 100644 index 0000000..db88ce3 --- /dev/null +++ b/config/initializers/activities_counter_cache.rb @@ -0,0 +1,4 @@ +job = Delayed::Backend::ActiveRecord::Job.all :conditions => ['handler LIKE ?', "%ActivitiesCounterCacheJob%"] +if job.blank? + Delayed::Backend::ActiveRecord::Job.enqueue(ActivitiesCounterCacheJob.new, -3) +end diff --git a/db/migrate/20140312151857_define_initial_value_for_profiles_activities_count.rb b/db/migrate/20140312151857_define_initial_value_for_profiles_activities_count.rb new file mode 100644 index 0000000..0626472 --- /dev/null +++ b/db/migrate/20140312151857_define_initial_value_for_profiles_activities_count.rb @@ -0,0 +1,14 @@ +class DefineInitialValueForProfilesActivitiesCount < ActiveRecord::Migration + def self.up + 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;") + 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;") + activities_counts = person_activities_counts.entries + organization_activities_counts.entries + activities_counts.each do |count| + execute("UPDATE profiles SET activities_count=#{count['count'].to_i} WHERE profiles.id=#{count['id']};") + end + end + + def self.down + execute("UPDATE profiles SET activities_count=0;") + end +end diff --git a/lib/activities_counter_cache_job.rb b/lib/activities_counter_cache_job.rb new file mode 100644 index 0000000..062c7f3 --- /dev/null +++ b/lib/activities_counter_cache_job.rb @@ -0,0 +1,11 @@ +class ActivitiesCounterCacheJob + def perform + 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;") + 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;") + activities_counts = person_activities_counts.entries + organization_activities_counts.entries + activities_counts.each do |count| + ActiveRecord::Base.connection.execute("UPDATE profiles SET activities_count=#{count['count'].to_i} WHERE profiles.id=#{count['id']};") + end + Delayed::Job.enqueue(ActivitiesCounterCacheJob.new, -3, 1.day.from_now) + end +end diff --git a/test/unit/activities_counter_cache_job_test.rb b/test/unit/activities_counter_cache_job_test.rb new file mode 100644 index 0000000..0a8de19 --- /dev/null +++ b/test/unit/activities_counter_cache_job_test.rb @@ -0,0 +1,34 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class ActivitiesCounterCacheJobTest < ActiveSupport::TestCase + + should 'correctly update the person activities counter cache' do + person = create_user('person').person + ActionTracker::Record.create!(:user => person, :verb => 'create_article') + ActionTracker::Record.create!(:user => person, :verb => 'create_article') + assert_equal 2, person.activities_count + + person.activities_count = 0 + person.save! + job = ActivitiesCounterCacheJob.new + job.perform + person.reload + assert_equal 2, person.activities_count + end + + should 'correctly update the organization activities counter cache' do + person = create_user('person').person + organization = Organization.create!(:name => 'Organization1', :identifier => 'organization1') + ActionTracker::Record.create!(:user => person, :verb => 'create_article', :target => organization) + ActionTracker::Record.create!(:user => person, :verb => 'create_article', :target => organization) + assert_equal 2, organization.activities_count + + organization.activities_count = 0 + organization.save! + job = ActivitiesCounterCacheJob.new + job.perform + organization.reload + assert_equal 2, organization.activities_count + end + +end diff --git a/test/unit/organization_test.rb b/test/unit/organization_test.rb index da51b74..3dcd50c 100644 --- a/test/unit/organization_test.rb +++ b/test/unit/organization_test.rb @@ -364,25 +364,7 @@ class OrganizationTest < ActiveSupport::TestCase fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => Time.now, :target_id => p3.id) fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => Time.now, :target_id => p3.id) - assert_equal [p3,p2,p1] , Organization.more_active - end - - should 'more active profile take in consideration only actions created only in the recent delay interval' do - ActionTracker::Record.destroy_all - recent_delay = ActionTracker::Record::RECENT_DELAY.days.ago - - person = fast_create(Person) - Organization.destroy_all - p1 = fast_create(Organization) - p2 = fast_create(Organization) - - ActionTracker::Record.destroy_all - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => recent_delay, :target_id => p1.id) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => recent_delay, :target_id => p1.id) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => recent_delay, :target_id => p2.id) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person, :created_at => recent_delay - 1.day, :target_id => p2.id) - - assert_equal [p1,p2] , Organization.more_active + assert_order [p3,p2,p1] , Organization.more_active end should 'list profiles that have no actions in more active list' do @@ -422,4 +404,34 @@ class OrganizationTest < ActiveSupport::TestCase assert !organization.visible end + should 'increase activities_count on new activity' do + person = fast_create(Person) + organization = fast_create(Organization) + assert_difference organization, :activities_count, 1 do + ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => organization + organization.reload + end + end + + should 'decrease activities_count on activity removal' do + person = fast_create(Person) + organization = fast_create(Organization) + record = ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => organization + assert_difference organization, :activities_count, -1 do + record.destroy + organization.reload + end + end + + should 'not decrease activities_count on activity removal after the recent delay' do + person = fast_create(Person) + organization = fast_create(Organization) + record = ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => organization, :created_at => (ActionTracker::Record::RECENT_DELAY + 1).days.ago + assert_no_difference organization, :activities_count do + record.destroy + organization.reload + end + end + + end diff --git a/test/unit/person_test.rb b/test/unit/person_test.rb index 4f48fb4..3335fad 100644 --- a/test/unit/person_test.rb +++ b/test/unit/person_test.rb @@ -904,14 +904,15 @@ class PersonTest < ActiveSupport::TestCase action_tracker = fast_create(ActionTracker::Record, :verb => 'create_article') action_tracker.target = community + action_tracker.user = p4 action_tracker.save! ActionTrackerNotification.delete_all - assert_difference(ActionTrackerNotification, :count, 3) do + assert_difference(ActionTrackerNotification, :count, 4) do Person.notify_activity(action_tracker) process_delayed_job_queue end ActionTrackerNotification.all.map{|a|a.profile}.map do |profile| - assert [community,p1,p3].include?(profile) + assert [community,p1,p3,p4].include?(profile) end end @@ -1014,9 +1015,9 @@ class PersonTest < ActiveSupport::TestCase should 'the community specific notification created when a member joins community could not be propagated to members' do ActionTracker::Record.delete_all - p1 = create_user('test_user').person - p2 = create_user('test_user').person - p3 = create_user('test_user').person + p1 = create_user('p1').person + p2 = create_user('p2').person + p3 = create_user('p3').person c = fast_create(Community, :name => "Foo") c.add_member(p1) process_delayed_job_queue @@ -1136,30 +1137,14 @@ class PersonTest < ActiveSupport::TestCase p3 = fast_create(Person) ActionTracker::Record.destroy_all - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p1, :created_at => Time.now) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2, :created_at => Time.now) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2, :created_at => Time.now) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3, :created_at => Time.now) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3, :created_at => Time.now) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3, :created_at => Time.now) - - assert_equal [p3,p2,p1] , Person.more_active - end - - should 'more active profile take in consideration only actions created only in the recent delay interval' do - Person.delete_all - ActionTracker::Record.destroy_all - recent_delay = ActionTracker::Record::RECENT_DELAY.days.ago + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p1) + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2) + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2) + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3) + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3) + fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p3) - p1 = fast_create(Person) - p2 = fast_create(Person) - - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p1, :created_at => recent_delay) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p1, :created_at => recent_delay) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2, :created_at => recent_delay) - fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => p2, :created_at => recent_delay - 1.day) - - assert_equal [p1,p2], Person.more_active + assert_order [p3,p2,p1] , Person.more_active end should 'list profiles that have no actions in more active list' do @@ -1429,7 +1414,7 @@ class PersonTest < ActiveSupport::TestCase end end - should 'decrease friends_count on new friendship' do + should 'decrease friends_count on friendship removal' do person = create_user('person').person friend = create_user('friend').person person.add_friend(friend) @@ -1443,4 +1428,33 @@ class PersonTest < ActiveSupport::TestCase person.reload end end + + should 'increase activities_count on new activity' do + person = fast_create(Person) + assert_difference person, :activities_count, 1 do + ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => fast_create(Profile) + person.reload + end + end + + should 'decrease activities_count on activity removal' do + person = fast_create(Person) + record = ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => fast_create(Profile) + assert_difference person, :activities_count, -1 do + record.destroy + person.reload + end + end + + should 'not decrease activities_count on activity removal after the recent delay' do + person = fast_create(Person) + record = ActionTracker::Record.create! :verb => :leave_scrap, :user => person, :target => fast_create(Profile) + record.created_at = record.created_at - ActionTracker::Record::RECENT_DELAY.days - 1.day + record.save! + assert_no_difference person, :activities_count do + record.destroy + person.reload + end + end + end diff --git a/vendor/plugins/action_tracker/lib/action_tracker_model.rb b/vendor/plugins/action_tracker/lib/action_tracker_model.rb index a711aeb..352ee0b 100644 --- a/vendor/plugins/action_tracker/lib/action_tracker_model.rb +++ b/vendor/plugins/action_tracker/lib/action_tracker_model.rb @@ -1,6 +1,8 @@ module ActionTracker class Record < ActiveRecord::Base + extend CacheCounterHelper + set_table_name 'action_tracker' belongs_to :user, :polymorphic => true @@ -14,6 +16,22 @@ module ActionTracker validates_presence_of :user validate :user_existence + after_create do |record| + update_cache_counter(:activities_count, record.user, 1) + if record.target.kind_of?(Organization) + update_cache_counter(:activities_count, record.target, 1) + end + end + + after_destroy do |record| + if record.created_at >= RECENT_DELAY.days.ago + update_cache_counter(:activities_count, record.user, -1) + if record.target.kind_of?(Organization) + update_cache_counter(:activities_count, record.target, -1) + end + end + end + def user_existence errors.add(:user, "user doesn't exists") if user && !user.class.exists?(user) end -- libgit2 0.21.2