diff --git a/app/assets/javascripts/admin/components/admin-report.js.es6 b/app/assets/javascripts/admin/components/admin-report.js.es6 index c7f03112084..05babdbdf45 100644 --- a/app/assets/javascripts/admin/components/admin-report.js.es6 +++ b/app/assets/javascripts/admin/components/admin-report.js.es6 @@ -1,7 +1,7 @@ import ReportLoader from "discourse/lib/reports-loader"; -import Category from "discourse/models/category"; import { exportEntity } from "discourse/lib/export-csv"; import { outputExportResult } from "discourse/lib/export-result"; +import { isNumeric } from "discourse/lib/utilities"; import { SCHEMA_VERSION, default as Report } from "admin/models/report"; import computed from "ember-addons/ember-computed-decorators"; @@ -50,21 +50,15 @@ export default Ember.Component.extend({ filters: null, startDate: null, endDate: null, - category: null, - groupId: null, - filter: null, showTrend: false, showHeader: true, showTitle: true, showFilteringUI: false, - showCategoryOptions: Ember.computed.alias("model.category_filtering"), showDatesOptions: Ember.computed.alias("model.dates_filtering"), - showGroupOptions: Ember.computed.alias("model.group_filtering"), showExport: Ember.computed.not("model.onlyTable"), showRefresh: Ember.computed.or( - "showCategoryOptions", "showDatesOptions", - "showGroupOptions" + "model.available_filters.length" ), shouldDisplayTrend: Ember.computed.and("showTrend", "model.prev_period"), @@ -74,19 +68,12 @@ export default Ember.Component.extend({ this._reports = []; }, + startDate: Ember.computed.alias("filters.startDate"), + endDate: Ember.computed.alias("filters.endDate"), + didReceiveAttrs() { this._super(...arguments); - const state = this.get("filters") || {}; - - this.setProperties({ - category: Category.findById(state.categoryId), - groupId: state.groupId, - filter: state.filter, - startDate: state.startDate, - endDate: state.endDate - }); - if (this.get("report")) { this._renderReport( this.get("report"), @@ -125,8 +112,6 @@ export default Ember.Component.extend({ return displayedModesLength > 1; }, - categoryId: Ember.computed.alias("category.id"), - @computed("currentMode", "model.modes", "forcedModes") displayedModes(currentMode, reportModes, forcedModes) { const modes = forcedModes ? forcedModes.split(",") : reportModes; @@ -143,35 +128,11 @@ export default Ember.Component.extend({ }); }, - @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("currentMode") modeComponent(currentMode) { return `admin-report-${currentMode}`; }, - @computed("model.filter_options") - filterOptions(options) { - if (options) { - return options.map(option => { - if (option.allowAny) { - option.choices.unshift(I18n.t("admin.dashboard.report_filter_any")); - } - return option; - }); - } - }, - @computed("startDate") normalizedStartDate(startDate) { return startDate && typeof startDate.isValid === "function" @@ -198,25 +159,25 @@ export default Ember.Component.extend({ @computed( "dataSourceName", - "categoryId", - "groupId", - "filter", "normalizedStartDate", - "normalizedEndDate" + "normalizedEndDate", + "filters.customFilters" ) - reportKey(dataSourceName, categoryId, groupId, filter, startDate, endDate) { + reportKey(dataSourceName, startDate, endDate, customFilters) { if (!dataSourceName || !startDate || !endDate) return null; let reportKey = "reports:"; reportKey += [ dataSourceName, - categoryId, startDate.replace(/-/g, ""), endDate.replace(/-/g, ""), - groupId, - filter, "[:prev_period]", this.get("reportOptions.table.limit"), + customFilters + ? JSON.stringify(customFilters, (key, value) => + isNumeric(value) ? value.toString() : value + ) + : null, SCHEMA_VERSION ] .filter(x => x) @@ -227,49 +188,40 @@ export default Ember.Component.extend({ }, actions: { - filter(filterOptionId, value) { - let params = []; - let paramPairs = {}; - let newParams = []; + applyFilter(id, value) { + let customFilters = this.get("filters.customFilters") || {}; - if (this.get("filter")) { - const filter = this.get("filter").slice(1, -1); - params = filter.split("&") || []; - params.map(p => { - const pair = p.split("="); - paramPairs[pair[0]] = pair[1]; - }); + if (typeof value === "undefined") { + delete customFilters[id]; + } else { + customFilters[id] = value; } - paramPairs[filterOptionId] = value; - Object.keys(paramPairs).forEach(key => { - if (paramPairs[key] !== I18n.t("admin.dashboard.report_filter_any")) { - newParams.push(`${key}=${paramPairs[key]}`); - } + this.attrs.onRefresh({ + type: this.get("model.type"), + startDate: this.get("startDate"), + endDate: this.get("endDate"), + filters: customFilters }); - - this.set("filter", `[${newParams.join("&")}]`); }, refreshReport() { this.attrs.onRefresh({ - categoryId: this.get("categoryId"), - groupId: this.get("groupId"), - filter: this.get("filter"), startDate: this.get("startDate"), - endDate: this.get("endDate") + endDate: this.get("endDate"), + filters: this.get("filters.customFilters") }); }, exportCsv() { + const customFilters = this.get("filters.customFilters"); + exportEntity("report", { 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"), - group_id: - this.get("groupId") === "all" ? undefined : this.get("groupId") + startDate: this.get("startDate"), + endDate: this.get("endDate"), + category_id: customFilters.category, + group_id: customFilters.group }).then(outputExportResult); }, @@ -383,22 +335,14 @@ export default Ember.Component.extend({ .toISOString(); } - if (this.get("groupId") && this.get("groupId") !== "all") { - payload.data.group_id = this.get("groupId"); - } - - if (this.get("categoryId") && this.get("categoryId") !== "all") { - payload.data.category_id = this.get("categoryId"); - } - - if (this.get("filter") && this.get("filter") !== "all") { - payload.data.filter = this.get("filter"); - } - if (this.get("reportOptions.table.limit")) { payload.data.limit = this.get("reportOptions.table.limit"); } + if (this.get("filters.customFilters")) { + payload.data.filters = this.get("filters.customFilters"); + } + return payload; }, @@ -443,8 +387,8 @@ export default Ember.Component.extend({ Report.fillMissingDates(jsonReport, { filledField: "prevChartData", dataField: "prev_data", - starDate: jsonReport.prev_start_date, - endDate: jsonReport.prev_end_date + starDate: jsonReport.prev_startDate, + endDate: jsonReport.prev_endDate }); if (jsonReport.prevChartData && jsonReport.prevChartData.length > 40) { diff --git a/app/assets/javascripts/admin/components/report-filters/category.js.es6 b/app/assets/javascripts/admin/components/report-filters/category.js.es6 new file mode 100644 index 00000000000..d1c910ad18d --- /dev/null +++ b/app/assets/javascripts/admin/components/report-filters/category.js.es6 @@ -0,0 +1,14 @@ +import Category from "discourse/models/category"; +import { default as computed } from "ember-addons/ember-computed-decorators"; +import FilterComponent from "admin/components/report-filters/filter"; + +export default FilterComponent.extend({ + classNames: ["category-filter"], + + layoutName: "admin/templates/components/report-filters/category", + + @computed("filter.default") + category(categoryId) { + return Category.findById(categoryId); + } +}); diff --git a/app/assets/javascripts/admin/components/report-filters/file-extension.js.es6 b/app/assets/javascripts/admin/components/report-filters/file-extension.js.es6 new file mode 100644 index 00000000000..d8eb1b5b062 --- /dev/null +++ b/app/assets/javascripts/admin/components/report-filters/file-extension.js.es6 @@ -0,0 +1,7 @@ +import FilterComponent from "admin/components/report-filters/filter"; + +export default FilterComponent.extend({ + classNames: ["file-extension-filter"], + + layoutName: "admin/templates/components/report-filters/file-extension" +}); diff --git a/app/assets/javascripts/admin/components/report-filters/filter.js.es6 b/app/assets/javascripts/admin/components/report-filters/filter.js.es6 new file mode 100644 index 00000000000..25f2464f951 --- /dev/null +++ b/app/assets/javascripts/admin/components/report-filters/filter.js.es6 @@ -0,0 +1,7 @@ +export default Ember.Component.extend({ + actions: { + onChange(value) { + this.applyFilter(this.get("filter.id"), value); + } + } +}); diff --git a/app/assets/javascripts/admin/components/report-filters/group.js.es6 b/app/assets/javascripts/admin/components/report-filters/group.js.es6 new file mode 100644 index 00000000000..54523f9446e --- /dev/null +++ b/app/assets/javascripts/admin/components/report-filters/group.js.es6 @@ -0,0 +1,20 @@ +import FilterComponent from "admin/components/report-filters/filter"; +import { default as computed } from "ember-addons/ember-computed-decorators"; + +export default FilterComponent.extend({ + classNames: ["group-filter"], + + layoutName: "admin/templates/components/report-filters/group", + + @computed() + groupOptions() { + return (this.site.groups || []).map(group => { + return { name: group["name"], value: group["id"] }; + }); + }, + + @computed("filter.default") + groupId(filterDefault) { + return filterDefault ? parseInt(filterDefault, 10) : null; + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-reports-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-reports-show.js.es6 index 8c773181ed9..2a78ece7f41 100644 --- a/app/assets/javascripts/admin/controllers/admin-reports-show.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-reports-show.js.es6 @@ -1,7 +1,10 @@ import computed from "ember-addons/ember-computed-decorators"; export default Ember.Controller.extend({ - queryParams: ["start_date", "end_date", "category_id", "group_id", "filter"], + queryParams: ["start_date", "end_date", "filters"], + start_date: null, + end_date: null, + filters: null, @computed("model.type") reportOptions(type) { @@ -12,28 +15,5 @@ export default Ember.Controller.extend({ } return options; - }, - - @computed("category_id", "group_id", "start_date", "end_date", "filter") - filters(categoryId, groupId, startDate, endDate, filter) { - return { - categoryId, - groupId, - filter, - startDate, - endDate - }; - }, - - actions: { - onParamsChange(params) { - this.setProperties({ - start_date: params.startDate, - filter: params.filter, - category_id: params.categoryId, - group_id: params.groupId, - end_date: params.endDate - }); - } } }); diff --git a/app/assets/javascripts/admin/models/report.js.es6 b/app/assets/javascripts/admin/models/report.js.es6 index 19a1579e5db..0f030b509ba 100644 --- a/app/assets/javascripts/admin/models/report.js.es6 +++ b/app/assets/javascripts/admin/models/report.js.es6 @@ -8,7 +8,7 @@ import { renderAvatar } from "discourse/helpers/user-avatar"; // Change this line each time report format change // and you want to ensure cache is reset -export const SCHEMA_VERSION = 3; +export const SCHEMA_VERSION = 4; const Report = Discourse.Model.extend({ average: false, diff --git a/app/assets/javascripts/admin/routes/admin-reports-show.js.es6 b/app/assets/javascripts/admin/routes/admin-reports-show.js.es6 index 55503091e13..9bf9ff8ebaf 100644 --- a/app/assets/javascripts/admin/routes/admin-reports-show.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-reports-show.js.es6 @@ -1,27 +1,65 @@ export default Discourse.Route.extend({ - setupController(controller) { - this._super(...arguments); + queryParams: { + start_date: { refreshModel: true }, + end_date: { refreshModel: true }, + filters: { refreshModel: true } + }, - if (!controller.get("start_date")) { - controller.set( - "start_date", - moment - .utc() - .subtract(1, "day") - .subtract(1, "month") - .startOf("day") - .format("YYYY-MM-DD") - ); + model(params) { + params.customFilters = params.filters; + delete params.filters; + + params.startDate = + params.start_date || + moment + .utc() + .subtract(1, "day") + .subtract(1, "month") + .startOf("day") + .format("YYYY-MM-DD"); + delete params.start_date; + + params.endDate = + params.end_date || + moment + .utc() + .endOf("day") + .format("YYYY-MM-DD"); + delete params.end_date; + + return params; + }, + + deserializeQueryParam(value, urlKey, defaultValueType) { + if (urlKey === "filters") { + return JSON.parse(decodeURIComponent(value)); } - if (!controller.get("end_date")) { - controller.set( - "end_date", - moment() - .utc() - .endOf("day") - .format("YYYY-MM-DD") - ); + return this._super(value, urlKey, defaultValueType); + }, + + serializeQueryParam(value, urlKey, defaultValueType) { + if (urlKey === "filters") { + if (value && Object.keys(value).length > 0) { + return JSON.stringify(value); + } else { + return null; + } + } + + return this._super(value, urlKey, defaultValueType); + }, + + actions: { + onParamsChange(params) { + const queryParams = { + type: params.type, + start_date: params.startDate, + filters: params.filters, + end_date: params.endDate + }; + + this.transitionTo("adminReports.show", { queryParams }); } } }); diff --git a/app/assets/javascripts/admin/templates/components/admin-report.hbs b/app/assets/javascripts/admin/templates/components/admin-report.hbs index 888be8dd22d..7181ac3dd11 100644 --- a/app/assets/javascripts/admin/templates/components/admin-report.hbs +++ b/app/assets/javascripts/admin/templates/components/admin-report.hbs @@ -149,38 +149,17 @@ {{/if}} - {{#if showCategoryOptions}} + {{#each model.available_filters as |filter|}}
-
- {{search-advanced-category-chooser - filterable=true - value=category - castInteger=true}} -
-
- {{/if}} + + {{i18n (concat "admin.dashboard.reports.filters." filter.id ".label")}} + - {{#if showGroupOptions}} -
- {{combo-box - castInteger=true - filterable=true - valueAttribute="value" - content=groupOptions - value=groupId}} -
-
- {{/if}} - - {{#each filterOptions as |filterOption|}} -
-
- {{combo-box content=filterOption.choices - filterable=true - allowAny=true - value=filterOption.selected - onSelect=(action "filter" filterOption.id)}} + {{component + (concat "report-filters/" filter.id) + filter=filter + applyFilter=(action "applyFilter")}}
{{/each}} diff --git a/app/assets/javascripts/admin/templates/components/report-filters/category.hbs b/app/assets/javascripts/admin/templates/components/report-filters/category.hbs new file mode 100644 index 00000000000..9ad96c6be60 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/report-filters/category.hbs @@ -0,0 +1,6 @@ +{{search-advanced-category-chooser + filterable=true + value=category + castInteger=true + onSelectNone=(action "onChange") + onSelect=(action "onChange")}} diff --git a/app/assets/javascripts/admin/templates/components/report-filters/file-extension.hbs b/app/assets/javascripts/admin/templates/components/report-filters/file-extension.hbs new file mode 100644 index 00000000000..871179d63d6 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/report-filters/file-extension.hbs @@ -0,0 +1,8 @@ +{{combo-box + content=filter.choices + filterable=true + allowAny=filter.allow_any + value=filter.default + none="admin.dashboard.report_filter_any" + onSelectNone=(action "onChange") + onSelect=(action "onChange")}} diff --git a/app/assets/javascripts/admin/templates/components/report-filters/group.hbs b/app/assets/javascripts/admin/templates/components/report-filters/group.hbs new file mode 100644 index 00000000000..0209c1cb6e8 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/report-filters/group.hbs @@ -0,0 +1,10 @@ +{{combo-box + castInteger=true + filterable=true + valueAttribute="value" + content=groupOptions + value=groupId + allowAny=filter.allow_any + none="admin.dashboard.reports.groups" + onSelectNone=(action "onChange") + onSelect=(action "onChange")}} diff --git a/app/assets/javascripts/admin/templates/reports-show.hbs b/app/assets/javascripts/admin/templates/reports-show.hbs index 899a8ebd9e7..dac1aa0bbe0 100644 --- a/app/assets/javascripts/admin/templates/reports-show.hbs +++ b/app/assets/javascripts/admin/templates/reports-show.hbs @@ -1,7 +1,7 @@ {{admin-report showAllReportsLink=true dataSourceName=model.type - filters=filters + filters=model reportOptions=reportOptions showFilteringUI=true - onRefresh=(action "onParamsChange")}} + onRefresh=(route-action "onParamsChange")}} diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index fa5a6e374df..8143d099f7b 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -91,18 +91,6 @@ class Admin::ReportsController < Admin::AdminController start_date = (report_params[:start_date].present? ? Time.parse(report_params[:start_date]).to_date : 1.days.ago).beginning_of_day end_date = (report_params[:end_date].present? ? Time.parse(report_params[:end_date]).to_date : start_date + 30.days).end_of_day - if report_params.has_key?(:category_id) && report_params[:category_id].to_i > 0 - category_id = report_params[:category_id].to_i - else - category_id = nil - end - - if report_params.has_key?(:group_id) && report_params[:group_id].to_i > 0 - group_id = report_params[:group_id].to_i - else - group_id = nil - end - facets = nil if Array === report_params[:facets] facets = report_params[:facets].map { |s| s.to_s.to_sym } @@ -113,17 +101,15 @@ class Admin::ReportsController < Admin::AdminController limit = report_params[:limit].to_i end - filter = nil - if report_params.has_key?(:filter) - filter = report_params[:filter] + filters = nil + if report_params.has_key?(:filters) + filters = report_params[:filters] end { start_date: start_date, end_date: end_date, - category_id: category_id, - group_id: group_id, - filter: filter, + filters: filters, facets: facets, limit: limit } diff --git a/app/models/report.rb b/app/models/report.rb index 67bcb26d82f..0fb79f2f3e2 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -3,14 +3,13 @@ require_dependency 'topic_subtype' class Report # Change this line each time report format change # and you want to ensure cache is reset - SCHEMA_VERSION = 3 + SCHEMA_VERSION = 4 attr_accessor :type, :data, :total, :prev30Days, :start_date, - :end_date, :category_id, :group_id, :filter, - :labels, :async, :prev_period, :facets, :limit, :processing, :average, :percent, - :higher_is_better, :icon, :modes, :category_filtering, - :group_filtering, :prev_data, :prev_start_date, :prev_end_date, - :dates_filtering, :error, :primary_color, :secondary_color, :filter_options + :end_date, :labels, :prev_period, :facets, :limit, :average, + :percent, :higher_is_better, :icon, :modes, :prev_data, + :prev_start_date, :prev_end_date, :dates_filtering, :error, + :primary_color, :secondary_color, :filters, :available_filters def self.default_days 30 @@ -24,13 +23,11 @@ class Report @average = false @percent = false @higher_is_better = true - @category_filtering = false - @group_filtering = false @modes = [:table, :chart] @prev_data = nil @dates_filtering = true - @filter_options = nil - @filter = nil + @available_filters = {} + @filters = {} tertiary = ColorScheme.hex_for_name('tertiary') || '0088cc' @primary_color = rgba_color(tertiary) @@ -41,17 +38,24 @@ class Report (+"reports:") << [ report.type, - report.category_id, report.start_date.to_date.strftime("%Y%m%d"), report.end_date.to_date.strftime("%Y%m%d"), - report.group_id, - report.filter, report.facets, report.limit, + report.filters.blank? ? nil : MultiJson.dump(report.filters), SCHEMA_VERSION, ].compact.map(&:to_s).join(':') end + def add_filter(name, options = {}) + default_filter = { allow_any: false, choices: [], default: nil } + available_filters[name] = default_filter.merge(options) + end + + def remove_filter(name) + available_filters.delete(name) + end + def self.clear_cache(type = nil) pattern = type ? "reports:#{type}:*" : "reports:*" @@ -76,13 +80,6 @@ class Report self.start_date end - def filter_values - if self.filter.present? - return self.filter.delete_prefix("[").delete_suffix("]").split("&").map { |param| param.split("=") }.to_h - end - {} - end - def as_json(options = nil) description = I18n.t("reports.#{type}.description", default: "") { @@ -97,14 +94,12 @@ class Report prev_data: self.prev_data, prev_start_date: prev_start_date&.iso8601, prev_end_date: prev_end_date&.iso8601, - category_id: category_id, - group_id: group_id, - filter: self.filter, prev30Days: self.prev30Days, dates_filtering: self.dates_filtering, report_key: Report.cache_key(self), primary_color: self.primary_color, secondary_color: self.secondary_color, + available_filters: self.available_filters.map { |k, v| { id: k }.merge(v) }, labels: labels || [ { type: :date, @@ -117,13 +112,9 @@ class Report title: I18n.t("reports.default.labels.count") }, ], - processing: self.processing, average: self.average, percent: self.percent, higher_is_better: self.higher_is_better, - category_filtering: self.category_filtering, - group_filtering: self.group_filtering, - filter_options: self.filter_options, modes: self.modes, }.tap do |json| json[:icon] = self.icon if self.icon @@ -150,15 +141,12 @@ class Report report = Report.new(type) 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.filter = opts[:filter] if opts[:filter] report.facets = opts[:facets] || [:total, :prev30Days] report.limit = opts[:limit] if opts[:limit] - report.processing = false report.average = opts[:average] if opts[:average] report.percent = opts[:percent] if opts[:percent] - report.higher_is_better = opts[:higher_is_better] if opts[:higher_is_better] + report.filters = opts[:filters] if opts[:filters] + report end @@ -192,7 +180,6 @@ class Report report.error = :timeout end rescue Exception => e - # In test mode, don't swallow exceptions by default to help debug errors. raise if Rails.env.test? && !opts[:wrap_exceptions_in_test] @@ -288,12 +275,15 @@ class Report end def self.post_action_report(report, post_action_type) + category_filter = report.filters.dig(:category) + report.add_filter('category', default: category_filter) + report.data = [] - PostAction.count_per_day_for_type(post_action_type, category_id: report.category_id, start_date: report.start_date, end_date: report.end_date).each do |date, count| + PostAction.count_per_day_for_type(post_action_type, category_id: category_filter, start_date: report.start_date, end_date: report.end_date).each do |date, count| report.data << { x: date, y: count } end countable = PostAction.unscoped.where(post_action_type_id: post_action_type) - countable = countable.joins(post: :topic).merge(Topic.in_category_and_subcategories(report.category_id)) if report.category_id + countable = countable.joins(post: :topic).merge(Topic.in_category_and_subcategories(category_filter)) if category_filter add_counts report, countable, 'post_actions.created_at' end diff --git a/app/models/reports/bookmarks.rb b/app/models/reports/bookmarks.rb index 1752b560ccb..2a98071c3fa 100644 --- a/app/models/reports/bookmarks.rb +++ b/app/models/reports/bookmarks.rb @@ -1,5 +1,5 @@ -Report.add_report("bookmarks") do |report| - report.category_filtering = true +Report.add_report('bookmarks') do |report| report.icon = 'bookmark' + post_action_report report, PostActionType.types[:bookmark] end diff --git a/app/models/reports/flags.rb b/app/models/reports/flags.rb index 8d66da9fd0f..0241089b16f 100644 --- a/app/models/reports/flags.rb +++ b/app/models/reports/flags.rb @@ -1,5 +1,7 @@ -Report.add_report("flags") do |report| - report.category_filtering = true +Report.add_report('flags') do |report| + category_filter = report.filters.dig(:category) + report.add_filter('category', default: category_filter) + report.icon = 'flag' report.higher_is_better = false @@ -9,11 +11,14 @@ Report.add_report("flags") do |report| :count_by_date, report.start_date, report.end_date, - report.category_id + category_filter ) countable = ReviewableFlaggedPost.scores_with_topics - countable.merge!(Topic.in_category_and_subcategories(report.category_id)) if report.category_id + + if category_filter + countable.merge!(Topic.in_category_and_subcategories(category_filter)) + end add_counts report, countable, 'reviewable_scores.created_at' end diff --git a/app/models/reports/likes.rb b/app/models/reports/likes.rb index 0212da61e0d..37f7ea3176e 100644 --- a/app/models/reports/likes.rb +++ b/app/models/reports/likes.rb @@ -1,5 +1,5 @@ Report.add_report("likes") do |report| - report.category_filtering = true report.icon = 'heart' + post_action_report report, PostActionType.types[:like] end diff --git a/app/models/reports/post_edits.rb b/app/models/reports/post_edits.rb index a708f4345af..43c039cc0c7 100644 --- a/app/models/reports/post_edits.rb +++ b/app/models/reports/post_edits.rb @@ -1,5 +1,7 @@ -Report.add_report("post_edits") do |report| - report.category_filtering = true +Report.add_report('post_edits') do |report| + category_filter = report.filters.dig(:category) + report.add_filter('category', default: category_filter) + report.modes = [:table] report.labels = [ @@ -77,14 +79,14 @@ Report.add_report("post_edits") do |report| ON u.id = p.user_id SQL - if report.category_id + if category_filter sql += <<~SQL JOIN topics t ON t.id = p.topic_id WHERE t.category_id = ? OR t.category_id IN (SELECT id FROM categories WHERE categories.parent_category_id = ?) SQL end - result = report.category_id ? DB.query(sql, report.category_id, report.category_id) : DB.query(sql) + result = category_filter ? DB.query(sql, category_filter, category_filter) : DB.query(sql) result.each do |r| revision = {} diff --git a/app/models/reports/posts.rb b/app/models/reports/posts.rb index 7e7e1f59c0c..51ecdcd8b9a 100644 --- a/app/models/reports/posts.rb +++ b/app/models/reports/posts.rb @@ -1,10 +1,14 @@ -Report.add_report("posts") do |report| +Report.add_report('posts') do |report| report.modes = [:table, :chart] - report.category_filtering = true - basic_report_about report, Post, :public_posts_count_per_day, report.start_date, report.end_date, report.category_id + + category_filter = report.filters.dig(:category) + report.add_filter('category', default: category_filter) + + basic_report_about report, Post, :public_posts_count_per_day, report.start_date, report.end_date, category_filter + countable = Post.public_posts.where(post_type: Post.types[:regular]) - if report.category_id - countable = countable.joins(:topic).merge(Topic.in_category_and_subcategories(report.category_id)) + if category_filter + countable = countable.joins(:topic).merge(Topic.in_category_and_subcategories(category_filter)) end add_counts report, countable, 'posts.created_at' end diff --git a/app/models/reports/profile_views.rb b/app/models/reports/profile_views.rb index a9123da0091..029740d71ec 100644 --- a/app/models/reports/profile_views.rb +++ b/app/models/reports/profile_views.rb @@ -1,9 +1,11 @@ -Report.add_report("profile_views") do |report| - report.group_filtering = true +Report.add_report('profile_views') do |report| + group_filter = report.filters.dig(:group) + report.add_filter('group', default: group_filter) + start_date = report.start_date end_date = report.end_date - basic_report_about report, UserProfileView, :profile_views_by_day, start_date, end_date, report.group_id + basic_report_about report, UserProfileView, :profile_views_by_day, start_date, end_date, group_filter 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 diff --git a/app/models/reports/signups.rb b/app/models/reports/signups.rb index e9646cd8c63..0531557325e 100644 --- a/app/models/reports/signups.rb +++ b/app/models/reports/signups.rb @@ -1,14 +1,13 @@ -Report.add_report("signups") do |report| - report.group_filtering = true - +Report.add_report('signups') do |report| report.icon = 'user-plus' - if report.group_id - basic_report_about report, User.real, :count_by_signup_date, report.start_date, report.end_date, report.group_id + group_filter = report.filters.dig(:group) + report.add_filter('group', default: group_filter) + + if group_filter + basic_report_about report, User.real, :count_by_signup_date, report.start_date, report.end_date, group_filter add_counts report, User.real, 'users.created_at' else report_about report, User.real, :count_by_signup_date end - - # add_prev_data report, User.real, :count_by_signup_date, report.prev_start_date, report.prev_end_date end diff --git a/app/models/reports/time_to_first_response.rb b/app/models/reports/time_to_first_response.rb index 33342b77b5b..d5c9ebf298a 100644 --- a/app/models/reports/time_to_first_response.rb +++ b/app/models/reports/time_to_first_response.rb @@ -1,11 +1,16 @@ -Report.add_report("time_to_first_response") do |report| - report.category_filtering = true +Report.add_report('time_to_first_response') do |report| + category_filter = report.filters.dig(:category) + report.add_filter('category', default: category_filter) + report.icon = 'reply' report.higher_is_better = false report.data = [] - Topic.time_to_first_response_per_day(report.start_date, report.end_date, category_id: report.category_id).each do |r| - report.data << { x: r["date"], y: r["hours"].to_f.round(2) } + + Topic.time_to_first_response_per_day(report.start_date, report.end_date, category_id: category_filter).each do |r| + report.data << { x: r['date'], y: r['hours'].to_f.round(2) } end - report.total = Topic.time_to_first_response_total(category_id: report.category_id) - report.prev30Days = Topic.time_to_first_response_total(start_date: report.start_date - 30.days, end_date: report.start_date, category_id: report.category_id) + + report.total = Topic.time_to_first_response_total(category_id: category_filter) + + report.prev30Days = Topic.time_to_first_response_total(start_date: report.start_date - 30.days, end_date: report.start_date, category_id: category_filter) end diff --git a/app/models/reports/top_referred_topics.rb b/app/models/reports/top_referred_topics.rb index 635aab136ce..a818215ff86 100644 --- a/app/models/reports/top_referred_topics.rb +++ b/app/models/reports/top_referred_topics.rb @@ -1,5 +1,6 @@ -Report.add_report("top_referred_topics") do |report| - report.category_filtering = true +Report.add_report('top_referred_topics') do |report| + category_filter = report.filters.dig(:category) + report.add_filter('category', default: category_filter) report.modes = [:table] @@ -10,12 +11,12 @@ Report.add_report("top_referred_topics") do |report| title: :topic_title, id: :topic_id }, - title: I18n.t("reports.top_referred_topics.labels.topic") + title: I18n.t('reports.top_referred_topics.labels.topic') }, { property: :num_clicks, type: :number, - title: I18n.t("reports.top_referred_topics.labels.num_clicks") + title: I18n.t('reports.top_referred_topics.labels.num_clicks') } ] @@ -23,7 +24,7 @@ Report.add_report("top_referred_topics") do |report| end_date: report.end_date, start_date: report.start_date, limit: report.limit || 8, - category_id: report.category_id + category_id: category_filter } result = nil result = IncomingLinksReport.find(:top_referred_topics, options) diff --git a/app/models/reports/top_traffic_sources.rb b/app/models/reports/top_traffic_sources.rb index 45c0ee91461..90fcb92ce5a 100644 --- a/app/models/reports/top_traffic_sources.rb +++ b/app/models/reports/top_traffic_sources.rb @@ -1,22 +1,23 @@ -Report.add_report("top_traffic_sources") do |report| - report.category_filtering = true +Report.add_report('top_traffic_sources') do |report| + category_filter = report.filters.dig(:category) + report.add_filter('category', default: category_filter) report.modes = [:table] report.labels = [ { property: :domain, - title: I18n.t("reports.top_traffic_sources.labels.domain") + title: I18n.t('reports.top_traffic_sources.labels.domain') }, { property: :num_clicks, type: :number, - title: I18n.t("reports.top_traffic_sources.labels.num_clicks") + title: I18n.t('reports.top_traffic_sources.labels.num_clicks') }, { property: :num_topics, type: :number, - title: I18n.t("reports.top_traffic_sources.labels.num_topics") + title: I18n.t('reports.top_traffic_sources.labels.num_topics') } ] @@ -24,7 +25,7 @@ Report.add_report("top_traffic_sources") do |report| end_date: report.end_date, start_date: report.start_date, limit: report.limit || 8, - category_id: report.category_id + category_id: category_filter } result = IncomingLinksReport.find(:top_traffic_sources, options) diff --git a/app/models/reports/top_uploads.rb b/app/models/reports/top_uploads.rb index f52c9e41a50..c27790a3775 100644 --- a/app/models/reports/top_uploads.rb +++ b/app/models/reports/top_uploads.rb @@ -1,14 +1,13 @@ -Report.add_report("top_uploads") do |report| +Report.add_report('top_uploads') do |report| report.modes = [:table] - report.filter_options = [ - { - id: "file-extension", - selected: report.filter_values.fetch("file-extension", "any"), - choices: (SiteSetting.authorized_extensions.split("|") + report.filter_values.values).uniq, - allowAny: true - } - ] + extension_filter = report.filters.dig(:"file-extension") + report.add_filter('file-extension', + default: extension_filter || 'any', + choices: ( + SiteSetting.authorized_extensions.split('|') + Array(extension_filter) + ).uniq + ) report.labels = [ { @@ -59,12 +58,15 @@ Report.add_report("top_uploads") do |report| LIMIT #{report.limit || 250} SQL - extension_filter = report.filter_values["file-extension"] builder = DB.build(sql) builder.where("up.id > :seeded_id_threshold", seeded_id_threshold: Upload::SEEDED_ID_THRESHOLD) builder.where("up.created_at >= :start_date", start_date: report.start_date) builder.where("up.created_at < :end_date", end_date: report.end_date) - builder.where("up.extension = :extension", extension: extension_filter) if extension_filter.present? + + if extension_filter + builder.where("up.extension = :extension", extension: extension_filter) + end + builder.query.each do |row| data = {} data[:author_id] = row.user_id diff --git a/app/models/reports/topics.rb b/app/models/reports/topics.rb index 851700f18a8..6994ae0f12b 100644 --- a/app/models/reports/topics.rb +++ b/app/models/reports/topics.rb @@ -1,7 +1,12 @@ -Report.add_report("topics") do |report| - report.category_filtering = true - basic_report_about report, Topic, :listable_count_per_day, report.start_date, report.end_date, report.category_id +Report.add_report('topics') do |report| + category_filter = report.filters.dig(:category) + report.add_filter('category', default: category_filter) + + basic_report_about report, Topic, :listable_count_per_day, report.start_date, report.end_date, category_filter + countable = Topic.listable_topics - countable = countable.in_category_and_subcategories(report.category_id) if report.category_id + if category_filter + countable = countable.in_category_and_subcategories(category_filter) + end add_counts report, countable, 'topics.created_at' end diff --git a/app/models/reports/topics_with_no_response.rb b/app/models/reports/topics_with_no_response.rb index 2b1d434db83..123da140b0e 100644 --- a/app/models/reports/topics_with_no_response.rb +++ b/app/models/reports/topics_with_no_response.rb @@ -1,9 +1,13 @@ -Report.add_report("topics_with_no_response") do |report| - report.category_filtering = true +Report.add_report('topics_with_no_response') do |report| + category_filter = report.filters.dig(:category) + report.add_filter('category', default: category_filter) + report.data = [] - Topic.with_no_response_per_day(report.start_date, report.end_date, report.category_id).each do |r| - report.data << { x: r["date"], y: r["count"].to_i } + Topic.with_no_response_per_day(report.start_date, report.end_date, category_filter).each do |r| + report.data << { x: r['date'], y: r['count'].to_i } end - report.total = Topic.with_no_response_total(category_id: report.category_id) - report.prev30Days = Topic.with_no_response_total(start_date: report.start_date - 30.days, end_date: report.start_date, category_id: report.category_id) + + report.total = Topic.with_no_response_total(category_id: category_filter) + + report.prev30Days = Topic.with_no_response_total(start_date: report.start_date - 30.days, end_date: report.start_date, category_id: category_filter) end diff --git a/app/models/reports/visits.rb b/app/models/reports/visits.rb index 4d95537c194..9e38b032b25 100644 --- a/app/models/reports/visits.rb +++ b/app/models/reports/visits.rb @@ -1,9 +1,11 @@ -Report.add_report("visits") do |report| - report.group_filtering = true +Report.add_report('visits') do |report| + group_filter = report.filters.dig(:group) + report.add_filter('group', default: group_filter) + report.icon = 'user' - basic_report_about report, UserVisit, :by_day, report.start_date, report.end_date, report.group_id + basic_report_about report, UserVisit, :by_day, report.start_date, report.end_date, group_filter add_counts report, UserVisit, 'visited_at' - report.prev30Days = UserVisit.where("visited_at >= ? and visited_at < ?", report.start_date - 30.days, report.start_date).count + report.prev30Days = UserVisit.where('visited_at >= ? and visited_at < ?', report.start_date - 30.days, report.start_date).count end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 859b845e850..6d217d85c46 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -3110,6 +3110,13 @@ en: trending_search: more: 'Search logs' disabled: 'Trending search report is disabled. Enable log search queries to collect data.' + filters: + file-extension: + label: File extension + group: + label: Group + category: + label: Category commits: latest_changes: "Latest changes: please update often!" diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb index 4efcbead189..6843ac9af55 100644 --- a/spec/models/report_spec.rb +++ b/spec/models/report_spec.rb @@ -20,7 +20,6 @@ describe Report do end shared_examples 'category filtering on subcategories' do - it 'returns the filtered data' do expect(report.total).to eq(1) end @@ -753,12 +752,12 @@ describe Report do end context "with category filtering" do - let(:report) { Report.find('flags', category_id: c1.id) } + let(:report) { Report.find('flags', filters: { category: c1.id }) } include_examples 'category filtering' context "on subcategories" do - let(:report) { Report.find('flags', category_id: c0.id) } + let(:report) { Report.find('flags', filters: { category: c0.id }) } include_examples 'category filtering on subcategories' end @@ -782,12 +781,12 @@ describe Report do end context "with category filtering" do - let(:report) { Report.find('topics', category_id: c1.id) } + let(:report) { Report.find('topics', filters: { category: c1.id }) } include_examples 'category filtering' context "on subcategories" do - let(:report) { Report.find('topics', category_id: c0.id) } + let(:report) { Report.find('topics', filters: { category: c0.id }) } include_examples 'category filtering on subcategories' end @@ -872,12 +871,12 @@ describe Report do end context "with category filtering" do - let(:report) { Report.find('posts', category_id: c1.id) } + let(:report) { Report.find('posts', filters: { category: c1.id }) } include_examples 'category filtering' context "on subcategories" do - let(:report) { Report.find('posts', category_id: c0.id) } + let(:report) { Report.find('posts', filters: { category: c0.id }) } include_examples 'category filtering on subcategories' end @@ -903,12 +902,12 @@ describe Report do end context "with category filtering" do - let(:report) { Report.find('topics_with_no_response', category_id: c1.id) } + let(:report) { Report.find('topics_with_no_response', filters: { category: c1.id }) } include_examples 'category filtering' context "on subcategories" do - let(:report) { Report.find('topics_with_no_response', category_id: c0.id) } + let(:report) { Report.find('topics_with_no_response', filters: { category: c0.id }) } include_examples 'category filtering on subcategories' end @@ -939,12 +938,12 @@ describe Report do end context "with category filtering" do - let(:report) { Report.find('likes', category_id: c1.id) } + let(:report) { Report.find('likes', filters: { category: c1.id }) } include_examples 'category filtering' context "on subcategories" do - let(:report) { Report.find('likes', category_id: c0.id) } + let(:report) { Report.find('likes', filters: { category: c0.id }) } include_examples 'category filtering on subcategories' end diff --git a/test/javascripts/acceptance/dashboard-test.js.es6 b/test/javascripts/acceptance/dashboard-test.js.es6 index 3a9f7011746..1a7675dac9a 100644 --- a/test/javascripts/acceptance/dashboard-test.js.es6 +++ b/test/javascripts/acceptance/dashboard-test.js.es6 @@ -4,6 +4,18 @@ acceptance("Dashboard", { loggedIn: true, settings: { dashboard_general_tab_activity_metrics: "page_view_total_reqs" + }, + site: { + groups: [ + { + id: 88, + name: "tl1" + }, + { + id: 89, + name: "tl2" + } + ] } }); @@ -86,3 +98,17 @@ QUnit.test("reports tab", async assert => { "filter is case insensitive" ); }); + +QUnit.test("report filters", async assert => { + await visit( + '/admin/reports/signups?end_date=2018-07-16&filters=%7B"group"%3A88%7D&start_date=2018-06-16' + ); + + const groupFilter = selectKit(".group-filter .combo-box"); + + assert.equal( + groupFilter.header().value(), + 88, + "its set the value of the filter from the query params" + ); +}); diff --git a/test/javascripts/fixtures/reports_bulk.js.es6 b/test/javascripts/fixtures/reports_bulk.js.es6 index 2e581278e0f..d12eb9377ab 100644 --- a/test/javascripts/fixtures/reports_bulk.js.es6 +++ b/test/javascripts/fixtures/reports_bulk.js.es6 @@ -57,21 +57,20 @@ let signups = { ], prev_start_date: "2018-05-17T00:00:00Z", prev_end_date: "2018-06-17T00:00:00Z", - category_id: null, - group_id: null, prev30Days: null, dates_filtering: true, - report_key: "reports:signups::20180616:20180716::[:prev_period]:", + report_key: + 'reports:signups:20180616:20180716:[:prev_period]:50:{"group":"88"}:4', + available_filters: [ + { id: "group", allow_any: false, choices: [], default: "88" } + ], labels: [ { type: "date", properties: ["x"], title: "Day" }, { type: "number", properties: ["y"], title: "Count" } ], - processing: false, average: false, percent: false, higher_is_better: true, - category_filtering: false, - group_filtering: true, modes: ["table", "chart"], prev_period: 961 }; @@ -158,8 +157,6 @@ const page_view_total_reqs = { prev_data: null, prev_start_date: "2018-06-20T00:00:00Z", prev_end_date: "2018-07-23T00:00:00Z", - category_id: null, - group_id: null, prev30Days: 58110, dates_filtering: true, report_key: `reports:page_view_total_reqs:${startDate.format( @@ -169,12 +166,9 @@ const page_view_total_reqs = { { type: "date", property: "x", title: "Day" }, { type: "number", property: "y", title: "Count" } ], - processing: false, average: false, percent: false, higher_is_better: true, - category_filtering: false, - group_filtering: false, modes: ["table", "chart"], icon: "file", total: 921672