diff --git a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 index bdafff9f0ac..9ceb1b0b093 100644 --- a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 @@ -1,6 +1,7 @@ import { exportEntity } from 'discourse/lib/export-csv'; import { outputExportResult } from 'discourse/lib/export-result'; import Report from 'admin/models/report'; +import computed from 'ember-addons/ember-computed-decorators'; export default Ember.Controller.extend({ viewMode: 'table', @@ -9,22 +10,32 @@ export default Ember.Controller.extend({ startDate: null, endDate: null, categoryId: null, + groupId: null, refreshing: false, - categoryOptions: function() { - var arr = [{name: I18n.t('category.all'), value: 'all'}]; - return arr.concat( Discourse.Site.currentProp('sortedCategories').map(function(i) { return {name: i.get('name'), value: i.get('id') }; }) ); - }.property(), + @computed() + categoryOptions() { + const arr = [{name: I18n.t('category.all'), value: 'all'}]; + 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: { refreshReport() { var q; this.set("refreshing", true); - if (this.get('categoryId') === "all") { - q = Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate")); - } else { - q = Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate"), this.get("categoryId")); - } + + q = Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate"), this.get("categoryId"), this.get("groupId")); 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"), start_date: this.get('startDate'), 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); } } diff --git a/app/assets/javascripts/admin/models/report.js.es6 b/app/assets/javascripts/admin/models/report.js.es6 index b4b5912eec9..1891984b47e 100644 --- a/app/assets/javascripts/admin/models/report.js.es6 +++ b/app/assets/javascripts/admin/models/report.js.es6 @@ -131,12 +131,13 @@ const Report = Discourse.Model.extend({ Report.reopenClass({ - find(type, startDate, endDate, categoryId) { + find(type, startDate, endDate, categoryId, groupId) { return Discourse.ajax("/admin/reports/" + type, { data: { start_date: startDate, end_date: endDate, - category_id: categoryId + category_id: categoryId, + group_id: groupId } }).then(json => { // Add a percent field to each tuple diff --git a/app/assets/javascripts/admin/templates/reports.hbs b/app/assets/javascripts/admin/templates/reports.hbs index 718a61a2c1d..1f5360a8af0 100644 --- a/app/assets/javascripts/admin/templates/reports.hbs +++ b/app/assets/javascripts/admin/templates/reports.hbs @@ -1,9 +1,12 @@

{{model.title}}

-
+
{{i18n 'admin.dashboard.reports.start_date'}} {{input type="date" value=startDate}} {{i18n 'admin.dashboard.reports.end_date'}} {{input type="date" value=endDate}} {{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="exportCsv" label="admin.export_csv.button_text" icon="download"}}
diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 85e9ac30e94..5a5df3b7381 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -38,6 +38,12 @@ $mobile-breakpoint: 700px; .filters input { margin-bottom: 0; } } +.admin-contents .admin-reports-filter { + input[type="date"] { + width: 140px; + } +} + td.flaggers td { border-bottom: none; border-top: none; diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index e4416b13094..f24ee3f5287 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -13,11 +13,19 @@ class Admin::ReportsController < Admin::AdminController end_date = start_date + 1.month end_date = Time.parse(params[:end_date]) if params[:end_date].present? - category_id = if params.has_key?(:category_id) - params[:category_id].blank? ? SiteSetting.uncategorized_category_id : params[:category_id].to_i + if params.has_key?(:category_id) && params[:category_id].to_i > 0 + category_id = params[:category_id].to_i + else + category_id = nil 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? diff --git a/app/controllers/export_csv_controller.rb b/app/controllers/export_csv_controller.rb index f632b93bee6..cbb6399acb3 100644 --- a/app/controllers/export_csv_controller.rb +++ b/app/controllers/export_csv_controller.rb @@ -30,7 +30,7 @@ class ExportCsvController < ApplicationController @_export_params ||= begin params.require(:entity) 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 diff --git a/app/jobs/regular/export_csv_file.rb b/app/jobs/regular/export_csv_file.rb index d32f1a3f782..bacf8c6f700 100644 --- a/app/jobs/regular/export_csv_file.rb +++ b/app/jobs/regular/export_csv_file.rb @@ -151,6 +151,7 @@ module Jobs @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[: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.data.map do |row| [row[:x].to_s(:db), row[:y].to_s(:db)] diff --git a/app/models/report.rb b/app/models/report.rb index 1da7954015e..e5135dcacf2 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -2,7 +2,7 @@ require_dependency 'topic_subtype' 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 30 @@ -25,6 +25,7 @@ class Report start_date: start_date, end_date: end_date, category_id: category_id, + group_id: group_id, prev30Days: self.prev30Days } end @@ -41,6 +42,7 @@ class Report report.start_date = opts[:start_date] if opts[:start_date] report.end_date = opts[:end_date] if opts[:end_date] report.category_id = opts[:category_id] if opts[:category_id] + report.group_id = opts[:group_id] if opts[:group_id] report_method = :"report_#{type}" if respond_to?(report_method) @@ -85,7 +87,8 @@ class 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' end @@ -96,13 +99,19 @@ class Report end 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 def self.report_profile_views(report) start_date = report.start_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.prev30Days = UserProfileView.where("viewed_at >= ? AND viewed_at < ?", start_date - 30.days, start_date + 1).count end diff --git a/app/models/user.rb b/app/models/user.rb index 6a74a641e7a..639846ee265 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -110,7 +110,7 @@ class User < ActiveRecord::Base attr_accessor :import_mode # 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 FROM user_custom_fields ucf WHERE @@ -637,8 +637,14 @@ class User < ActiveRecord::Base .limit(limit) end - def self.count_by_signup_date(start_date, end_date) - where('created_at >= ? and created_at <= ?', start_date, end_date).group('date(created_at)').order('date(created_at)').count + def self.count_by_signup_date(start_date, end_date, group_id=nil) + 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 diff --git a/app/models/user_profile_view.rb b/app/models/user_profile_view.rb index 0a62a426d1c..2fd3cd1c209 100644 --- a/app/models/user_profile_view.rb +++ b/app/models/user_profile_view.rb @@ -40,8 +40,13 @@ class UserProfileView < ActiveRecord::Base 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) + 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 end end diff --git a/app/models/user_visit.rb b/app/models/user_visit.rb index 551361586e1..5ebe059167b 100644 --- a/app/models/user_visit.rb +++ b/app/models/user_visit.rb @@ -1,16 +1,23 @@ class UserVisit < ActiveRecord::Base - def self.counts_by_day_query(start_date, end_date) - where('visited_at >= ? and visited_at <= ?', start_date.to_date, end_date.to_date).group(:visited_at).order(:visited_at) + def self.counts_by_day_query(start_date, end_date, group_id=nil) + 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 # A count of visits in a date range by day - def self.by_day(start_date, end_date) - counts_by_day_query(start_date, end_date).count + def self.by_day(start_date, end_date, group_id=nil) + counts_by_day_query(start_date, end_date, group_id).count end - def self.mobile_by_day(start_date, end_date) - counts_by_day_query(start_date, end_date).where(mobile: true).count + def self.mobile_by_day(start_date, end_date, group_id=nil) + counts_by_day_query(start_date, end_date, group_id).where(mobile: true).count end def self.ensure_consistency! diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 4feee950c7f..d98e1eb65ef 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1929,6 +1929,7 @@ en: refresh_report: "Refresh Report" start_date: "Start Date" end_date: "End Date" + groups: "All groups" commits: latest_changes: "Latest changes: please update often!"