Skip to content

Commit 2272a95

Browse files
committed
feat: add on_execution_submitted to gooddata_pandas
Users can now provide a callback that will be called with the Execution object as soon as it is available. This is useful for getting some of the information available in the Execution object before the actual data is loaded. The Execution object can also be used to cancel the execution result operation. JIRA: CQ-1387 risk: low
1 parent be33aeb commit 2272a95

File tree

3 files changed

+81
-17
lines changed

3 files changed

+81
-17
lines changed

gooddata-pandas/gooddata_pandas/data_access.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# (C) 2021 GoodData Corporation
22
from __future__ import annotations
33

4-
from typing import Any, Optional, Union
4+
from typing import Any, Callable, Optional, Union
55

66
from gooddata_sdk import (
77
Attribute,
88
AttributeFilter,
99
CatalogAttribute,
10+
Execution,
1011
ExecutionDefinition,
1112
ExecutionResponse,
1213
Filter,
@@ -412,6 +413,7 @@ def compute_and_extract(
412413
columns: ColumnsDef,
413414
index_by: Optional[IndexDef] = None,
414415
filter_by: Optional[Union[Filter, list[Filter]]] = None,
416+
on_execution_submitted: Optional[Callable[[Execution], None]] = None,
415417
) -> tuple[dict, dict]:
416418
"""
417419
Convenience function that computes and extracts data from the execution response.
@@ -422,14 +424,16 @@ def compute_and_extract(
422424
columns (ColumnsDef): The columns definition.
423425
index_by (Optional[IndexDef]): The index definition, if any.
424426
filter_by (Optional[Union[Filter, list[Filter]]]): A filter or a list of filters, if any.
427+
on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was
428+
submitted to the backend.
425429
426430
Returns:
427431
tuple: A tuple containing the following dictionaries:
428432
- dict: A dictionary with data for each column in `columns`.
429433
- dict: A dictionary with data for constructing index(es) for each index in index_by.
430434
431-
Note: For convenience it is possible to pass just single index. in that case the index dict will contain exactly
432-
one key of '0' (just get first value from dict when consuming the result).
435+
Note: For convenience, it is possible to pass just a single index. In that case, the index dict will contain exactly
436+
one key of '0' (just get the first value from dict when consuming the result).
433437
"""
434438
result = _compute(
435439
sdk=sdk,
@@ -441,6 +445,9 @@ def compute_and_extract(
441445

442446
response, col_to_attr_idx, col_to_metric_idx, index_to_attr_idx = result
443447

448+
if on_execution_submitted is not None:
449+
on_execution_submitted(response)
450+
444451
exec_def = response.exec_def
445452
cols = list(columns.keys())
446453

gooddata-pandas/gooddata_pandas/dataframe.py

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# (C) 2021 GoodData Corporation
22
from __future__ import annotations
33

4-
from typing import Optional, Union
4+
from typing import Callable, Optional, Union
55

66
import pandas
77
from gooddata_api_client import models
88
from gooddata_sdk import (
99
Attribute,
1010
BareExecutionResponse,
11+
Execution,
1112
ExecutionDefinition,
1213
Filter,
1314
GoodDataSdk,
@@ -68,19 +69,25 @@ def __init__(self, sdk: GoodDataSdk, workspace_id: str) -> None:
6869
self._workspace_id = workspace_id
6970

7071
def indexed(
71-
self, index_by: IndexDef, columns: ColumnsDef, filter_by: Optional[Union[Filter, list[Filter]]] = None
72+
self,
73+
index_by: IndexDef,
74+
columns: ColumnsDef,
75+
filter_by: Optional[Union[Filter, list[Filter]]] = None,
76+
on_execution_submitted: Optional[Callable[[Execution], None]] = None,
7277
) -> pandas.DataFrame:
7378
"""
7479
Creates a data frame indexed by values of the label. The data frame columns will be created from either
7580
metrics or other label values.
7681
77-
Note that depending on composition of the labels, the DataFrame's index may or may not be unique.
82+
Note that depending on the composition of the labels, the DataFrame's index may or may not be unique.
7883
7984
Args:
8085
index_by (IndexDef): One or more labels to index by.
8186
columns (ColumnsDef): Dictionary mapping column name to its definition.
8287
filter_by (Optional[Union[Filter, list[Filter]]]):
8388
Optional filters to apply during computation on the server.
89+
on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was
90+
submitted to the backend.
8491
8592
Returns:
8693
pandas.DataFrame: A DataFrame instance.
@@ -91,14 +98,18 @@ def indexed(
9198
columns=columns,
9299
index_by=index_by,
93100
filter_by=filter_by,
101+
on_execution_submitted=on_execution_submitted,
94102
)
95103

96104
_idx = make_pandas_index(index)
97105

98106
return pandas.DataFrame(data=data, index=_idx)
99107

100108
def not_indexed(
101-
self, columns: ColumnsDef, filter_by: Optional[Union[Filter, list[Filter]]] = None
109+
self,
110+
columns: ColumnsDef,
111+
filter_by: Optional[Union[Filter, list[Filter]]] = None,
112+
on_execution_submitted: Optional[Callable[[Execution], None]] = None,
102113
) -> pandas.DataFrame:
103114
"""
104115
Creates a data frame with columns created from metrics and or labels.
@@ -107,28 +118,42 @@ def not_indexed(
107118
columns (ColumnsDef): Dictionary mapping column name to its definition.
108119
filter_by (Optional[Union[Filter, list[Filter]]]): Optionally specify filters to apply during
109120
computation on the server.
121+
on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was
122+
submitted to the backend.
110123
111124
Returns:
112125
pandas.DataFrame: A DataFrame instance.
113126
"""
114127

115-
data, _ = compute_and_extract(self._sdk, self._workspace_id, columns=columns, filter_by=filter_by)
128+
data, _ = compute_and_extract(
129+
self._sdk,
130+
self._workspace_id,
131+
columns=columns,
132+
filter_by=filter_by,
133+
on_execution_submitted=on_execution_submitted,
134+
)
116135

117136
return pandas.DataFrame(data=data)
118137

119138
def for_items(
120-
self, items: ColumnsDef, filter_by: Optional[Union[Filter, list[Filter]]] = None, auto_index: bool = True
139+
self,
140+
items: ColumnsDef,
141+
filter_by: Optional[Union[Filter, list[Filter]]] = None,
142+
auto_index: bool = True,
143+
on_execution_submitted: Optional[Callable[[Execution], None]] = None,
121144
) -> pandas.DataFrame:
122145
"""
123146
Creates a data frame for named items. This is a convenience method that will create DataFrame with or
124-
without index based on the context of the items that you pass.
147+
without an index based on the context of the items that you pass.
125148
126149
Args:
127150
items (ColumnsDef): Dictionary mapping item name to its definition.
128151
filter_by (Optional[Union[Filter, list[Filter]]]): Optionally specify filters to apply during computation
129152
on the server.
130153
auto_index (bool): Default True. Enables creation of DataFrame with index depending on the contents
131154
of the items.
155+
on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was
156+
submitted to the backend.
132157
133158
Returns:
134159
pandas.DataFrame: A DataFrame instance.
@@ -157,16 +182,24 @@ def for_items(
157182
index_by=resolved_attr_cols,
158183
columns=resolved_measure_cols,
159184
filter_by=filter_by,
185+
on_execution_submitted=on_execution_submitted,
160186
)
161187

162-
def for_visualization(self, visualization_id: str, auto_index: bool = True) -> pandas.DataFrame:
188+
def for_visualization(
189+
self,
190+
visualization_id: str,
191+
auto_index: bool = True,
192+
on_execution_submitted: Optional[Callable[[Execution], None]] = None,
193+
) -> pandas.DataFrame:
163194
"""
164195
Creates a data frame with columns based on the content of the visualization with the provided identifier.
165196
166197
Args:
167198
visualization_id (str): Visualization identifier.
168199
auto_index (bool): Default True. Enables creation of DataFrame with index depending on the contents
169200
of the visualization.
201+
on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was
202+
submitted to the backend.
170203
171204
Returns:
172205
pandas.DataFrame: A DataFrame instance.
@@ -181,22 +214,31 @@ def for_visualization(self, visualization_id: str, auto_index: bool = True) -> p
181214
**{naming.col_name_for_metric(m): m.as_computable() for m in visualization.metrics},
182215
}
183216

184-
return self.for_items(columns, filter_by=filter_by, auto_index=auto_index)
217+
return self.for_items(
218+
columns, filter_by=filter_by, auto_index=auto_index, on_execution_submitted=on_execution_submitted
219+
)
185220

186221
def for_created_visualization(
187-
self, created_visualizations_response: dict
222+
self,
223+
created_visualizations_response: dict,
224+
on_execution_submitted: Optional[Callable[[Execution], None]] = None,
188225
) -> tuple[pandas.DataFrame, DataFrameMetadata]:
189226
"""
190227
Creates a data frame using a created visualization.
191228
192229
Args:
193230
created_visualizations_response (dict): Created visualization response.
231+
on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was
232+
submitted to the backend.
194233
195234
Returns:
196235
pandas.DataFrame: A DataFrame instance.
197236
"""
198237
execution_definition = self._sdk.compute.build_exec_def_from_chat_result(created_visualizations_response)
199-
return self.for_exec_def(exec_def=execution_definition)
238+
return self.for_exec_def(
239+
exec_def=execution_definition,
240+
on_execution_submitted=on_execution_submitted,
241+
)
200242

201243
def result_cache_metadata_for_exec_result_id(self, result_id: str) -> ResultCacheMetadata:
202244
"""
@@ -217,6 +259,7 @@ def for_exec_def(
217259
result_size_dimensions_limits: ResultSizeDimensions = (),
218260
result_size_bytes_limit: Optional[int] = None,
219261
page_size: int = _DEFAULT_PAGE_SIZE,
262+
on_execution_submitted: Optional[Callable[[Execution], None]] = None,
220263
) -> tuple[pandas.DataFrame, DataFrameMetadata]:
221264
"""
222265
Creates a data frame using an execution definition.
@@ -247,6 +290,8 @@ def for_exec_def(
247290
result_size_dimensions_limits (ResultSizeDimensions): A tuple containing maximum size of result dimensions.
248291
result_size_bytes_limit (Optional[int]): Maximum size of result in bytes.
249292
page_size (int): Number of records per page.
293+
on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was
294+
submitted to the backend.
250295
251296
Returns:
252297
Tuple[pandas.DataFrame, DataFrameMetadata]: Tuple holding DataFrame and DataFrame metadata.
@@ -257,6 +302,9 @@ def for_exec_def(
257302
execution = self._sdk.compute.for_exec_def(workspace_id=self._workspace_id, exec_def=exec_def)
258303
result_cache_metadata = self.result_cache_metadata_for_exec_result_id(execution.result_id)
259304

305+
if on_execution_submitted is not None:
306+
on_execution_submitted(execution)
307+
260308
return convert_execution_response_to_dataframe(
261309
execution_response=execution.bare_exec_response,
262310
result_cache_metadata=result_cache_metadata,
@@ -302,7 +350,7 @@ def for_exec_result_id(
302350
label_overrides (Optional[LabelOverrides]): Label overrides for metrics and attributes.
303351
result_cache_metadata (Optional[ResultCacheMetadata]): Cache metadata for the execution result.
304352
result_size_dimensions_limits (ResultSizeDimensions): A tuple containing maximum size of result dimensions.
305-
result_size_bytes_limit (Optional[int]): Maximum size of result in bytes.
353+
result_size_bytes_limit (Optional[int]): Maximum size of the result in bytes.
306354
use_local_ids_in_headers (bool): Use local identifier in headers.
307355
use_primary_labels_in_attributes (bool): Use primary labels in attributes.
308356
page_size (int): Number of records per page.

gooddata-pandas/gooddata_pandas/series.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# (C) 2021 GoodData Corporation
22
from __future__ import annotations
33

4-
from typing import Optional, Union
4+
from typing import Callable, Optional, Union
55

66
import pandas
7-
from gooddata_sdk import Attribute, Filter, GoodDataSdk, ObjId, SimpleMetric
7+
from gooddata_sdk import Attribute, Execution, Filter, GoodDataSdk, ObjId, SimpleMetric
88

99
from gooddata_pandas.data_access import compute_and_extract
1010
from gooddata_pandas.utils import IndexDef, LabelItemDef, make_pandas_index
@@ -28,6 +28,7 @@ def indexed(
2828
index_by: IndexDef,
2929
data_by: Union[SimpleMetric, str, ObjId, Attribute],
3030
filter_by: Optional[Union[Filter, list[Filter]]] = None,
31+
on_execution_submitted: Optional[Callable[[Execution], None]] = None,
3132
) -> pandas.Series:
3233
"""Creates pandas Series from data points calculated from a single `data_by`.
3334
@@ -61,6 +62,9 @@ def indexed(
6162
- object identifier: ``ObjId(id='some_label_id', type='<type>')``
6263
- Attribute or Metric depending on type of filter
6364
65+
on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was
66+
submitted to the backend.
67+
6468
Returns:
6569
pandas.Series: pandas series instance
6670
"""
@@ -71,6 +75,7 @@ def indexed(
7175
index_by=index_by,
7276
columns={"_series": data_by},
7377
filter_by=filter_by,
78+
on_execution_submitted=on_execution_submitted,
7479
)
7580

7681
_idx = make_pandas_index(index)
@@ -82,6 +87,7 @@ def not_indexed(
8287
data_by: Union[SimpleMetric, str, ObjId, Attribute],
8388
granularity: Optional[Union[list[LabelItemDef], IndexDef]] = None,
8489
filter_by: Optional[Union[Filter, list[Filter]]] = None,
90+
on_execution_submitted: Optional[Callable[[Execution], None]] = None,
8591
) -> pandas.Series:
8692
"""
8793
Creates a pandas.Series from data points calculated from a single `data_by` without constructing an index.
@@ -108,6 +114,8 @@ def not_indexed(
108114
- ObjId: ObjId(id='some_label_id', type='<type>')
109115
- Attribute or Metric depending on the type of filter
110116
Defaults to None.
117+
on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was
118+
submitted to the backend.
111119
112120
Returns:
113121
pandas.Series: The resulting pandas Series instance.
@@ -124,6 +132,7 @@ def not_indexed(
124132
index_by=_index,
125133
columns={"_series": data_by},
126134
filter_by=filter_by,
135+
on_execution_submitted=on_execution_submitted,
127136
)
128137

129138
return pandas.Series(data=data["_series"])

0 commit comments

Comments
 (0)