UX: improve new dashboard
- top referred topics - limit search logs to 8 results
This commit is contained in:
parent
8d6a9eb511
commit
193b6d5651
|
@ -8,12 +8,31 @@ export default Ember.Component.extend(AsyncReport, {
|
|||
help: null,
|
||||
helpPage: null,
|
||||
|
||||
loadReport(report_json) {
|
||||
this._setPropertiesFromReport(Report.create(report_json));
|
||||
},
|
||||
|
||||
fetchReport() {
|
||||
this.set("isLoading", true);
|
||||
|
||||
ajax(this.get("dataSource"))
|
||||
let payload = { data: { async: true } };
|
||||
|
||||
if (this.get("startDate")) {
|
||||
payload.data.start_date = this.get("startDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
||||
}
|
||||
|
||||
if (this.get("endDate")) {
|
||||
payload.data.end_date = this.get("endDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
||||
}
|
||||
|
||||
if (this.get("limit")) {
|
||||
payload.data.limit = this.get("limit");
|
||||
}
|
||||
|
||||
ajax(this.get("dataSource"), payload)
|
||||
.then((response) => {
|
||||
this._setPropertiesFromReport(Report.create(response.report));
|
||||
this.set('reportKey', response.report.report_key);
|
||||
this.loadReport(response.report);
|
||||
}).finally(() => {
|
||||
if (!Ember.isEmpty(this.get("report.data"))) {
|
||||
this.set("isLoading", false);
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import DashboardTable from "admin/components/dashboard-table";
|
||||
import AsyncReport from "admin/mixins/async-report";
|
||||
|
||||
export default DashboardTable.extend(AsyncReport, {
|
||||
layoutName: "admin/templates/components/dashboard-table",
|
||||
|
||||
classNames: ["dashboard-table", "dashboard-table-trending-search"]
|
||||
});
|
|
@ -1,59 +0,0 @@
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import Report from "admin/models/report";
|
||||
import AsyncReport from "admin/mixins/async-report";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { number } from 'discourse/lib/formatter';
|
||||
|
||||
export default Ember.Component.extend(AsyncReport, {
|
||||
classNames: ["dashboard-table"],
|
||||
classNameBindings : ["isDisabled"],
|
||||
help: null,
|
||||
helpPage: null,
|
||||
isDisabled: Ember.computed.not("siteSettings.log_search_queries"),
|
||||
disabledLabel: "admin.dashboard.reports.disabled",
|
||||
|
||||
@computed("report")
|
||||
values(report) {
|
||||
if (!report) return;
|
||||
return Ember.makeArray(report.data)
|
||||
.map(x => {
|
||||
return [ x[0], number(x[1]), x[2] ];
|
||||
});
|
||||
},
|
||||
|
||||
@computed("report")
|
||||
labels(report) {
|
||||
if (!report) return;
|
||||
return Ember.makeArray(report.labels);
|
||||
},
|
||||
|
||||
loadReport(report_json) {
|
||||
this._setPropertiesFromReport(Report.create(report_json));
|
||||
},
|
||||
|
||||
fetchReport() {
|
||||
if (this.get("isDisabled")) return;
|
||||
|
||||
this.set("isLoading", true);
|
||||
|
||||
let payload = { data: { async: true } };
|
||||
|
||||
if (this.get("startDate")) {
|
||||
payload.data.start_date = this.get("startDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
||||
}
|
||||
|
||||
if (this.get("endDate")) {
|
||||
payload.data.end_date = this.get("endDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
||||
}
|
||||
|
||||
ajax(this.get("dataSource"), payload)
|
||||
.then((response) => {
|
||||
this.set('reportKey', response.report.report_key);
|
||||
this.loadReport(response.report);
|
||||
}).finally(() => {
|
||||
if (!Ember.isEmpty(this.get("report.data"))) {
|
||||
this.set("isLoading", false);
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
|
@ -57,7 +57,11 @@ export default Ember.Mixin.create({
|
|||
@computed("report")
|
||||
labels(report) {
|
||||
if (!report) return;
|
||||
return Ember.makeArray(report.data).map(r => r.x);
|
||||
if (report.labels) {
|
||||
return Ember.makeArray(report.labels);
|
||||
} else {
|
||||
return Ember.makeArray(report.data).map(r => r.x);
|
||||
}
|
||||
},
|
||||
|
||||
@computed("report")
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{{#unless hasBlock}}
|
||||
{{#each values as |value|}}
|
||||
{{#unless hasBlock}}
|
||||
{{#each values as |value|}}
|
||||
<tr>
|
||||
<td>{{number value}}</td>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
{{yield (hash report=report)}}
|
||||
{{/unless}}
|
||||
</tr>
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
{{yield (hash report=report)}}
|
||||
{{/unless}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
{{#if isDisabled}}
|
||||
{{{i18n disabledLabel}}}
|
||||
{{else}}
|
||||
{{#conditional-loading-section isLoading=isLoading title=report.title}}
|
||||
<div class="table-title">
|
||||
<h3>{{report.title}}</h3>
|
||||
|
||||
{{#if help}}
|
||||
<a href="{{helpPage}}">{{i18n help}}</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{{#each labels as |label|}}
|
||||
<th>{{label}}</th>
|
||||
{{/each}}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{{#each values as |value|}}
|
||||
<tr>
|
||||
{{#each value as |v|}}
|
||||
<td>{{v}}</td>
|
||||
{{/each}}
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{yield}}
|
||||
</div>
|
||||
{{/conditional-loading-section}}
|
||||
{{/if}}
|
|
@ -87,6 +87,7 @@
|
|||
</div>
|
||||
|
||||
{{#dashboard-inline-table dataSourceName="users_by_type" lastRefreshedAt=lastRefreshedAt as |context|}}
|
||||
<tr>
|
||||
{{#each context.report.data as |data|}}
|
||||
<td>
|
||||
<a href="/admin/users/list/{{data.key}}">
|
||||
|
@ -94,9 +95,11 @@
|
|||
</a>
|
||||
</td>
|
||||
{{/each}}
|
||||
</tr>
|
||||
{{/dashboard-inline-table}}
|
||||
|
||||
{{#dashboard-inline-table dataSourceName="users_by_trust_level" lastRefreshedAt=lastRefreshedAt as |context|}}
|
||||
<tr>
|
||||
{{#each context.report.data as |data|}}
|
||||
<td>
|
||||
<a href="/admin/users/list/{{data.key}}">
|
||||
|
@ -104,6 +107,7 @@
|
|||
</a>
|
||||
</td>
|
||||
{{/each}}
|
||||
</tr>
|
||||
{{/dashboard-inline-table}}
|
||||
|
||||
{{#conditional-loading-section isLoading=isLoading title=(i18n "admin.dashboard.backups")}}
|
||||
|
@ -148,13 +152,47 @@
|
|||
</div>
|
||||
|
||||
<div class="section-column">
|
||||
{{#dashboard-table-trending-search
|
||||
{{#dashboard-inline-table
|
||||
dataSourceName="top_referred_topics"
|
||||
lastRefreshedAt=lastRefreshedAt
|
||||
limit=8
|
||||
as |context|}}
|
||||
{{#each context.report.data as |data|}}
|
||||
<tr>
|
||||
<td class='left'>
|
||||
<a href="{{data.topic_url}}">
|
||||
{{data.topic_title}}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{data.num_clicks}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{/dashboard-inline-table}}
|
||||
|
||||
{{#dashboard-inline-table
|
||||
limit=8
|
||||
dataSourceName="trending_search"
|
||||
isEnabled=logSearchQueriesEnabled
|
||||
disabledLabel="admin.dashboard.reports.trending_search.disabled"
|
||||
startDate=lastWeek
|
||||
endDate=endDate}}
|
||||
endDate=endDate as |context|}}
|
||||
{{#each context.report.data as |data|}}
|
||||
<tr>
|
||||
<td class='left'>
|
||||
{{data.term}}
|
||||
</td>
|
||||
<td>
|
||||
{{number data.unique_searches}}
|
||||
</td>
|
||||
<td>
|
||||
{{data.ctr}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{{i18n "admin.dashboard.reports.trending_search.more"}}}
|
||||
{{/dashboard-table-trending-search}}
|
||||
</div>
|
||||
{{/dashboard-inline-table}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -120,6 +120,9 @@
|
|||
border: 1px solid $primary-low;
|
||||
text-align: center;
|
||||
}
|
||||
td.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td.value {
|
||||
i {
|
||||
|
|
|
@ -27,12 +27,18 @@ class Admin::ReportsController < Admin::AdminController
|
|||
facets = params[:facets].map { |s| s.to_s.to_sym }
|
||||
end
|
||||
|
||||
limit = nil
|
||||
if params.has_key?(:limit) && params[:limit].to_i > 0
|
||||
limit = params[:limit].to_i
|
||||
end
|
||||
|
||||
report = Report.find(report_type,
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
category_id: category_id,
|
||||
group_id: group_id,
|
||||
facets: facets,
|
||||
limit: limit,
|
||||
async: params[:async])
|
||||
|
||||
raise Discourse::NotFound if report.blank?
|
||||
|
|
|
@ -14,6 +14,7 @@ module Jobs
|
|||
report.category_id = args['category_id'] if args['category_id']
|
||||
report.group_id = args['group_id'] if args['group_id']
|
||||
report.facets = args['facets'].map(&:to_sym) if args['facets']
|
||||
report.limit = args['limit'].to_i if args['limit']
|
||||
|
||||
Report.send("report_#{type}", report)
|
||||
json = report.as_json
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class IncomingLinksReport
|
||||
|
||||
attr_accessor :type, :data, :y_titles
|
||||
attr_accessor :type, :data, :y_titles, :start_date, :limit
|
||||
|
||||
def initialize(type)
|
||||
@type = type
|
||||
|
@ -14,7 +14,8 @@ class IncomingLinksReport
|
|||
title: I18n.t("reports.#{self.type}.title"),
|
||||
xaxis: I18n.t("reports.#{self.type}.xaxis"),
|
||||
ytitles: self.y_titles,
|
||||
data: self.data
|
||||
data: self.data,
|
||||
start_date: start_date
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -24,6 +25,10 @@ class IncomingLinksReport
|
|||
|
||||
# Load the report
|
||||
report = IncomingLinksReport.new(type)
|
||||
|
||||
report.start_date = _opts[:start_date] || 30.days.ago
|
||||
report.limit = _opts[:limit].to_i if _opts[:limit]
|
||||
|
||||
send(report_method, report)
|
||||
report
|
||||
end
|
||||
|
@ -43,19 +48,19 @@ class IncomingLinksReport
|
|||
report.data = report.data.sort_by { |x| x[:num_clicks] }.reverse[0, 10]
|
||||
end
|
||||
|
||||
def self.per_user
|
||||
def self.per_user(start_date:)
|
||||
@per_user_query ||= IncomingLink
|
||||
.where('incoming_links.created_at > ? AND incoming_links.user_id IS NOT NULL', 30.days.ago)
|
||||
.where('incoming_links.created_at > ? AND incoming_links.user_id IS NOT NULL', start_date)
|
||||
.joins(:user)
|
||||
.group('users.username')
|
||||
end
|
||||
|
||||
def self.link_count_per_user
|
||||
per_user.count
|
||||
def self.link_count_per_user(start_date:)
|
||||
per_user(start_date: start_date).count
|
||||
end
|
||||
|
||||
def self.topic_count_per_user
|
||||
per_user.joins(:post).count("DISTINCT posts.topic_id")
|
||||
def self.topic_count_per_user(start_date:)
|
||||
per_user(start_date: start_date).joins(:post).count("DISTINCT posts.topic_id")
|
||||
end
|
||||
|
||||
# Return top 10 domains that brought traffic to the site within the last 30 days
|
||||
|
@ -64,7 +69,7 @@ class IncomingLinksReport
|
|||
report.y_titles[:num_topics] = I18n.t("reports.#{report.type}.num_topics")
|
||||
report.y_titles[:num_users] = I18n.t("reports.#{report.type}.num_users")
|
||||
|
||||
num_clicks = link_count_per_domain
|
||||
num_clicks = link_count_per_domain(start_date: start_date)
|
||||
num_topics = topic_count_per_domain(num_clicks.keys)
|
||||
report.data = []
|
||||
num_clicks.each_key do |domain|
|
||||
|
@ -73,8 +78,8 @@ class IncomingLinksReport
|
|||
report.data = report.data.sort_by { |x| x[:num_clicks] }.reverse[0, 10]
|
||||
end
|
||||
|
||||
def self.link_count_per_domain(limit = 10)
|
||||
IncomingLink.where('incoming_links.created_at > ?', 30.days.ago)
|
||||
def self.link_count_per_domain(limit: 10, start_date:)
|
||||
IncomingLink.where('incoming_links.created_at > ?', start_date)
|
||||
.joins(incoming_referer: :incoming_domain)
|
||||
.group('incoming_domains.name')
|
||||
.order('count_all DESC')
|
||||
|
@ -95,8 +100,8 @@ class IncomingLinksReport
|
|||
|
||||
def self.report_top_referred_topics(report)
|
||||
report.y_titles[:num_clicks] = I18n.t("reports.#{report.type}.num_clicks")
|
||||
num_clicks = link_count_per_topic
|
||||
num_clicks = num_clicks.to_a.sort_by { |x| x[1] }.last(10).reverse # take the top 10
|
||||
num_clicks = link_count_per_topic(start_date: report.start_date)
|
||||
num_clicks = num_clicks.to_a.sort_by { |x| x[1] }.last(report.limit || 10).reverse
|
||||
report.data = []
|
||||
topics = Topic.select('id, slug, title').where('id in (?)', num_clicks.map { |z| z[0] })
|
||||
num_clicks.each do |topic_id, num_clicks_element|
|
||||
|
@ -108,9 +113,9 @@ class IncomingLinksReport
|
|||
report.data
|
||||
end
|
||||
|
||||
def self.link_count_per_topic
|
||||
def self.link_count_per_topic(start_date:)
|
||||
IncomingLink.joins(:post)
|
||||
.where('incoming_links.created_at > ? AND topic_id IS NOT NULL', 30.days.ago)
|
||||
.where('incoming_links.created_at > ? AND topic_id IS NOT NULL', start_date)
|
||||
.group('topic_id')
|
||||
.count
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ class Report
|
|||
|
||||
attr_accessor :type, :data, :total, :prev30Days, :start_date,
|
||||
:end_date, :category_id, :group_id, :labels, :async,
|
||||
:prev_period, :facets
|
||||
:prev_period, :facets, :limit
|
||||
|
||||
def self.default_days
|
||||
30
|
||||
|
@ -24,7 +24,8 @@ class Report
|
|||
report.start_date.to_date.strftime("%Y%m%d"),
|
||||
report.end_date.to_date.strftime("%Y%m%d"),
|
||||
report.group_id,
|
||||
report.facets
|
||||
report.facets,
|
||||
report.limit
|
||||
].map(&:to_s).join(':')
|
||||
end
|
||||
|
||||
|
@ -55,6 +56,7 @@ class Report
|
|||
json[:total] = total if total
|
||||
json[:prev_period] = prev_period if prev_period
|
||||
json[:prev30Days] = self.prev30Days if self.prev30Days
|
||||
json[:limit] = self.limit if self.limit
|
||||
|
||||
if type == 'page_view_crawler_reqs'
|
||||
json[:related_report] = Report.find('web_crawlers', start_date: start_date, end_date: end_date)&.as_json
|
||||
|
@ -77,6 +79,7 @@ class Report
|
|||
report.group_id = opts[:group_id] if opts[:group_id]
|
||||
report.async = opts[:async] || false
|
||||
report.facets = opts[:facets] || [:total, :prev30Days]
|
||||
report.limit = opts[:limit] if opts[:limit]
|
||||
report_method = :"report_#{type}"
|
||||
|
||||
if respond_to?(report_method)
|
||||
|
@ -405,11 +408,18 @@ class Report
|
|||
report.data << { key: "silenced", x: label.call("silenced"), y: silenced } if silenced > 0
|
||||
end
|
||||
|
||||
def self.report_top_referred_topics(report)
|
||||
report.labels = [I18n.t("reports.top_referred_topics.xaxis"),
|
||||
I18n.t("reports.top_referred_topics.num_clicks")]
|
||||
result = IncomingLinksReport.find(:top_referred_topics, start_date: 7.days.ago, limit: report.limit)
|
||||
report.data = result.data
|
||||
end
|
||||
|
||||
def self.report_trending_search(report)
|
||||
report.data = []
|
||||
|
||||
select_sql = <<~SQL
|
||||
term,
|
||||
lower(term) term,
|
||||
COUNT(*) AS searches,
|
||||
SUM(CASE
|
||||
WHEN search_result_id IS NOT NULL THEN 1
|
||||
|
@ -420,9 +430,9 @@ class Report
|
|||
|
||||
trends = SearchLog.select(select_sql)
|
||||
.where('created_at > ? AND created_at <= ?', report.start_date, report.end_date)
|
||||
.group(:term)
|
||||
.group('lower(term)')
|
||||
.order('unique_searches DESC, click_through ASC, term ASC')
|
||||
.limit(20).to_a
|
||||
.limit(report.limit || 20).to_a
|
||||
|
||||
report.labels = [:term, :searches, :click_through].map { |key|
|
||||
I18n.t("reports.trending_search.labels.#{key}")
|
||||
|
@ -436,11 +446,11 @@ class Report
|
|||
trend.click_through.to_f / trend.searches.to_f
|
||||
end
|
||||
|
||||
report.data << [
|
||||
trend.term,
|
||||
trend.unique_searches,
|
||||
(ctr * 100).ceil(1).to_s + "%"
|
||||
]
|
||||
report.data << {
|
||||
term: trend.term,
|
||||
unique_searches: trend.unique_searches,
|
||||
ctr: (ctr * 100).ceil(1).to_s + "%"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue