diff --git a/backend/app/services/source_freshness_service.py b/backend/app/services/source_freshness_service.py index 113e0879..27d98a44 100644 --- a/backend/app/services/source_freshness_service.py +++ b/backend/app/services/source_freshness_service.py @@ -81,6 +81,21 @@ def _latest_dataset(datasets: list[Dataset]) -> Dataset: ) +def _latest_source_version(policy: SourcePolicy, datasets: list[Dataset]) -> str | None: + versioned = [item for item in datasets if item.source_version] + if not versioned: + return None + if policy.refresh_policy == "rolling_snapshot": + named_current = [ + item + for item in versioned + if any(token in (item.source_version or "").lower() for token in ("most_recent", "latest", "current")) + ] + if named_current: + return _latest_dataset(named_current).source_version + return _latest_dataset(versioned).source_version + + def _is_local_storage_path(storage_path: str) -> bool: normalized = storage_path.strip().lower() return bool(normalized) and "://" not in normalized and not normalized.startswith("/vsi") @@ -225,7 +240,6 @@ class SourceFreshnessService: next_review_at = None reason = "Voor deze bron is nog geen expliciete publicatie- of controlecyclus vastgelegd." recommended_action = "Classificeer de bron eerst als snapshot, jaargang, vaste editie, scenario, archief of lokaal." - latest = _latest_dataset(source_datasets) observed_values = {value for item in source_datasets if (value := _as_utc(item.observed_at)) is not None} temporal_keys = {item.temporal_series_key for item in source_datasets if item.temporal_series_key} items.append( @@ -237,21 +251,7 @@ class SourceFreshnessService: version_count=sum(len(versions_by_dataset.get(item.id, [])) for item in source_datasets), latest_imported_at=_latest_datetime(item.imported_at for item in source_datasets), latest_observed_at=_latest_datetime(item.observed_at for item in source_datasets), - latest_source_version=next( - ( - item.source_version - for item in sorted( - source_datasets, - key=lambda value: ( - _as_utc(value.observed_at) or datetime.min.replace(tzinfo=timezone.utc), - _as_utc(value.imported_at) or datetime.min.replace(tzinfo=timezone.utc), - ), - reverse=True, - ) - if item.source_version - ), - latest.source_version, - ), + latest_source_version=_latest_source_version(policy, source_datasets), refresh_policy=policy.refresh_policy, review_interval_days=policy.review_interval_days, next_review_at=next_review_at, diff --git a/backend/tests/test_sprint221_source_freshness_audit.py b/backend/tests/test_sprint221_source_freshness_audit.py index 0c7b4196..55c2ac3f 100644 --- a/backend/tests/test_sprint221_source_freshness_audit.py +++ b/backend/tests/test_sprint221_source_freshness_audit.py @@ -133,6 +133,30 @@ def test_source_freshness_requires_dataset_version_and_marks_temporal_series() - assert item.integrity.missing_version_count == 1 +def test_rolling_orthophoto_prefers_explicit_current_snapshot_over_historical_observation() -> None: + current = _dataset( + "digitaal_vlaanderen_orthophoto", + imported_at=NOW - timedelta(days=2), + observed_at=None, + source_version="most_recent_at_2026-07-14", + ) + historical = _dataset( + "digitaal_vlaanderen_orthophoto", + imported_at=NOW - timedelta(days=1), + observed_at=datetime(2020, 6, 1, tzinfo=timezone.utc), + source_version="2020", + ) + + report = SourceFreshnessService.build_report( + current.project_id, + [current, historical], + [_version(current), _version(historical)], + now=NOW, + ) + + assert report.items[0].latest_source_version == "most_recent_at_2026-07-14" + + def test_source_freshness_route_returns_canonical_envelope(monkeypatch) -> None: from app.api.routes import datasets as dataset_routes