diff --git a/app/jobs/scheduled/periodical_updates.rb b/app/jobs/scheduled/periodical_updates.rb index f1848775e95..7836c88348a 100644 --- a/app/jobs/scheduled/periodical_updates.rb +++ b/app/jobs/scheduled/periodical_updates.rb @@ -17,7 +17,7 @@ module Jobs CategoryFeaturedTopic.feature_topics # Update view counts for users - User.update_view_counts + UserStat.update_view_counts # Update the scores of posts ScoreCalculator.new.calculate diff --git a/app/models/post_timing.rb b/app/models/post_timing.rb index 62a473e2350..85a51a08d80 100644 --- a/app/models/post_timing.rb +++ b/app/models/post_timing.rb @@ -61,7 +61,7 @@ class PostTiming < ActiveRecord::Base def self.process_timings(current_user, topic_id, topic_time, timings) - current_user.update_time_read! + current_user.user_stat.update_time_read! highest_seen = 1 timings.each do |post_number, time| diff --git a/app/models/site_customization.rb b/app/models/site_customization.rb index c9e5053c30e..a11cb1da716 100644 --- a/app/models/site_customization.rb +++ b/app/models/site_customization.rb @@ -191,18 +191,21 @@ end # # Table name: site_customizations # -# id :integer not null, primary key -# name :string(255) not null -# stylesheet :text -# header :text -# position :integer not null -# user_id :integer not null -# enabled :boolean not null -# key :string(255) not null -# created_at :datetime not null -# updated_at :datetime not null -# override_default_style :boolean default(FALSE), not null -# stylesheet_baked :text default(""), not null +# id :integer not null, primary key +# name :string(255) not null +# stylesheet :text +# header :text +# position :integer not null +# user_id :integer not null +# enabled :boolean not null +# key :string(255) not null +# created_at :datetime not null +# updated_at :datetime not null +# override_default_style :boolean default(FALSE), not null +# stylesheet_baked :text default(""), not null +# mobile_stylesheet :text +# mobile_header :text +# mobile_stylesheet_baked :text # # Indexes # diff --git a/app/models/topic.rb b/app/models/topic.rb index 33b3160e391..978cdb37717 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -663,7 +663,9 @@ end # # Indexes # -# idx_topics_user_id_deleted_at (user_id) -# index_forum_threads_on_bumped_at (bumped_at) +# idx_topics_user_id_deleted_at (user_id) +# index_forum_threads_on_bumped_at (bumped_at) +# index_topics_on_deleted_at_and_visible_and_archetype_and_id (deleted_at,visible,archetype,id) +# index_topics_on_id_and_deleted_at (id,deleted_at) # diff --git a/app/models/upload.rb b/app/models/upload.rb index 5ef3e11d26f..68b291be9f6 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -106,8 +106,9 @@ end # # Indexes # -# index_uploads_on_sha1 (sha1) UNIQUE -# index_uploads_on_url (url) -# index_uploads_on_user_id (user_id) +# index_uploads_on_id_and_url (id,url) +# index_uploads_on_sha1 (sha1) UNIQUE +# index_uploads_on_url (url) +# index_uploads_on_user_id (user_id) # diff --git a/app/models/user.rb b/app/models/user.rb index b1d41ad933a..7ee42fb25d1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -259,7 +259,7 @@ class User < ActiveRecord::Base def update_visit_record!(date) unless has_visit_record?(date) - update_column(:days_visited, days_visited + 1) + user_stat.update_column(:days_visited, user_stat.days_visited + 1) user_visits.create!(visited_at: date) end end @@ -316,42 +316,6 @@ class User < ActiveRecord::Base uploaded_avatar_path || User.gravatar_template(email) end - # Updates the denormalized view counts for all users - def self.update_view_counts - - # NOTE: we only update the counts for users we have seen in the last hour - # this avoids a very expensive query that may run on the entire user base - # we also ensure we only touch the table if data changes - - # Update denormalized topics_entered - exec_sql "UPDATE users SET topics_entered = X.c - FROM - (SELECT v.user_id, - COUNT(DISTINCT parent_id) AS c - FROM views AS v - WHERE parent_type = 'Topic' AND v.user_id IN ( - SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at - ) - GROUP BY v.user_id) AS X - WHERE - X.user_id = users.id AND - X.c <> topics_entered - ", seen_at: 1.hour.ago - - # Update denormalzied posts_read_count - exec_sql "UPDATE users SET posts_read_count = X.c - FROM - (SELECT pt.user_id, - COUNT(*) AS c - FROM post_timings AS pt - WHERE pt.user_id IN ( - SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at - ) - GROUP BY pt.user_id) AS X - WHERE X.user_id = users.id AND - X.c <> posts_read_count - ", seen_at: 1.hour.ago - end # The following count methods are somewhat slow - definitely don't use them in a loop. # They might need to be denormalized @@ -458,20 +422,6 @@ class User < ActiveRecord::Base end end - MAX_TIME_READ_DIFF = 100 - # attempt to add total read time to user based on previous time this was called - def update_time_read! - last_seen_key = "user-last-seen:#{id}" - last_seen = $redis.get(last_seen_key) - if last_seen.present? - diff = (Time.now.to_f - last_seen.to_f).round - if diff > 0 && diff < MAX_TIME_READ_DIFF - User.where(id: id, time_read: time_read).update_all ["time_read = time_read + ?", diff] - end - end - $redis.set(last_seen_key, Time.now.to_f) - end - def readable_name return "#{name} (#{username})" if name.present? && name != username username @@ -486,18 +436,6 @@ class User < ActiveRecord::Base where('created_at > ?', sinceDaysAgo.days.ago).group('date(created_at)').order('date(created_at)').count end - def update_topic_reply_count - self.topic_reply_count = - Topic - .where(['id in ( - SELECT topic_id FROM posts p - JOIN topics t2 ON t2.id = p.topic_id - WHERE p.deleted_at IS NULL AND - t2.user_id <> p.user_id AND - p.user_id = ? - )', self.id]) - .count - end def secure_category_ids cats = self.staff? ? Category.where(read_restricted: true) : secure_categories.references(:categories) @@ -549,7 +487,7 @@ class User < ActiveRecord::Base def create_user_stat stat = UserStat.new - stat.user_id = self.id + stat.user_id = id stat.save! end @@ -647,8 +585,6 @@ end # approved :boolean default(FALSE), not null # approved_by_id :integer # approved_at :datetime -# topics_entered :integer default(0), not null -# posts_read_count :integer default(0), not null # digest_after_days :integer # previous_visit_at :datetime # banned_at :datetime @@ -657,22 +593,18 @@ end # auto_track_topics_after_msecs :integer # views :integer default(0), not null # flag_level :integer default(0), not null -# time_read :integer default(0), not null -# days_visited :integer default(0), not null # ip_address :string # new_topic_duration_minutes :integer # external_links_in_new_tab :boolean default(FALSE), not null # enable_quoting :boolean default(TRUE), not null # moderator :boolean default(FALSE) -# likes_given :integer default(0), not null -# likes_received :integer default(0), not null -# topic_reply_count :integer default(0), not null # blocked :boolean default(FALSE) # dynamic_favicon :boolean default(FALSE), not null # title :string(255) # use_uploaded_avatar :boolean default(FALSE) # uploaded_avatar_template :string(255) # uploaded_avatar_id :integer +# email_always :boolean default(FALSE), not null # # Indexes # diff --git a/app/models/user_action.rb b/app/models/user_action.rb index a37f1e90e8d..69765c17087 100644 --- a/app/models/user_action.rb +++ b/app/models/user_action.rb @@ -254,9 +254,9 @@ SQL def self.update_like_count(user_id, action_type, delta) if action_type == LIKE - User.where(id: user_id).update_all("likes_given = likes_given + #{delta.to_i}") + UserStat.where(user_id: user_id).update_all("likes_given = likes_given + #{delta.to_i}") elsif action_type == WAS_LIKED - User.where(id: user_id).update_all("likes_received = likes_received + #{delta.to_i}") + UserStat.where(user_id: user_id).update_all("likes_received = likes_received + #{delta.to_i}") end end diff --git a/app/models/user_history.rb b/app/models/user_history.rb index 5cb133bb4d6..1177b3395dd 100644 --- a/app/models/user_history.rb +++ b/app/models/user_history.rb @@ -66,11 +66,11 @@ end # == Schema Information # -# Table name: staff_action_logs +# Table name: user_histories # # id :integer not null, primary key # action :integer not null -# staff_user_id :integer not null +# acting_user_id :integer # target_user_id :integer # details :text # created_at :datetime not null @@ -81,12 +81,13 @@ end # subject :text # previous_value :text # new_value :text +# topic_id :integer # # Indexes # -# index_staff_action_logs_on_action_and_id (action,id) -# index_staff_action_logs_on_staff_user_id_and_id (staff_user_id,id) -# index_staff_action_logs_on_subject_and_id (subject,id) -# index_staff_action_logs_on_target_user_id_and_id (target_user_id,id) +# index_staff_action_logs_on_action_and_id (action,id) +# index_staff_action_logs_on_subject_and_id (subject,id) +# index_staff_action_logs_on_target_user_id_and_id (target_user_id,id) +# index_user_histories_on_acting_user_id_and_action_and_id (acting_user_id,action,id) # diff --git a/app/models/user_stat.rb b/app/models/user_stat.rb index 6d9dc27042c..bb86ec4c219 100644 --- a/app/models/user_stat.rb +++ b/app/models/user_stat.rb @@ -2,4 +2,84 @@ class UserStat < ActiveRecord::Base belongs_to :user + # Updates the denormalized view counts for all users + def self.update_view_counts + + # NOTE: we only update the counts for users we have seen in the last hour + # this avoids a very expensive query that may run on the entire user base + # we also ensure we only touch the table if data changes + + # Update denormalized topics_entered + exec_sql "UPDATE user_stats SET topics_entered = X.c + FROM + (SELECT v.user_id, + COUNT(DISTINCT parent_id) AS c + FROM views AS v + WHERE parent_type = 'Topic' AND v.user_id IN ( + SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at + ) + GROUP BY v.user_id) AS X + WHERE + X.user_id = user_stats.user_id AND + X.c <> topics_entered + ", seen_at: 1.hour.ago + + # Update denormalzied posts_read_count + exec_sql "UPDATE user_stats SET posts_read_count = X.c + FROM + (SELECT pt.user_id, + COUNT(*) AS c + FROM post_timings AS pt + WHERE pt.user_id IN ( + SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at + ) + GROUP BY pt.user_id) AS X + WHERE X.user_id = user_stats.user_id AND + X.c <> posts_read_count + ", seen_at: 1.hour.ago + end + + def update_topic_reply_count + self.topic_reply_count = + Topic + .where(['id in ( + SELECT topic_id FROM posts p + JOIN topics t2 ON t2.id = p.topic_id + WHERE p.deleted_at IS NULL AND + t2.user_id <> p.user_id AND + p.user_id = ? + )', self.user_id]) + .count + end + + MAX_TIME_READ_DIFF = 100 + # attempt to add total read time to user based on previous time this was called + def update_time_read! + last_seen_key = "user-last-seen:#{id}" + last_seen = $redis.get(last_seen_key) + if last_seen.present? + diff = (Time.now.to_f - last_seen.to_f).round + if diff > 0 && diff < MAX_TIME_READ_DIFF + UserStat.where(user_id: id, time_read: time_read).update_all ["time_read = time_read + ?", diff] + end + end + $redis.set(last_seen_key, Time.now.to_f) + end + end + +# == Schema Information +# +# Table name: user_stats +# +# user_id :integer not null, primary key +# has_custom_avatar :boolean default(FALSE), not null +# topics_entered :integer default(0), not null +# time_read :integer default(0), not null +# days_visited :integer default(0), not null +# posts_read_count :integer default(0), not null +# likes_given :integer default(0), not null +# likes_received :integer default(0), not null +# topic_reply_count :integer default(0), not null +# + diff --git a/app/models/user_visit.rb b/app/models/user_visit.rb index 9f236f43dae..737fa9f058a 100644 --- a/app/models/user_visit.rb +++ b/app/models/user_visit.rb @@ -7,13 +7,13 @@ class UserVisit < ActiveRecord::Base def self.ensure_consistency! exec_sql < ( - SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.id + SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.user_id ) SQL end diff --git a/app/models/view.rb b/app/models/view.rb index f1da903f488..253c63a2751 100644 --- a/app/models/view.rb +++ b/app/models/view.rb @@ -43,6 +43,7 @@ end # # Indexes # -# index_views_on_parent_id_and_parent_type (parent_id,parent_type) +# index_views_on_parent_id_and_parent_type (parent_id,parent_type) +# index_views_on_user_id_and_parent_type_and_parent_id (user_id,parent_type,parent_id) # diff --git a/app/serializers/admin_user_serializer.rb b/app/serializers/admin_user_serializer.rb index cb5860dfa7d..aac71f82021 100644 --- a/app/serializers/admin_user_serializer.rb +++ b/app/serializers/admin_user_serializer.rb @@ -5,7 +5,6 @@ class AdminUserSerializer < BasicUserSerializer :admin, :moderator, :last_seen_age, - :days_visited, :last_emailed_age, :created_at_age, :username_lower, @@ -14,9 +13,6 @@ class AdminUserSerializer < BasicUserSerializer :username, :title, :avatar_template, - :topics_entered, - :posts_read_count, - :time_read, :can_approve, :approved, :banned_at, @@ -26,7 +22,15 @@ class AdminUserSerializer < BasicUserSerializer :can_send_activation_email, :can_activate, :can_deactivate, - :blocked + :blocked, + :time_read + + [:days_visited,:posts_read_count,:topics_entered].each do |sym| + attributes sym + define_method sym do + object.user_stat.send(sym) + end + end def is_banned object.is_banned? @@ -47,8 +51,8 @@ class AdminUserSerializer < BasicUserSerializer end def time_read - return nil if object.time_read.blank? - AgeWords.age_words(object.time_read) + return nil if object.user_stat.time_read.blank? + AgeWords.age_words(object.user_stat.time_read) end def created_at_age diff --git a/app/serializers/current_user_serializer.rb b/app/serializers/current_user_serializer.rb index 69079b4f369..58a2d43652a 100644 --- a/app/serializers/current_user_serializer.rb +++ b/app/serializers/current_user_serializer.rb @@ -25,7 +25,7 @@ class CurrentUserSerializer < BasicUserSerializer end def reply_count - object.topic_reply_count + object.user_stat.topic_reply_count end def site_flagged_posts_count diff --git a/db/migrate/20131003061137_move_columns_to_user_stats.rb b/db/migrate/20131003061137_move_columns_to_user_stats.rb new file mode 100644 index 00000000000..93fe3eab5a1 --- /dev/null +++ b/db/migrate/20131003061137_move_columns_to_user_stats.rb @@ -0,0 +1,61 @@ +class MoveColumnsToUserStats < ActiveRecord::Migration + def up + add_column :user_stats, :topics_entered, :integer, default: 0, null: false + add_column :user_stats, :time_read, :integer, default: 0, null: false + add_column :user_stats, :days_visited, :integer, default: 0, null: false + add_column :user_stats, :posts_read_count, :integer, default: 0, null: false + add_column :user_stats, :likes_given, :integer, default: 0, null: false + add_column :user_stats, :likes_received, :integer, default: 0, null: false + add_column :user_stats, :topic_reply_count, :integer, default: 0, null: false + + execute 'UPDATE user_stats + SET topics_entered = u.topics_entered, + time_read = u.time_read, + days_visited = u.days_visited, + posts_read_count = u.posts_read_count, + likes_given = u.likes_given, + likes_received = u.likes_received, + topic_reply_count = u.topic_reply_count + FROM user_stats s + JOIN users u on u.id = s.user_id + ' + + remove_column :users, :topics_entered + remove_column :users, :time_read + remove_column :users, :days_visited + remove_column :users, :posts_read_count + remove_column :users, :likes_given + remove_column :users, :likes_received + remove_column :users, :topic_reply_count + end + + def down + add_column :users, :topics_entered, :integer + add_column :users, :time_read, :integer + add_column :users, :days_visited, :integer + add_column :users, :posts_read_count, :integer + add_column :users, :likes_given, :integer + add_column :users, :likes_received, :integer + add_column :users, :topic_reply_count, :integer + + execute 'UPDATE users + SET topics_entered = u.topics_entered, + time_read = u.time_read, + days_visited = u.days_visited, + posts_read_count = u.posts_read_count, + likes_given = u.likes_given, + likes_received = u.likes_received, + topic_reply_count = u.topic_reply_count + FROM users s + JOIN user_stats u on s.id = u.user_id + ' + + remove_column :user_stats, :topics_entered + remove_column :user_stats, :time_read + remove_column :user_stats, :days_visited + remove_column :user_stats, :posts_read_count + remove_column :user_stats, :likes_given + remove_column :user_stats, :likes_received + remove_column :user_stats, :topic_reply_count + end +end diff --git a/lib/autospec/runner.rb b/lib/autospec/runner.rb index bc8feb97f64..b5d0d44f92c 100644 --- a/lib/autospec/runner.rb +++ b/lib/autospec/runner.rb @@ -116,6 +116,7 @@ class Autospec::Runner puts s end puts + queue_specs(specs.zip specs) end end end diff --git a/lib/guardian.rb b/lib/guardian.rb index 6cc15b1c76d..c032ac3c40f 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -425,10 +425,13 @@ class Guardian private def is_my_own?(obj) - @user.present? && - (obj.respond_to?(:user) || obj.respond_to?(:user_id)) && - (obj.respond_to?(:user) ? obj.user == @user : true) && - (obj.respond_to?(:user_id) ? (obj.user_id == @user.id) : true) + + unless anonymous? + return obj.user_id == @user.id if obj.respond_to?(:user_id) + return obj.user == @user if obj.respond_to?(:user) + end + + false end def is_me?(other) diff --git a/lib/post_creator.rb b/lib/post_creator.rb index 5a6d6495240..90114d69d96 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -232,7 +232,8 @@ class PostCreator def update_user_counts # We don't count replies to your own topics if @user.id != @topic.user_id - @user.update_topic_reply_count + @user.user_stat.update_topic_reply_count + @user.user_stat.save! end @user.last_posted_at = @post.created_at diff --git a/lib/promotion.rb b/lib/promotion.rb index 921cbc5c605..656f1121b03 100644 --- a/lib/promotion.rb +++ b/lib/promotion.rb @@ -22,9 +22,10 @@ class Promotion end def review_newuser - return false if @user.topics_entered < SiteSetting.basic_requires_topics_entered - return false if @user.posts_read_count < SiteSetting.basic_requires_read_posts - return false if (@user.time_read / 60) < SiteSetting.basic_requires_time_spent_mins + stat = @user.user_stat + return false if stat.topics_entered < SiteSetting.basic_requires_topics_entered + return false if stat.posts_read_count < SiteSetting.basic_requires_read_posts + return false if (stat.time_read / 60) < SiteSetting.basic_requires_time_spent_mins @user.change_trust_level!(:basic) @@ -32,13 +33,14 @@ class Promotion end def review_basic - return false if @user.topics_entered < SiteSetting.regular_requires_topics_entered - return false if @user.posts_read_count < SiteSetting.regular_requires_read_posts - return false if (@user.time_read / 60) < SiteSetting.regular_requires_time_spent_mins - return false if @user.days_visited < SiteSetting.regular_requires_days_visited - return false if @user.likes_received < SiteSetting.regular_requires_likes_received - return false if @user.likes_given < SiteSetting.regular_requires_likes_given - return false if @user.topic_reply_count < SiteSetting.regular_requires_topic_reply_count + stat = @user.user_stat + return false if stat.topics_entered < SiteSetting.regular_requires_topics_entered + return false if stat.posts_read_count < SiteSetting.regular_requires_read_posts + return false if (stat.time_read / 60) < SiteSetting.regular_requires_time_spent_mins + return false if stat.days_visited < SiteSetting.regular_requires_days_visited + return false if stat.likes_received < SiteSetting.regular_requires_likes_received + return false if stat.likes_given < SiteSetting.regular_requires_likes_given + return false if stat.topic_reply_count < SiteSetting.regular_requires_topic_reply_count @user.change_trust_level!(:regular) end diff --git a/spec/components/boost_trust_level_spec.rb b/spec/components/boost_trust_level_spec.rb index 951775cc317..83884e22f4a 100644 --- a/spec/components/boost_trust_level_spec.rb +++ b/spec/components/boost_trust_level_spec.rb @@ -39,9 +39,10 @@ describe BoostTrustLevel do context "for a user that has done the requisite things to attain their trust level" do before do - user.topics_entered = SiteSetting.basic_requires_topics_entered + 1 - user.posts_read_count = SiteSetting.basic_requires_read_posts + 1 - user.time_read = SiteSetting.basic_requires_time_spent_mins * 60 + stat = user.user_stat + stat.topics_entered = SiteSetting.basic_requires_topics_entered + 1 + stat.posts_read_count = SiteSetting.basic_requires_read_posts + 1 + stat.time_read = SiteSetting.basic_requires_time_spent_mins * 60 user.save! user.update_attributes(trust_level: TrustLevel.levels[:basic]) end diff --git a/spec/components/post_creator_spec.rb b/spec/components/post_creator_spec.rb index 528367c01f5..c9a762b8326 100644 --- a/spec/components/post_creator_spec.rb +++ b/spec/components/post_creator_spec.rb @@ -163,12 +163,15 @@ describe PostCreator do topic_user.seen_post_count.should == first_post.post_number user2 = Fabricate(:coding_horror) - user2.topic_reply_count.should == 0 - first_post.user.reload.topic_reply_count.should == 0 + user2.user_stat.topic_reply_count.should == 0 + + first_post.user.user_stat.reload.topic_reply_count.should == 0 PostCreator.new(user2, topic_id: first_post.topic_id, raw: "this is my test post 123").create - user2.reload.topic_reply_count.should == 1 - first_post.user.reload.topic_reply_count.should == 0 + + first_post.user.user_stat.reload.topic_reply_count.should == 0 + + user2.user_stat.reload.topic_reply_count.should == 1 end end diff --git a/spec/components/promotion_spec.rb b/spec/components/promotion_spec.rb index 3f1c3e7e3e2..74c1a0e86a3 100644 --- a/spec/components/promotion_spec.rb +++ b/spec/components/promotion_spec.rb @@ -27,9 +27,10 @@ describe Promotion do context "that has done the requisite things" do before do - user.topics_entered = SiteSetting.basic_requires_topics_entered - user.posts_read_count = SiteSetting.basic_requires_read_posts - user.time_read = SiteSetting.basic_requires_time_spent_mins * 60 + stat = user.user_stat + stat.topics_entered = SiteSetting.basic_requires_topics_entered + stat.posts_read_count = SiteSetting.basic_requires_read_posts + stat.time_read = SiteSetting.basic_requires_time_spent_mins * 60 @result = promotion.review end @@ -64,13 +65,14 @@ describe Promotion do context "that has done the requisite things" do before do - user.topics_entered = SiteSetting.regular_requires_topics_entered - user.posts_read_count = SiteSetting.regular_requires_read_posts - user.time_read = SiteSetting.regular_requires_time_spent_mins * 60 - user.days_visited = SiteSetting.regular_requires_days_visited * 60 - user.likes_received = SiteSetting.regular_requires_likes_received - user.likes_given = SiteSetting.regular_requires_likes_given - user.topic_reply_count = SiteSetting.regular_requires_topic_reply_count + stat = user.user_stat + stat.topics_entered = SiteSetting.regular_requires_topics_entered + stat.posts_read_count = SiteSetting.regular_requires_read_posts + stat.time_read = SiteSetting.regular_requires_time_spent_mins * 60 + stat.days_visited = SiteSetting.regular_requires_days_visited * 60 + stat.likes_received = SiteSetting.regular_requires_likes_received + stat.likes_given = SiteSetting.regular_requires_likes_given + stat.topic_reply_count = SiteSetting.regular_requires_topic_reply_count @result = promotion.review end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index b3ea66a2fd1..ea21dd90adc 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -145,10 +145,11 @@ describe Admin::UsersController do it "raises an error when demoting a user below their current trust level" do StaffActionLogger.any_instance.expects(:log_trust_level_change).never - @another_user.topics_entered = SiteSetting.basic_requires_topics_entered + 1 - @another_user.posts_read_count = SiteSetting.basic_requires_read_posts + 1 - @another_user.time_read = SiteSetting.basic_requires_time_spent_mins * 60 - @another_user.save! + stat = @another_user.user_stat + stat.topics_entered = SiteSetting.basic_requires_topics_entered + 1 + stat.posts_read_count = SiteSetting.basic_requires_read_posts + 1 + stat.time_read = SiteSetting.basic_requires_time_spent_mins * 60 + stat.save! @another_user.update_attributes(trust_level: TrustLevel.levels[:basic]) xhr :put, :trust_level, user_id: @another_user.id, level: TrustLevel.levels[:newuser] response.should be_forbidden diff --git a/spec/fabricators/user_action_fabricator.rb b/spec/fabricators/user_action_fabricator.rb index 8e8595dd008..0763828c034 100644 --- a/spec/fabricators/user_action_fabricator.rb +++ b/spec/fabricators/user_action_fabricator.rb @@ -1,6 +1,4 @@ Fabricator(:user_action) do - user action_type UserAction::STAR - end diff --git a/spec/fabricators/user_fabricator.rb b/spec/fabricators/user_fabricator.rb index 4b400035d1c..03dd6cc5900 100644 --- a/spec/fabricators/user_fabricator.rb +++ b/spec/fabricators/user_fabricator.rb @@ -1,3 +1,6 @@ +Fabricator(:user_stat) do +end + Fabricator(:user) do name 'Bruce Wayne' username { sequence(:username) { |i| "bruce#{i}" } } @@ -57,4 +60,4 @@ Fabricator(:active_user, from: :user) do trust_level TrustLevel.levels[:basic] active true bio_raw "Don't as me about my dad!" -end \ No newline at end of file +end diff --git a/spec/jobs/periodical_updates_spec.rb b/spec/jobs/periodical_updates_spec.rb index 93485a408b1..cbb45e78d18 100644 --- a/spec/jobs/periodical_updates_spec.rb +++ b/spec/jobs/periodical_updates_spec.rb @@ -20,7 +20,7 @@ describe Jobs::PeriodicalUpdates do end it "updates view counts" do - User.expects(:update_view_counts).once + UserStat.expects(:update_view_counts).once end it "calculates scores" do diff --git a/spec/models/user_action_spec.rb b/spec/models/user_action_spec.rb index da75437e5ec..98f553e55d9 100644 --- a/spec/models/user_action_spec.rb +++ b/spec/models/user_action_spec.rb @@ -123,12 +123,12 @@ describe UserAction do it 'should result in correct data assignment' do @liker_action.should_not be_nil @likee_action.should_not be_nil - likee.reload.likes_received.should == 1 - liker.reload.likes_given.should == 1 + likee.user_stat.reload.likes_received.should == 1 + liker.user_stat.reload.likes_given.should == 1 PostAction.remove_act(liker, post, PostActionType.types[:like]) - likee.reload.likes_received.should == 0 - liker.reload.likes_given.should == 0 + likee.user_stat.reload.likes_received.should == 0 + liker.user_stat.reload.likes_given.should == 0 end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 757cd636e34..c9b751f87ef 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require_dependency 'user' describe User do @@ -21,66 +22,6 @@ describe User do it { should validate_presence_of :username } it { should validate_presence_of :email } - context '#update_view_counts' do - - let(:user) { Fabricate(:user) } - - context 'topics_entered' do - context 'without any views' do - it "doesn't increase the user's topics_entered" do - lambda { User.update_view_counts; user.reload }.should_not change(user, :topics_entered) - end - end - - context 'with a view' do - let(:topic) { Fabricate(:topic) } - let!(:view) { View.create_for(topic, '127.0.0.1', user) } - - before do - user.update_column :last_seen_at, 1.second.ago - end - - it "adds one to the topics entered" do - User.update_view_counts - user.reload - user.topics_entered.should == 1 - end - - it "won't record a second view as a different topic" do - View.create_for(topic, '127.0.0.1', user) - User.update_view_counts - user.reload - user.topics_entered.should == 1 - end - - end - end - - context 'posts_read_count' do - context 'without any post timings' do - it "doesn't increase the user's posts_read_count" do - lambda { User.update_view_counts; user.reload }.should_not change(user, :posts_read_count) - end - end - - context 'with a post timing' do - let!(:post) { Fabricate(:post) } - let!(:post_timings) do - PostTiming.record_timing(msecs: 1234, topic_id: post.topic_id, user_id: user.id, post_number: post.post_number) - end - - before do - user.update_column :last_seen_at, 1.second.ago - end - - it "increases posts_read_count" do - User.update_view_counts - user.reload - user.posts_read_count.should == 1 - end - end - end - end context '.enqueue_welcome_message' do let(:user) { Fabricate(:user) } @@ -266,7 +207,6 @@ describe User do its(:approved_by_id) { should be_blank } its(:email_private_messages) { should be_true } its(:email_direct ) { should be_true } - its(:time_read) { should == 0} context 'digest emails' do it 'defaults to digests every week' do @@ -295,8 +235,6 @@ describe User do its(:email_tokens) { should be_present } its(:bio_cooked) { should be_present } its(:bio_summary) { should be_present } - its(:topics_entered) { should == 0 } - its(:posts_read_count) { should == 0 } end end @@ -664,7 +602,7 @@ describe User do end it "should have 0 for days_visited" do - user.days_visited.should == 0 + user.user_stat.days_visited.should == 0 end describe 'with no previous values' do @@ -685,7 +623,7 @@ describe User do it "should have 0 for days_visited" do user.reload - user.days_visited.should == 1 + user.user_stat.days_visited.should == 1 end it "should log a user_visit with the date" do @@ -706,7 +644,7 @@ describe User do end it "doesn't increase days_visited twice" do - user.days_visited.should == 1 + user.user_stat.days_visited.should == 1 end end @@ -811,33 +749,6 @@ describe User do end - describe 'update_time_read!' do - let(:user) { Fabricate(:user) } - - it 'makes no changes if nothing is cached' do - $redis.expects(:get).with("user-last-seen:#{user.id}").returns(nil) - user.update_time_read! - user.reload - user.time_read.should == 0 - end - - it 'makes a change if time read is below threshold' do - $redis.expects(:get).with("user-last-seen:#{user.id}").returns(Time.now - 10.0) - user.update_time_read! - user.reload - user.time_read.should == 10 - end - - it 'makes no change if time read is above threshold' do - t = Time.now - 1 - User::MAX_TIME_READ_DIFF - $redis.expects(:get).with("user-last-seen:#{user.id}").returns(t) - user.update_time_read! - user.reload - user.time_read.should == 0 - end - - end - describe '#readable_name' do context 'when name is missing' do it 'returns just the username' do diff --git a/spec/models/user_stat_spec.rb b/spec/models/user_stat_spec.rb index 8d30dd8d7b6..be4516f12da 100644 --- a/spec/models/user_stat_spec.rb +++ b/spec/models/user_stat_spec.rb @@ -9,4 +9,96 @@ describe UserStat do user.user_stat.should be_present end -end \ No newline at end of file + context '#update_view_counts' do + + let(:user) { Fabricate(:user) } + let(:stat) { user.user_stat } + + context 'topics_entered' do + context 'without any views' do + it "doesn't increase the user's topics_entered" do + lambda { UserStat.update_view_counts; stat.reload }.should_not change(stat, :topics_entered) + end + end + + context 'with a view' do + let(:topic) { Fabricate(:topic) } + let!(:view) { View.create_for(topic, '127.0.0.1', user) } + + before do + user.update_column :last_seen_at, 1.second.ago + end + + it "adds one to the topics entered" do + UserStat.update_view_counts + stat.reload + stat.topics_entered.should == 1 + end + + it "won't record a second view as a different topic" do + View.create_for(topic, '127.0.0.1', user) + UserStat.update_view_counts + stat.reload + stat.topics_entered.should == 1 + end + + end + end + + context 'posts_read_count' do + context 'without any post timings' do + it "doesn't increase the user's posts_read_count" do + lambda { UserStat.update_view_counts; stat.reload }.should_not change(stat, :posts_read_count) + end + end + + context 'with a post timing' do + let!(:post) { Fabricate(:post) } + let!(:post_timings) do + PostTiming.record_timing(msecs: 1234, topic_id: post.topic_id, user_id: user.id, post_number: post.post_number) + end + + before do + user.update_column :last_seen_at, 1.second.ago + end + + it "increases posts_read_count" do + UserStat.update_view_counts + stat.reload + stat.posts_read_count.should == 1 + end + end + end + end + + + describe 'update_time_read!' do + let(:user) { Fabricate(:user) } + let(:stat) { user.user_stat } + + it 'makes no changes if nothing is cached' do + $redis.expects(:get).with("user-last-seen:#{user.id}").returns(nil) + stat.update_time_read! + stat.reload + stat.time_read.should == 0 + end + + it 'makes a change if time read is below threshold' do + $redis.expects(:get).with("user-last-seen:#{user.id}").returns(Time.now - 10.0) + stat.update_time_read! + stat.reload + stat.time_read.should == 10 + end + + it 'makes no change if time read is above threshold' do + t = Time.now - 1 - UserStat::MAX_TIME_READ_DIFF + $redis.expects(:get).with("user-last-seen:#{user.id}").returns(t) + stat.update_time_read! + stat.reload + stat.time_read.should == 0 + end + + end + + +end diff --git a/spec/models/user_visit_spec.rb b/spec/models/user_visit_spec.rb index 333f25e1383..07278b46e3f 100644 --- a/spec/models/user_visit_spec.rb +++ b/spec/models/user_visit_spec.rb @@ -9,13 +9,13 @@ describe UserVisit do u.update_visit_record!(1.day.ago.to_date) u.reload - u.days_visited.should == 2 + u.user_stat.days_visited.should == 2 - u.days_visited = 1 + u.user_stat.days_visited = 1 u.save UserVisit.ensure_consistency! u.reload - u.days_visited.should == 2 + u.user_stat.days_visited.should == 2 end end