[Git][debian-gis-team/asf-search][master] 4 commits: New upstream version 10.2.1
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Thu Dec 4 07:01:26 GMT 2025
Antonio Valentino pushed to branch master at Debian GIS Project / asf-search
Commits:
865cfe5d by Antonio Valentino at 2025-12-04T06:58:08+00:00
New upstream version 10.2.1
- - - - -
4f6a7d83 by Antonio Valentino at 2025-12-04T06:58:10+00:00
Update upstream source from tag 'upstream/10.2.1'
Update to upstream version '10.2.1'
with Debian dir ea012832405086c13066874e720848eb74c1056e
- - - - -
e1dbff18 by Antonio Valentino at 2025-12-04T06:58:51+00:00
New upstream release
- - - - -
185fd61e by Antonio Valentino at 2025-12-04T07:00:01+00:00
Set distribution to unstable
- - - - -
12 changed files:
- .github/workflows/lint.yml
- CHANGELOG.md
- README.md
- asf_search/search/__init__.py
- + asf_search/search/collection_attributes.py
- debian/changelog
- + tests/Search/test_collection_attributes.py
- tests/pytest-config.yml
- tests/pytest-managers.py
- tests/yml_tests/test_authenticated/test_ASFSubproduct_Auth.yml
- + tests/yml_tests/test_authenticated/test_collection_attributes_Auth.yml
- + tests/yml_tests/test_collection_attributes.yml
Changes:
=====================================
.github/workflows/lint.yml
=====================================
@@ -8,4 +8,5 @@ jobs:
- uses: actions/checkout at v5
- uses: astral-sh/ruff-action at v3
with:
- src: './asf_search'
\ No newline at end of file
+ src: './asf_search'
+ version-file: 'pyproject.toml'
\ No newline at end of file
=====================================
CHANGELOG.md
=====================================
@@ -25,6 +25,11 @@ and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-
-->
+------
+## [v10.2.1](https://github.com/asfadmin/Discovery-asf_search/compare/v10.2.0...v10.2.1)
+### Added
+- Added `get_searchable_attributes()` search method, return dict mapping of additional attribute names to `AdditionalAttribute` data class object
+
------
## [v10.2.0](https://github.com/asfadmin/Discovery-asf_search/compare/v10.1.2...v10.2.0)
### Changed
=====================================
README.md
=====================================
@@ -168,6 +168,11 @@ For test cases that require authentication you can use your EDL credentials
python3 -m pytest tests/yml_tests/test_authenticated -s --auth_with_creds
```
+Or if you'd rather use your EDL token
+```bash
+python3 -m pytest tests/yml_tests/test_authenticated -s --auth_with_token
+```
+
Tests should be written to relevant subfolder & files in `/tests`
The test suite uses the `pytest-automation` pytest plugin which allows us to define and re-use input for test cases in the yaml format. Test cases are written to files in `tests/yml_tests/`, and reusable resources for those tests `tests/yml_tests/Resources/`.
=====================================
asf_search/search/__init__.py
=====================================
@@ -6,3 +6,4 @@ from .baseline_search import stack_from_id # noqa: F401
from .campaigns import campaigns # noqa: F401
from .search_count import search_count # noqa: F401
from .search_generator import search_generator, preprocess_opts # noqa: F401
+from .collection_attributes import get_searchable_attributes # noqa: F401
=====================================
asf_search/search/collection_attributes.py
=====================================
@@ -0,0 +1,95 @@
+from dataclasses import dataclass
+from typing import Optional
+from asf_search.CMR.datasets import collections_by_processing_level
+from asf_search.ASFSession import ASFSession
+
+from asf_search.exceptions import ASFSearchError, CMRError
+
+
+ at dataclass(frozen=True)
+class AdditionalAttribute:
+ """Wrapper dataclass around CMR Additional Attributes"""
+
+ name: str
+ """The `Name` of the additional attribute in CMR"""
+ data_type: str
+ """The `DataType` of the additional attribute in CMR"""
+ description: str
+ """The `Description` of the additional attribute in CMR"""
+
+
+def get_searchable_attributes(
+ shortName: Optional[str] = None,
+ conceptID: Optional[str] = None,
+ processingLevel: Optional[str] = None,
+ session: ASFSession = ASFSession(),
+) -> dict[str, AdditionalAttribute]:
+ """Using a provided processingLevel, collection shortName, or conceptID query CMR's `/collections` endpoint and
+ return a dictionary of additional attributes mapping the attribute's name to the additional attribute entry in CMR
+
+ ``` python
+ from pprint import pp
+ SLCRcord = asf.get_searchable_attributes(processingLevel='SLC')
+ pp(SLCRcord.additionalAttributes)
+ ```
+ """
+ query_data = None
+ method = None
+
+ if shortName is not None:
+ method = {'type': 'shortName', 'value': shortName}
+ query_data = [('shortName', shortName)]
+ elif conceptID is not None:
+ query_data = [('concept-id', conceptID)]
+ method = {'type': 'conceptID', 'value': conceptID}
+ elif processingLevel is not None:
+ method = {'type': 'processingLevel', 'value': processingLevel}
+ query_data = _get_concept_ids_for_processing_level(processingLevel)
+ else:
+ raise ValueError(
+ 'Error: `get_collection_searchable_attributes()` expects `shortName`, `conceptID`, or `processingLevel`'
+ )
+
+ cmr_response = _query_cmr(session=session, query_data=query_data, method=method)
+
+ if 'errors' in cmr_response:
+ raise ValueError(f"CMR responded with an error. Original error(s): {' '.join(cmr_response['errors'])}")
+ if len(cmr_response['items']) == 0:
+ raise ValueError(
+ f'Error: no collections found in CMR for given parameter `{method["type"]}`: "{method["value"]}" '
+ )
+
+ additionalAttributes = {}
+
+ for entry in cmr_response['items']:
+ umm = entry['umm']
+ attributes = umm.get('AdditionalAttributes')
+ if attributes is not None:
+ for attribute in attributes:
+ additionalAttributes[attribute.get('Name')] = AdditionalAttribute(
+ name=attribute.get('Name'),
+ description=attribute.get('Description'),
+ data_type=attribute.get('DataType'),
+ )
+
+ return additionalAttributes
+
+
+def _get_concept_ids_for_processing_level(processing_level: str):
+ collections = collections_by_processing_level.get(processing_level)
+ if collections is None:
+ raise ValueError(f'asf-search is missing concept-id aliases for processing level "{processing_level}". Please use `shortName` or `conceptID')
+ return [('concept-id[]', collection) for collection in collections]
+
+
+def _query_cmr(session: ASFSession, query_data: list[tuple[str, str]], method: dict) -> dict:
+ url = 'https://cmr.earthdata.nasa.gov/search/collections.umm_json'
+
+ response = session.post(url=url, data=query_data)
+
+ try:
+ return response.json()
+ except Exception as exc:
+ raise ASFSearchError(
+ f'Failed to find collection attributes for {method["type"]} "{method["value"]}". original exception: {str(exc)}'
+ )
=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+asf-search (10.2.1-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it> Thu, 04 Dec 2025 06:59:44 +0000
+
asf-search (10.2.0-1) unstable; urgency=medium
* New upstream release.
=====================================
tests/Search/test_collection_attributes.py
=====================================
@@ -0,0 +1,11 @@
+from asf_search.ASFSession import ASFSession
+from asf_search.search.collection_attributes import get_searchable_attributes
+import pytest
+
+def run_test_collection_attributes(params: dict, expected_attributes: list[str], session: ASFSession, expect_failure: bool) -> None:
+ if expect_failure:
+ with pytest.raises(ValueError):
+ actual_attributes = get_searchable_attributes(**params, session=session)
+ else:
+ actual_attributes = get_searchable_attributes(**params, session=session)
+ assert sorted(expected_attributes) == sorted(actual_attributes.keys())
=====================================
tests/pytest-config.yml
=====================================
@@ -213,6 +213,11 @@ test_types:
required_keys: ['params', 'expected']
method: test_build_subqueries
+- For running collection_attributes tests:
+ required_in_title: test-collection-attributes
+ required_keys: ['params', 'expected_attributes']
+ method: test_collection_attributes
+
- For running jupyter notebook example tests:
required_keys: notebook
method: test_notebook_examples
=====================================
tests/pytest-managers.py
=====================================
@@ -81,6 +81,7 @@ import nbformat
from nbconvert.preprocessors import ExecutePreprocessor
from ASFProduct.test_ASFSubproduct import run_test_ASFSubproduct
+from tests.Search.test_collection_attributes import run_test_collection_attributes
# asf_search.ASFProduct Tests
@@ -642,6 +643,12 @@ def test_keyword_aliasing_results(**args) -> None:
run_test_keyword_aliasing_results(opts)
+def test_collection_attributes(**args) -> None:
+ params = args['test_info']['params']
+ expected_attributes = args['test_info']['expected_attributes']
+ expect_failure = args['test_info'].get('expect_failure', False)
+ session = args["config"].getoption("authenticated_session")
+ run_test_collection_attributes(params, expected_attributes, session, expect_failure)
# Finds and loads file from yml_tests/Resouces/ if loaded field ends with .yml/yaml extension
def get_resource(yml_file):
=====================================
tests/yml_tests/test_authenticated/test_ASFSubproduct_Auth.yml
=====================================
@@ -1,6 +1,5 @@
tests:
- - Test SEASAT ASFSubproduct:
- scenes: ["SS_01502_STD_F2536"]
- opts:
- dataset: SEASAT
- expected_subclass: SEASATProduct
+- test-collection-attributes RSLC:
+ params:
+ processingLevel: RSLC
+ expected_attributes: ['PRODUCT_TYPE', 'RANGE_BANDWIDTH_CONCAT', 'FREQUENCY_A_POLARIZATION_CONCAT', 'STACK_ID', 'FULL_FRAME', 'ASCENDING_DESCENDING', 'FREQUENCY_B_POLARIZATION_CONCAT', 'PRODUCT_VERSION', 'EPHEMERIS_ACCURACY', 'FRAME_NUMBER', 'FREQUENCIES', 'PRODUCT_TYPE_DESC', 'JOINT_OBSERVATION', 'FREQUENCY_A_POLARIZATION', 'FREQUENCY_B_POLARIZATION', 'FREQUENCY_A_RANGE_BANDWIDTH', 'FREQUENCY_B_RANGE_BANDWIDTH', 'PRODUCTION_PIPELINE', 'PATH_NUMBER', 'PROCESSING_CENTER', 'PROCESSING_LEVEL']
=====================================
tests/yml_tests/test_authenticated/test_collection_attributes_Auth.yml
=====================================
@@ -0,0 +1,6 @@
+tests:
+ - Test SEASAT ASFSubproduct:
+ scenes: ["SS_01502_STD_F2536"]
+ opts:
+ dataset: SEASAT
+ expected_subclass: SEASATProduct
=====================================
tests/yml_tests/test_collection_attributes.yml
=====================================
@@ -0,0 +1,46 @@
+tests:
+- test-collection-attributes SLC:
+ params:
+ processingLevel: SLC
+ expected_attributes: ['SV_POSITION_PRE', 'SV_VELOCITY_PRE', 'SV_POSITION_POST', 'SV_VELOCITY_POST', 'ASC_NODE_TIME', 'SV_START_POSITION', 'SV_START_VELOCITY', 'SV_CENTER_POSITION', 'SV_CENTER_VELOCITY', 'SV_END_POSITION', 'SV_END_VELOCITY', 'POLARIZATION', 'FLIGHT_LINE', 'BEAM_MODE', 'BEAM_MODE_DESC', 'BEAM_MODE_TYPE', 'FARADAY_ROTATION', 'INSAR_STACK_ID', 'INSAR_STACK_SIZE', 'INSAR_BASELINE', 'DOPPLER', 'ASCENDING_DESCENDING', 'THUMBNAIL_URL', 'CENTER_FRAME_ID', 'PATH_NUMBER', 'FRAME_NUMBER', 'CENTER_ESA_FRAME', 'MISSION_NAME', 'GRANULE_TYPE', 'PROCESSING_DATE', 'PROCESSING_DESCRIPTION', 'LOOK_DIRECTION', 'PROCESSING_TYPE', 'PROCESSING_LEVEL', 'PROCESSING_TYPE_DISPLAY', 'BYTES', 'MD5SUM', 'OFF_NADIR_ANGLE', 'ACQUISITION_DATE', 'CENTER_LON', 'CENTER_LAT', 'NEAR_START_LAT', 'NEAR_START_LON', 'FAR_START_LAT', 'FAR_START_LON', 'NEAR_END_LAT', 'NEAR_END_LON', 'FAR_END_LAT', 'FAR_END_LON', 'ASF_PLATFORM', 'GROUP_ID', 'SV_POSITION', 'SV_VELOCITY']
+
+- test-collection-attributes DISP-S1:
+ params:
+ processingLevel: DISP-S1
+ expected_attributes: ['POLARIZATION', 'PRODUCT_VERSION', 'PROCESSING_TYPE', 'FRAME_ID', 'STACK_ID', 'ASCENDING_DESCENDING', 'PATH_NUMBER', 'REFERENCE_ZERO_DOPPLER_START_TIME', 'REFERENCE_ZERO_DOPPLER_END_TIME', 'SECONDARY_ZERO_DOPPLER_START_TIME', 'SECONDARY_ZERO_DOPPLER_END_TIME', 'FRAME_NUMBER']
+
+- test-collection-attributes ALOS2_L1_PSR2:
+ params:
+ shortName: ALOS2_L1_PSR2
+ expected_attributes: ['ASCENDING_DESCENDING', 'BEAM_MODE', 'BEAM_MODE_DESC', 'CENTER_LAT', 'CENTER_LON', 'FRAME_NUMBER', 'LOOK_DIRECTION', 'OFF_NADIR_ANGLE', 'OPERATIONAL_MODE', 'OPERATIONAL_MODE_DESC', 'PATH_NUMBER', 'POLARIZATION', 'PROCESSING_LEVEL', 'SV_POSITION', 'SV_VELOCITY']
+
+- test-collection-attributes SEASAT C3576379529-ASF:
+ params:
+ conceptID: C3576379529-ASF
+ expected_attributes: ['ASCENDING_DESCENDING', 'BEAM_MODE', 'BEAM_MODE_DESC', 'FRAME_NUMBER', 'PATH_NUMBER', 'POLARIZATION']
+
+- test-collection-attributes Fake Concept ID:
+ params:
+ conceptID: NotAValidConceptID-ASF
+ expected_attributes: []
+ expect_failure: True
+
+- test-collection-attributes Wrong CMR Host Maturity Correct Concept ID:
+ params:
+ conceptID: 'C1271768606-ASF'
+ expected_attributes: []
+ expect_failure: True
+
+- test-collection-attributes Wrong CMR Host Maturity Correct Concept ID:
+ params:
+ conceptID: None
+ shortName: None
+ processingLevel: None
+ expected_attributes: []
+ expect_failure: True
+
+- test-collection-attributes Wrong CMR invalid/missing processing level:
+ params:
+ processingLevel: NotARealProcessingLevel
+ expected_attributes: []
+ expect_failure: True
View it on GitLab: https://salsa.debian.org/debian-gis-team/asf-search/-/compare/07a5b46d0bccbb217b472fe58a271e1236713519...185fd61e7594a168c89853ad6f111b6b67da7b7d
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/asf-search/-/compare/07a5b46d0bccbb217b472fe58a271e1236713519...185fd61e7594a168c89853ad6f111b6b67da7b7d
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20251204/afbdf54c/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list