Skip to content

Commit 9749e03

Browse files
[O2B-970] Add mean efficiency line (#1276)
* [O2B-970] Add mean efficiency line * Handle empty datasets * Add time label
1 parent 20d043a commit 9749e03

5 files changed

Lines changed: 152 additions & 62 deletions

File tree

lib/public/components/common/chart/rendering/LineChartRenderer.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { quadtree } from '/assets/d3.min.js';
1515
import { ChartRenderer } from './ChartRenderer.js';
1616
import { renderDatasetAsLine } from './dataset/renderDatasetAsLine.js';
1717
import { renderDatasetAsPointCloud } from './dataset/renderDatasetAsPointCloud.js';
18+
import { renderDatasetLegend } from './legend/renderDatasetLegend.js';
1819

1920
/**
2021
* @typedef LineChartDatasetConfiguration
@@ -68,7 +69,7 @@ export class LineChartRenderer extends ChartRenderer {
6869
getY: this._stackedY ? ({ y }) => this.yScale(y[datasetIndex]) : ({ y }) => this.yScale(y),
6970
};
7071

71-
const { line: lineConfiguration, point: pointConfiguration } = this._datasets[datasetIndex] || {};
72+
const { line: lineConfiguration, point: pointConfiguration, legend: legendConfiguration } = this._datasets[datasetIndex] || {};
7273

7374
// Display datasets
7475

@@ -79,8 +80,6 @@ export class LineChartRenderer extends ChartRenderer {
7980
renderDatasetAsLine(
8081
svg,
8182
points.filter((point) => (pointLocator.getY(point) ?? null) !== null),
82-
this.xScale,
83-
this.yScale,
8483
pointLocator,
8584
this._chartDrawingZone,
8685
lineConfiguration,
@@ -92,6 +91,11 @@ export class LineChartRenderer extends ChartRenderer {
9291
renderDatasetAsPointCloud(svg, points, pointLocator, this._chartDrawingZone, pointConfiguration);
9392
}
9493

94+
// Display dataset legend
95+
if (legendConfiguration) {
96+
renderDatasetLegend(svg, points, pointLocator, this._chartDrawingZone, legendConfiguration);
97+
}
98+
9599
const datasetQuadtree = quadtree()
96100
.x(pointLocator.getX)
97101
.y(pointLocator.getY)

lib/public/components/common/chart/rendering/dataset/renderDatasetAsLine.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ import { getUniqueId } from '../../../../../utilities/getUniqueId.js';
2525
*
2626
* @param {SVGElement} svg the svg in which graph must be rendered
2727
* @param {Point[]} points the dataset to display
28-
* @param {d3.AxisScale} xScale the horizontal scale of the dataset
29-
* @param {d3.AxisScale} yScale the vertical scale of the dataset
3028
* @param {PointLocator} pointLocator locator to compute the points coordinates
3129
* @param {BoundingBox} chartDrawingZone the chart drawing zone outside which points are not rendered
3230
* @param {LineGraphConfiguration} [configuration] the line graph configuration
@@ -35,8 +33,6 @@ import { getUniqueId } from '../../../../../utilities/getUniqueId.js';
3533
export const renderDatasetAsLine = (
3634
svg,
3735
points,
38-
xScale,
39-
yScale,
4036
pointLocator,
4137
chartDrawingZone,
4238
configuration = {},
@@ -59,18 +55,23 @@ export const renderDatasetAsLine = (
5955
.x(pointLocator.getX)
6056
.y(pointLocator.getY)(points);
6157

62-
const xLeft = chartDrawingZone.left;
63-
const xRight = chartDrawingZone.left + chartDrawingZone.width;
64-
const yBottom = chartDrawingZone.top + chartDrawingZone.height;
65-
if (points.length > 1) {
66-
pathDraw = `M ${xLeft},${yBottom} ${pathDraw.substring(1)} ${xRight},${yBottom}`;
58+
if (configuration.fill) {
59+
const xLeft = chartDrawingZone.left;
60+
const xRight = chartDrawingZone.left + chartDrawingZone.width;
61+
const yBottom = chartDrawingZone.top + chartDrawingZone.height;
62+
if (points.length > 1) {
63+
pathDraw = `M ${xLeft},${yBottom} ${pathDraw.substring(1)} ${xRight},${yBottom}`;
64+
}
6765
}
6866

6967
path.attr('d', pathDraw);
7068

7169
if (configuration.stroke) {
7270
path.attr('stroke', configuration.stroke);
7371
}
72+
if (configuration.thickness) {
73+
path.attr('stroke-width', configuration.thickness);
74+
}
7475
if (configuration.fill) {
7576
path.attr('fill', configuration.fill);
7677
} else {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
14+
import { select } from '/assets/d3.min.js';
15+
16+
/**
17+
* @typedef DatasetLegendConfiguration
18+
* @property {string} text the content of the legend
19+
*/
20+
21+
const DATASET_MARGIN = 10;
22+
const FONT_SIZE = 20;
23+
24+
/**
25+
* Render the given dataset configuration
26+
*
27+
* @param {SVGElement} svg the svg in which legend should be displayed
28+
* @param {Point[]} points the points of the dataset, used to compute the legend position
29+
* @param {PointLocator} pointLocator the point locator to locate points on chart
30+
* @param {BoundingBox} chartDrawingZone the chart drawing zone
31+
* @param {DatasetLegendConfiguration} legendConfiguration the configuration of the dataset's legend
32+
* @return {void}
33+
*/
34+
export const renderDatasetLegend = (svg, points, pointLocator, chartDrawingZone, legendConfiguration) => {
35+
if (!points.length) {
36+
return;
37+
}
38+
39+
const { text } = legendConfiguration;
40+
41+
// For now, dataset position is only top-end
42+
const lastPoint = points[points.length - 1];
43+
const left = Math.min(pointLocator.getX(lastPoint) - DATASET_MARGIN, chartDrawingZone.left + chartDrawingZone.width);
44+
const top = Math.max(pointLocator.getY(lastPoint) - DATASET_MARGIN, chartDrawingZone.top);
45+
46+
select(svg)
47+
.append('text')
48+
.text(text)
49+
.attr('text-anchor', 'end')
50+
.attr('fill', 'black')
51+
.attr('stroke', 'white')
52+
.attr('paint-order', 'stroke')
53+
.attr('stroke-width', '5')
54+
.attr('font-size', FONT_SIZE)
55+
.attr('transform', `translate(${left}, ${top})`);
56+
};

lib/public/views/Statistics/StatisticsPage.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,11 @@ export const StatisticsPage = ({ statisticsModel }) => {
6464
h('h3', `Efficiency - ${periodLabel}`),
6565
h(
6666
'.flex-grow.chart-box',
67-
efficiencyChartComponent(fillStatistics, () => statisticsModel.notify()),
67+
efficiencyChartComponent(
68+
fillStatistics,
69+
() => statisticsModel.notify(),
70+
statisticsModel.timeRangeFilterModel.periodLabel,
71+
),
6872
),
6973
],
7074
}),

lib/public/views/Statistics/charts/efficiencyChartComponent.js

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -25,61 +25,86 @@ import { formatPercentage } from '../../../utilities/formatting/formatPercentage
2525
*
2626
* @param {LhcFillStatistics[]} statistics the statistics to represent
2727
* @param {function} [onPointHover] function called when a point is hovered
28+
* @param {string} [periodLabel] if it applies, the label of the currently selected period
2829
* @return {Component} the resulting component
2930
*/
30-
export const efficiencyChartComponent = (statistics, onPointHover) => lineChartComponent(
31-
statistics.map(({ fillNumber, efficiency, efficiencyLossAtStart }) => ({
32-
x: fillNumber,
33-
y: [efficiency + efficiencyLossAtStart, efficiency],
34-
})),
35-
{
36-
placeholder: 'No fill on the given period',
37-
tooltip: ({ x, y }) => h('', [
38-
h('h5', `Fill #${x}`),
39-
h('.flex-row.items-center.g2', [
40-
h('.tooltip-bullet', { style: `background-color: ${ChartColors.Blue.dark}` }),
41-
h('', `Efficiency: ${formatPercentage(y[1], 0)}`),
42-
]),
43-
h('.flex-row.items-center.g2', [
44-
h('.tooltip-bullet', { style: `background-color: ${ChartColors.Red.dark}` }),
45-
h('', `Eff. loss at start: ${formatPercentage(y[0] - y[1], 2)}`),
31+
export const efficiencyChartComponent = (statistics, onPointHover, periodLabel) => {
32+
const points = [];
33+
let efficiencySum = 0;
34+
for (const { fillNumber, efficiency, efficiencyLossAtStart } of statistics) {
35+
points.push({
36+
x: fillNumber,
37+
y: [efficiency + efficiencyLossAtStart, efficiency],
38+
});
39+
efficiencySum += efficiency;
40+
}
41+
42+
const meanEfficiency = efficiencySum / points.length;
43+
if (points.length) {
44+
points[0].y.push(meanEfficiency);
45+
points[points.length - 1].y.push(meanEfficiency);
46+
}
47+
48+
return lineChartComponent(
49+
points,
50+
{
51+
placeholder: 'No fill on the given period',
52+
tooltip: ({ x, y }) => h('', [
53+
h('h5', `Fill #${x}`),
54+
h('.flex-row.items-center.g2', [
55+
h('.tooltip-bullet', { style: `background-color: ${ChartColors.Blue.dark}` }),
56+
h('', `Efficiency: ${formatPercentage(y[1], 0)}`),
57+
]),
58+
h('.flex-row.items-center.g2', [
59+
h('.tooltip-bullet', { style: `background-color: ${ChartColors.Red.dark}` }),
60+
h('', `Eff. loss at start: ${formatPercentage(y[0] - y[1], 2)}`),
61+
]),
4662
]),
47-
]),
48-
onPointHover,
49-
chartConfiguration: {
50-
axis: {
51-
y: {
52-
ticks: { format: (efficiency) => formatPercentage(efficiency, 0) },
53-
label: 'Efficiency',
54-
min: 0,
55-
max: 1,
56-
},
57-
x: {
58-
label: 'FILL',
59-
},
60-
},
61-
datasets: [
62-
{
63-
point: {
64-
fill: ChartColors.Red.dark,
65-
radius: 5,
63+
onPointHover,
64+
chartConfiguration: {
65+
axis: {
66+
y: {
67+
ticks: { format: (efficiency) => formatPercentage(efficiency, 0) },
68+
label: 'Efficiency',
69+
min: 0,
70+
max: 1,
6671
},
67-
line: {
68-
stroke: ChartColors.Red.dark,
69-
fill: ChartColors.Red.light,
72+
x: {
73+
label: 'FILL',
7074
},
7175
},
72-
{
73-
point: {
74-
fill: ChartColors.Blue.dark,
75-
radius: 5,
76+
datasets: [
77+
{
78+
point: {
79+
fill: ChartColors.Red.dark,
80+
radius: 5,
81+
},
82+
line: {
83+
stroke: ChartColors.Red.dark,
84+
fill: ChartColors.Red.light,
85+
},
7686
},
77-
line: {
78-
stroke: ChartColors.Blue.dark,
79-
fill: ChartColors.Blue.light,
87+
{
88+
point: {
89+
fill: ChartColors.Blue.dark,
90+
radius: 5,
91+
},
92+
line: {
93+
stroke: ChartColors.Blue.dark,
94+
fill: ChartColors.Blue.light,
95+
},
8096
},
81-
},
82-
],
97+
{
98+
line: {
99+
stroke: ChartColors.Orange.dark,
100+
thickness: 3,
101+
},
102+
legend: {
103+
text: ['ALICE efficiency', periodLabel || '', '=', formatPercentage(meanEfficiency)].join(' '),
104+
},
105+
},
106+
],
107+
},
83108
},
84-
},
85-
);
109+
);
110+
};

0 commit comments

Comments
 (0)