Skip to content

Commit 5d13e29

Browse files
committed
feat: show registered anomalies as dashed segments on download charts
1 parent 433494c commit 5d13e29

3 files changed

Lines changed: 55 additions & 13 deletions

File tree

app/components/Package/TrendsChart.vue

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,9 @@ function formatXyDataset(
219219
color: accent.value,
220220
temperatureColors,
221221
useArea: true,
222+
dashIndices: dataset
223+
.map((item, index) => (item.hasAnomaly ? index : -1))
224+
.filter(index => index !== -1),
222225
}
223226
224227
if (selectedGranularity === 'weekly' && isWeeklyDataset(dataset)) {
@@ -275,19 +278,26 @@ function formatXyDataset(
275278
function extractSeriesPoints(
276279
selectedGranularity: ChartTimeGranularity,
277280
dataset: EvolutionData,
278-
): Array<{ timestamp: number; value: number }> {
281+
): Array<{ timestamp: number; value: number; hasAnomaly: boolean }> {
279282
if (selectedGranularity === 'weekly' && isWeeklyDataset(dataset)) {
280-
return dataset.map(d => ({ timestamp: d.timestampEnd, value: d.value }))
283+
return dataset.map(d => ({
284+
timestamp: d.timestampEnd,
285+
value: d.value,
286+
hasAnomaly: !!d.hasAnomaly,
287+
}))
281288
}
282289
if (
283290
(selectedGranularity === 'daily' && isDailyDataset(dataset)) ||
284291
(selectedGranularity === 'monthly' && isMonthlyDataset(dataset)) ||
285292
(selectedGranularity === 'yearly' && isYearlyDataset(dataset))
286293
) {
287-
return (dataset as Array<{ timestamp: number; value: number }>).map(d => ({
288-
timestamp: d.timestamp,
289-
value: d.value,
290-
}))
294+
return (dataset as Array<{ timestamp: number; value: number; hasAnomaly?: boolean }>).map(
295+
d => ({
296+
timestamp: d.timestamp,
297+
value: d.value,
298+
hasAnomaly: !!d.hasAnomaly,
299+
}),
300+
)
291301
}
292302
return []
293303
}
@@ -380,6 +390,11 @@ const isEstimationGranularity = computed(
380390
const supportsEstimation = computed(
381391
() => isEstimationGranularity.value && selectedMetric.value !== 'contributors',
382392
)
393+
394+
const hasDownloadAnomalies = computed(() =>
395+
normalisedDataset.value?.some(datapoint => !!datapoint.dashIndices.length),
396+
)
397+
383398
const shouldRenderEstimationOverlay = computed(() => !pending.value && supportsEstimation.value)
384399
385400
const startDate = usePermalink<string>('start', '', {
@@ -955,11 +970,13 @@ const effectiveDataSingle = computed<EvolutionData>(() => {
955970
granularity: displayedGranularity.value,
956971
})
957972
}
973+
958974
return applyDataCorrection(
959975
data as Array<{ value: number }>,
960976
settings.value.chartFilter,
961977
) as EvolutionData
962978
}
979+
963980
return data
964981
})
965982
@@ -991,7 +1008,10 @@ const chartData = computed<{
9911008
const granularity = displayedGranularity.value
9921009
9931010
const timestampSet = new Set<number>()
994-
const pointsByPackage = new Map<string, Array<{ timestamp: number; value: number }>>()
1011+
const pointsByPackage = new Map<
1012+
string,
1013+
Array<{ timestamp: number; value: number; hasAnomaly?: boolean }>
1014+
>()
9951015
9961016
for (const pkg of names) {
9971017
let data = state.evolutionsByPackage[pkg] ?? []
@@ -1005,6 +1025,7 @@ const chartData = computed<{
10051025
) as EvolutionData
10061026
}
10071027
const points = extractSeriesPoints(granularity, data)
1028+
10081029
pointsByPackage.set(pkg, points)
10091030
for (const p of points) timestampSet.add(p.timestamp)
10101031
}
@@ -1023,6 +1044,9 @@ const chartData = computed<{
10231044
name: pkg,
10241045
type: 'line',
10251046
series,
1047+
dashIndices: points
1048+
.map((item, index) => (item.hasAnomaly ? index : -1))
1049+
.filter(index => index !== -1),
10261050
} as VueUiXyDatasetItem
10271051
10281052
if (isListedFramework(pkg)) {
@@ -1045,6 +1069,7 @@ const normalisedDataset = computed(() => {
10451069
return {
10461070
...d,
10471071
series: [...d.series.slice(0, -1), projectedLastValue],
1072+
dashIndices: d.dashIndices ?? [],
10481073
}
10491074
})
10501075
})
@@ -1408,7 +1433,10 @@ function drawSvgPrintLegend(svg: Record<string, any>) {
14081433
})
14091434
14101435
// Inject the estimation legend item when necessary
1411-
if (supportsEstimation.value && !isEndDateOnPeriodEnd.value && !isZoomed.value) {
1436+
if (
1437+
(supportsEstimation.value && !isEndDateOnPeriodEnd.value && !isZoomed.value) ||
1438+
hasDownloadAnomalies.value
1439+
) {
14121440
seriesNames.push(`
14131441
<line
14141442
x1="${svg.drawingArea.left + 12}"
@@ -1955,7 +1983,10 @@ watch(selectedMetric, value => {
19551983
</template>
19561984

19571985
<!-- Estimation extra legend item -->
1958-
<div class="flex gap-1 place-items-center" v-if="supportsEstimation">
1986+
<div
1987+
class="flex gap-1 place-items-center"
1988+
v-if="supportsEstimation || hasDownloadAnomalies"
1989+
>
19591990
<svg viewBox="0 0 20 2" width="20">
19601991
<line
19611992
x1="0"

app/types/chart.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,28 @@ export type DateRangeFields = {
55
endDate?: string
66
}
77

8-
export type DailyDataPoint = { value: number; day: string; timestamp: number }
8+
export type DailyDataPoint = { value: number; day: string; timestamp: number; hasAnomaly?: boolean }
99
export type WeeklyDataPoint = {
1010
value: number
1111
weekKey: string
1212
weekStart: string
1313
weekEnd: string
1414
timestampStart: number
1515
timestampEnd: number
16+
hasAnomaly?: boolean
17+
}
18+
export type MonthlyDataPoint = {
19+
value: number
20+
month: string
21+
timestamp: number
22+
hasAnomaly?: boolean
23+
}
24+
export type YearlyDataPoint = {
25+
value: number
26+
year: string
27+
timestamp: number
28+
hasAnomaly?: boolean
1629
}
17-
export type MonthlyDataPoint = { value: number; month: string; timestamp: number }
18-
export type YearlyDataPoint = { value: number; year: string; timestamp: number }
1930

2031
export type EvolutionData =
2132
| DailyDataPoint[]

app/utils/download-anomalies.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ export function applyBlocklistCorrection(opts: {
113113
for (let i = 0; i < count; i++) {
114114
const t = (i + 1) / (count + 1)
115115
result[affectedIndices[i]!]!.value = Math.round(startVal + t * (endVal - startVal))
116+
result[affectedIndices[i]!]!.hasAnomaly = true
116117
}
117118
}
118-
119119
return result as EvolutionData
120120
}

0 commit comments

Comments
 (0)