Skip to content

Commit

Permalink
return STAC Items with valid date/time for time series job results
Browse files Browse the repository at this point in the history
* be explicit about which metadata

Open-EO/openeo-geopyspark-driver#852

* expose start/end_datetime from asset metadata

Open-EO/openeo-geopyspark-driver#852

* update CHANGELOG

Open-EO/openeo-geopyspark-driver#852

* update version and CHANGELOG

Open-EO/openeo-geopyspark-driver#852
  • Loading branch information
bossie committed Sep 13, 2024
1 parent 911cfff commit adf218f
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ and start a new "In Progress" section above it.

## In progress

## 0.107.7

- return STAC Items with valid date/time for time series job results ([Open-EO/openeo-geopyspark-driver#852](https://github.com/Open-EO/openeo-geopyspark-driver/issues/852))

## 0.107.6

- support passing the output of `raster_to_vector` to `aggregate_spatial` during dry run ([EU-GRASSLAND-WATCH/EUGW#7](https://github.com/EU-GRASSLAND-WATCH/EUGW/issues/7))
Expand Down
2 changes: 1 addition & 1 deletion openeo_driver/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.107.6a1"
__version__ = "0.107.7a1"
24 changes: 24 additions & 0 deletions openeo_driver/dummy/dummy_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,30 @@ def get_result_assets(self, job_id: str, user_id: str) -> Dict[str, dict]:
},
}

if job_id == "j-24083059866540a38cab32b028be0ab5":
return {
"timeseries.parquet": {
"roles": ["data"],
"type": "application/parquet; profile=geo",
"href": "/data/projects/OpenEO/j-24083059866540a38cab32b028be0ab5/timeseries.parquet",
"bands": [Band(name="S1-SIGMA0-VV"), Band(name="S1-SIGMA0-VH"), Band(name="S2-L2A-B01")],
"geometry": {
"type": "Polygon",
"coordinates": [
[
[34.456156, -0.910085],
[34.456156, -0.345477],
[34.796396, -0.345477],
[34.796396, -0.910085],
[34.456156, -0.910085],
]
],
},
"start_datetime": "2016-10-30T00:00:00+00:00",
"end_datetime": "2018-05-03T00:00:00+00:00",
},
}

return {
"output.tiff": {
"output_dir": f"{self._output_root()}/{job_id}",
Expand Down
12 changes: 9 additions & 3 deletions openeo_driver/save_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,15 @@ def write_assets(self, directory: Union[str, Path]) -> Dict[str, StacAsset]:
with open(filename, 'w') as f:
json.dump(self.prepare_for_json(), f)
asset["href"] = filename
if self._metadata is not None and self._metadata.has_band_dimension():
bands = [b._asdict() for b in self._metadata.bands]
asset["bands"] = bands

if self._metadata is not None:
if self._metadata.has_band_dimension():
bands = [b._asdict() for b in self._metadata.bands]
asset["bands"] = bands
if self._metadata.has_temporal_dimension():
start_datetime, end_datetime = self._metadata.temporal_dimension.extent
asset["start_datetime"] = start_datetime
asset["end_datetime"] = end_datetime

the_file = Path(filename)
if the_file.exists():
Expand Down
14 changes: 7 additions & 7 deletions openeo_driver/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1297,19 +1297,19 @@ def _get_job_result_item(job_id, item_id, user_id):
if len(assets_for_item_id) != 1:
raise AssertionError(f"expected exactly 1 asset with file name {item_id}")

asset_filename, metadata = next(iter(assets_for_item_id.items()))
asset_filename, asset_metadata = next(iter(assets_for_item_id.items()))

job_info = backend_implementation.batch_jobs.get_job_info(job_id, user_id)

geometry = metadata.get("geometry", job_info.geometry)
bbox = metadata.get("bbox", job_info.bbox)
geometry = asset_metadata.get("geometry", job_info.geometry)
bbox = asset_metadata.get("bbox", job_info.bbox)

properties = {"datetime": metadata.get("datetime")}
properties = {"datetime": asset_metadata.get("datetime")}
if properties["datetime"] is None:
to_datetime = Rfc3339(propagate_none=True).datetime

start_datetime = to_datetime(job_info.start_datetime)
end_datetime = to_datetime(job_info.end_datetime)
start_datetime = asset_metadata.get("start_datetime") or to_datetime(job_info.start_datetime)
end_datetime = asset_metadata.get("end_datetime") or to_datetime(job_info.end_datetime)

if start_datetime == end_datetime:
properties["datetime"] = start_datetime
Expand Down Expand Up @@ -1358,7 +1358,7 @@ def _get_job_result_item(job_id, item_id, user_id):
"type": "application/json",
},
],
"assets": {asset_filename: _asset_object(job_id, user_id, asset_filename, metadata,job_info)},
"assets": {asset_filename: _asset_object(job_id, user_id, asset_filename, asset_metadata, job_info)},
"collection": job_id,
}
# Add optional items, if they are present.
Expand Down
77 changes: 75 additions & 2 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,14 @@ def _fresh_job_registry(next_job_id="job-1234", output_root: Optional[Path] = No
start_datetime=datetime(2020, 1, 1, 0, 0, 0),
end_datetime=datetime(2020, 3, 15, 0, 0, 0),
),
(TEST_USER, "j-24083059866540a38cab32b028be0ab5"): BatchJobMetadata(
id="j-24083059866540a38cab32b028be0ab5",
status='finished',
created=datetime(2024, 8, 30, 12, 16, 34),
bbox=[34.456156, -0.910085, 34.796396, -0.345477],
start_datetime=None,
end_datetime=None,
),
}
dummy_backend.DummyBatchJobs._job_result_registry = {}

Expand Down Expand Up @@ -1383,6 +1391,12 @@ def test_list_user_jobs_100(self, api100):
'progress': 100,
'created': "2024-06-04T14:20:23Z",
},
{
"id": "j-24083059866540a38cab32b028be0ab5",
"status": "finished",
"progress": 100,
"created": "2024-08-30T12:16:34Z",
}
],
"links": []
}
Expand Down Expand Up @@ -2684,8 +2698,7 @@ def test_get_job_result_item(self, flask_app, api110, backend_config_overrides):
("timeseries.csv", "text/csv"),
("timeseries.parquet", "application/parquet; profile=geo"),
])
def test_get_vector_cube_job_result_item(self, flask_app, api110, backend_config_overrides, vector_item_id,
vector_asset_media_type):
def test_get_vector_cube_job_result_item(self, flask_app, api110, vector_item_id, vector_asset_media_type):
vector_asset_filename = vector_item_id

with self._fresh_job_registry():
Expand Down Expand Up @@ -2747,6 +2760,66 @@ def test_get_vector_cube_job_result_item(self, flask_app, api110, backend_config
extensions=resp_data.get("stac_extensions", []),
)

def test_get_job_result_item_with_temporal_extent_on_asset(self, flask_app, api110):
with self._fresh_job_registry():
resp = api110.get(f"/jobs/j-24083059866540a38cab32b028be0ab5/results/items/timeseries.parquet",
headers=self.AUTH_HEADER)

resp_data = resp.assert_status_code(200).json

assert resp_data == DictSubSet({
"type": "Feature",
"stac_version": "1.0.0",
"id": "timeseries.parquet",
"geometry": {"type": "Polygon",
"coordinates": [[[34.456156, -0.910085],
[34.456156, -0.345477],
[34.796396, -0.345477],
[34.796396, -0.910085],
[34.456156, -0.910085]]]},
"bbox": [34.456156, -0.910085, 34.796396, -0.345477],
"properties": {
"datetime": None,
"start_datetime": "2016-10-30T00:00:00+00:00",
"end_datetime": "2018-05-03T00:00:00+00:00",
},
"collection": "j-24083059866540a38cab32b028be0ab5",
"links": [
{
"href": f"http://oeo.net/openeo/1.1.0/jobs/j-24083059866540a38cab32b028be0ab5/results/items/timeseries.parquet",
"rel": "self",
"type": "application/geo+json",
},
{
"href": "http://oeo.net/openeo/1.1.0/jobs/j-24083059866540a38cab32b028be0ab5/results",
"rel": "collection",
"type": "application/json",
}
],
"assets": {
"timeseries.parquet": {
"href": f"http://oeo.net/openeo/1.1.0/jobs/j-24083059866540a38cab32b028be0ab5/results/assets/timeseries.parquet",
"type": "application/parquet; profile=geo",
"roles": ["data"],
"title": "timeseries.parquet",
"eo:bands": [
{"name": "S1-SIGMA0-VV"},
{"name": "S1-SIGMA0-VH"},
{"name": "S2-L2A-B01"}
],
}
}
})

assert resp.headers["Content-Type"] == "application/geo+json"

pystac.validation.stac_validator.JsonSchemaSTACValidator().validate(
stac_dict=resp_data,
stac_object_type=pystac.STACObjectType.ITEM,
stac_version=resp_data.get("stac_version", "0.9.0"),
extensions=resp_data.get("stac_extensions", []),
)

@mock.patch("time.time", mock.MagicMock(return_value=1234))
@pytest.mark.parametrize("backend_config_overrides", [{"url_signer": UrlSigner(secret="123&@#", expiration=1000)}])
def test_download_ml_model_metadata_signed(self, flask_app, api110, backend_config_overrides):
Expand Down

0 comments on commit adf218f

Please sign in to comment.