|
1 |
| -from functools import lru_cache |
2 | 1 | import re
|
| 2 | +import warnings |
| 3 | +from functools import lru_cache |
3 | 4 | from typing import (
|
4 | 5 | TYPE_CHECKING,
|
5 | 6 | Any,
|
6 | 7 | Callable,
|
7 |
| - cast, |
8 | 8 | Dict,
|
9 | 9 | Iterator,
|
10 | 10 | List,
|
11 | 11 | Optional,
|
12 | 12 | Union,
|
| 13 | + cast, |
13 | 14 | )
|
14 |
| -import warnings |
15 | 15 |
|
16 | 16 | import pystac
|
17 | 17 | import pystac.utils
|
|
22 | 22 | from pystac_client._utils import Modifiable, call_modifier
|
23 | 23 | from pystac_client.collection_client import CollectionClient
|
24 | 24 | from pystac_client.conformance import ConformanceClasses
|
25 |
| - |
26 | 25 | from pystac_client.errors import ClientTypeError
|
27 | 26 | from pystac_client.exceptions import APIError
|
28 | 27 | from pystac_client.item_search import (
|
|
39 | 38 | QueryLike,
|
40 | 39 | SortbyLike,
|
41 | 40 | )
|
42 |
| -from pystac_client.mixins import QueryablesMixin |
| 41 | +from pystac_client.mixins import QUERYABLES_ENDPOINT, QueryablesMixin |
43 | 42 | from pystac_client.stac_api_io import StacApiIO, Timeout
|
44 |
| -from pystac_client.warnings import ( |
45 |
| - DoesNotConformTo, |
46 |
| - FallbackToPystac, |
47 |
| - NoConformsTo, |
48 |
| -) |
| 43 | +from pystac_client.warnings import DoesNotConformTo, FallbackToPystac, NoConformsTo |
49 | 44 |
|
50 | 45 | if TYPE_CHECKING:
|
51 | 46 | from pystac.item import Item as Item_Type
|
@@ -340,17 +335,60 @@ def _warn_about_fallback(self, *args: str) -> None:
|
340 | 335 | warnings.warn(DoesNotConformTo(*args), stacklevel=2)
|
341 | 336 | warnings.warn(FallbackToPystac(), stacklevel=2)
|
342 | 337 |
|
| 338 | + def get_merged_queryables(self, collections: List[str]) -> Dict[str, Any]: |
| 339 | + """Return the set of queryables in common to the specified collections. |
| 340 | +
|
| 341 | + Queryables from multiple collections are unioned together, except in the case |
| 342 | + when the same queryable key has a different definition, in which case that key |
| 343 | + is dropped. |
| 344 | +
|
| 345 | + Output is a dictionary that can be used in ``jsonshema.validate`` |
| 346 | +
|
| 347 | + Args: |
| 348 | + collections List[str]: The IDs of the collections to inspect. |
| 349 | +
|
| 350 | + Return: |
| 351 | + Dict[str, Any]: Dictionary containing queryable fields |
| 352 | + """ |
| 353 | + if not collections: |
| 354 | + raise ValueError("cannot get_merged_queryables from empty Iterable") |
| 355 | + |
| 356 | + if not self.conforms_to(ConformanceClasses.FILTER): |
| 357 | + raise DoesNotConformTo(ConformanceClasses.FILTER.name) |
| 358 | + response = self.get_queryables_from( |
| 359 | + self._get_collection_queryables_href(collections[0]) |
| 360 | + ) |
| 361 | + response.pop("$id") |
| 362 | + addl_props = response.get("additionalProperties", False) |
| 363 | + for collection in collections[1:]: |
| 364 | + resp = self.get_queryables_from( |
| 365 | + self._get_collection_queryables_href(collection) |
| 366 | + ) |
| 367 | + |
| 368 | + # additionalProperties is false if any collection doesn't support additional |
| 369 | + # properties |
| 370 | + addl_props &= resp.get("additionalProperties", False) |
| 371 | + |
| 372 | + # drop queryables if their keys match, but the descriptions differ |
| 373 | + for k in set(resp["properties"]).intersection(response["properties"]): |
| 374 | + if resp["properties"][k] != response["properties"][k]: |
| 375 | + resp["properties"].pop(k) |
| 376 | + response["properties"].pop(k) |
| 377 | + response["properties"].update(resp["properties"]) |
| 378 | + return response |
| 379 | + |
343 | 380 | @lru_cache()
|
344 |
| - def get_collection( |
345 |
| - self, collection_id: str |
346 |
| - ) -> Optional[Union[Collection, CollectionClient]]: |
| 381 | + def get_collection(self, collection_id: str) -> Union[Collection, CollectionClient]: |
347 | 382 | """Get a single collection from this Catalog/API
|
348 | 383 |
|
349 | 384 | Args:
|
350 | 385 | collection_id: The Collection ID to get
|
351 | 386 |
|
352 | 387 | Returns:
|
353 | 388 | Union[Collection, CollectionClient]: A STAC Collection
|
| 389 | +
|
| 390 | + Raises: |
| 391 | + NotFoundError if collection_id does not exist. |
354 | 392 | """
|
355 | 393 | collection: Union[Collection, CollectionClient]
|
356 | 394 |
|
@@ -602,3 +640,9 @@ def _collections_href(self, collection_id: Optional[str] = None) -> str:
|
602 | 640 | if collection_id is not None:
|
603 | 641 | return f"{href.rstrip('/')}/{collection_id}"
|
604 | 642 | return href
|
| 643 | + |
| 644 | + def _get_collection_queryables_href( |
| 645 | + self, collection_id: Optional[str] = None |
| 646 | + ) -> str: |
| 647 | + href = self._collections_href(collection_id) |
| 648 | + return f"{href.rstrip('/')}/{QUERYABLES_ENDPOINT}" |
0 commit comments