Skip to content

Commit 62c582f

Browse files
jdriesbossieElienVandermaesenVITO
authored
1111 return stac11 items (#411)
* add field for items to metadata Open-EO/openeo-geopyspark-driver#1111 * basics to return new-style item links if they exist Open-EO/openeo-geopyspark-driver#1111 * basics to return new-style item links if they exist Open-EO/openeo-geopyspark-driver#1111 * add test skeletons for results Collection and items Open-EO/openeo-geopyspark-driver#1111 * issue 1111 get_result_metadata also returns items * issue 1111 get_result_metadata also returns items * issue 1111 improve exception * issue 1111 add signing to assets in items * illustrate latest PR review remarks Open-EO/openeo-geopyspark-driver#1111 * issue 1111 add mandatory properties * issue 1111 small change * issue 1111 set stac_version to 1.1.0 --------- Co-authored-by: Jan Van den bosch <jan@bossie.org> Co-authored-by: elienVandermaesenVITO <elien.vandermaesen@vito.be>
1 parent 748b931 commit 62c582f

File tree

3 files changed

+335
-14
lines changed

3 files changed

+335
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ and start a new "In Progress" section above it.
2323

2424
- Start supporting custom `UdfRuntimes` implementation in `OpenEoBackendImplementation` ([#415](https://github.com/Open-EO/openeo-python-driver/issues/415))
2525
- Process graph parsing (dry-run) for very large graphs got faster. ([#426](https://github.com/Open-EO/openeo-python-driver/issues/426))
26+
- `get_result_metadata` can return items ([Open-EO/openeo-geopyspark-driver#1111](https://github.com/Open-EO/openeo-geopyspark-driver/issues/1111))
2627

2728
## 0.135.0
2829

openeo_driver/views.py

Lines changed: 133 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,16 +1163,20 @@ def job_results_canonical_url() -> str:
11631163
if TREAT_JOB_RESULTS_V100_LIKE_V110 or requested_api_version().at_least("1.1.0"):
11641164
ml_model_metadata = None
11651165

1166-
def job_result_item_url(item_id) -> str:
1166+
def job_result_item_url(item_id, is11 = False) -> str:
11671167
signer = get_backend_config().url_signer
1168+
1169+
method_start = ".get_job_result_item"
1170+
if is11:
1171+
method_start = method_start + "11"
11681172
if not signer:
1169-
return url_for(".get_job_result_item", job_id=job_id, item_id=item_id, _external=True)
1173+
return url_for(method_start, job_id=job_id, item_id=item_id, _external=True)
11701174

11711175
expires = signer.get_expires()
11721176
secure_key = signer.sign_job_item(job_id=job_id, user_id=user_id, item_id=item_id, expires=expires)
11731177
user_base64 = user_id_b64_encode(user_id)
11741178
return url_for(
1175-
".get_job_result_item_signed",
1179+
method_start + "_signed",
11761180
job_id=job_id,
11771181
user_base64=user_base64,
11781182
secure_key=secure_key,
@@ -1181,19 +1185,27 @@ def job_result_item_url(item_id) -> str:
11811185
_external=True,
11821186
)
11831187

1184-
for filename, metadata in result_assets.items():
1185-
if ("data" in metadata.get("roles", []) and
1186-
any(media_type in metadata.get("type", "") for media_type in
1187-
["geotiff", "netcdf", "text/csv", "application/parquet"])):
1188-
links.append(
1189-
{"rel": "item", "href": job_result_item_url(item_id=filename), "type": stac_item_media_type}
1190-
)
1191-
elif metadata.get("ml_model_metadata", False):
1192-
# TODO: Currently we only support one ml_model per batch job.
1193-
ml_model_metadata = metadata
1188+
1189+
if len(result_metadata.items) > 0 :
1190+
for item_id in result_metadata.items.keys():
11941191
links.append(
1195-
{"rel": "item", "href": job_result_item_url(item_id=filename), "type": "application/json"}
1192+
{"rel": "item", "href": job_result_item_url(item_id=item_id, is11=True), "type": stac_item_media_type}
11961193
)
1194+
else:
1195+
1196+
for filename, metadata in result_assets.items():
1197+
if ("data" in metadata.get("roles", []) and
1198+
any(media_type in metadata.get("type", "") for media_type in
1199+
["geotiff", "netcdf", "text/csv", "application/parquet"])):
1200+
links.append(
1201+
{"rel": "item", "href": job_result_item_url(item_id=filename), "type": stac_item_media_type}
1202+
)
1203+
elif metadata.get("ml_model_metadata", False):
1204+
# TODO: Currently we only support one ml_model per batch job.
1205+
ml_model_metadata = metadata
1206+
links.append(
1207+
{"rel": "item", "href": job_result_item_url(item_id=filename), "type": "application/json"}
1208+
)
11971209

11981210
result = dict_no_none(
11991211
{
@@ -1360,11 +1372,118 @@ def get_job_result_item_signed(job_id, user_base64, secure_key, item_id):
13601372
signer.verify_job_item(signature=secure_key, job_id=job_id, user_id=user_id, item_id=item_id, expires=expires)
13611373
return _get_job_result_item(job_id, item_id, user_id)
13621374

1375+
@api_endpoint
1376+
@blueprint.route('/jobs/<job_id>/results/items11/<user_base64>/<secure_key>/<item_id>', methods=['GET'])
1377+
def get_job_result_item11_signed(job_id, user_base64, secure_key, item_id):
1378+
expires = request.args.get('expires')
1379+
signer = get_backend_config().url_signer
1380+
user_id = user_id_b64_decode(user_base64)
1381+
signer.verify_job_item(signature=secure_key, job_id=job_id, user_id=user_id, item_id=item_id, expires=expires)
1382+
return _get_job_result_item11(job_id, item_id, user_id)
1383+
13631384
@blueprint.route('/jobs/<job_id>/results/items/<item_id>', methods=['GET'])
13641385
@auth_handler.requires_bearer_auth
13651386
def get_job_result_item(job_id: str, item_id: str, user: User) -> flask.Response:
13661387
return _get_job_result_item(job_id, item_id, user.user_id)
13671388

1389+
@api_endpoint(version=ComparableVersion("1.1.0").or_higher)
1390+
@blueprint.route('/jobs/<job_id>/results/items11/<item_id>', methods=['GET'])
1391+
@auth_handler.requires_bearer_auth
1392+
def get_job_result_item11(job_id: str, item_id: str, user: User) -> flask.Response:
1393+
return _get_job_result_item11(job_id, item_id, user.user_id)
1394+
1395+
def _get_job_result_item11(job_id, item_id, user_id):
1396+
if item_id == DriverMlModel.METADATA_FILE_NAME:
1397+
return _download_ml_model_metadata(job_id, item_id, user_id)
1398+
1399+
metadata = backend_implementation.batch_jobs.get_result_metadata(
1400+
job_id=job_id, user_id=user_id
1401+
)
1402+
1403+
if item_id not in metadata.items:
1404+
raise OpenEOApiException("Item with id {item_id!r} not found in job {job_id!r}".format(item_id=item_id, job_id=job_id), status_code=404)
1405+
item_metadata = metadata.items.get(item_id,None)
1406+
1407+
job_info = backend_implementation.batch_jobs.get_job_info(job_id, user_id)
1408+
1409+
assets = {}
1410+
for asset_key, asset in item_metadata.get("assets", {}).items():
1411+
assets[asset_key] = _asset_object(job_id, user_id, asset_key, asset, job_info)
1412+
1413+
geometry = item_metadata.get("geometry", job_info.geometry)
1414+
bbox = item_metadata.get("bbox", job_info.bbox)
1415+
1416+
properties = item_metadata.get("properties", {"datetime": item_metadata.get("datetime")})
1417+
if properties["datetime"] is None:
1418+
to_datetime = Rfc3339(propagate_none=True).datetime
1419+
1420+
start_datetime = item_metadata.get("start_datetime") or to_datetime(job_info.start_datetime)
1421+
end_datetime = item_metadata.get("end_datetime") or to_datetime(job_info.end_datetime)
1422+
1423+
if start_datetime == end_datetime:
1424+
properties["datetime"] = start_datetime
1425+
else:
1426+
if start_datetime:
1427+
properties["start_datetime"] = start_datetime
1428+
if end_datetime:
1429+
properties["end_datetime"] = end_datetime
1430+
1431+
if job_info.proj_shape:
1432+
properties["proj:shape"] = job_info.proj_shape
1433+
if job_info.proj_bbox:
1434+
properties["proj:bbox"] = job_info.proj_bbox
1435+
if job_info.epsg:
1436+
properties["proj:epsg"] = job_info.epsg
1437+
1438+
if job_info.proj_bbox and job_info.epsg:
1439+
if not bbox:
1440+
bbox = BoundingBox.from_wsen_tuple(job_info.proj_bbox, job_info.epsg).reproject(4326).as_wsen_tuple()
1441+
if not geometry:
1442+
geometry = BoundingBox.from_wsen_tuple(job_info.proj_bbox, job_info.epsg).as_polygon()
1443+
geometry = mapping(reproject_geometry(geometry, CRS.from_epsg(job_info.epsg), CRS.from_epsg(4326)))
1444+
1445+
stac_item = {
1446+
"type": "Feature",
1447+
"stac_version": "1.1.0",
1448+
"stac_extensions": [
1449+
STAC_EXTENSION.EO_V110,
1450+
STAC_EXTENSION.FILEINFO,
1451+
STAC_EXTENSION.PROJECTION,
1452+
],
1453+
"id": item_id,
1454+
"geometry": geometry,
1455+
"bbox": bbox,
1456+
"properties": properties,
1457+
"links": [
1458+
{
1459+
"rel": "self",
1460+
# MUST be absolute
1461+
"href": url_for(".get_job_result_item", job_id=job_id, item_id=item_id, _external=True),
1462+
"type": stac_item_media_type,
1463+
},
1464+
{
1465+
"rel": "collection",
1466+
"href": url_for(".list_job_results", job_id=job_id, _external=True), # SHOULD be absolute
1467+
"type": "application/json",
1468+
},
1469+
],
1470+
"assets": assets,
1471+
"collection": job_id,
1472+
}
1473+
# Add optional items, if they are present.
1474+
stac_item.update(
1475+
**dict_no_none(
1476+
{
1477+
"epsg": job_info.epsg,
1478+
}
1479+
)
1480+
)
1481+
1482+
resp = jsonify(stac_item)
1483+
resp.mimetype = stac_item_media_type
1484+
return resp
1485+
1486+
13681487
def _get_job_result_item(job_id, item_id, user_id):
13691488
if item_id == DriverMlModel.METADATA_FILE_NAME:
13701489
return _download_ml_model_metadata(job_id, item_id, user_id)

0 commit comments

Comments
 (0)