mirror of
https://github.com/discourse/discourse.git
synced 2025-02-12 03:15:31 +08:00
UI: improves dashboard table reports
- support for avatars - support for topic/post/user type in reports - improved totals row UI - minor css tweaks
This commit is contained in:
parent
6aee22b88f
commit
37252c1a5e
|
@ -3,10 +3,10 @@ import computed from "ember-addons/ember-computed-decorators";
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
tagName: "th",
|
tagName: "th",
|
||||||
classNames: ["admin-report-table-header"],
|
classNames: ["admin-report-table-header"],
|
||||||
classNameBindings: ["label.property", "isCurrentSort"],
|
classNameBindings: ["label.mainProperty", "isCurrentSort"],
|
||||||
attributeBindings: ["label.title:title"],
|
attributeBindings: ["label.title:title"],
|
||||||
|
|
||||||
@computed("currentSortLabel.sort_property", "label.sort_property")
|
@computed("currentSortLabel.sortProperty", "label.sortProperty")
|
||||||
isCurrentSort(currentSortField, labelSortField) {
|
isCurrentSort(currentSortField, labelSortField) {
|
||||||
return currentSortField === labelSortField;
|
return currentSortField === labelSortField;
|
||||||
},
|
},
|
||||||
|
|
|
@ -67,14 +67,14 @@ export default Ember.Component.extend({
|
||||||
const computedLabel = label.compute(row);
|
const computedLabel = label.compute(row);
|
||||||
const value = computedLabel.value;
|
const value = computedLabel.value;
|
||||||
|
|
||||||
if (computedLabel.type === "link" || (value && !isNumeric(value))) {
|
if (!computedLabel.countable || !value || !isNumeric(value)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
} else {
|
} else {
|
||||||
return sum + value;
|
return sum + value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
totalsRow[label.property] = rows.reduce(reducer, 0);
|
totalsRow[label.mainProperty] = rows.reduce(reducer, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
return totalsRow;
|
return totalsRow;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Category from "discourse/models/category";
|
||||||
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 { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import Report from "admin/models/report";
|
import { SCHEMA_VERSION, default as Report } from "admin/models/report";
|
||||||
import computed from "ember-addons/ember-computed-decorators";
|
import computed from "ember-addons/ember-computed-decorators";
|
||||||
import { registerTooltip, unregisterTooltip } from "discourse/lib/tooltip";
|
import { registerTooltip, unregisterTooltip } from "discourse/lib/tooltip";
|
||||||
|
|
||||||
|
@ -189,24 +189,20 @@ export default Ember.Component.extend({
|
||||||
reportKey(dataSourceName, categoryId, groupId, startDate, endDate) {
|
reportKey(dataSourceName, categoryId, groupId, startDate, endDate) {
|
||||||
if (!dataSourceName || !startDate || !endDate) return null;
|
if (!dataSourceName || !startDate || !endDate) return null;
|
||||||
|
|
||||||
let reportKey = `reports:${dataSourceName}`;
|
let reportKey = "reports:";
|
||||||
|
reportKey += [
|
||||||
if (categoryId && categoryId !== "all") {
|
dataSourceName,
|
||||||
reportKey += `:${categoryId}`;
|
categoryId,
|
||||||
} else {
|
startDate.replace(/-/g, ""),
|
||||||
reportKey += `:`;
|
endDate.replace(/-/g, ""),
|
||||||
}
|
groupId,
|
||||||
|
"[:prev_period]",
|
||||||
reportKey += `:${startDate.replace(/-/g, "")}`;
|
this.get("reportOptions.table.limit"),
|
||||||
reportKey += `:${endDate.replace(/-/g, "")}`;
|
SCHEMA_VERSION
|
||||||
|
]
|
||||||
if (groupId && groupId !== "all") {
|
.filter(x => x)
|
||||||
reportKey += `:${groupId}`;
|
.map(x => x.toString())
|
||||||
} else {
|
.join(":");
|
||||||
reportKey += `:`;
|
|
||||||
}
|
|
||||||
|
|
||||||
reportKey += `:`;
|
|
||||||
|
|
||||||
return reportKey;
|
return reportKey;
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,87 +1,24 @@
|
||||||
import { escapeExpression } from "discourse/lib/utilities";
|
import { escapeExpression } from "discourse/lib/utilities";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import round from "discourse/lib/round";
|
import round from "discourse/lib/round";
|
||||||
import { fillMissingDates, isNumeric } from "discourse/lib/utilities";
|
import {
|
||||||
|
fillMissingDates,
|
||||||
|
isNumeric,
|
||||||
|
formatUsername
|
||||||
|
} from "discourse/lib/utilities";
|
||||||
import computed from "ember-addons/ember-computed-decorators";
|
import computed from "ember-addons/ember-computed-decorators";
|
||||||
import { number, durationTiny } from "discourse/lib/formatter";
|
import { number, durationTiny } from "discourse/lib/formatter";
|
||||||
|
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 = 1;
|
||||||
|
|
||||||
const Report = Discourse.Model.extend({
|
const Report = Discourse.Model.extend({
|
||||||
average: false,
|
average: false,
|
||||||
percent: false,
|
percent: false,
|
||||||
higher_is_better: true,
|
higher_is_better: true,
|
||||||
|
|
||||||
@computed("labels")
|
|
||||||
computedLabels(labels) {
|
|
||||||
return labels.map(label => {
|
|
||||||
const type = label.type;
|
|
||||||
const properties = label.properties;
|
|
||||||
const property = properties[0];
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: label.title,
|
|
||||||
sort_property: label.sort_property || property,
|
|
||||||
property,
|
|
||||||
compute: row => {
|
|
||||||
let value = row[property];
|
|
||||||
let escapedValue = escapeExpression(value);
|
|
||||||
let tooltip;
|
|
||||||
let base = { property, value, type };
|
|
||||||
|
|
||||||
if (value === null || typeof value === "undefined") {
|
|
||||||
return _.assign(base, {
|
|
||||||
value: null,
|
|
||||||
formatedValue: "-",
|
|
||||||
type: "undefined"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "seconds") {
|
|
||||||
return _.assign(base, {
|
|
||||||
formatedValue: escapeExpression(durationTiny(value))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "link") {
|
|
||||||
return _.assign(base, {
|
|
||||||
formatedValue: `<a href="${escapeExpression(
|
|
||||||
row[properties[1]]
|
|
||||||
)}">${escapedValue}</a>`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "percent") {
|
|
||||||
return _.assign(base, {
|
|
||||||
formatedValue: `${escapedValue}%`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "number" || isNumeric(value))
|
|
||||||
return _.assign(base, {
|
|
||||||
type: "number",
|
|
||||||
formatedValue: number(value)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (type === "date") {
|
|
||||||
const date = moment(value, "YYYY-MM-DD");
|
|
||||||
if (date.isValid()) {
|
|
||||||
return _.assign(base, {
|
|
||||||
formatedValue: date.format("LL")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "text") tooltip = escapedValue;
|
|
||||||
|
|
||||||
return _.assign(base, {
|
|
||||||
tooltip,
|
|
||||||
type: type || "string",
|
|
||||||
formatedValue: escapedValue
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
@computed("modes")
|
@computed("modes")
|
||||||
onlyTable(modes) {
|
onlyTable(modes) {
|
||||||
return modes.length === 1 && modes[0] === "table";
|
return modes.length === 1 && modes[0] === "table";
|
||||||
|
@ -312,6 +249,179 @@ const Report = Discourse.Model.extend({
|
||||||
return this.data && this.data[0].x.match(/\d{4}-\d{1,2}-\d{1,2}/);
|
return this.data && this.data[0].x.match(/\d{4}-\d{1,2}-\d{1,2}/);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@computed("labels")
|
||||||
|
computedLabels(labels) {
|
||||||
|
return labels.map(label => {
|
||||||
|
const type = label.type;
|
||||||
|
|
||||||
|
let mainProperty;
|
||||||
|
if (label.property) mainProperty = label.property;
|
||||||
|
else if (type === "user") mainProperty = label.properties["username"];
|
||||||
|
else if (type === "topic") mainProperty = label.properties["title"];
|
||||||
|
else if (type === "post")
|
||||||
|
mainProperty = label.properties["truncated_raw"];
|
||||||
|
else mainProperty = label.properties[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: label.title,
|
||||||
|
sortProperty: label.sort_property || mainProperty,
|
||||||
|
mainProperty,
|
||||||
|
compute: row => {
|
||||||
|
const value = row[mainProperty];
|
||||||
|
|
||||||
|
if (type === "user") return this._userLabel(label.properties, row);
|
||||||
|
if (type === "post") return this._postLabel(label.properties, row);
|
||||||
|
if (type === "topic") return this._topicLabel(label.properties, row);
|
||||||
|
if (type === "seconds")
|
||||||
|
return this._secondsLabel(mainProperty, value);
|
||||||
|
if (type === "link") return this._linkLabel(label.properties, row);
|
||||||
|
if (type === "percent")
|
||||||
|
return this._percentLabel(mainProperty, value);
|
||||||
|
if (type === "number" || isNumeric(value)) {
|
||||||
|
return this._numberLabel(mainProperty, value);
|
||||||
|
}
|
||||||
|
if (type === "date") {
|
||||||
|
const date = moment(value, "YYYY-MM-DD");
|
||||||
|
if (date.isValid())
|
||||||
|
return this._dateLabel(mainProperty, value, date);
|
||||||
|
}
|
||||||
|
if (type === "text") return this._textLabel(mainProperty, value);
|
||||||
|
if (!value) return this._undefinedLabel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
property: mainProperty,
|
||||||
|
value,
|
||||||
|
type: type || "string",
|
||||||
|
formatedValue: escapeExpression(value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_undefinedLabel() {
|
||||||
|
return {
|
||||||
|
value: null,
|
||||||
|
formatedValue: "-",
|
||||||
|
type: "undefined"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_userLabel(properties, row) {
|
||||||
|
const username = row[properties.username];
|
||||||
|
|
||||||
|
if (!username) return this._undefinedLabel();
|
||||||
|
|
||||||
|
const user = Ember.Object.create({
|
||||||
|
username,
|
||||||
|
name: formatUsername(username),
|
||||||
|
avatar_template: row[properties.avatar]
|
||||||
|
});
|
||||||
|
|
||||||
|
const avatarImg = renderAvatar(user, {
|
||||||
|
imageSize: "small",
|
||||||
|
ignoreTitle: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const href = `/admin/users/${row[properties.id]}/${username}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "user",
|
||||||
|
property: properties.username,
|
||||||
|
value: username,
|
||||||
|
formatedValue: `<a href='${href}'>${avatarImg}<span class='username'>${username}</span></a>`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_topicLabel(properties, row) {
|
||||||
|
const topicTitle = row[properties.title];
|
||||||
|
const topicId = row[properties.id];
|
||||||
|
const href = `/t/-/${topicId}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "topic",
|
||||||
|
property: properties.title,
|
||||||
|
value: topicTitle,
|
||||||
|
formatedValue: `<a href='${href}'>${topicTitle}</a>`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_postLabel(properties, row) {
|
||||||
|
const postTitle = row[properties.truncated_raw];
|
||||||
|
const postNumber = row[properties.number];
|
||||||
|
const topicId = row[properties.topic_id];
|
||||||
|
const href = `/t/-/${topicId}/${postNumber}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "post",
|
||||||
|
property: properties.title,
|
||||||
|
value: postTitle,
|
||||||
|
formatedValue: `<a href='${href}'>${postTitle}</a>`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_secondsLabel(property, value) {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
property,
|
||||||
|
countable: true,
|
||||||
|
type: "seconds",
|
||||||
|
formatedValue: durationTiny(value)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_percentLabel(property, value) {
|
||||||
|
return {
|
||||||
|
type: "percent",
|
||||||
|
property,
|
||||||
|
value,
|
||||||
|
formatedValue: `${value}%`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_numberLabel(property, value) {
|
||||||
|
return {
|
||||||
|
type: "number",
|
||||||
|
countable: true,
|
||||||
|
property,
|
||||||
|
value,
|
||||||
|
formatedValue: number(value)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_dateLabel(property, value, date) {
|
||||||
|
return {
|
||||||
|
type: "date",
|
||||||
|
property,
|
||||||
|
value,
|
||||||
|
formatedValue: date.format("LL")
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_textLabel(property, value) {
|
||||||
|
const escaped = escapeExpression(value);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "text",
|
||||||
|
property,
|
||||||
|
value,
|
||||||
|
formatedValue: escaped
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_linkLabel(properties, row) {
|
||||||
|
const property = properties[0];
|
||||||
|
const value = row[property];
|
||||||
|
return {
|
||||||
|
type: "link",
|
||||||
|
property,
|
||||||
|
value,
|
||||||
|
formatedValue: `<a href="${escapeExpression(
|
||||||
|
row[properties[1]]
|
||||||
|
)}">${escapeExpression(value)}</a>`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
_computeChange(valAtT1, valAtT2) {
|
_computeChange(valAtT1, valAtT2) {
|
||||||
return ((valAtT2 - valAtT1) / valAtT1) * 100;
|
return ((valAtT2 - valAtT1) / valAtT1) * 100;
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,33 +21,35 @@
|
||||||
{{#each paginatedData as |data|}}
|
{{#each paginatedData as |data|}}
|
||||||
{{admin-report-table-row data=data labels=model.computedLabels}}
|
{{admin-report-table-row data=data labels=model.computedLabels}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{{#if showTotalForSample}}
|
{{#if showTotalForSample}}
|
||||||
<small>{{i18n 'admin.dashboard.reports.totals_for_sample'}}</small>
|
<tr class="total-row">
|
||||||
<table class="totals-sample-table">
|
<td colspan="{{totalsForSample.length}}">
|
||||||
<tbody>
|
{{i18n 'admin.dashboard.reports.totals_for_sample'}}
|
||||||
<tr>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="admin-report-table-row">
|
||||||
{{#each totalsForSample as |total|}}
|
{{#each totalsForSample as |total|}}
|
||||||
<td>{{total.formatedValue}}</td>
|
<td class="admin-report-table-row {{total.type}} {{total.property}}">
|
||||||
|
{{total.formatedValue}}
|
||||||
|
</td>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showTotal}}
|
{{#if showTotal}}
|
||||||
<small>{{i18n 'admin.dashboard.reports.total'}}</small>
|
<tr class="total-row">
|
||||||
<table class="totals-table">
|
<td colspan="2">
|
||||||
<tbody>
|
{{i18n 'admin.dashboard.reports.total'}}
|
||||||
<tr>
|
</td>
|
||||||
<td>-</td>
|
|
||||||
<td>{{number model.total}}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class="admin-report-table-row">
|
||||||
|
<td class="date x">-</td>
|
||||||
|
<td class="number y">{{number model.total}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
{{#each pages as |pageState|}}
|
{{#each pages as |pageState|}}
|
||||||
|
|
|
@ -193,7 +193,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-report.post-edits {
|
.admin-report.post-edits {
|
||||||
.admin-report-table {
|
.report-table {
|
||||||
table-layout: auto;
|
table-layout: auto;
|
||||||
|
|
||||||
tbody tr td,
|
tbody tr td,
|
||||||
|
@ -203,7 +203,7 @@
|
||||||
|
|
||||||
thead tr th.edit_reason,
|
thead tr th.edit_reason,
|
||||||
tbody tr td.edit_reason {
|
tbody tr td.edit_reason {
|
||||||
width: 60%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
.report-table {
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
border: 1px solid $primary-low;
|
border: 1px solid $primary-low;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -41,16 +41,28 @@
|
||||||
tbody {
|
tbody {
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
|
.total-row {
|
||||||
|
td {
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tr {
|
tr {
|
||||||
td {
|
td {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
|
&.user {
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.username {
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,6 @@ class AdminDashboardNextData
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.stats_cache_key
|
def self.stats_cache_key
|
||||||
'dashboard-next-data'
|
"dashboard-next-data-#{Report::SCHEMA_VERSION}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,6 +20,6 @@ class AdminDashboardNextGeneralData < AdminDashboardNextData
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.stats_cache_key
|
def self.stats_cache_key
|
||||||
'general-dashboard-data'
|
"general-dashboard-data-#{Report::SCHEMA_VERSION}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ class AdminDashboardNextIndexData < AdminDashboardNextData
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.stats_cache_key
|
def self.stats_cache_key
|
||||||
'index-dashboard-data'
|
"index-dashboard-data-#{Report::SCHEMA_VERSION}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: problems should be loaded from this model
|
# TODO: problems should be loaded from this model
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
require_dependency 'topic_subtype'
|
require_dependency 'topic_subtype'
|
||||||
|
|
||||||
class Report
|
class Report
|
||||||
|
# Change this line each time report format change
|
||||||
|
# and you want to ensure cache is reset
|
||||||
|
SCHEMA_VERSION = 1
|
||||||
|
|
||||||
attr_accessor :type, :data, :total, :prev30Days, :start_date,
|
attr_accessor :type, :data, :total, :prev30Days, :start_date,
|
||||||
:end_date, :category_id, :group_id, :labels, :async,
|
:end_date, :category_id, :group_id, :labels, :async,
|
||||||
:prev_period, :facets, :limit, :processing, :average, :percent,
|
:prev_period, :facets, :limit, :processing, :average, :percent,
|
||||||
|
@ -15,7 +19,7 @@ class Report
|
||||||
def initialize(type)
|
def initialize(type)
|
||||||
@type = type
|
@type = type
|
||||||
@start_date ||= Report.default_days.days.ago.beginning_of_day
|
@start_date ||= Report.default_days.days.ago.beginning_of_day
|
||||||
@end_date ||= Time.zone.now.end_of_day
|
@end_date ||= Time.now.end_of_day
|
||||||
@prev_end_date = @start_date
|
@prev_end_date = @start_date
|
||||||
@average = false
|
@average = false
|
||||||
@percent = false
|
@percent = false
|
||||||
|
@ -36,8 +40,9 @@ class Report
|
||||||
report.end_date.to_date.strftime("%Y%m%d"),
|
report.end_date.to_date.strftime("%Y%m%d"),
|
||||||
report.group_id,
|
report.group_id,
|
||||||
report.facets,
|
report.facets,
|
||||||
report.limit
|
report.limit,
|
||||||
].map(&:to_s).join(':')
|
SCHEMA_VERSION,
|
||||||
|
].compact.map(&:to_s).join(':')
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.clear_cache
|
def self.clear_cache
|
||||||
|
@ -89,8 +94,16 @@ class Report
|
||||||
dates_filtering: self.dates_filtering,
|
dates_filtering: self.dates_filtering,
|
||||||
report_key: Report.cache_key(self),
|
report_key: Report.cache_key(self),
|
||||||
labels: labels || [
|
labels: labels || [
|
||||||
{ type: :date, properties: [:x], title: I18n.t("reports.default.labels.day") },
|
{
|
||||||
{ type: :number, properties: [:y], title: I18n.t("reports.default.labels.count") },
|
type: :date,
|
||||||
|
property: :x,
|
||||||
|
title: I18n.t("reports.default.labels.day")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: :number,
|
||||||
|
property: :y,
|
||||||
|
title: I18n.t("reports.default.labels.count")
|
||||||
|
},
|
||||||
],
|
],
|
||||||
processing: self.processing,
|
processing: self.processing,
|
||||||
average: self.average,
|
average: self.average,
|
||||||
|
@ -98,7 +111,7 @@ class Report
|
||||||
higher_is_better: self.higher_is_better,
|
higher_is_better: self.higher_is_better,
|
||||||
category_filtering: self.category_filtering,
|
category_filtering: self.category_filtering,
|
||||||
group_filtering: self.group_filtering,
|
group_filtering: self.group_filtering,
|
||||||
modes: self.modes
|
modes: self.modes,
|
||||||
}.tap do |json|
|
}.tap do |json|
|
||||||
json[:timeout] = self.timeout if self.timeout
|
json[:timeout] = self.timeout if self.timeout
|
||||||
json[:total] = self.total if self.total
|
json[:total] = self.total if self.total
|
||||||
|
@ -416,8 +429,14 @@ class Report
|
||||||
report.dates_filtering = false
|
report.dates_filtering = false
|
||||||
|
|
||||||
report.labels = [
|
report.labels = [
|
||||||
{ properties: [:key], title: I18n.t("reports.users_by_trust_level.labels.level") },
|
{
|
||||||
{ properties: [:y], title: I18n.t("reports.default.labels.count") },
|
property: :key,
|
||||||
|
title: I18n.t("reports.users_by_trust_level.labels.level")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :y,
|
||||||
|
title: I18n.t("reports.default.labels.count")
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
User.real.group('trust_level').count.sort.each do |level, count|
|
User.real.group('trust_level').count.sort.each do |level, count|
|
||||||
|
@ -505,8 +524,15 @@ class Report
|
||||||
|
|
||||||
def self.report_web_crawlers(report)
|
def self.report_web_crawlers(report)
|
||||||
report.labels = [
|
report.labels = [
|
||||||
{ type: :string, properties: [:user_agent], title: I18n.t("reports.web_crawlers.labels.user_agent") },
|
{
|
||||||
{ properties: [:count], title: I18n.t("reports.web_crawlers.labels.page_views") }
|
type: :string,
|
||||||
|
property: :user_agent,
|
||||||
|
title: I18n.t("reports.web_crawlers.labels.user_agent")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :count,
|
||||||
|
title: I18n.t("reports.web_crawlers.labels.page_views")
|
||||||
|
}
|
||||||
]
|
]
|
||||||
report.modes = [:table]
|
report.modes = [:table]
|
||||||
report.data = WebCrawlerRequest.where('date >= ? and date <= ?', report.start_date, report.end_date)
|
report.data = WebCrawlerRequest.where('date >= ? and date <= ?', report.start_date, report.end_date)
|
||||||
|
@ -524,8 +550,14 @@ class Report
|
||||||
report.dates_filtering = false
|
report.dates_filtering = false
|
||||||
|
|
||||||
report.labels = [
|
report.labels = [
|
||||||
{ properties: [:x], title: I18n.t("reports.users_by_type.labels.type") },
|
{
|
||||||
{ properties: [:y], title: I18n.t("reports.default.labels.count") },
|
property: :x,
|
||||||
|
title: I18n.t("reports.users_by_type.labels.type")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :y,
|
||||||
|
title: I18n.t("reports.default.labels.count")
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
label = Proc.new { |x| I18n.t("reports.users_by_type.xaxis_labels.#{x}") }
|
label = Proc.new { |x| I18n.t("reports.users_by_type.xaxis_labels.#{x}") }
|
||||||
|
@ -548,8 +580,18 @@ class Report
|
||||||
report.modes = [:table]
|
report.modes = [:table]
|
||||||
|
|
||||||
report.labels = [
|
report.labels = [
|
||||||
{ type: :link, properties: [:topic_title, :topic_url], title: I18n.t("reports.top_referred_topics.labels.topic") },
|
{
|
||||||
{ properties: [:num_clicks], title: I18n.t("reports.top_referred_topics.labels.num_clicks") }
|
type: :topic,
|
||||||
|
properties: {
|
||||||
|
title: :topic_title,
|
||||||
|
id: :topic_id
|
||||||
|
},
|
||||||
|
title: I18n.t("reports.top_referred_topics.labels.topic")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :num_clicks,
|
||||||
|
title: I18n.t("reports.top_referred_topics.labels.num_clicks")
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
options = { end_date: report.end_date, start_date: report.start_date, limit: report.limit || 8 }
|
options = { end_date: report.end_date, start_date: report.start_date, limit: report.limit || 8 }
|
||||||
|
@ -564,9 +606,18 @@ class Report
|
||||||
report.modes = [:table]
|
report.modes = [:table]
|
||||||
|
|
||||||
report.labels = [
|
report.labels = [
|
||||||
{ properties: [:domain], title: I18n.t("reports.top_traffic_sources.labels.domain") },
|
{
|
||||||
{ properties: [:num_clicks], title: I18n.t("reports.top_traffic_sources.labels.num_clicks") },
|
property: :domain,
|
||||||
{ properties: [:num_topics], title: I18n.t("reports.top_traffic_sources.labels.num_topics") }
|
title: I18n.t("reports.top_traffic_sources.labels.domain")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :num_clicks,
|
||||||
|
title: I18n.t("reports.top_traffic_sources.labels.num_clicks")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :num_topics,
|
||||||
|
title: I18n.t("reports.top_traffic_sources.labels.num_topics")
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
options = { end_date: report.end_date, start_date: report.start_date, limit: report.limit || 8 }
|
options = { end_date: report.end_date, start_date: report.start_date, limit: report.limit || 8 }
|
||||||
|
@ -579,9 +630,19 @@ class Report
|
||||||
|
|
||||||
def self.report_trending_search(report)
|
def self.report_trending_search(report)
|
||||||
report.labels = [
|
report.labels = [
|
||||||
{ properties: [:term], title: I18n.t("reports.trending_search.labels.term") },
|
{
|
||||||
{ properties: [:unique_searches], title: I18n.t("reports.trending_search.labels.searches") },
|
property: :term,
|
||||||
{ type: :percent, properties: [:ctr], title: I18n.t("reports.trending_search.labels.click_through") }
|
title: I18n.t("reports.trending_search.labels.term")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :unique_searches,
|
||||||
|
title: I18n.t("reports.trending_search.labels.searches")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: :percent,
|
||||||
|
property: :ctr,
|
||||||
|
title: I18n.t("reports.trending_search.labels.click_through")
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
report.data = []
|
report.data = []
|
||||||
|
@ -622,12 +683,36 @@ class Report
|
||||||
|
|
||||||
def self.report_moderators_activity(report)
|
def self.report_moderators_activity(report)
|
||||||
report.labels = [
|
report.labels = [
|
||||||
{ type: :link, properties: [:username, :user_url], title: I18n.t("reports.moderators_activity.labels.moderator") },
|
{
|
||||||
{ properties: [:flag_count], title: I18n.t("reports.moderators_activity.labels.flag_count") },
|
type: :user,
|
||||||
{ type: :seconds, properties: [:time_read], title: I18n.t("reports.moderators_activity.labels.time_read") },
|
properties: {
|
||||||
{ properties: [:topic_count], title: I18n.t("reports.moderators_activity.labels.topic_count") },
|
username: :username,
|
||||||
{ properties: [:pm_count], title: I18n.t("reports.moderators_activity.labels.pm_count") },
|
id: :user_id,
|
||||||
{ properties: [:post_count], title: I18n.t("reports.moderators_activity.labels.post_count") }
|
avatar: :user_avatar_template,
|
||||||
|
},
|
||||||
|
title: I18n.t("reports.moderators_activity.labels.moderator"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :flag_count,
|
||||||
|
title: I18n.t("reports.moderators_activity.labels.flag_count")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: :seconds,
|
||||||
|
property: :time_read,
|
||||||
|
title: I18n.t("reports.moderators_activity.labels.time_read")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :topic_count,
|
||||||
|
title: I18n.t("reports.moderators_activity.labels.topic_count")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :pm_count,
|
||||||
|
title: I18n.t("reports.moderators_activity.labels.pm_count")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: :post_count,
|
||||||
|
title: I18n.t("reports.moderators_activity.labels.post_count")
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
report.modes = [:table]
|
report.modes = [:table]
|
||||||
|
@ -638,8 +723,8 @@ class Report
|
||||||
User.real.where(moderator: true).find_each do |u|
|
User.real.where(moderator: true).find_each do |u|
|
||||||
mod_data[u.id] = {
|
mod_data[u.id] = {
|
||||||
user_id: u.id,
|
user_id: u.id,
|
||||||
username: u.username,
|
username: u.username_lower,
|
||||||
user_url: "/admin/users/#{u.id}/#{u.username}"
|
user_avatar_template: u.avatar_template,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -766,11 +851,42 @@ class Report
|
||||||
report.modes = [:table]
|
report.modes = [:table]
|
||||||
|
|
||||||
report.labels = [
|
report.labels = [
|
||||||
{ properties: [:action_type], title: I18n.t("reports.flags_status.labels.flag") },
|
{
|
||||||
{ type: :link, properties: [:staff_username, :staff_url], title: I18n.t("reports.flags_status.labels.assigned") },
|
property: :action_type,
|
||||||
{ type: :link, properties: [:poster_username, :poster_url], title: I18n.t("reports.flags_status.labels.poster") },
|
title: I18n.t("reports.flags_status.labels.flag")
|
||||||
{ type: :link, properties: [:flagger_username, :flagger_url], title: I18n.t("reports.flags_status.labels.flagger") },
|
},
|
||||||
{ type: :seconds, properties: [:response_time], title: I18n.t("reports.flags_status.labels.time_to_resolution") }
|
{
|
||||||
|
type: :user,
|
||||||
|
properties: {
|
||||||
|
username: :staff_username,
|
||||||
|
id: :staff_id,
|
||||||
|
avatar: :staff_avatar_template
|
||||||
|
},
|
||||||
|
title: I18n.t("reports.flags_status.labels.assigned")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: :user,
|
||||||
|
properties: {
|
||||||
|
username: :poster_username,
|
||||||
|
id: :poster_id,
|
||||||
|
avatar: :poster_avatar_template
|
||||||
|
},
|
||||||
|
title: I18n.t("reports.flags_status.labels.poster")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: :user,
|
||||||
|
properties: {
|
||||||
|
username: :flagger_username,
|
||||||
|
id: :flagger_id,
|
||||||
|
avatar: :flagger_avatar_template
|
||||||
|
},
|
||||||
|
title: I18n.t("reports.flags_status.labels.flagger")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: :seconds,
|
||||||
|
property: :response_time,
|
||||||
|
title: I18n.t("reports.flags_status.labels.time_to_resolution")
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
report.data = []
|
report.data = []
|
||||||
|
@ -799,7 +915,8 @@ class Report
|
||||||
poster_data AS (
|
poster_data AS (
|
||||||
SELECT pa.id,
|
SELECT pa.id,
|
||||||
p.user_id AS poster_id,
|
p.user_id AS poster_id,
|
||||||
u.username AS poster_username
|
u.username_lower AS poster_username,
|
||||||
|
u.uploaded_avatar_id AS poster_avatar_id
|
||||||
FROM period_actions pa
|
FROM period_actions pa
|
||||||
JOIN posts p
|
JOIN posts p
|
||||||
ON p.id = pa.post_id
|
ON p.id = pa.post_id
|
||||||
|
@ -809,7 +926,8 @@ class Report
|
||||||
flagger_data AS (
|
flagger_data AS (
|
||||||
SELECT pa.id,
|
SELECT pa.id,
|
||||||
u.id AS flagger_id,
|
u.id AS flagger_id,
|
||||||
u.username AS flagger_username
|
u.username_lower AS flagger_username,
|
||||||
|
u.uploaded_avatar_id AS flagger_avatar_id
|
||||||
FROM period_actions pa
|
FROM period_actions pa
|
||||||
JOIN users u
|
JOIN users u
|
||||||
ON u.id = pa.user_id
|
ON u.id = pa.user_id
|
||||||
|
@ -817,7 +935,8 @@ class Report
|
||||||
staff_data AS (
|
staff_data AS (
|
||||||
SELECT pa.id,
|
SELECT pa.id,
|
||||||
u.id AS staff_id,
|
u.id AS staff_id,
|
||||||
u.username AS staff_username
|
u.username_lower AS staff_username,
|
||||||
|
u.uploaded_avatar_id AS staff_avatar_id
|
||||||
FROM period_actions pa
|
FROM period_actions pa
|
||||||
JOIN users u
|
JOIN users u
|
||||||
ON u.id = COALESCE(pa.agreed_by_id, pa.disagreed_by_id, pa.deferred_by_id)
|
ON u.id = COALESCE(pa.agreed_by_id, pa.disagreed_by_id, pa.deferred_by_id)
|
||||||
|
@ -825,10 +944,13 @@ class Report
|
||||||
SELECT
|
SELECT
|
||||||
sd.staff_username,
|
sd.staff_username,
|
||||||
sd.staff_id,
|
sd.staff_id,
|
||||||
|
sd.staff_avatar_id,
|
||||||
pd.poster_username,
|
pd.poster_username,
|
||||||
pd.poster_id,
|
pd.poster_id,
|
||||||
|
pd.poster_avatar_id,
|
||||||
fd.flagger_username,
|
fd.flagger_username,
|
||||||
fd.flagger_id,
|
fd.flagger_id,
|
||||||
|
fd.flagger_avatar_id,
|
||||||
pa.post_action_type_id,
|
pa.post_action_type_id,
|
||||||
pa.created_at,
|
pa.created_at,
|
||||||
pa.agreed_at,
|
pa.agreed_at,
|
||||||
|
@ -850,17 +972,23 @@ class Report
|
||||||
DB.query(sql).each do |row|
|
DB.query(sql).each do |row|
|
||||||
data = {}
|
data = {}
|
||||||
data[:action_type] = flag_types.key(row.post_action_type_id).to_s
|
data[:action_type] = flag_types.key(row.post_action_type_id).to_s
|
||||||
|
|
||||||
|
if row.staff_id
|
||||||
data[:staff_username] = row.staff_username
|
data[:staff_username] = row.staff_username
|
||||||
data[:staff_id] = row.staff_id
|
data[:staff_id] = row.staff_id
|
||||||
if row.staff_username && row.staff_id
|
data[:staff_avatar_template] = User.avatar_template(row.staff_username, row.staff_avatar_id)
|
||||||
data[:staff_url] = "/admin/users/#{row.staff_id}/#{row.staff_username}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if row.poster_id
|
||||||
data[:poster_username] = row.poster_username
|
data[:poster_username] = row.poster_username
|
||||||
data[:poster_id] = row.poster_id
|
data[:poster_id] = row.poster_id
|
||||||
data[:poster_url] = "/admin/users/#{row.poster_id}/#{row.poster_username}"
|
data[:poster_avatar_template] = User.avatar_template(row.poster_username, row.poster_avatar_id)
|
||||||
|
end
|
||||||
|
|
||||||
data[:flagger_id] = row.flagger_id
|
data[:flagger_id] = row.flagger_id
|
||||||
data[:flagger_username] = row.flagger_username
|
data[:flagger_username] = row.flagger_username
|
||||||
data[:flagger_url] = "/admin/users/#{row.flagger_id}/#{row.flagger_username}"
|
data[:flagger_avatar_template] = User.avatar_template(row.flagger_username, row.flagger_avatar_id)
|
||||||
|
|
||||||
if row.agreed_by_id
|
if row.agreed_by_id
|
||||||
data[:resolution] = I18n.t("reports.flags_status.values.agreed")
|
data[:resolution] = I18n.t("reports.flags_status.values.agreed")
|
||||||
elsif row.disagreed_by_id
|
elsif row.disagreed_by_id
|
||||||
|
@ -879,10 +1007,38 @@ class Report
|
||||||
report.modes = [:table]
|
report.modes = [:table]
|
||||||
|
|
||||||
report.labels = [
|
report.labels = [
|
||||||
{ type: :link, properties: [:post_id, :post_url], title: I18n.t("reports.post_edits.labels.post") },
|
{
|
||||||
{ type: :link, properties: [:editor_username, :editor_url], title: I18n.t("reports.post_edits.labels.editor") },
|
type: :post,
|
||||||
{ type: :link, properties: [:author_username, :author_url], title: I18n.t("reports.post_edits.labels.author") },
|
properties: {
|
||||||
{ type: :text, properties: [:edit_reason], title: I18n.t("reports.post_edits.labels.edit_reason") }
|
topic_id: :topic_id,
|
||||||
|
number: :post_number,
|
||||||
|
truncated_raw: :post_raw
|
||||||
|
},
|
||||||
|
title: I18n.t("reports.post_edits.labels.post")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: :user,
|
||||||
|
properties: {
|
||||||
|
username: :editor_username,
|
||||||
|
id: :editor_id,
|
||||||
|
avatar: :editor_avatar_template,
|
||||||
|
},
|
||||||
|
title: I18n.t("reports.post_edits.labels.editor")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: :user,
|
||||||
|
properties: {
|
||||||
|
username: :author_username,
|
||||||
|
id: :author_id,
|
||||||
|
avatar: :author_avatar_template,
|
||||||
|
},
|
||||||
|
title: I18n.t("reports.post_edits.labels.author")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: :text,
|
||||||
|
property: :edit_reason,
|
||||||
|
title: I18n.t("reports.post_edits.labels.edit_reason")
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
report.data = []
|
report.data = []
|
||||||
|
@ -893,7 +1049,8 @@ class Report
|
||||||
pr.number AS revision_version,
|
pr.number AS revision_version,
|
||||||
pr.created_at,
|
pr.created_at,
|
||||||
pr.post_id,
|
pr.post_id,
|
||||||
u.username AS editor_username
|
u.username AS editor_username,
|
||||||
|
u.uploaded_avatar_id as editor_avatar_id
|
||||||
FROM post_revisions pr
|
FROM post_revisions pr
|
||||||
JOIN users u
|
JOIN users u
|
||||||
ON u.id = pr.user_id
|
ON u.id = pr.user_id
|
||||||
|
@ -905,11 +1062,14 @@ class Report
|
||||||
)
|
)
|
||||||
SELECT pr.editor_id,
|
SELECT pr.editor_id,
|
||||||
pr.editor_username,
|
pr.editor_username,
|
||||||
|
pr.editor_avatar_id,
|
||||||
p.user_id AS author_id,
|
p.user_id AS author_id,
|
||||||
u.username AS author_username,
|
u.username AS author_username,
|
||||||
|
u.uploaded_avatar_id AS author_avatar_id,
|
||||||
pr.revision_version,
|
pr.revision_version,
|
||||||
p.version AS post_version,
|
p.version AS post_version,
|
||||||
pr.post_id,
|
pr.post_id,
|
||||||
|
left(p.raw, 40) AS post_raw,
|
||||||
p.topic_id,
|
p.topic_id,
|
||||||
p.post_number,
|
p.post_number,
|
||||||
p.edit_reason,
|
p.edit_reason,
|
||||||
|
@ -925,14 +1085,15 @@ class Report
|
||||||
revision = {}
|
revision = {}
|
||||||
revision[:editor_id] = r.editor_id
|
revision[:editor_id] = r.editor_id
|
||||||
revision[:editor_username] = r.editor_username
|
revision[:editor_username] = r.editor_username
|
||||||
revision[:editor_url] = "/admin/users/#{r.editor_id}/#{r.editor_username}"
|
revision[:editor_avatar_template] = User.avatar_template(r.editor_username, r.editor_avatar_id)
|
||||||
revision[:author_id] = r.author_id
|
revision[:author_id] = r.author_id
|
||||||
revision[:author_username] = r.author_username
|
revision[:author_username] = r.author_username
|
||||||
revision[:author_url] = "/admin/users/#{r.author_id}/#{r.author_username}"
|
revision[:author_avatar_template] = User.avatar_template(r.author_username, r.author_avatar_id)
|
||||||
revision[:edit_reason] = r.revision_version == r.post_version ? r.edit_reason : nil
|
revision[:edit_reason] = r.revision_version == r.post_version ? r.edit_reason : nil
|
||||||
revision[:created_at] = r.created_at
|
revision[:created_at] = r.created_at
|
||||||
revision[:post_id] = r.post_id
|
revision[:post_raw] = r.post_raw
|
||||||
revision[:post_url] = "/t/-/#{r.topic_id}/#{r.post_number}"
|
revision[:topic_id] = r.topic_id
|
||||||
|
revision[:post_number] = r.post_number
|
||||||
|
|
||||||
report.data << revision
|
report.data << revision
|
||||||
end
|
end
|
||||||
|
|
|
@ -60,7 +60,7 @@ componentTest("default", {
|
||||||
"it has rows"
|
"it has rows"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.ok(exists(".totals-sample-table"), "it has totals");
|
assert.ok(exists(".total-row"), "it has totals");
|
||||||
|
|
||||||
await click(".admin-report-table-header.y .sort-button");
|
await click(".admin-report-table-header.y .sort-button");
|
||||||
assert.equal(
|
assert.equal(
|
||||||
|
|
|
@ -396,22 +396,48 @@ QUnit.test("computed labels", assert => {
|
||||||
const data = [
|
const data = [
|
||||||
{
|
{
|
||||||
username: "joffrey",
|
username: "joffrey",
|
||||||
user_url: "/admin/users/1/joffrey",
|
user_id: 1,
|
||||||
|
user_avatar: "/",
|
||||||
flag_count: 1876,
|
flag_count: 1876,
|
||||||
time_read: 287362,
|
time_read: 287362,
|
||||||
note: "This is a long note"
|
note: "This is a long note",
|
||||||
|
topic_id: 2,
|
||||||
|
topic_title: "Test topic",
|
||||||
|
post_number: 3,
|
||||||
|
post_raw: "This is the beginning of"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const labels = [
|
const labels = [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "user",
|
||||||
properties: ["username", "user_url"],
|
properties: {
|
||||||
title: "Username"
|
username: "username",
|
||||||
|
id: "user_id",
|
||||||
|
avatar: "user_avatar"
|
||||||
},
|
},
|
||||||
{ properties: ["flag_count"], title: "Flag count" },
|
title: "Moderator"
|
||||||
{ type: "seconds", properties: ["time_read"], title: "Time read" },
|
},
|
||||||
{ type: "text", properties: ["note"], title: "Note" }
|
{ property: "flag_count", title: "Flag count" },
|
||||||
|
{ type: "seconds", property: "time_read", title: "Time read" },
|
||||||
|
{ type: "text", property: "note", title: "Note" },
|
||||||
|
{
|
||||||
|
type: "topic",
|
||||||
|
properties: {
|
||||||
|
title: "topic_title",
|
||||||
|
id: "topic_id"
|
||||||
|
},
|
||||||
|
title: "Topic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "post",
|
||||||
|
properties: {
|
||||||
|
topic_id: "topic_id",
|
||||||
|
number: "post_number",
|
||||||
|
truncated_raw: "post_raw"
|
||||||
|
},
|
||||||
|
title: "Post"
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const report = Report.create({
|
const report = Report.create({
|
||||||
|
@ -424,20 +450,20 @@ QUnit.test("computed labels", assert => {
|
||||||
const computedLabels = report.get("computedLabels");
|
const computedLabels = report.get("computedLabels");
|
||||||
|
|
||||||
const usernameLabel = computedLabels[0];
|
const usernameLabel = computedLabels[0];
|
||||||
assert.equal(usernameLabel.property, "username");
|
assert.equal(usernameLabel.mainProperty, "username");
|
||||||
assert.equal(usernameLabel.sort_property, "username");
|
assert.equal(usernameLabel.sortProperty, "username");
|
||||||
assert.equal(usernameLabel.title, "Username");
|
assert.equal(usernameLabel.title, "Moderator");
|
||||||
const computedUsernameLabel = usernameLabel.compute(row);
|
const computedUsernameLabel = usernameLabel.compute(row);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
computedUsernameLabel.formatedValue,
|
computedUsernameLabel.formatedValue,
|
||||||
'<a href="/admin/users/1/joffrey">joffrey</a>'
|
"<a href='/admin/users/1/joffrey'><img alt='' width='25' height='25' src='/' class='avatar' title='joffrey'><span class='username'>joffrey</span></a>"
|
||||||
);
|
);
|
||||||
assert.equal(computedUsernameLabel.type, "link");
|
assert.equal(computedUsernameLabel.type, "user");
|
||||||
assert.equal(computedUsernameLabel.value, "joffrey");
|
assert.equal(computedUsernameLabel.value, "joffrey");
|
||||||
|
|
||||||
const flagCountLabel = computedLabels[1];
|
const flagCountLabel = computedLabels[1];
|
||||||
assert.equal(flagCountLabel.property, "flag_count");
|
assert.equal(flagCountLabel.mainProperty, "flag_count");
|
||||||
assert.equal(flagCountLabel.sort_property, "flag_count");
|
assert.equal(flagCountLabel.sortProperty, "flag_count");
|
||||||
assert.equal(flagCountLabel.title, "Flag count");
|
assert.equal(flagCountLabel.title, "Flag count");
|
||||||
const computedFlagCountLabel = flagCountLabel.compute(row);
|
const computedFlagCountLabel = flagCountLabel.compute(row);
|
||||||
assert.equal(computedFlagCountLabel.formatedValue, "1.9k");
|
assert.equal(computedFlagCountLabel.formatedValue, "1.9k");
|
||||||
|
@ -445,8 +471,8 @@ QUnit.test("computed labels", assert => {
|
||||||
assert.equal(computedFlagCountLabel.value, 1876);
|
assert.equal(computedFlagCountLabel.value, 1876);
|
||||||
|
|
||||||
const timeReadLabel = computedLabels[2];
|
const timeReadLabel = computedLabels[2];
|
||||||
assert.equal(timeReadLabel.property, "time_read");
|
assert.equal(timeReadLabel.mainProperty, "time_read");
|
||||||
assert.equal(timeReadLabel.sort_property, "time_read");
|
assert.equal(timeReadLabel.sortProperty, "time_read");
|
||||||
assert.equal(timeReadLabel.title, "Time read");
|
assert.equal(timeReadLabel.title, "Time read");
|
||||||
const computedTimeReadLabel = timeReadLabel.compute(row);
|
const computedTimeReadLabel = timeReadLabel.compute(row);
|
||||||
assert.equal(computedTimeReadLabel.formatedValue, "3d");
|
assert.equal(computedTimeReadLabel.formatedValue, "3d");
|
||||||
|
@ -454,11 +480,35 @@ QUnit.test("computed labels", assert => {
|
||||||
assert.equal(computedTimeReadLabel.value, 287362);
|
assert.equal(computedTimeReadLabel.value, 287362);
|
||||||
|
|
||||||
const noteLabel = computedLabels[3];
|
const noteLabel = computedLabels[3];
|
||||||
assert.equal(noteLabel.property, "note");
|
assert.equal(noteLabel.mainProperty, "note");
|
||||||
assert.equal(noteLabel.sort_property, "note");
|
assert.equal(noteLabel.sortProperty, "note");
|
||||||
assert.equal(noteLabel.title, "Note");
|
assert.equal(noteLabel.title, "Note");
|
||||||
const computedNoteLabel = noteLabel.compute(row);
|
const computedNoteLabel = noteLabel.compute(row);
|
||||||
assert.equal(computedNoteLabel.formatedValue, "This is a long note");
|
assert.equal(computedNoteLabel.formatedValue, "This is a long note");
|
||||||
assert.equal(computedNoteLabel.type, "text");
|
assert.equal(computedNoteLabel.type, "text");
|
||||||
assert.equal(computedNoteLabel.value, "This is a long note");
|
assert.equal(computedNoteLabel.value, "This is a long note");
|
||||||
|
|
||||||
|
const topicLabel = computedLabels[4];
|
||||||
|
assert.equal(topicLabel.mainProperty, "topic_title");
|
||||||
|
assert.equal(topicLabel.sortProperty, "topic_title");
|
||||||
|
assert.equal(topicLabel.title, "Topic");
|
||||||
|
const computedTopicLabel = topicLabel.compute(row);
|
||||||
|
assert.equal(
|
||||||
|
computedTopicLabel.formatedValue,
|
||||||
|
"<a href='/t/-/2'>Test topic</a>"
|
||||||
|
);
|
||||||
|
assert.equal(computedTopicLabel.type, "topic");
|
||||||
|
assert.equal(computedTopicLabel.value, "Test topic");
|
||||||
|
|
||||||
|
const postLabel = computedLabels[5];
|
||||||
|
assert.equal(postLabel.mainProperty, "post_raw");
|
||||||
|
assert.equal(postLabel.sortProperty, "post_raw");
|
||||||
|
assert.equal(postLabel.title, "Post");
|
||||||
|
const computedPostLabel = postLabel.compute(row);
|
||||||
|
assert.equal(
|
||||||
|
computedPostLabel.formatedValue,
|
||||||
|
"<a href='/t/-/2/3'>This is the beginning of</a>"
|
||||||
|
);
|
||||||
|
assert.equal(computedPostLabel.type, "post");
|
||||||
|
assert.equal(computedPostLabel.value, "This is the beginning of");
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user