FEATURE: initial implementation of generic filters for reports

This commit is contained in:
Joffrey JAFFEUX
2019-04-26 12:17:10 +02:00
committed by GitHub
parent 4b455e741e
commit bcca2b5d73
33 changed files with 361 additions and 314 deletions
@@ -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) {
@@ -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);
}
});
@@ -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"
});
@@ -0,0 +1,7 @@
export default Ember.Component.extend({
actions: {
onChange(value) {
this.applyFilter(this.get("filter.id"), value);
}
}
});
@@ -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;
}
});
@@ -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
});
}
}
});
@@ -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,
@@ -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 });
}
}
});
@@ -149,38 +149,17 @@
</div>
{{/if}}
{{#if showCategoryOptions}}
{{#each model.available_filters as |filter|}}
<div class="control">
<div class="input">
{{search-advanced-category-chooser
filterable=true
value=category
castInteger=true}}
</div>
</div>
{{/if}}
<span class="label">
{{i18n (concat "admin.dashboard.reports.filters." filter.id ".label")}}
</span>
{{#if showGroupOptions}}
<div class="control">
<div class="input">
{{combo-box
castInteger=true
filterable=true
valueAttribute="value"
content=groupOptions
value=groupId}}
</div>
</div>
{{/if}}
{{#each filterOptions as |filterOption|}}
<div class="control">
<div class="input">
{{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")}}
</div>
</div>
{{/each}}
@@ -0,0 +1,6 @@
{{search-advanced-category-chooser
filterable=true
value=category
castInteger=true
onSelectNone=(action "onChange")
onSelect=(action "onChange")}}
@@ -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")}}
@@ -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")}}
@@ -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")}}