@@ -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