blob: 67aaeab0e93c161f4896728bef1aee0b3c14a94e [file] [log] [blame]
// Contains functions to create plot data.
import { MISSING_DATA_SENTINEL } from '../const/const';
import { Anomaly, ColumnHeader, TraceSet } from '../json';
// Google chart's default color palette, two shades down
export const defaultColors = [
'#e36041', // rgb(227, 96, 65)
'#3fab46', // rgb(63, 171, 70)
'#ad32ad', // rgb(173, 50, 173)
'#6264bc', // rgb(98, 100, 188)
'#32add1', // rgb(50, 173, 209)
'#e36992', // rgb(227, 105, 146)
'#ffad32', // rgb(255, 173, 50)
'#84bb32', // rgb(132, 187, 50)
'#c65757', // rgb(184, 46, 46)
'#5a82aa', // rgb(90, 130, 170)
'#ad69ad', // rgb(173, 105, 173)
'#4ebbad', // rgb(78, 87, 173)
'#bbbb40', // rgb(187, 187, 64)
'#845bd6', // rgb(132, 91, 214)
'#eb8f32', // rgb(235, 143, 50)
'#5b84d6', // rgb(91, 132, 214)
'#a23838', // rgb(162, 56, 56)
'#5aa781', // rgb(90, 167, 129)
'#5574A6', // rgb(85, 116, 166)
'#3B3EAC', // rgb(59, 62, 172)
];
export interface DataPoint {
x: number | Date;
y: number;
anomaly: Anomaly | null;
}
export enum ChartAxisFormat {
Commit,
Date,
}
export interface ChartData {
xLabel: string;
yLabel: string;
chartAxisFormat: ChartAxisFormat;
lines: { [key: string]: DataPoint[] };
start: number | Date;
end: number | Date;
}
// convertFromDataframe converts DataFrame to any[][] that can be plugged into
// GoogleChart.data.
// This function effectively transposes the traceset so that the keys
// (i.e. jetstream2/box2D/etc) become the columns and the individual data points
// are the rows
export const convertFromDataframe = (
df: {
traceset: TraceSet;
header: (ColumnHeader | null)[] | null;
} | null,
domain: 'commit' | 'date' | 'both' = 'commit',
traceKey?: string
) => {
if ((df?.header?.length || 0) === 0) {
return null;
}
if (traceKey && !(traceKey in df!.traceset)) {
return null;
}
const keys = traceKey ? [traceKey] : Object.keys(df!.traceset);
const firstRow: any[] = [];
if (domain === 'commit' || domain === 'both') {
firstRow.push({ type: 'number', role: 'domain', label: 'Commit Position' });
}
if (domain === 'date' || domain === 'both') {
firstRow.push({ type: 'date', role: 'domain', label: 'Date' });
}
keys.forEach((k) => firstRow.push(k));
const rows: any[][] = [firstRow];
df!.header?.forEach((column, idx) => {
const row: any[] = [];
if (domain === 'commit' || domain === 'both') {
row.push(column!.offset);
}
if (domain === 'date' || domain === 'both') {
row.push(new Date(column!.timestamp * 1000));
}
keys.forEach((k) => {
const val = df!.traceset[k][idx];
row.push(val === MISSING_DATA_SENTINEL ? null : val);
});
rows.push(row);
});
return rows;
};
export function ConvertData(chartData: ChartData) {
/*
The data in the plot needs to be in the following format.
[
[x-axis_label, line1_label, line2_label, ...],
[x_value, line1_value, line2_value, ...], // first point
[x_value, line1_value, line2_value, ...], // second point
...
...
]
*/
const columns = [chartData.xLabel];
const lineKeys = Object.keys(chartData.lines);
lineKeys.forEach((key) => {
columns.push(key);
});
// The first row needs to be the column names
const rows: [any] = [columns];
const rowCount = chartData.lines[lineKeys[0]].length;
for (let i = 0; i < rowCount; i++) {
// Add the xValue which is the same for all lines
const row = [chartData.lines[lineKeys[0]][i].x];
lineKeys.forEach((key) => {
// For each line, add the y value for datapoint at index i.
row.push(chartData.lines[key][i].y);
});
rows[i + 1] = row;
}
return rows;
}
export function mainChartOptions(
style: CSSStyleDeclaration,
domain: string,
yAxis: string | undefined,
showZero: boolean = false
): google.visualization.LineChartOptions {
// The X axis can support either commit, or dates. Change the format
// based on the current chart's format.
const gridlineColor = style.getPropertyValue('--md-sys-color-inverse-on-surface');
const minAxisValue: number | undefined = showZero ? 0 : undefined;
const format = domain === 'commit' ? '#' : 'M/d/yy';
return {
// interpolateNulls will continue a line from the last known point to the
// next available if there's nulls inbetween.
interpolateNulls: true,
// selectionMode multiple allows you to select multiple points on a plot.
// we are going to only allow single so that on selection over an area, we
// can estimate the delta range. note that the default is single.
selectionMode: 'single',
// https://developers.google.com/chart/interactive/docs/gallery/areachart
// Defines how multiple data selections are rolled into the tooltip.
// for selectionMode: single, this should be none, such that we only allow
// one tooltip per seelction
aggregationTarget: 'none',
annotations: {
style: 'line',
},
tooltip: { trigger: 'none' },
pointSize: 3,
dataOpacity: 0.7,
titleTextStyle: { color: style.color },
hAxis: {
titleTextStyle: {
color: style.getPropertyValue('--plot-axes-title-color'),
},
textPosition: 'out',
textStyle: {
color: style.color,
},
gridlines: {
color: gridlineColor,
},
minorGridlines: {
color: gridlineColor,
},
format: format,
},
vAxis: {
minValue: minAxisValue,
title: yAxis,
titleTextStyle: {
color: style.getPropertyValue('--plot-axes-title-color'),
},
textPosition: 'out',
textStyle: { color: style.color },
gridlines: {
color: gridlineColor,
},
minorGridlines: {
color: gridlineColor,
},
viewWindowMode: 'maximized',
},
// define clearance to prevent axis labels and data from getting clipped
chartArea: {
left: 120,
right: 10,
top: 5,
bottom: 25,
},
colors: defaultColors,
legend: {
position: 'none',
},
backgroundColor: style.getPropertyValue('--plot-background-color-sk'),
series: {},
crosshair: {
trigger: 'both',
focused: {
opacity: 0.5,
},
},
};
}
export function SummaryChartOptions(
style: CSSStyleDeclaration,
domain: 'commit' | 'date'
): google.visualization.LineChartOptions {
const format = domain === 'commit' ? '#' : 'MMM dd, yy';
return {
interpolateNulls: true,
curveType: 'function',
hAxis: {
textPosition: 'out',
textStyle: {
color: style.color,
fontSize: 8,
},
gridlines: {
count: 10,
},
format: format,
},
vAxis: {
textPosition: 'out',
gridlines: {
color: 'transparent',
},
viewWindowMode: 'maximized',
},
tooltip: {
trigger: 'none',
},
chartArea: {
width: '100%',
bottom: 25, // prevents commit positions from being clipped
top: 0,
backgroundColor: {
stroke: 'black',
strokeWidth: 1,
},
},
// this value is inherited from plot-google-chart-sk
backgroundColor: style.getPropertyValue('--plot-background-color-sk'),
colors: [style.color],
};
}