From d9cdde9aa73cc76f86f74c2ad70a6c5888b4e944 Mon Sep 17 00:00:00 2001
From: Neil Lalonde <neillalonde@gmail.com>
Date: Fri, 15 Mar 2013 18:08:46 -0400
Subject: [PATCH] Add user counts for each trust level to admin dashboard

---
 .../admin/helpers/report_helpers.js           | 18 ++++++++++++
 .../admin/templates/dashboard.js.handlebars   | 25 ++++++++++++----
 .../reports/trust_levels_report.js.handlebars |  9 ++++++
 .../report/admin_report_trust_levels_view.js  |  4 +++
 app/assets/stylesheets/admin/admin_base.scss  |  4 +++
 app/controllers/admin/dashboard_controller.rb |  3 +-
 app/models/report.rb                          |  9 ++++++
 app/models/user.rb                            |  4 +++
 config/locales/server.en.yml                  |  4 +++
 spec/models/report_spec.rb                    | 29 ++++++++++++++++---
 10 files changed, 99 insertions(+), 10 deletions(-)
 create mode 100644 app/assets/javascripts/admin/templates/reports/trust_levels_report.js.handlebars
 create mode 100644 app/assets/javascripts/admin/views/report/admin_report_trust_levels_view.js

diff --git a/app/assets/javascripts/admin/helpers/report_helpers.js b/app/assets/javascripts/admin/helpers/report_helpers.js
index c71878d6d..2f4277ddc 100644
--- a/app/assets/javascripts/admin/helpers/report_helpers.js
+++ b/app/assets/javascripts/admin/helpers/report_helpers.js
@@ -36,3 +36,21 @@ Handlebars.registerHelper('sumLast', function(property, numDays) {
     return sum;
   }
 });
+
+/**
+  Return the count of users at the given trust level.
+
+  @method valueAtTrustLevel
+  @for Handlebars
+**/
+Handlebars.registerHelper('valueAtTrustLevel', function(property, trustLevel) {
+  var data = Ember.Handlebars.get(this, property);
+  if( data ) {
+    var item = data.find( function(d, i, arr) { return parseInt(d.x,10) === parseInt(trustLevel,10); } );
+    if( item ) {
+      return item.y;
+    } else {
+      return 0;
+    }
+  }
+});
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/templates/dashboard.js.handlebars b/app/assets/javascripts/admin/templates/dashboard.js.handlebars
index bd4cde74b..d2e651540 100644
--- a/app/assets/javascripts/admin/templates/dashboard.js.handlebars
+++ b/app/assets/javascripts/admin/templates/dashboard.js.handlebars
@@ -38,12 +38,27 @@
     </p>
   </div>
 
-  <div class="dashboard-stats totals">
+
+  <div class="dashboard-stats">
+    {{i18n admin.dashboard.total_users}}: <strong>{{#unless loading}}{{ totalUsers }}{{/unless}}</strong><br/>
+  </div>
+
+  <div class="dashboard-stats trust-levels">
     <table class="table table-condensed table-hover">
-      <tr>
-        <td class="title">{{i18n admin.dashboard.total_users}}</td>
-        <td class="value">{{#unless loading}}{{ totalUsers }}{{/unless}}</td>
-      </tr>
+      <thead>
+        <tr>
+          <th>&nbsp;</th>
+          <th>0</th>
+          <th>1</th>
+          <th>2</th>
+          <th>3</th>
+          <th>4</th>
+          <th>5</th>
+        </tr>
+      </thead>
+      {{#unless loading}}
+        {{ render 'admin_report_trust_levels' users_by_trust_level }}
+      {{/unless}}
     </table>
   </div>
 
diff --git a/app/assets/javascripts/admin/templates/reports/trust_levels_report.js.handlebars b/app/assets/javascripts/admin/templates/reports/trust_levels_report.js.handlebars
new file mode 100644
index 000000000..842705c2c
--- /dev/null
+++ b/app/assets/javascripts/admin/templates/reports/trust_levels_report.js.handlebars
@@ -0,0 +1,9 @@
+<tr>
+  <td class="title">{{title}}</td>
+  <td class="value">{{valueAtTrustLevel data 0}}</td>
+  <td class="value">{{valueAtTrustLevel data 1}}</td>
+  <td class="value">{{valueAtTrustLevel data 2}}</td>
+  <td class="value">{{valueAtTrustLevel data 3}}</td>
+  <td class="value">{{valueAtTrustLevel data 4}}</td>
+  <td class="value">{{valueAtTrustLevel data 5}}</td>
+</tr>
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/views/report/admin_report_trust_levels_view.js b/app/assets/javascripts/admin/views/report/admin_report_trust_levels_view.js
new file mode 100644
index 000000000..1731ff382
--- /dev/null
+++ b/app/assets/javascripts/admin/views/report/admin_report_trust_levels_view.js
@@ -0,0 +1,4 @@
+Discourse.AdminReportTrustLevelsView = Discourse.View.extend({
+  templateName: 'admin/templates/reports/trust_levels_report',
+  tagName: 'tbody'
+});
\ No newline at end of file
diff --git a/app/assets/stylesheets/admin/admin_base.scss b/app/assets/stylesheets/admin/admin_base.scss
index 2d6a1e38c..826de9a00 100644
--- a/app/assets/stylesheets/admin/admin_base.scss
+++ b/app/assets/stylesheets/admin/admin_base.scss
@@ -332,6 +332,10 @@ table {
       border-top: none;
     }
   }
+
+  &.trust-levels {
+    margin-top: 0px;
+  }
 }
 
 
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index 3bf4263bd..d40ec8f81 100644
--- a/app/controllers/admin/dashboard_controller.rb
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -1,8 +1,9 @@
+
 class Admin::DashboardController < Admin::AdminController
 
   def index
     render_json_dump({
-      reports: ['visits', 'signups', 'topics', 'posts', 'flags'].map { |type| Report.find(type) },
+      reports: ['visits', 'signups', 'topics', 'posts', 'flags', 'users_by_trust_level'].map { |type| Report.find(type) },
       total_users: User.count
     }.merge(
       SiteSetting.version_checks? ? {version_check: DiscourseUpdates.check_version} : {}
diff --git a/app/models/report.rb b/app/models/report.rb
index 038fe34ec..3c6a60873 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -80,6 +80,15 @@ class Report
     end
   end
 
+  def self.report_users_by_trust_level(report)
+    report.data = []
+    fetch report do
+      User.counts_by_trust_level.each do |level, count|
+        report.data << {x: level.to_i, y: count}
+      end
+    end
+  end
+
 
   private
 
diff --git a/app/models/user.rb b/app/models/user.rb
index 1d91355b2..506b57cea 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -462,6 +462,10 @@ class User < ActiveRecord::Base
     where('created_at > ?', since).group('date(created_at)').order('date(created_at)').count
   end
 
+  def self.counts_by_trust_level
+    group('trust_level').count
+  end
+
   protected
 
     def cook
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 6ebca0023..63e51aec9 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -271,6 +271,10 @@ en:
       title: "Flags"
       xaxis: "Day"
       yaxis: "Number of flags"
+    users_by_trust_level:
+      title: "Users per Trust Level"
+      xaxis: "Trust Level"
+      yaxis: "Number of Users"
 
   site_settings:
     default_locale: "The default language of this Discourse instance (ISO 639-1 Code)"
diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb
index daa7033e7..bd3782866 100644
--- a/spec/models/report_spec.rb
+++ b/spec/models/report_spec.rb
@@ -4,7 +4,6 @@ describe Report do
 
 
   describe 'visits report' do
-
     let(:report) { Report.find('visits', cache: false) }
 
     context "no visits" do
@@ -24,10 +23,7 @@ describe Report do
       it "returns a report with data" do
         report.data.should be_present
       end
-
     end
-
-
   end
 
   [:signup, :topic, :post, :flag].each do |arg|
@@ -58,6 +54,31 @@ describe Report do
     end
   end
 
+  describe 'users by trust level report' do
+    let(:report) { Report.find('users_by_trust_level', cache: false) }
+
+    context "no users" do
+      it "returns an empty report" do
+        report.data.should be_blank
+      end
+    end
+
+    context "with users at different trust levels" do
+      before do
+        3.times { Fabricate(:user, trust_level: TrustLevel.levels[:new]) }
+        2.times { Fabricate(:user, trust_level: TrustLevel.levels[:regular]) }
+        Fabricate(:user, trust_level: TrustLevel.levels[:moderator])
+      end
+
+      it "returns a report with data" do
+        report.data.should be_present
+        report.data.find {|d| d[:x] == TrustLevel.levels[:new]} [:y].should == 3
+        report.data.find {|d| d[:x] == TrustLevel.levels[:regular]}[:y].should == 2
+        report.data.find {|d| d[:x] == TrustLevel.levels[:moderator]}[:y].should == 1
+      end
+    end
+  end
+
   describe '#fetch' do
     context 'signups' do
       let(:report) { Report.find('signups', cache: true) }