From a29c7d37dbc92e358000d1ae19b9d8c148757b3d Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Sun, 8 Mar 2026 17:25:33 -0400 Subject: [PATCH 1/6] Replace manual anomalies with a hampel filter --- app/components/Package/TrendsChart.vue | 70 +----- .../Package/WeeklyDownloadStats.vue | 8 +- app/utils/download-anomalies.data.ts | 30 --- app/utils/download-anomalies.ts | 181 ++++++--------- .../unit/app/utils/download-anomalies.spec.ts | 211 +++++------------- 5 files changed, 135 insertions(+), 365 deletions(-) delete mode 100644 app/utils/download-anomalies.data.ts diff --git a/app/components/Package/TrendsChart.vue b/app/components/Package/TrendsChart.vue index 7d08014719..cee8fae8e6 100644 --- a/app/components/Package/TrendsChart.vue +++ b/app/components/Package/TrendsChart.vue @@ -23,7 +23,7 @@ import { endDateOnlyToUtcMs, DEFAULT_PREDICTION_POINTS, } from '~/utils/chart-data-prediction' -import { applyBlocklistCorrection, getAnomaliesForPackages } from '~/utils/download-anomalies' +import { applyHampelCorrection } from '~/utils/download-anomalies' import { copyAltTextForTrendLineChart, sanitise, loadFile, applyEllipsis } from '~/utils/charts' import('vue-data-ui/style.css') @@ -978,11 +978,7 @@ const effectiveDataSingle = computed(() => { if (isDownloadsMetric.value && data.length) { const pkg = effectivePackageNames.value[0] ?? props.packageName ?? '' if (settings.value.chartFilter.anomaliesFixed) { - data = applyBlocklistCorrection({ - data, - packageName: pkg, - granularity: displayedGranularity.value, - }) + data = applyHampelCorrection(data) } } @@ -1026,7 +1022,7 @@ const chartData = computed<{ let data = state.evolutionsByPackage[pkg] ?? [] if (isDownloadsMetric.value && data.length) { if (settings.value.chartFilter.anomaliesFixed) { - data = applyBlocklistCorrection({ data, packageName: pkg, granularity }) + data = applyHampelCorrection(data) } } const points = extractSeriesPoints(granularity, data) @@ -1556,20 +1552,6 @@ const chartConfig = computed(() => { const isDownloadsMetric = computed(() => selectedMetric.value === 'downloads') const showCorrectionControls = shallowRef(false) -const packageAnomalies = computed(() => getAnomaliesForPackages(effectivePackageNames.value)) -const hasAnomalies = computed(() => packageAnomalies.value.length > 0) - -function formatAnomalyDate(dateStr: string) { - const [y, m, d] = dateStr.split('-').map(Number) - if (!y || !m || !d) return dateStr - return new Intl.DateTimeFormat(locale.value, { - year: 'numeric', - month: 'short', - day: 'numeric', - timeZone: 'UTC', - }).format(new Date(Date.UTC(y, m - 1, d))) -} - // Trigger data loading when the metric is switched watch(selectedMetric, value => { if (!isMounted.value) return @@ -1722,64 +1704,28 @@ watch(selectedMetric, value => { class="text-2xs font-mono text-fg-subtle tracking-wide uppercase flex items-center justify-between" > {{ $t('package.trends.known_anomalies') }} - +