FEATURE: adds a new chart report to track pageviews (#6913)

This commit is contained in:
Joffrey JAFFEUX 2019-01-21 15:17:04 +01:00 committed by GitHub
parent 52f2e0d6b9
commit b95165b838
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 283 additions and 16 deletions

View File

@ -0,0 +1,127 @@
import { number } from "discourse/lib/formatter";
import loadScript from "discourse/lib/load-script";
export default Ember.Component.extend({
classNames: ["admin-report-chart", "admin-report-stacked-chart"],
init() {
this._super(...arguments);
this.resizeHandler = () =>
Ember.run.debounce(this, this._scheduleChartRendering, 500);
},
didInsertElement() {
this._super(...arguments);
$(window).on("resize.chart", this.resizeHandler);
},
willDestroyElement() {
this._super(...arguments);
$(window).off("resize.chart", this.resizeHandler);
this._resetChart();
},
didReceiveAttrs() {
this._super(...arguments);
Ember.run.debounce(this, this._scheduleChartRendering, 100);
},
_scheduleChartRendering() {
Ember.run.schedule("afterRender", () => {
this._renderChart(this.get("model"), this.$(".chart-canvas"));
});
},
_renderChart(model, $chartCanvas) {
if (!$chartCanvas || !$chartCanvas.length) return;
const context = $chartCanvas[0].getContext("2d");
const chartData = Ember.makeArray(
model.get("chartData") || model.get("data")
);
const data = {
labels: chartData[0].data.map(cd => cd.x),
datasets: chartData.map(cd => {
return {
label: cd.req,
stack: "pageviews-stack",
data: cd.data.map(d => Math.round(parseFloat(d.y))),
backgroundColor: cd.color
};
})
};
loadScript("/javascripts/Chart.min.js").then(() => {
this._resetChart();
this._chart = new window.Chart(context, this._buildChartConfig(data));
});
},
_buildChartConfig(data) {
return {
type: "bar",
data,
responsive: true,
maintainAspectRatio: false,
options: {
hover: { mode: "index" },
tooltips: {
mode: "index",
intersect: false,
callbacks: {
title: tooltipItem =>
moment(tooltipItem[0].xLabel, "YYYY-MM-DD").format("LL")
}
},
legend: { display: false },
layout: {
padding: {
left: 0,
top: 0,
right: 0,
bottom: 0
}
},
scales: {
yAxes: [
{
stacked: true,
display: true,
ticks: {
userCallback: label => {
if (Math.floor(label) === label) return label;
},
callback: label => number(label)
}
}
],
xAxes: [
{
display: true,
gridLines: { display: false },
type: "time",
time: {
parser: "YYYY-MM-DD",
minUnit: "day"
}
}
]
}
}
};
},
_resetChart() {
if (this._chart) {
this._chart.destroy();
this._chart = null;
}
}
});

View File

@ -34,6 +34,7 @@ function collapseWeekly(data, average) {
bucket = bucket || { x: data[i].x, y: 0 };
bucket.y += data[i].y;
}
return aggregate;
}
@ -389,7 +390,19 @@ export default Ember.Component.extend({
_loadReport(jsonReport) {
Report.fillMissingDates(jsonReport, { filledField: "chartData" });
if (jsonReport.chartData && jsonReport.chartData.length > 40) {
if (jsonReport.chartData && jsonReport.modes[0] === "stacked_chart") {
jsonReport.chartData = jsonReport.chartData.map(chartData => {
if (chartData.length > 40) {
return {
data: collapseWeekly(chartData.data),
req: chartData.req,
color: chartData.color
};
} else {
return chartData;
}
});
} else if (jsonReport.chartData && jsonReport.chartData.length > 40) {
jsonReport.chartData = collapseWeekly(
jsonReport.chartData,
jsonReport.average

View File

@ -473,11 +473,26 @@ Report.reopenClass({
.utc(report[endDate])
.locale("en")
.format("YYYY-MM-DD");
report[filledField] = fillMissingDates(
JSON.parse(JSON.stringify(report[dataField])),
startDateFormatted,
endDateFormatted
);
if (report.modes[0] === "stacked_chart") {
report[filledField] = report[dataField].map(rep => {
return {
req: rep.req,
color: rep.color,
data: fillMissingDates(
JSON.parse(JSON.stringify(rep.data)),
startDateFormatted,
endDateFormatted
)
};
});
} else {
report[filledField] = fillMissingDates(
JSON.parse(JSON.stringify(report[dataField])),
startDateFormatted,
endDateFormatted
);
}
}
},

View File

@ -0,0 +1,3 @@
<div class="chart-canvas-container">
<canvas class="chart-canvas" height="250"></canvas>
</div>

View File

@ -14,6 +14,11 @@
<div class="section-body">
<div class="charts">
{{admin-report
dataSourceName="consolidated_page_views"
forcedModes="stacked-chart"
filters=filters}}
{{admin-report
dataSourceName="signups"
showTrend=true

View File

@ -998,6 +998,7 @@ table#user-badges {
@import "common/admin/admin_report";
@import "common/admin/admin_report_counters";
@import "common/admin/admin_report_chart";
@import "common/admin/admin_report_stacked_chart";
@import "common/admin/admin_report_table";
@import "common/admin/admin_report_inline_table";
@import "common/admin/dashboard_previous";

View File

@ -0,0 +1,9 @@
.admin-report-stacked-chart {
display: flex;
justify-content: space-between;
.chart-canvas-container {
flex: 5;
margin: 0;
}
}

View File

@ -144,6 +144,11 @@
-ms-grid-rows: 1fr 1fr;
.admin-report {
-ms-grid-column-span: 4;
&.consolidated-page-views {
-ms-grid-column-span: 12;
}
&:nth-of-type(1) {
-ms-grid-row: 1;
-ms-grid-column: 1;
@ -172,6 +177,10 @@
.admin-report {
grid-column: span 4;
&.consolidated-page-views {
grid-column: span 12;
}
}
@include breakpoint(medium) {

View File

@ -196,6 +196,47 @@ class Report
report
end
def self.report_consolidated_page_views(report)
filters = %w[
page_view_crawler
page_view_logged_in
page_view_anon
]
report.modes = [:stacked_chart]
tertiary = ColorScheme.hex_for_name('tertiary') || '0088cc'
danger = ColorScheme.hex_for_name('danger') || 'e45735'
requests = filters.map do |filter|
color = report.rgba_color(tertiary)
if filter == "page_view_anon"
color = report.rgba_color(tertiary, 0.5)
end
if filter == "page_view_crawler"
color = report.rgba_color(danger, 0.75)
end
{
req: filter,
color: color,
data: ApplicationRequest.where(req_type: ApplicationRequest.req_types[filter])
}
end
requests.each do |request|
request[:data] = request[:data].where('date >= ? AND date <= ?', report.start_date, report.end_date)
.order(date: :asc)
.group(:date)
.sum(:count)
.map { |date, count| { x: date, y: count } }
end
report.data = requests
end
def self.req_report(report, filter = nil)
data =
if filter == :page_view_total
@ -1505,16 +1546,6 @@ class Report
end
end
private
def hex_to_rgbs(hex_color)
hex_color = hex_color.gsub('#', '')
rgbs = hex_color.scan(/../)
rgbs
.map! { |color| color.hex }
.map! { |rgb| rgb.to_i }
end
def rgba_color(hex, opacity = 1)
if hex.size == 3
chars = hex.scan(/\w/)
@ -1529,4 +1560,14 @@ class Report
"rgba(#{rgbs.join(',')},#{opacity})"
end
private
def hex_to_rgbs(hex_color)
hex_color = hex_color.gsub('#', '')
rgbs = hex_color.scan(/../)
rgbs
.map! { |color| color.hex }
.map! { |rgb| rgb.to_i }
end
end

View File

@ -967,6 +967,11 @@ en:
xaxis: "Day"
yaxis: "Number of new contributors"
description: "Number of users who made their first post during this period."
consolidated_page_views:
title: "Consolidated Pageviews"
xaxis: "Pagesviews"
yaxis: "Day"
description: "Pageviews for logged in users, anonymous users and crawlers."
dau_by_mau:
title: "DAU/MAU"
xaxis: "Day"

View File

@ -1097,4 +1097,43 @@ describe Report do
include_examples "no data"
end
describe "report_top_uploads" do
after do
ApplicationRequest.clear_cache!
end
let(:reports) { Report.find('consolidated_page_views') }
context "with no data" do
it "works" do
reports.data.each do |report|
expect(report[:data]).to be_empty
end
end
end
context "with data" do
it "works" do
freeze_time(Time.now.at_midnight)
3.times { ApplicationRequest.increment!(:page_view_crawler) }
2.times { ApplicationRequest.increment!(:page_view_logged_in) }
ApplicationRequest.increment!(:page_view_anon)
ApplicationRequest.write_cache!
page_view_crawler_report = reports.data.find { |r| r[:req] == "page_view_crawler" }
page_view_logged_in_report = reports.data.find { |r| r[:req] == "page_view_logged_in" }
page_view_anon_report = reports.data.find { |r| r[:req] == "page_view_anon" }
expect(page_view_crawler_report[:color]).to eql("rgba(228,87,53,0.75)")
expect(page_view_crawler_report[:data][0][:y]).to eql(3)
expect(page_view_logged_in_report[:color]).to eql("rgba(0,136,204,1)")
expect(page_view_logged_in_report[:data][0][:y]).to eql(2)
expect(page_view_anon_report[:color]).to eql("rgba(0,136,204,0.5)")
expect(page_view_anon_report[:data][0][:y]).to eql(1)
end
end
end
end