FEATURE: filter admin reports via user group

This commit is contained in:
Arpit Jalan 2016-02-03 07:59:51 +05:30
parent 7d992cb4c5
commit eec8436cfe
12 changed files with 90 additions and 31 deletions

View File

@ -1,6 +1,7 @@
import { exportEntity } from 'discourse/lib/export-csv'; import { exportEntity } from 'discourse/lib/export-csv';
import { outputExportResult } from 'discourse/lib/export-result'; import { outputExportResult } from 'discourse/lib/export-result';
import Report from 'admin/models/report'; import Report from 'admin/models/report';
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Controller.extend({ export default Ember.Controller.extend({
viewMode: 'table', viewMode: 'table',
@ -9,22 +10,32 @@ export default Ember.Controller.extend({
startDate: null, startDate: null,
endDate: null, endDate: null,
categoryId: null, categoryId: null,
groupId: null,
refreshing: false, refreshing: false,
categoryOptions: function() { @computed()
var arr = [{name: I18n.t('category.all'), value: 'all'}]; categoryOptions() {
return arr.concat( Discourse.Site.currentProp('sortedCategories').map(function(i) { return {name: i.get('name'), value: i.get('id') }; }) ); const arr = [{name: I18n.t('category.all'), value: 'all'}];
}.property(), return arr.concat(Discourse.Site.currentProp('sortedCategories').map((i) => {return {name: i.get('name'), value: i.get('id')};}));
},
@computed()
groupOptions() {
const arr = [{name: I18n.t('admin.dashboard.reports.groups'), value: 'all'}];
return arr.concat(this.site.groups.map((i) => {return {name: i['name'], value: i['id']};}));
},
@computed('model.type')
showGroupOptions(modelType) {
return modelType === "visits" || modelType === "signups" || modelType === "profile_views";
},
actions: { actions: {
refreshReport() { refreshReport() {
var q; var q;
this.set("refreshing", true); this.set("refreshing", true);
if (this.get('categoryId') === "all") {
q = Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate")); q = Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate"), this.get("categoryId"), this.get("groupId"));
} else {
q = Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate"), this.get("categoryId"));
}
q.then(m => this.set("model", m)).finally(() => this.set("refreshing", false)); q.then(m => this.set("model", m)).finally(() => this.set("refreshing", false));
}, },
@ -41,7 +52,8 @@ export default Ember.Controller.extend({
name: this.get("model.type"), name: this.get("model.type"),
start_date: this.get('startDate'), start_date: this.get('startDate'),
end_date: this.get('endDate'), end_date: this.get('endDate'),
category_id: this.get('categoryId') === 'all' ? undefined : this.get('categoryId') category_id: this.get('categoryId') === 'all' ? undefined : this.get('categoryId'),
group_id: this.get('groupId') === 'all' ? undefined : this.get('groupId')
}).then(outputExportResult); }).then(outputExportResult);
} }
} }

View File

@ -131,12 +131,13 @@ const Report = Discourse.Model.extend({
Report.reopenClass({ Report.reopenClass({
find(type, startDate, endDate, categoryId) { find(type, startDate, endDate, categoryId, groupId) {
return Discourse.ajax("/admin/reports/" + type, { return Discourse.ajax("/admin/reports/" + type, {
data: { data: {
start_date: startDate, start_date: startDate,
end_date: endDate, end_date: endDate,
category_id: categoryId category_id: categoryId,
group_id: groupId
} }
}).then(json => { }).then(json => {
// Add a percent field to each tuple // Add a percent field to each tuple

View File

@ -1,9 +1,12 @@
<h3>{{model.title}}</h3> <h3>{{model.title}}</h3>
<div> <div class="admin-reports-filter">
{{i18n 'admin.dashboard.reports.start_date'}} {{input type="date" value=startDate}} {{i18n 'admin.dashboard.reports.start_date'}} {{input type="date" value=startDate}}
{{i18n 'admin.dashboard.reports.end_date'}} {{input type="date" value=endDate}} {{i18n 'admin.dashboard.reports.end_date'}} {{input type="date" value=endDate}}
{{combo-box valueAttribute="value" content=categoryOptions value=categoryId}} {{combo-box valueAttribute="value" content=categoryOptions value=categoryId}}
{{#if showGroupOptions}}
{{combo-box valueAttribute="value" content=groupOptions value=groupId}}
{{/if}}
{{d-button action="refreshReport" class="btn-primary" label="admin.dashboard.reports.refresh_report" icon="refresh"}} {{d-button action="refreshReport" class="btn-primary" label="admin.dashboard.reports.refresh_report" icon="refresh"}}
{{d-button action="exportCsv" label="admin.export_csv.button_text" icon="download"}} {{d-button action="exportCsv" label="admin.export_csv.button_text" icon="download"}}
</div> </div>

View File

@ -38,6 +38,12 @@ $mobile-breakpoint: 700px;
.filters input { margin-bottom: 0; } .filters input { margin-bottom: 0; }
} }
.admin-contents .admin-reports-filter {
input[type="date"] {
width: 140px;
}
}
td.flaggers td { td.flaggers td {
border-bottom: none; border-bottom: none;
border-top: none; border-top: none;

View File

@ -13,11 +13,19 @@ class Admin::ReportsController < Admin::AdminController
end_date = start_date + 1.month end_date = start_date + 1.month
end_date = Time.parse(params[:end_date]) if params[:end_date].present? end_date = Time.parse(params[:end_date]) if params[:end_date].present?
category_id = if params.has_key?(:category_id) if params.has_key?(:category_id) && params[:category_id].to_i > 0
params[:category_id].blank? ? SiteSetting.uncategorized_category_id : params[:category_id].to_i category_id = params[:category_id].to_i
else
category_id = nil
end end
report = Report.find(report_type, start_date: start_date, end_date: end_date, category_id: category_id) if params.has_key?(:group_id) && params[:group_id].to_i > 0
group_id = params[:group_id].to_i
else
group_id = nil
end
report = Report.find(report_type, start_date: start_date, end_date: end_date, category_id: category_id, group_id: group_id)
raise Discourse::NotFound if report.blank? raise Discourse::NotFound if report.blank?

View File

@ -30,7 +30,7 @@ class ExportCsvController < ApplicationController
@_export_params ||= begin @_export_params ||= begin
params.require(:entity) params.require(:entity)
params.require(:entity_type) params.require(:entity_type)
params.permit(:entity, :entity_type, args: [:name, :start_date, :end_date, :category_id]) params.permit(:entity, :entity_type, args: [:name, :start_date, :end_date, :category_id, :group_id])
end end
end end
end end

View File

@ -151,6 +151,7 @@ module Jobs
@extra[:start_date] = @extra[:start_date].to_date if @extra[:start_date].is_a?(String) @extra[:start_date] = @extra[:start_date].to_date if @extra[:start_date].is_a?(String)
@extra[:end_date] = @extra[:end_date].to_date if @extra[:end_date].is_a?(String) @extra[:end_date] = @extra[:end_date].to_date if @extra[:end_date].is_a?(String)
@extra[:category_id] = @extra[:category_id].to_i if @extra[:category_id] @extra[:category_id] = @extra[:category_id].to_i if @extra[:category_id]
@extra[:group_id] = @extra[:group_id].to_i if @extra[:group_id]
r = Report.find(@extra[:name], @extra) r = Report.find(@extra[:name], @extra)
r.data.map do |row| r.data.map do |row|
[row[:x].to_s(:db), row[:y].to_s(:db)] [row[:x].to_s(:db), row[:y].to_s(:db)]

View File

@ -2,7 +2,7 @@ require_dependency 'topic_subtype'
class Report class Report
attr_accessor :type, :data, :total, :prev30Days, :start_date, :end_date, :category_id attr_accessor :type, :data, :total, :prev30Days, :start_date, :end_date, :category_id, :group_id
def self.default_days def self.default_days
30 30
@ -25,6 +25,7 @@ class Report
start_date: start_date, start_date: start_date,
end_date: end_date, end_date: end_date,
category_id: category_id, category_id: category_id,
group_id: group_id,
prev30Days: self.prev30Days prev30Days: self.prev30Days
} }
end end
@ -41,6 +42,7 @@ class Report
report.start_date = opts[:start_date] if opts[:start_date] report.start_date = opts[:start_date] if opts[:start_date]
report.end_date = opts[:end_date] if opts[:end_date] report.end_date = opts[:end_date] if opts[:end_date]
report.category_id = opts[:category_id] if opts[:category_id] report.category_id = opts[:category_id] if opts[:category_id]
report.group_id = opts[:group_id] if opts[:group_id]
report_method = :"report_#{type}" report_method = :"report_#{type}"
if respond_to?(report_method) if respond_to?(report_method)
@ -85,7 +87,8 @@ class Report
def self.report_visits(report) def self.report_visits(report)
basic_report_about report, UserVisit, :by_day, report.start_date, report.end_date basic_report_about report, UserVisit, :by_day, report.start_date, report.end_date, report.group_id
add_counts report, UserVisit, 'visited_at' add_counts report, UserVisit, 'visited_at'
end end
@ -96,13 +99,19 @@ class Report
end end
def self.report_signups(report) def self.report_signups(report)
report_about report, User.real, :count_by_signup_date if report.group_id
basic_report_about report, User.real, :count_by_signup_date, report.start_date, report.end_date, report.group_id
add_counts report, User.real, 'users.created_at'
else
report_about report, User.real, :count_by_signup_date
end
end end
def self.report_profile_views(report) def self.report_profile_views(report)
start_date = report.start_date.to_date start_date = report.start_date.to_date
end_date = report.end_date.to_date end_date = report.end_date.to_date
basic_report_about report, UserProfileView, :profile_views_by_day, start_date, end_date basic_report_about report, UserProfileView, :profile_views_by_day, start_date, end_date, report.group_id
report.total = UserProfile.sum(:views) report.total = UserProfile.sum(:views)
report.prev30Days = UserProfileView.where("viewed_at >= ? AND viewed_at < ?", start_date - 30.days, start_date + 1).count report.prev30Days = UserProfileView.where("viewed_at >= ? AND viewed_at < ?", start_date - 30.days, start_date + 1).count
end end

View File

@ -110,7 +110,7 @@ class User < ActiveRecord::Base
attr_accessor :import_mode attr_accessor :import_mode
# excluding fake users like the system user or anonymous users # excluding fake users like the system user or anonymous users
scope :real, -> { where('id > 0').where('NOT EXISTS( scope :real, -> { where('users.id > 0').where('NOT EXISTS(
SELECT 1 SELECT 1
FROM user_custom_fields ucf FROM user_custom_fields ucf
WHERE WHERE
@ -637,8 +637,14 @@ class User < ActiveRecord::Base
.limit(limit) .limit(limit)
end end
def self.count_by_signup_date(start_date, end_date) def self.count_by_signup_date(start_date, end_date, group_id=nil)
where('created_at >= ? and created_at <= ?', start_date, end_date).group('date(created_at)').order('date(created_at)').count result = where('users.created_at >= ? and users.created_at <= ?', start_date, end_date)
if group_id
result = result.joins("INNER JOIN group_users ON group_users.user_id = users.id")
result = result.where("group_users.group_id = ?", group_id)
end
result.group('date(users.created_at)').order('date(users.created_at)').count
end end

View File

@ -40,8 +40,13 @@ class UserProfileView < ActiveRecord::Base
end end
end end
def self.profile_views_by_day(start_date, end_date) def self.profile_views_by_day(start_date, end_date, group_id=nil)
profile_views = self.where("viewed_at >= ? AND viewed_at < ?", start_date, end_date + 1.day) profile_views = self.where("viewed_at >= ? AND viewed_at < ?", start_date, end_date + 1.day)
if group_id
profile_views = profile_views.joins("INNER JOIN users ON users.id = user_profile_views.user_id")
profile_views = profile_views.joins("INNER JOIN group_users ON group_users.user_id = users.id")
profile_views = profile_views.where("group_users.group_id = ?", group_id)
end
profile_views.group("date(viewed_at)").order("date(viewed_at)").count profile_views.group("date(viewed_at)").order("date(viewed_at)").count
end end
end end

View File

@ -1,16 +1,23 @@
class UserVisit < ActiveRecord::Base class UserVisit < ActiveRecord::Base
def self.counts_by_day_query(start_date, end_date) def self.counts_by_day_query(start_date, end_date, group_id=nil)
where('visited_at >= ? and visited_at <= ?', start_date.to_date, end_date.to_date).group(:visited_at).order(:visited_at) result = where('visited_at >= ? and visited_at <= ?', start_date.to_date, end_date.to_date)
if group_id
result = result.joins("INNER JOIN users ON users.id = user_visits.user_id")
result = result.joins("INNER JOIN group_users ON group_users.user_id = users.id")
result = result.where("group_users.group_id = ?", group_id)
end
result.group(:visited_at).order(:visited_at)
end end
# A count of visits in a date range by day # A count of visits in a date range by day
def self.by_day(start_date, end_date) def self.by_day(start_date, end_date, group_id=nil)
counts_by_day_query(start_date, end_date).count counts_by_day_query(start_date, end_date, group_id).count
end end
def self.mobile_by_day(start_date, end_date) def self.mobile_by_day(start_date, end_date, group_id=nil)
counts_by_day_query(start_date, end_date).where(mobile: true).count counts_by_day_query(start_date, end_date, group_id).where(mobile: true).count
end end
def self.ensure_consistency! def self.ensure_consistency!

View File

@ -1929,6 +1929,7 @@ en:
refresh_report: "Refresh Report" refresh_report: "Refresh Report"
start_date: "Start Date" start_date: "Start Date"
end_date: "End Date" end_date: "End Date"
groups: "All groups"
commits: commits:
latest_changes: "Latest changes: please update often!" latest_changes: "Latest changes: please update often!"