Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept search with ecmwf properties without "ecmwf:" prefix. #1421

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 1 addition & 18 deletions eodag/api/product/metadata_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,7 @@ def convert_get_hydrological_year(date: str):
date_object = datetime.strptime(utc_date, "%Y-%m-%dT%H:%M:%S.%fZ")
date_object_second_year = date_object + relativedelta(years=1)
return [
f'{date_object.strftime("%Y")}_{date_object_second_year.strftime("%y")}'
f"{date_object.strftime('%Y')}_{date_object_second_year.strftime('%y')}"
]

@staticmethod
Expand Down Expand Up @@ -1546,23 +1546,6 @@ def get_provider_queryable_key(
return eodag_key


def eodag_key_from_provider_key(
provider_key: str,
metadata_mapping: Dict[str, Union[List[Any], str]],
) -> str:
"""Get eodag key for provider key based on the metadata mapping if the provider key
appears in the metadata mapping, otherwise the provider key is returned

:param provider_key: name of the variable received from the provider
:param metadata_mapping: metadata mapping of the provider
:returns: eodag key
"""
for mm, mv in metadata_mapping.items():
if isinstance(mv, list) and len(mv) > 1 and provider_key == mv[0]:
return mm
return provider_key


# Keys taken from OpenSearch extension for Earth Observation http://docs.opengeospatial.org/is/13-026r9/13-026r9.html
# For a metadata to be queryable, The way to query it must be specified in the
# provider metadata_mapping configuration parameter. It will be automatically
Expand Down
56 changes: 39 additions & 17 deletions eodag/plugins/search/build_search_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
from eodag.api.product.metadata_mapping import (
NOT_AVAILABLE,
NOT_MAPPED,
eodag_key_from_provider_key,
format_metadata,
format_query_params,
mtd_cfg_as_conversion_and_querypath,
Expand Down Expand Up @@ -317,6 +316,11 @@ def _update_properties_from_element(
prop["description"] = description


def ecmwf_format(v: str) -> str:
"""Add ECMWF prefix to value v if v is a ECMWF keyword."""
return "ecmwf:" + v if v in ECMWF_KEYWORDS + COP_DS_KEYWORDS else v


class ECMWFSearch(PostJsonSearch):
"""ECMWF search plugin.

Expand Down Expand Up @@ -423,6 +427,12 @@ def query(
if not product_type:
product_type = kwargs.get("productType", None)
self._preprocess_search_params(kwargs, product_type)

kwargs = {
"ecmwf:" + k if k in ECMWF_KEYWORDS + COP_DS_KEYWORDS else k: v
for k, v in kwargs.items()
}

result, num_items = super().query(prep, **kwargs)
if prep.count and not num_items:
num_items = 1
Expand Down Expand Up @@ -589,7 +599,7 @@ def discover_queryables(
):
formated_kwargs[key] = kwargs[key]
else:
raise ValidationError(f"{key} in not a queryable parameter")
raise ValidationError(f"{key} is not a queryable parameter")

# we use non empty kwargs as default to integrate user inputs
# it is needed because pydantic json schema does not represent "value"
Expand Down Expand Up @@ -877,15 +887,11 @@ def queryables_by_form(
if default and prop["type"] == "string" and isinstance(default, list):
default = ",".join(default)

# rename keywords from form with metadata mapping.
# needed to map constraints like "xxxx" to eodag parameter "cop_cds:xxxx"
key = eodag_key_from_provider_key(name, self.config.metadata_mapping)

is_required = bool(element.get("required"))
if is_required:
required_list.append(name)

queryables[key] = Annotated[
queryables[ecmwf_format(name)] = Annotated[
get_args(
json_field_definition_to_python(
prop,
Expand Down Expand Up @@ -914,16 +920,13 @@ def queryables_by_values(
"""
# Rename keywords from form with metadata mapping.
# Needed to map constraints like "xxxx" to eodag parameter "ecmwf:xxxx"
required = [
eodag_key_from_provider_key(k, self.config.metadata_mapping)
for k in required_keywords
]
required = [ecmwf_format(k) for k in required_keywords]

queryables: Dict[str, Annotated[Any, FieldInfo]] = {}
for name, values in available_values.items():
# Rename keywords from form with metadata mapping.
# Needed to map constraints like "xxxx" to eodag parameter "ecmwf:xxxx"
key = eodag_key_from_provider_key(name, self.config.metadata_mapping)
key = ecmwf_format(name)

default = defaults.get(key)

Expand Down Expand Up @@ -1229,12 +1232,22 @@ def normalize_results(
:param kwargs: Search arguments
:returns: list of single :class:`~eodag.api.product._product.EOProduct`
"""

# formating of orderLink requires access to the productType value.
results.data = [
{**result, **results.product_type_def_params} for result in results
]

normalized = QueryStringSearch.normalize_results(self, results, **kwargs)

if len(normalized) > 0:
normalized[0].properties["_dc_qs"] = quote_plus(
orjson.dumps(results.query_params)
)
if not normalized:
return normalized

query_params_encoded = quote_plus(orjson.dumps(results.query_params))
for product in normalized:
properties = {**product.properties, **results.query_params}
properties["_dc_qs"] = query_params_encoded
product.properties = {ecmwf_format(k): v for k, v in properties.items()}

return normalized

Expand All @@ -1256,6 +1269,15 @@ def build_query_string(
:param kwargs: keyword arguments to be used in the query string
:return: formatted query params and encode query string
"""
# Reorder kwargs to make sure year/month/day/time if set overwrite default datetime.
# strip_quotes to remove duplicated quotes like "'1_1'" produced by convertors like to_geojson.
priority_keys = [
"startTimeFromAscendingNode",
"completionTimeFromAscendingNode",
]
ordered_kwargs = {k: kwargs[k] for k in priority_keys if k in kwargs}
ordered_kwargs.update({k: strip_quotes(v) for k, v in kwargs.items()})

return QueryStringSearch.build_query_string(
self, product_type=product_type, **kwargs
self, product_type=product_type, **ordered_kwargs
)
2 changes: 1 addition & 1 deletion eodag/resources/product_types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2396,7 +2396,7 @@ CAMS_EU_AIR_QUALITY_FORECAST:
sensorType: ATMOSPHERIC
license: proprietary
title: CAMS European air quality forecasts
missionStartDate: "2021-11-22T00:00:00Z"
missionStartDate: "2021-11-27T00:00:00Z"

CAMS_GFE_GFAS:
abstract: |
Expand Down
Loading
Loading