From f73f9d73070a2326ad9ee9d0e805a62d3a505aba Mon Sep 17 00:00:00 2001 From: "Peter A. Jonsson" Date: Sat, 6 Sep 2025 11:12:13 +0200 Subject: [PATCH 1/5] model: fix type signature The function expects the iterable to contain None. --- cubedash/summary/_model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cubedash/summary/_model.py b/cubedash/summary/_model.py index dddcd6fd6..b683bc558 100644 --- a/cubedash/summary/_model.py +++ b/cubedash/summary/_model.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import warnings from collections import Counter from collections.abc import Sequence @@ -126,7 +128,7 @@ def add_periods( cls, product_name: str, product_refresh_time: datetime, - periods: Iterable["TimePeriodOverview"], + periods: Iterable["TimePeriodOverview" | None], # This is in CRS units. Albers, so 1KM. # Lower value will have a more accurate footprint and much larger page load times. footprint_tolerance: float = 1000.0, From f26931a4dfad1da3b87054a6eee4c2cc0a700483 Mon Sep 17 00:00:00 2001 From: "Peter A. Jonsson" Date: Sat, 6 Sep 2025 11:14:05 +0200 Subject: [PATCH 2/5] stores: add type ignores The type checker is just missing information for these, so ignore them. --- cubedash/summary/_stores.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cubedash/summary/_stores.py b/cubedash/summary/_stores.py index 7a4c34c73..2e7d6b5a1 100644 --- a/cubedash/summary/_stores.py +++ b/cubedash/summary/_stores.py @@ -814,7 +814,7 @@ def _persist_product_extent(self, product: ProductSummary) -> None: ) row = self.e_index.upsert_product_record(product.name, fields) - self._product.cache_clear() + self._product.cache_clear() # type: ignore[attr-defined] product_id, last_refresh_time = row product.id_ = product_id @@ -930,7 +930,7 @@ def _get_field_exprs( for value in self.e_index.get_mutable_dataset_search_fields( product.metadata_type ).values(): - expr = value.alchemy_expression + expr = value.alchemy_expression # type: ignore[attr-defined] if hasattr(value, "offset"): field_exprs[value.offset[-1]] = expr field_exprs[value.name] = expr @@ -1403,7 +1403,7 @@ def _mark_product_refresh_completed( """ assert product.id_ is not None self.e_index.update_product_refresh_timestamp(product.id_, refresh_timestamp) - self._product.cache_clear() + self._product.cache_clear() # type: ignore[attr-defined] def list_complete_products(self) -> list[str]: """ From b932f650be57c5137d158fb141eb12eb21d3b65d Mon Sep 17 00:00:00 2001 From: "Peter A. Jonsson" Date: Sat, 6 Sep 2025 11:18:34 +0200 Subject: [PATCH 3/5] stores: initialize crses correctly --- cubedash/summary/_stores.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cubedash/summary/_stores.py b/cubedash/summary/_stores.py index 2e7d6b5a1..155213e65 100644 --- a/cubedash/summary/_stores.py +++ b/cubedash/summary/_stores.py @@ -1563,7 +1563,7 @@ def _summary_from_row( product_refresh_time=res["product_refresh_time"], # When this summary was last generated summary_gen_time=res["generation_time"], - crses=set(res["crses"]) if res["crses"] is not None else None, + crses=set(crses) if (crses := res["crses"]) is not None else set(), ) From 400fa0c5dc037d5635345f1104bddfe9798c3798 Mon Sep 17 00:00:00 2001 From: "Peter A. Jonsson" Date: Sat, 6 Sep 2025 11:25:52 +0200 Subject: [PATCH 4/5] stores: check for None --- cubedash/summary/_stores.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cubedash/summary/_stores.py b/cubedash/summary/_stores.py index 155213e65..50f25a9dd 100644 --- a/cubedash/summary/_stores.py +++ b/cubedash/summary/_stores.py @@ -1386,12 +1386,13 @@ def _database_time_now(self) -> datetime: """ return self.e_index.execute_query(select(func.now())).scalar() - def _newest_known_dataset_addition_time(self, product_name: str) -> datetime: + def _newest_known_dataset_addition_time(self, product_name: str) -> datetime | None: """ Of all the datasets that are present in Explorer's own tables, when was the most recent one indexed to ODC? """ - return self.e_index.latest_dataset_added_time(self.get_product(product_name).id) + id_ = self.get_product(product_name).id + return None if id_ is None else self.e_index.latest_dataset_added_time(id_) def _mark_product_refresh_completed( self, product: ProductSummary, refresh_timestamp: datetime From e7d08d96c5b9d43b27b377c8828558da5b07f849 Mon Sep 17 00:00:00 2001 From: "Peter A. Jonsson" Date: Sat, 6 Sep 2025 11:59:39 +0200 Subject: [PATCH 5/5] index: fix return types This is what is currently returned, so adjust the type to reflect that. --- cubedash/index/api.py | 6 +++--- cubedash/index/postgis/_api.py | 5 +++-- cubedash/index/postgres/_api.py | 5 +++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cubedash/index/api.py b/cubedash/index/api.py index 1bd70f2e0..4125e8320 100644 --- a/cubedash/index/api.py +++ b/cubedash/index/api.py @@ -7,7 +7,7 @@ from datacube.index import Index from datacube.model import Dataset, MetadataType, Product, Range from datacube.model.fields import Field -from sqlalchemy import Result, Row, Select +from sqlalchemy import CursorResult, Result, Row, Select from sqlalchemy.sql import ColumnElement from sqlalchemy.sql.elements import ClauseElement, Label @@ -150,10 +150,10 @@ def upsert_product_record( ) -> tuple[int, datetime]: ... @abstractmethod - def upsert_product_regions(self, product_id: int) -> Result: ... + def upsert_product_regions(self, product_id: int) -> CursorResult: ... @abstractmethod - def delete_product_empty_regions(self, product_id: int) -> Result: ... + def delete_product_empty_regions(self, product_id: int) -> CursorResult: ... @abstractmethod def product_region_summary(self, product_id: int) -> Result: ... diff --git a/cubedash/index/postgis/_api.py b/cubedash/index/postgis/_api.py index 06b827f44..ace65fa7f 100644 --- a/cubedash/index/postgis/_api.py +++ b/cubedash/index/postgis/_api.py @@ -19,6 +19,7 @@ from geoalchemy2.shape import from_shape from sqlalchemy import ( ClauseElement, + CursorResult, Integer, Label, Result, @@ -300,7 +301,7 @@ def product_summary_cols(self, product_name: str) -> Row: ).fetchone() @override - def upsert_product_regions(self, product_id: int) -> Result: + def upsert_product_regions(self, product_id: int) -> CursorResult: # add new regions row and/or update existing regions based on dataset_spatial with self.index._active_connection() as conn: return conn.execute( @@ -336,7 +337,7 @@ def upsert_product_regions(self, product_id: int) -> Result: ) @override - def delete_product_empty_regions(self, product_id: int) -> Result: + def delete_product_empty_regions(self, product_id: int) -> CursorResult: with self.index._active_connection() as conn: return conn.execute( text(f""" diff --git a/cubedash/index/postgres/_api.py b/cubedash/index/postgres/_api.py index d497f57d6..2fe415363 100644 --- a/cubedash/index/postgres/_api.py +++ b/cubedash/index/postgres/_api.py @@ -21,6 +21,7 @@ from geoalchemy2.shape import from_shape from sqlalchemy import ( ClauseElement, + CursorResult, Integer, Label, Result, @@ -316,7 +317,7 @@ def product_summary_cols(self, product_name: str) -> Row: ).fetchone() @override - def upsert_product_regions(self, product_id: int) -> Result: + def upsert_product_regions(self, product_id: int) -> CursorResult: # add new regions row and/or update existing regions based on dataset_spatial with self.index._active_connection() as conn: return conn.execute( @@ -352,7 +353,7 @@ def upsert_product_regions(self, product_id: int) -> Result: ) @override - def delete_product_empty_regions(self, product_id: int) -> Result: + def delete_product_empty_regions(self, product_id: int) -> CursorResult: with self.index._active_connection() as conn: return conn.execute( text(f"""