Skip to content

Commit 4543842

Browse files
m-mohrsoxofaan
authored andcommitted
Support for the federation extension in Jupyter visual outputs #668
1 parent c9d135f commit 4543842

File tree

6 files changed

+111
-35
lines changed

6 files changed

+111
-35
lines changed

openeo/metadata.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -519,8 +519,9 @@ class CollectionMetadata(CubeMetadata):
519519
520520
"""
521521

522-
def __init__(self, metadata: dict, dimensions: List[Dimension] = None):
522+
def __init__(self, metadata: dict, dimensions: List[Dimension] = None, connection = None):
523523
self._orig_metadata = metadata
524+
self._connection = connection
524525
if dimensions is None:
525526
dimensions = self._parse_dimensions(self._orig_metadata)
526527

@@ -640,7 +641,8 @@ def extent(self) -> dict:
640641
return self._orig_metadata.get("extent")
641642

642643
def _repr_html_(self):
643-
return render_component("collection", data=self._orig_metadata)
644+
federation = self._connection.capabilities().ext_federation_backend_details()
645+
return render_component("collection", data=self._orig_metadata, parameters={'federation': federation})
644646

645647
def __str__(self) -> str:
646648
bands = self.band_names if self.has_band_dimension() else "no bands dimension"

openeo/rest/connection.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ def list_collections(self) -> CollectionListingResponse:
734734
# TODO: add caching #383, but reset cache on auth change #254
735735
# TODO #677 add pagination support?
736736
data = self.get("/collections", expected_status=200).json()
737-
return CollectionListingResponse(response_data=data)
737+
return CollectionListingResponse(response_data=data, connection=self)
738738

739739
def list_collection_ids(self) -> List[str]:
740740
"""
@@ -776,7 +776,11 @@ def list_file_formats(self) -> dict:
776776
key="file_formats",
777777
load=lambda: self.get('/file_formats', expected_status=200).json()
778778
)
779-
return VisualDict("file-formats", data=formats)
779+
federation = self.capabilities().ext_federation_backend_details()
780+
return VisualDict("file-formats", data=formats, parameters={
781+
'missing': formats.get("federation:missing", None),
782+
'federation': federation
783+
})
780784

781785
def list_service_types(self) -> dict:
782786
"""
@@ -800,7 +804,8 @@ def list_udf_runtimes(self) -> dict:
800804
key="udf_runtimes",
801805
load=lambda: self.get('/udf_runtimes', expected_status=200).json()
802806
)
803-
return VisualDict("udf-runtimes", data=runtimes)
807+
federation = self.capabilities().ext_federation_backend_details()
808+
return VisualDict("udf-runtimes", data=runtimes, parameters={'federation': federation})
804809

805810
def list_services(self) -> dict:
806811
"""
@@ -810,7 +815,12 @@ def list_services(self) -> dict:
810815
"""
811816
# TODO return parsed service objects
812817
services = self.get('/services', expected_status=200).json()["services"]
813-
return VisualList("data-table", data=services, parameters={'columns': 'services'})
818+
federation = self.capabilities().ext_federation_backend_details()
819+
return VisualList("data-table", data=services, parameters={
820+
'columns': 'services',
821+
'missing': services.get("federation:missing", None),
822+
'federation': federation
823+
})
814824

815825
def describe_collection(self, collection_id: str) -> dict:
816826
"""
@@ -827,7 +837,8 @@ def describe_collection(self, collection_id: str) -> dict:
827837
# TODO: duplication with `Connection.collection_metadata`: deprecate one or the other?
828838
# TODO: add caching #383
829839
data = self.get(f"/collections/{collection_id}", expected_status=200).json()
830-
return VisualDict("collection", data=data)
840+
federation = self.capabilities().ext_federation_backend_details()
841+
return VisualDict("collection", data=data, parameters={'federation': federation})
831842

832843
def collection_items(
833844
self,
@@ -865,11 +876,12 @@ def collection_items(
865876
if limit is not None and limit > 0:
866877
params['limit'] = limit
867878

868-
return paginate(self, url, params, lambda response, page: VisualDict("items", data = response, parameters = {'show-map': True, 'heading': 'Page {} - Items'.format(page)}))
879+
federation = self.capabilities().ext_federation_backend_details()
880+
return paginate(self, url, params, lambda response, page: VisualDict("items", data = response, parameters = {'show-map': True, 'heading': 'Page {} - Items'.format(page), 'federation': federation}))
869881

870882
def collection_metadata(self, name) -> CollectionMetadata:
871883
# TODO: duplication with `Connection.describe_collection`: deprecate one or the other?
872-
return CollectionMetadata(metadata=self.describe_collection(name))
884+
return CollectionMetadata(metadata=self.describe_collection(name), connection=self)
873885

874886
def list_processes(self, namespace: Optional[str] = None) -> ProcessListingResponse:
875887
"""
@@ -891,7 +903,7 @@ def list_processes(self, namespace: Optional[str] = None) -> ProcessListingRespo
891903
)
892904
else:
893905
response = self.get("/processes/" + namespace, expected_status=200).json()
894-
return ProcessListingResponse(response_data=response)
906+
return ProcessListingResponse(response_data=response, connection=self)
895907

896908
def describe_process(self, id: str, namespace: Optional[str] = None) -> dict:
897909
"""
@@ -904,9 +916,14 @@ def describe_process(self, id: str, namespace: Optional[str] = None) -> dict:
904916
"""
905917

906918
processes = self.list_processes(namespace)
919+
federation = self.capabilities().ext_federation_backend_details()
907920
for process in processes:
908921
if process["id"] == id:
909-
return VisualDict("process", data=process, parameters={'show-graph': True, 'provide-download': False})
922+
return VisualDict("process", data=process, parameters={
923+
'show-graph': True,
924+
'provide-download': False,
925+
'federation': federation
926+
})
910927

911928
raise OpenEoClientException("Process does not exist.")
912929

@@ -933,7 +950,7 @@ def list_jobs(self, limit: Union[int, None] = 100) -> JobListingResponse:
933950
# TODO: Parse the result so that Job classes returned?
934951
# TODO: when pagination is enabled: how to expose link to next page?
935952
resp = self.get("/jobs", params={"limit": limit}, expected_status=200).json()
936-
return JobListingResponse(response_data=resp)
953+
return JobListingResponse(response_data=resp, connection=self)
937954

938955
def assert_user_defined_process_support(self):
939956
"""
@@ -996,7 +1013,7 @@ def list_user_defined_processes(self) -> ProcessListingResponse:
9961013
# TODO #677 add pagination support?
9971014
self.assert_user_defined_process_support()
9981015
data = self.get("/process_graphs", expected_status=200).json()
999-
return ProcessListingResponse(response_data=data)
1016+
return ProcessListingResponse(response_data=data, connection=self)
10001017

10011018
def user_defined_process(self, user_defined_process_id: str) -> RESTUserDefinedProcess:
10021019
"""
@@ -1496,9 +1513,14 @@ def list_files(self) -> List[UserFile]:
14961513
14971514
:return: List of the user-uploaded files.
14981515
"""
1499-
files = self.get('/files', expected_status=200).json()['files']
1500-
files = [UserFile.from_metadata(metadata=f, connection=self) for f in files]
1501-
return VisualList("data-table", data=files, parameters={'columns': 'files'})
1516+
data = self.get('/files', expected_status=200).json()
1517+
files = [UserFile.from_metadata(metadata=f, connection=self) for f in data.get('files', [])]
1518+
federation = self.capabilities().ext_federation_backend_details()
1519+
return VisualList("data-table", data=files, parameters={
1520+
'columns': 'files',
1521+
'missing': data.get('federation:missing', None),
1522+
'federation': federation,
1523+
})
15021524

15031525
def get_file(
15041526
self, path: Union[str, PurePosixPath], metadata: Optional[dict] = None

openeo/rest/job.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,11 @@ def __repr__(self):
8181

8282
def _repr_html_(self):
8383
data = self.describe()
84-
currency = self.connection.capabilities().currency()
85-
return render_component('job', data=data, parameters={'currency': currency})
84+
capabilities = self.connection.capabilities()
85+
return render_component('job', data=data, parameters={
86+
'currency': capabilities.currency(),
87+
'federation': capabilities.ext_federation_backend_details(),
88+
})
8689

8790
@openeo_endpoint("GET /jobs/{job_id}")
8891
def describe(self) -> dict:
@@ -235,7 +238,7 @@ def logs(self, offset: Optional[str] = None, level: Union[str, int, None] = None
235238
if level is not None:
236239
params["level"] = log_level_name(level)
237240
response_data = self.connection.get(url, params=params, expected_status=200).json()
238-
return LogsResponse(response_data=response_data, log_level=level)
241+
return LogsResponse(response_data=response_data, log_level=level, connection=self.connection)
239242

240243
@deprecated("Use start_and_wait instead", version="0.39.0")
241244
def run_synchronous(

openeo/rest/models/general.py

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,33 @@ class CollectionListingResponse(list):
4343
but now also provides methods/properties to access additional response data.
4444
4545
:param response_data: response data from a ``GET /collections`` request
46+
:param connection: optional connection object to use for federation extension
4647
4748
.. seealso:: :py:meth:`openeo.rest.connection.Connection.list_collections()`
4849
4950
.. versionadded:: 0.38.0
5051
"""
5152

52-
__slots__ = ["_data"]
53+
__slots__ = ["_data", "_connection"]
5354

54-
def __init__(self, response_data: dict):
55+
def __init__(self, response_data: dict, connection = None):
5556
self._data = response_data
57+
self._connection = connection
5658
# Mimic original list of collection metadata dictionaries
5759
super().__init__(response_data["collections"])
5860

5961
self.ext_federation_missing(auto_warn=True)
6062

6163
def _repr_html_(self):
62-
return render_component(component="collections", data=self)
64+
federation = self._connection.capabilities().ext_federation_backend_details() if self._connection else None
65+
return render_component(
66+
component="collections",
67+
data=self,
68+
parameters={
69+
"missing": self.ext_federation_missing(),
70+
"federation": federation,
71+
}
72+
)
6373

6474
@property
6575
def links(self) -> List[Link]:
@@ -94,24 +104,34 @@ class ProcessListingResponse(list):
94104
but now also provides methods/properties to access additional response data.
95105
96106
:param response_data: response data from a ``GET /processes`` request
107+
:param connection: optional connection object to use for federation extension
97108
98109
.. seealso:: :py:meth:`openeo.rest.connection.Connection.list_processes()`
99110
100111
.. versionadded:: 0.38.0
101112
"""
102113

103-
__slots__ = ["_data"]
114+
__slots__ = ["_data", "_connection"]
104115

105-
def __init__(self, response_data: dict):
116+
def __init__(self, response_data: dict, connection = None):
106117
self._data = response_data
118+
self._connection = connection
107119
# Mimic original list of process metadata dictionaries
108120
super().__init__(response_data["processes"])
109121

110122
self.ext_federation_missing(auto_warn=True)
111123

112124
def _repr_html_(self):
125+
federation = self._connection.capabilities().ext_federation_backend_details() if self._connection else None
113126
return render_component(
114-
component="processes", data=self, parameters={"show-graph": True, "provide-download": False}
127+
component="processes",
128+
data=self,
129+
parameters={
130+
"show-graph": True,
131+
"provide-download": False,
132+
"missing": self.ext_federation_missing(),
133+
"federation": federation,
134+
}
115135
)
116136

117137
@property
@@ -148,23 +168,34 @@ class JobListingResponse(list):
148168
but now also provides methods/properties to access additional response data.
149169
150170
:param response_data: response data from a ``GET /jobs`` request
171+
:param connection: optional connection object to use for federation extension
151172
152173
.. seealso:: :py:meth:`openeo.rest.connection.Connection.list_jobs()`
153174
154175
.. versionadded:: 0.38.0
155176
"""
156177

157-
__slots__ = ["_data"]
178+
__slots__ = ["_data", "_connection"]
158179

159-
def __init__(self, response_data: dict):
180+
def __init__(self, response_data: dict, connection = None):
160181
self._data = response_data
182+
self._connection = connection
161183
# Mimic original list of process metadata dictionaries
162184
super().__init__(response_data["jobs"])
163185

164186
self.ext_federation_missing(auto_warn=True)
165187

166188
def _repr_html_(self):
167-
return render_component(component="data-table", data=self, parameters={"columns": "jobs"})
189+
federation = self._connection.capabilities().ext_federation_backend_details() if self._connection else None
190+
return render_component(
191+
component="data-table",
192+
data=self,
193+
parameters={
194+
"columns": "jobs",
195+
"missing": self.ext_federation_missing(),
196+
"federation": federation,
197+
}
198+
)
168199

169200
@property
170201
def links(self) -> List[Link]:
@@ -202,17 +233,19 @@ class LogsResponse(list):
202233
203234
:param response_data: response data from a ``GET /jobs/{job_id}/logs``
204235
or ``GET /services/{service_id}/logs`` request.
236+
:param connection: optional connection object to use for federation extension
205237
206238
.. seealso:: :py:meth:`~openeo.rest.job.BatchJob.logs()`
207239
and :py:meth:`~openeo.rest.service.Service.logs()`
208240
209241
.. versionadded:: 0.38.0
210242
"""
211243

212-
__slots__ = ["_data"]
244+
__slots__ = ["_data", "_connection"]
213245

214-
def __init__(self, response_data: dict, *, log_level: Optional[str] = None):
246+
def __init__(self, response_data: dict, *, log_level: Optional[str] = None, connection = None):
215247
self._data = response_data
248+
self._connection = connection
216249

217250
logs = response_data.get("logs", [])
218251
# Extra client-side level filtering (in case the back-end does not support that)
@@ -237,7 +270,15 @@ def accept_level(level: str) -> bool:
237270
self.ext_federation_missing(auto_warn=True)
238271

239272
def _repr_html_(self):
240-
return render_component(component="logs", data=self)
273+
federation = self._connection.capabilities().ext_federation_backend_details() if self._connection else None
274+
return render_component(
275+
component="logs",
276+
data=self,
277+
parameters={
278+
"missing": self.ext_federation_missing(),
279+
"federation": federation,
280+
}
281+
)
241282

242283
@property
243284
def logs(self) -> List[LogEntry]:

openeo/rest/service.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ def __repr__(self):
2525

2626
def _repr_html_(self):
2727
data = self.describe_service()
28-
currency = self.connection.capabilities().currency()
29-
return VisualDict('service', data = data, parameters = {'currency': currency})
28+
capabilities = self.connection.capabilities()
29+
return VisualDict('service', data = data, parameters = {
30+
'currency': capabilities.currency(),
31+
'federation': capabilities.ext_federation_backend_details(),
32+
})
3033

3134
def describe_service(self):
3235
""" Get all information about a secondary web service."""
@@ -52,4 +55,4 @@ def logs(self, offset: Optional[str] = None, level: Union[str, int, None] = None
5255
if level is not None:
5356
params["level"] = log_level_name(level)
5457
response_data = self.connection.get(url, params=params, expected_status=200).json()
55-
return LogsResponse(response_data=response_data, log_level=level)
58+
return LogsResponse(response_data=response_data, log_level=level, connection=self.connection)

openeo/rest/udp.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,12 @@ def __init__(self, user_defined_process_id: str, connection: Connection):
8989

9090
def _repr_html_(self):
9191
process = self.describe()
92-
return render_component('process', data=process, parameters = {'show-graph': True, 'provide-download': False})
92+
federation = self._connection.capabilities().ext_federation_backend_details()
93+
return render_component('process', data=process, parameters = {
94+
'show-graph': True,
95+
'provide-download': False,
96+
'federation': federation,
97+
})
9398

9499
def store(
95100
self,

0 commit comments

Comments
 (0)