From 5fe55071282c837d5a090c06dd1cd6314b5e53a4 Mon Sep 17 00:00:00 2001
From: Jason Robinson <mail@jasonrobinson.me>
Date: Sat, 22 Feb 2014 23:22:46 +0200
Subject: [PATCH] Add devise_lastseenable for user last seen tracking. Adds a
 timestamp to user model which is touched when user comes back. Statistics
 active users counts now use these more reliable timestamps. Fixes #4734

---
 Changelog.md                                  |  1 +
 Gemfile                                       |  1 +
 Gemfile.lock                                  |  8 +++++
 app/models/user.rb                            |  4 +--
 ...0222162826_devise_add_lastseenable_user.rb | 12 +++++++
 db/schema.rb                                  |  3 +-
 spec/models/user_spec.rb                      | 33 ++++++++++---------
 7 files changed, 43 insertions(+), 19 deletions(-)
 create mode 100644 db/migrate/20140222162826_devise_add_lastseenable_user.rb

diff --git a/Changelog.md b/Changelog.md
index 1483e0aaa6..24c7fd75cc 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -15,6 +15,7 @@
 ## Bug fixes
 * Improve time agos by updating the plugin [#4280](https://github.com/diaspora/diaspora/issues/4280)
 * Do not add a space after adding a mention [#4767](https://github.com/diaspora/diaspora/issues/4767)
+* Fix active user statistics by saving a last seen timestamp for users [#4734](https://github.com/diaspora/diaspora/issues/4734)
 
 ## Features
 * You can report a single post by clicking the correct icon in the controler section [#4517](https://github.com/diaspora/diaspora/pull/4517)
diff --git a/Gemfile b/Gemfile
index a75f31973a..46d258f587 100644
--- a/Gemfile
+++ b/Gemfile
@@ -14,6 +14,7 @@ gem 'json',        '1.8.1'
 # Authentication
 
 gem 'devise', '3.2.2'
+gem 'devise_lastseenable', '0.0.4'
 
 # Captcha
 
diff --git a/Gemfile.lock b/Gemfile.lock
index a82351d6dd..d41e859572 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -103,6 +103,13 @@ GEM
       railties (>= 3.2.6, < 5)
       thread_safe (~> 0.1)
       warden (~> 1.2.3)
+    devise_lastseenable (0.0.4)
+      devise
+      devise
+      rails (>= 3.0.4)
+      rails (>= 3.0.4)
+      warden
+      warden
     diff-lcs (1.2.5)
     eco (1.0.0)
       coffee-script
@@ -460,6 +467,7 @@ DEPENDENCIES
   cucumber-rails (= 1.4.0)
   database_cleaner (= 1.2.0)
   devise (= 3.2.2)
+  devise_lastseenable (= 0.0.4)
   entypo-rails
   factory_girl_rails (= 4.3.0)
   faraday (= 0.8.9)
diff --git a/app/models/user.rb b/app/models/user.rb
index 8d0becddc5..8105d0961c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -10,7 +10,7 @@ class User < ActiveRecord::Base
 
   apply_simple_captcha :message => I18n.t('simple_captcha.message.failed'), :add_to_base => true
 
-  scope :logged_in_since, lambda { |time| where('last_sign_in_at > ?', time) }
+  scope :logged_in_since, lambda { |time| where('last_seen > ?', time) }
   scope :monthly_actives, lambda { |time = Time.now| logged_in_since(time - 1.month) }
   scope :daily_actives, lambda { |time = Time.now| logged_in_since(time - 1.day) }
   scope :yearly_actives, lambda { |time = Time.now| logged_in_since(time - 1.year) }
@@ -18,7 +18,7 @@ class User < ActiveRecord::Base
 
   devise :database_authenticatable, :registerable,
          :recoverable, :rememberable, :trackable, :validatable,
-         :lockable, :lock_strategy => :none, :unlock_strategy => :none
+         :lockable, :lastseenable, :lock_strategy => :none, :unlock_strategy => :none
 
   before_validation :strip_and_downcase_username
   before_validation :set_current_language, :on => :create
diff --git a/db/migrate/20140222162826_devise_add_lastseenable_user.rb b/db/migrate/20140222162826_devise_add_lastseenable_user.rb
new file mode 100644
index 0000000000..5c62814afe
--- /dev/null
+++ b/db/migrate/20140222162826_devise_add_lastseenable_user.rb
@@ -0,0 +1,12 @@
+class DeviseAddLastseenableUser < ActiveRecord::Migration
+  def self.up
+    add_column :users, :last_seen, :datetime
+    User.find_each do |user|
+      user.update_column(:last_seen, user.last_sign_in_at)
+    end
+  end
+  
+  def self.down
+    remove_column :users, :last_seen
+  end
+end
\ No newline at end of file
diff --git a/db/schema.rb b/db/schema.rb
index b4c80620d5..60fab83be6 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version => 20131213171804) do
+ActiveRecord::Schema.define(:version => 20140222162826) do
 
   create_table "account_deletions", :force => true do |t|
     t.string  "diaspora_handle"
@@ -493,6 +493,7 @@ ActiveRecord::Schema.define(:version => 20131213171804) do
     t.integer  "auto_follow_back_aspect_id"
     t.text     "hidden_shareables"
     t.datetime "reset_password_sent_at"
+    t.datetime "last_seen"
   end
 
   add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token", :unique => true
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 20375f60f2..1ca106c88d 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -48,64 +48,64 @@ describe User do
   end
 
   describe 'yearly_actives' do
-    it 'returns list which includes users who latest signed in within last year' do
+    it 'returns list which includes users within last year' do
       user = FactoryGirl.build(:user)
-      user.last_sign_in_at = Time.now - 1.month
+      user.last_seen = Time.now - 1.month
       user.save
       User.yearly_actives.should include user
     end
 
-    it 'returns list which does not include users who did not sign in within last year' do
+    it 'returns list which does not include users seen within last year' do
       user = FactoryGirl.build(:user)
-      user.last_sign_in_at = Time.now - 2.year
+      user.last_seen = Time.now - 2.year
       user.save
       User.yearly_actives.should_not include user
     end
   end
 
   describe 'monthly_actives' do
-    it 'returns list which includes users who latest signed in within last month' do
+    it 'returns list which includes users seen within last month' do
       user = FactoryGirl.build(:user)
-      user.last_sign_in_at = Time.now - 1.day
+      user.last_seen = Time.now - 1.day
       user.save
       User.monthly_actives.should include user
     end
 
-     it 'returns list which does not include users who did not sign in within last month' do
+     it 'returns list which does not include users seen within last month' do
       user = FactoryGirl.build(:user)
-      user.last_sign_in_at = Time.now - 2.month
+      user.last_seen = Time.now - 2.month
       user.save
       User.monthly_actives.should_not include user
     end
   end
 
   describe 'daily_actives' do
-    it 'returns list which includes users who latest signed in within last day' do
+    it 'returns list which includes users seen within last day' do
       user = FactoryGirl.build(:user)
-      user.last_sign_in_at = Time.now - 1.hour
+      user.last_seen = Time.now - 1.hour
       user.save
       User.daily_actives.should include(user)
     end
 
-    it 'returns list which does not include users who did not sign in within last day' do
+    it 'returns list which does not include users seen within last day' do
       user = FactoryGirl.build(:user)
-      user.last_sign_in_at = Time.now - 2.day
+      user.last_seen = Time.now - 2.day
       user.save
       User.daily_actives.should_not include(user)
     end
   end
 
   describe 'halfyear_actives' do
-    it 'returns list which includes users who latest signed in within half a year' do
+    it 'returns list which includes users seen within half a year' do
       user = FactoryGirl.build(:user)
-      user.last_sign_in_at = Time.now - 4.month
+      user.last_seen = Time.now - 4.month
       user.save
       User.halfyear_actives.should include user
     end
 
-     it 'returns list which does not include users who did not sign in within the last half a year' do
+     it 'returns list which does not include users seen within the last half a year' do
       user = FactoryGirl.build(:user)
-      user.last_sign_in_at = Time.now - 7.month
+      user.last_seen = Time.now - 7.month
       user.save
       User.halfyear_actives.should_not include user
     end
@@ -1000,6 +1000,7 @@ describe User do
           auto_follow_back_aspect_id
           unconfirmed_email
           confirm_email_token
+          last_seen
         }.sort
       end
     end
-- 
GitLab