[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