Skip to content

Commit

Permalink
feat(stats): Display subseries in tooltip (#73774)
Browse files Browse the repository at this point in the history
  • Loading branch information
priscilawebdev authored Jul 5, 2024
1 parent c6a7245 commit 479ea5a
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 20 deletions.
4 changes: 4 additions & 0 deletions static/app/components/charts/baseChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ interface TooltipOption
name: string,
seriesParams?: TooltipComponentFormatterCallback<any>
) => string;
/**
* If true does not display sublabels with a value of 0.
*/
skipZeroValuedSubLabels?: boolean;
/**
* Array containing data that is used to display indented sublabels.
*/
Expand Down
13 changes: 12 additions & 1 deletion static/app/components/charts/components/tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ export type FormatterOptions = Pick<
* Limit the number of series rendered in the tooltip and display "+X more".
*/
limit?: number;
/**
* If true does not display sublabels with a value of 0.
*/
skipZeroValuedSubLabels?: boolean;
/**
* Array containing data that is used to display indented sublabels.
*/
Expand All @@ -138,6 +142,7 @@ export function getFormatter({
subLabels = [],
addSecondsToTimeFormat = false,
limit,
skipZeroValuedSubLabels,
}: FormatterOptions): TooltipComponentFormatterCallback<any> {
const getFilter = (seriesParam: any) => {
// Series do not necessarily have `data` defined, e.g. releases don't have `data`, but rather
Expand Down Expand Up @@ -255,13 +260,17 @@ export function getFormatter({
for (const subLabel of filteredSubLabels) {
const serieValue = subLabel.data[serie.dataIndex].value;

if (skipZeroValuedSubLabels && serieValue === 0) {
continue;
}

labelWithSubLabels.push(
`<div><span class="tooltip-label tooltip-label-indent"><strong>${
subLabel.label
}</strong></span> ${valueFormatter(serieValue)}</div>`
);

acc.total = acc.total + subLabel.data[serie.dataIndex].value;
acc.total = acc.total + serieValue;
}

acc.series.push(labelWithSubLabels.join(''));
Expand Down Expand Up @@ -336,6 +345,7 @@ export function computeChartTooltip(
hideDelay,
subLabels,
chartId,
skipZeroValuedSubLabels,
...props
}: Props,
theme: Theme
Expand All @@ -355,6 +365,7 @@ export function computeChartTooltip(
nameFormatter,
markerFormatter,
subLabels,
skipZeroValuedSubLabels,
});

return {
Expand Down
6 changes: 3 additions & 3 deletions static/app/views/organizationStats/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ describe('OrganizationStats', function () {
UsageStatsOrg: {
statsPeriod: DEFAULT_STATS_PERIOD,
interval: '1h',
groupBy: ['category', 'outcome'],
groupBy: ['category', 'outcome', 'reason'],
project: [-1],
field: ['sum(quantity)'],
},
Expand Down Expand Up @@ -314,7 +314,7 @@ describe('OrganizationStats', function () {
query: {
statsPeriod: DEFAULT_STATS_PERIOD,
interval: '1h',
groupBy: ['category', 'outcome'],
groupBy: ['category', 'outcome', 'reason'],
project: selectedProjects,
field: ['sum(quantity)'],
},
Expand Down Expand Up @@ -355,7 +355,7 @@ describe('OrganizationStats', function () {
query: {
statsPeriod: DEFAULT_STATS_PERIOD,
interval: '1h',
groupBy: ['category', 'outcome'],
groupBy: ['category', 'outcome', 'reason'],
project: selectedProject,
field: ['sum(quantity)'],
},
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/organizationStats/usageChart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const CHART_OPTIONS_DATA_TRANSFORM: SelectValue<ChartDataTransform>[] = [
},
];

const enum SeriesTypes {
export const enum SeriesTypes {
ACCEPTED = 'Accepted',
DROPPED = 'Dropped',
PROJECTED = 'Projected',
Expand Down
89 changes: 74 additions & 15 deletions static/app/views/organizationStats/usageStatsOrg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import type {WithRouterProps} from 'react-router';
import styled from '@emotion/styled';
import * as Sentry from '@sentry/react';
import isEqual from 'lodash/isEqual';
import startCase from 'lodash/startCase';
import moment from 'moment';

import {navigateTo} from 'sentry/actionCreators/navigation';
import type {TooltipSubLabel} from 'sentry/components/charts/components/tooltip';
import OptionSelector from 'sentry/components/charts/optionSelector';
import {InlineContainer, SectionHeading} from 'sentry/components/charts/styles';
import type {DateTimeObject} from 'sentry/components/charts/utils';
Expand All @@ -30,7 +32,11 @@ import {
} from './usageChart/utils';
import type {UsageSeries, UsageStat} from './types';
import type {ChartStats, UsageChartProps} from './usageChart';
import UsageChart, {CHART_OPTIONS_DATA_TRANSFORM, ChartDataTransform} from './usageChart';
import UsageChart, {
CHART_OPTIONS_DATA_TRANSFORM,
ChartDataTransform,
SeriesTypes,
} from './usageChart';
import UsageStatsPerMin from './usageStatsPerMin';
import {formatUsageWithUnits, getFormatUsageOptions, isDisplayUtc} from './utils';

Expand Down Expand Up @@ -115,7 +121,7 @@ class UsageStatsOrganization<
return {
...queryDatetime,
interval: getSeriesApiInterval(dataDatetime),
groupBy: ['category', 'outcome'],
groupBy: ['category', 'outcome', 'reason'],
project: projectIds,
field: ['sum(quantity)'],
};
Expand All @@ -136,6 +142,7 @@ class UsageStatsOrganization<
chartDateTimezoneDisplay: string;
chartDateUtc: boolean;
chartStats: ChartStats;
chartSubLabels: TooltipSubLabel[];
chartTransform: ChartDataTransform;
dataError?: Error;
} {
Expand Down Expand Up @@ -232,6 +239,7 @@ class UsageStatsOrganization<
chartDateEnd,
chartDateUtc,
chartTransform,
chartSubLabels,
} = this.chartData;

const hasError = error || !!dataError;
Expand All @@ -249,6 +257,10 @@ class UsageStatsOrganization<
usageDateShowUtc: chartDateUtc,
usageDateInterval: chartDateInterval,
usageStats: chartStats,
chartTooltip: {
subLabels: chartSubLabels,
skipZeroValuedSubLabels: true,
},
} as UsageChartProps;

return chartProps;
Expand Down Expand Up @@ -318,6 +330,7 @@ class UsageStatsOrganization<
total?: string;
};
chartStats: ChartStats;
chartSubLabels: TooltipSubLabel[];
dataError?: Error;
} {
const cardStats = {
Expand All @@ -332,9 +345,10 @@ class UsageStatsOrganization<
projected: [],
filtered: [],
};
const chartSubLabels: TooltipSubLabel[] = [];

if (!orgStats) {
return {cardStats, chartStats};
return {cardStats, chartStats, chartSubLabels};
}

try {
Expand Down Expand Up @@ -383,20 +397,63 @@ class UsageStatsOrganization<
count[outcome] += group.totals['sum(quantity)'];

group.series['sum(quantity)'].forEach((stat, i) => {
switch (outcome) {
case Outcome.ACCEPTED:
case Outcome.FILTERED:
usageStats[i][outcome] += stat;
return;
case Outcome.DROPPED:
case Outcome.RATE_LIMITED:
case Outcome.CARDINALITY_LIMITED:
case Outcome.INVALID:
usageStats[i].dropped.total += stat;
// TODO: add client discards to dropped?
const dataObject = {
name: orgStats.intervals[i],
value: stat,
};

const reason = String(group.by.reason ?? '');
const label = startCase(reason.replace(/-|_/g, ' '));
const existingSubLabel = chartSubLabels.find(
subLabel => subLabel.label === label
);

if (outcome === Outcome.FILTERED) {
usageStats[i][outcome] += stat;

if (existingSubLabel) {
existingSubLabel.data.push({
...dataObject,
value: dataObject.value,
});
return;
default:
}

chartSubLabels.push({
parentLabel: SeriesTypes.FILTERED,
label,
data: [dataObject],
});

return;
}

if (outcome === Outcome.ACCEPTED) {
usageStats[i][outcome] += stat;
return;
}

if (
outcome === Outcome.DROPPED ||
outcome === Outcome.RATE_LIMITED ||
outcome === Outcome.CARDINALITY_LIMITED ||
outcome === Outcome.INVALID
) {
usageStats[i].dropped.total += stat;

if (existingSubLabel) {
existingSubLabel.data.push({
...dataObject,
value: dataObject.value,
});
return;
}

chartSubLabels.push({
parentLabel: SeriesTypes.DROPPED,
label,
data: [dataObject],
});
}
});
});
Expand Down Expand Up @@ -441,6 +498,7 @@ class UsageStatsOrganization<
),
},
chartStats,
chartSubLabels,
};
} catch (err) {
Sentry.withScope(scope => {
Expand All @@ -452,6 +510,7 @@ class UsageStatsOrganization<
return {
cardStats,
chartStats,
chartSubLabels,
dataError: new Error('Failed to parse stats data'),
};
}
Expand Down

0 comments on commit 479ea5a

Please sign in to comment.