From 5b1156e989c15a924df8a5d36ef8d263fe47c6d5 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 15 Jul 2026 07:57:59 +0200 Subject: [PATCH] fix: separate historical source methodologies --- CHANGELOG.md | 3 ++ ...t_sprint202_temporal_metrics_and_ollama.py | 2 + docs/CODEX_EXECUTION_LOG.md | 4 ++ .../datasets/SourceCatalogPanel.tsx | 48 ++++++++++++++++--- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ba9deba..dc5e8822 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,9 @@ - Added deterministic estimate disclosure for population answers and instructed the local model to copy governed values literally instead of inventing averages, rates or derived trends. +- Clarified the source inventory so current GRB layers are shown as a different + methodology beside, rather than as part of, the comparable 2013-2025 land-use + time series. ## Sprint 201 Semantic area-selection metrics (2026-07-15) diff --git a/backend/tests/test_sprint202_temporal_metrics_and_ollama.py b/backend/tests/test_sprint202_temporal_metrics_and_ollama.py index 00461600..525597fa 100644 --- a/backend/tests/test_sprint202_temporal_metrics_and_ollama.py +++ b/backend/tests/test_sprint202_temporal_metrics_and_ollama.py @@ -328,3 +328,5 @@ def test_frontend_exposes_source_inventory_timeline_and_ai_window() -> None: assert "nextAssistantMessageId" in assistant_hook assert "crypto.randomUUID" not in assistant_hook assert "Officiële bronnen die hierna kunnen worden ingeladen" in catalog + assert "vergelijkbare meetmomenten" in catalog + assert "andere bronmethode" in catalog diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index 67349025..9e92d779 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -8422,6 +8422,10 @@ Validation evidence: limitation whenever an answer discusses estimated population data, while the model prompt forbids independent averages, rates and derived trends. Focused tests and full readiness passed with 619 backend tests. +- Browser review identified that the source summary visually combined current + GRB geometry with historical 10 m land-use observations. The UI now reports + comparable observations per temporal-series key and labels singleton/current + datasets as a separate source method. Full readiness remained green. Known limitations: - Water volume remains unavailable until a governed depth/bathymetry source is diff --git a/frontend/src/components/datasets/SourceCatalogPanel.tsx b/frontend/src/components/datasets/SourceCatalogPanel.tsx index 22316164..70bad2d2 100644 --- a/frontend/src/components/datasets/SourceCatalogPanel.tsx +++ b/frontend/src/components/datasets/SourceCatalogPanel.tsx @@ -66,12 +66,42 @@ function datasetTheme(dataset: DatasetCreateResponse): string | null { return null } +function timelineSummary( + temporalCount: number, + temporalSeriesCount: number, + otherMethodCount: number, + firstYear: number | null, + lastYear: number | null, +): string { + if (temporalCount < 2 || !firstYear || !lastYear) { + return 'Alleen de huidige toestand is vergelijkbaar beschikbaar.' + } + const seriesNote = temporalSeriesCount > 1 + ? ` in ${temporalSeriesCount} afzonderlijke reeksen` + : '' + const methodNote = otherMethodCount > 0 + ? `plus ${otherMethodCount} andere bronmethode${otherMethodCount === 1 ? '' : 'n'}` + : null + return [ + `${temporalCount} vergelijkbare meetmomenten${seriesNote}`, + `${firstYear}-${lastYear}`, + methodNote, + ].filter(Boolean).join(' · ') +} + export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.Element { const ready = datasets.filter((dataset) => dataset.status === 'ready') const themes = Object.keys(THEME_LABELS).map((theme) => { const matches = ready.filter((dataset) => datasetTheme(dataset) === theme) const temporal = matches.filter((dataset) => dataset.temporal_series_key && dataset.observed_at) - const years = temporal.map((dataset) => new Date(dataset.observed_at as string).getUTCFullYear()) + const seriesGroups = new Map() + temporal.forEach((dataset) => { + const seriesKey = dataset.temporal_series_key as string + seriesGroups.set(seriesKey, [...(seriesGroups.get(seriesKey) ?? []), dataset]) + }) + const comparableSeries = [...seriesGroups.values()].filter((series) => series.length >= 2) + const comparableObservations = comparableSeries.flat() + const years = comparableObservations.map((dataset) => new Date(dataset.observed_at as string).getUTCFullYear()) const latest = [...matches].sort( (left, right) => new Date(right.observed_at ?? right.imported_at ?? 0).getTime() - new Date(left.observed_at ?? left.imported_at ?? 0).getTime(), )[0] @@ -79,7 +109,9 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E theme, label: THEME_LABELS[theme], datasetCount: matches.length, - temporalCount: temporal.length, + temporalCount: comparableObservations.length, + temporalSeriesCount: comparableSeries.length, + otherMethodCount: matches.length - comparableObservations.length, firstYear: years.length ? Math.min(...years) : null, lastYear: years.length ? Math.max(...years) : null, source: latest?.source_name ?? latest?.source ?? null, @@ -105,11 +137,13 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E {theme.source ? theme.source.replaceAll('_', ' ') : 'Nog niet ingeladen'} {theme.datasetCount > 0 ? 'Beschikbaar' : 'Ontbreekt'} -

- {theme.temporalCount >= 2 && theme.firstYear && theme.lastYear - ? `${theme.temporalCount} officiële meetmomenten · ${theme.firstYear}-${theme.lastYear}` - : 'Alleen de huidige toestand is vergelijkbaar beschikbaar.'} -

+

{timelineSummary( + theme.temporalCount, + theme.temporalSeriesCount, + theme.otherMethodCount, + theme.firstYear, + theme.lastYear, + )}

))}