From f7f40bbebcd1c3ec5920bbddecdad520be387355 Mon Sep 17 00:00:00 2001 From: Dan Homola Date: Wed, 18 Jun 2025 16:19:10 +0200 Subject: [PATCH 1/2] feat: add is_cancellable support to GoodPandas As already done in GoodData SDK, GoodPandas now also supports this parameter with the same default. JIRA: CQ-1411 risk: low --- .../gooddata_pandas/data_access.py | 15 ++++++++++-- gooddata-pandas/gooddata_pandas/dataframe.py | 23 +++++++++++++++++-- gooddata-pandas/gooddata_pandas/series.py | 7 ++++++ gooddata-sdk/gooddata_sdk/compute/service.py | 15 +++++++++--- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/gooddata-pandas/gooddata_pandas/data_access.py b/gooddata-pandas/gooddata_pandas/data_access.py index df76c1771..a6b6e0412 100644 --- a/gooddata-pandas/gooddata_pandas/data_access.py +++ b/gooddata-pandas/gooddata_pandas/data_access.py @@ -33,7 +33,7 @@ class ExecutionDefinitionBuilder: _DEFAULT_INDEX_NAME: str = "0" - def __init__(self, columns: ColumnsDef, index_by: Optional[IndexDef] = None) -> None: + def __init__(self, columns: ColumnsDef, index_by: Optional[IndexDef] = None, is_cancellable: bool = False) -> None: """ Initializes the ExecutionDefinitionBuilder instance with columns and an optional index_by definition. Processes the given columns and index_by @@ -42,6 +42,8 @@ def __init__(self, columns: ColumnsDef, index_by: Optional[IndexDef] = None) -> Args: columns (ColumnsDef): Input columns to process and build internal mappings. index_by (Optional[IndexDef], optional): Index definition to process. Defaults to None. + is_cancellable (Optional[bool]): Whether the execution of this definition should be cancelled when + the connection is cancelled. """ self._attributes: list[Attribute] = [] @@ -49,6 +51,7 @@ def __init__(self, columns: ColumnsDef, index_by: Optional[IndexDef] = None) -> self._col_to_attr_idx: dict[str, int] = dict() self._index_to_attr_idx: dict[str, int] = dict() self._col_to_metric_idx: dict[str, int] = dict() + self._is_cancellable = is_cancellable self._process_columns(columns) self._process_index(index_by) @@ -248,6 +251,7 @@ def build_execution_definition( metrics=self._metrics, filters=filters, dimensions=dimensions, + is_cancellable=self._is_cancellable, ) @@ -257,6 +261,7 @@ def _compute( columns: ColumnsDef, index_by: Optional[IndexDef] = None, filter_by: Optional[Union[Filter, list[Filter]]] = None, + is_cancellable: bool = False, ) -> tuple[Execution, dict[str, int], dict[str, int], dict[str, int]]: """ Internal function that computes an execution-by-convention to retrieve data for a data frame with the provided @@ -268,6 +273,8 @@ def _compute( columns (ColumnsDef): The columns definition. index_by (Optional[IndexDef]): The index definition, if any. filter_by (Optional[Union[Filter, list[Filter]]]): A filter or a list of filters, if any. + is_cancellable (bool, optional): Whether the execution of this definition should be cancelled when + the connection is cancelled. Returns: tuple: A tuple containing the following elements: @@ -277,7 +284,7 @@ def _compute( - dict[str, int]: A mapping of pandas index names to attribute dimension indices. """ - builder = ExecutionDefinitionBuilder(columns, index_by) + builder = ExecutionDefinitionBuilder(columns, index_by, is_cancellable=is_cancellable) exec_def = builder.build_execution_definition(filter_by) return ( @@ -413,6 +420,7 @@ def compute_and_extract( index_by: Optional[IndexDef] = None, filter_by: Optional[Union[Filter, list[Filter]]] = None, on_execution_submitted: Optional[Callable[[Execution], None]] = None, + is_cancellable: bool = False, ) -> tuple[dict, dict]: """ Convenience function that computes and extracts data from the execution response. @@ -425,6 +433,8 @@ def compute_and_extract( filter_by (Optional[Union[Filter, list[Filter]]]): A filter or a list of filters, if any. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. + is_cancellable (bool, optional): Whether the execution of this definition should be cancelled when + the connection is cancelled. Returns: tuple: A tuple containing the following dictionaries: @@ -440,6 +450,7 @@ def compute_and_extract( index_by=index_by, columns=columns, filter_by=filter_by, + is_cancellable=is_cancellable, ) execution, col_to_attr_idx, col_to_metric_idx, index_to_attr_idx = result diff --git a/gooddata-pandas/gooddata_pandas/dataframe.py b/gooddata-pandas/gooddata_pandas/dataframe.py index d90ae14da..f4dfbe5ab 100644 --- a/gooddata-pandas/gooddata_pandas/dataframe.py +++ b/gooddata-pandas/gooddata_pandas/dataframe.py @@ -74,6 +74,7 @@ def indexed( columns: ColumnsDef, filter_by: Optional[Union[Filter, list[Filter]]] = None, on_execution_submitted: Optional[Callable[[Execution], None]] = None, + is_cancellable: bool = False, ) -> pandas.DataFrame: """ Creates a data frame indexed by values of the label. The data frame columns will be created from either @@ -88,6 +89,7 @@ def indexed( Optional filters to apply during computation on the server. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. Returns: pandas.DataFrame: A DataFrame instance. @@ -99,6 +101,7 @@ def indexed( index_by=index_by, filter_by=filter_by, on_execution_submitted=on_execution_submitted, + is_cancellable=is_cancellable, ) _idx = make_pandas_index(index) @@ -110,6 +113,7 @@ def not_indexed( columns: ColumnsDef, filter_by: Optional[Union[Filter, list[Filter]]] = None, on_execution_submitted: Optional[Callable[[Execution], None]] = None, + is_cancellable: bool = False, ) -> pandas.DataFrame: """ Creates a data frame with columns created from metrics and or labels. @@ -120,6 +124,7 @@ def not_indexed( computation on the server. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. Returns: pandas.DataFrame: A DataFrame instance. @@ -131,6 +136,7 @@ def not_indexed( columns=columns, filter_by=filter_by, on_execution_submitted=on_execution_submitted, + is_cancellable=is_cancellable, ) return pandas.DataFrame(data=data) @@ -141,6 +147,7 @@ def for_items( filter_by: Optional[Union[Filter, list[Filter]]] = None, auto_index: bool = True, on_execution_submitted: Optional[Callable[[Execution], None]] = None, + is_cancellable: bool = False, ) -> pandas.DataFrame: """ Creates a data frame for named items. This is a convenience method that will create DataFrame with or @@ -154,6 +161,7 @@ def for_items( of the items. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. Returns: pandas.DataFrame: A DataFrame instance. @@ -183,6 +191,7 @@ def for_items( columns=resolved_measure_cols, filter_by=filter_by, on_execution_submitted=on_execution_submitted, + is_cancellable=is_cancellable, ) def for_visualization( @@ -190,6 +199,7 @@ def for_visualization( visualization_id: str, auto_index: bool = True, on_execution_submitted: Optional[Callable[[Execution], None]] = None, + is_cancellable: bool = False, ) -> pandas.DataFrame: """ Creates a data frame with columns based on the content of the visualization with the provided identifier. @@ -200,6 +210,7 @@ def for_visualization( of the visualization. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. Returns: pandas.DataFrame: A DataFrame instance. @@ -215,13 +226,18 @@ def for_visualization( } return self.for_items( - columns, filter_by=filter_by, auto_index=auto_index, on_execution_submitted=on_execution_submitted + columns, + filter_by=filter_by, + auto_index=auto_index, + on_execution_submitted=on_execution_submitted, + is_cancellable=is_cancellable, ) def for_created_visualization( self, created_visualizations_response: dict, on_execution_submitted: Optional[Callable[[Execution], None]] = None, + is_cancellable: bool = False, ) -> tuple[pandas.DataFrame, DataFrameMetadata]: """ Creates a data frame using a created visualization. @@ -230,11 +246,14 @@ def for_created_visualization( created_visualizations_response (dict): Created visualization response. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. Returns: pandas.DataFrame: A DataFrame instance. """ - execution_definition = self._sdk.compute.build_exec_def_from_chat_result(created_visualizations_response) + execution_definition = self._sdk.compute.build_exec_def_from_chat_result( + created_visualizations_response, is_cancellable=is_cancellable + ) return self.for_exec_def( exec_def=execution_definition, on_execution_submitted=on_execution_submitted, diff --git a/gooddata-pandas/gooddata_pandas/series.py b/gooddata-pandas/gooddata_pandas/series.py index d47cc214f..c0d45047f 100644 --- a/gooddata-pandas/gooddata_pandas/series.py +++ b/gooddata-pandas/gooddata_pandas/series.py @@ -29,6 +29,7 @@ def indexed( data_by: Union[SimpleMetric, str, ObjId, Attribute], filter_by: Optional[Union[Filter, list[Filter]]] = None, on_execution_submitted: Optional[Callable[[Execution], None]] = None, + is_cancellable: bool = False, ) -> pandas.Series: """Creates pandas Series from data points calculated from a single `data_by`. @@ -65,6 +66,8 @@ def indexed( on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. + Returns: pandas.Series: pandas series instance """ @@ -76,6 +79,7 @@ def indexed( columns={"_series": data_by}, filter_by=filter_by, on_execution_submitted=on_execution_submitted, + is_cancellable=is_cancellable, ) _idx = make_pandas_index(index) @@ -88,6 +92,7 @@ def not_indexed( granularity: Optional[Union[list[LabelItemDef], IndexDef]] = None, filter_by: Optional[Union[Filter, list[Filter]]] = None, on_execution_submitted: Optional[Callable[[Execution], None]] = None, + is_cancellable: bool = False, ) -> pandas.Series: """ Creates a pandas.Series from data points calculated from a single `data_by` without constructing an index. @@ -116,6 +121,7 @@ def not_indexed( Defaults to None. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. Returns: pandas.Series: The resulting pandas Series instance. @@ -133,6 +139,7 @@ def not_indexed( columns={"_series": data_by}, filter_by=filter_by, on_execution_submitted=on_execution_submitted, + is_cancellable=is_cancellable, ) return pandas.Series(data=data["_series"]) diff --git a/gooddata-sdk/gooddata_sdk/compute/service.py b/gooddata-sdk/gooddata_sdk/compute/service.py index 9e1c4323b..e6c129519 100644 --- a/gooddata-sdk/gooddata_sdk/compute/service.py +++ b/gooddata-sdk/gooddata_sdk/compute/service.py @@ -89,12 +89,16 @@ def retrieve_result_cache_metadata(self, workspace_id: str, result_id: str) -> R ) return ResultCacheMetadata(result_cache_metadata=result_cache_metadata) - def build_exec_def_from_chat_result(self, chat_result: ChatResult) -> ExecutionDefinition: + def build_exec_def_from_chat_result( + self, chat_result: ChatResult, is_cancellable: bool = False + ) -> ExecutionDefinition: """ Build execution definition from chat result. Args: chat_result: ChatResult object containing visualization details from AI chat response + is_cancellable (bool, optional): Whether the execution of this definition should be cancelled when + the connection is cancelled. Returns: ExecutionDefinition: Execution definition built from chat result visualization @@ -112,8 +116,13 @@ def build_exec_def_from_chat_result(self, chat_result: ChatResult) -> ExecutionD TableDimension(item_ids=["measureGroup"]), ] - exec_def = ExecutionDefinition(dimensions=dimensions, metrics=metrics, filters=filters, attributes=attributes) - return exec_def + return ExecutionDefinition( + dimensions=dimensions, + metrics=metrics, + filters=filters, + attributes=attributes, + is_cancellable=is_cancellable, + ) def ai_chat(self, workspace_id: str, question: str) -> ChatResult: """ From 11af815de372ba010cdaefadb9415e787147f9d3 Mon Sep 17 00:00:00 2001 From: Dan Homola Date: Thu, 19 Jun 2025 10:27:10 +0200 Subject: [PATCH 2/2] refactor: improve is_cancelled docs Use the more general "interrupted" to be more precise. JIRA: CQ-1411 risk: low --- gooddata-pandas/gooddata_pandas/data_access.py | 6 +++--- gooddata-pandas/gooddata_pandas/dataframe.py | 10 +++++----- gooddata-pandas/gooddata_pandas/series.py | 4 ++-- gooddata-sdk/gooddata_sdk/compute/service.py | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gooddata-pandas/gooddata_pandas/data_access.py b/gooddata-pandas/gooddata_pandas/data_access.py index a6b6e0412..c6f39e9ab 100644 --- a/gooddata-pandas/gooddata_pandas/data_access.py +++ b/gooddata-pandas/gooddata_pandas/data_access.py @@ -43,7 +43,7 @@ def __init__(self, columns: ColumnsDef, index_by: Optional[IndexDef] = None, is_ columns (ColumnsDef): Input columns to process and build internal mappings. index_by (Optional[IndexDef], optional): Index definition to process. Defaults to None. is_cancellable (Optional[bool]): Whether the execution of this definition should be cancelled when - the connection is cancelled. + the connection is interrupted. """ self._attributes: list[Attribute] = [] @@ -274,7 +274,7 @@ def _compute( index_by (Optional[IndexDef]): The index definition, if any. filter_by (Optional[Union[Filter, list[Filter]]]): A filter or a list of filters, if any. is_cancellable (bool, optional): Whether the execution of this definition should be cancelled when - the connection is cancelled. + the connection is interrupted. Returns: tuple: A tuple containing the following elements: @@ -434,7 +434,7 @@ def compute_and_extract( on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. is_cancellable (bool, optional): Whether the execution of this definition should be cancelled when - the connection is cancelled. + the connection is interrupted. Returns: tuple: A tuple containing the following dictionaries: diff --git a/gooddata-pandas/gooddata_pandas/dataframe.py b/gooddata-pandas/gooddata_pandas/dataframe.py index f4dfbe5ab..aa1342736 100644 --- a/gooddata-pandas/gooddata_pandas/dataframe.py +++ b/gooddata-pandas/gooddata_pandas/dataframe.py @@ -89,7 +89,7 @@ def indexed( Optional filters to apply during computation on the server. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. - is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is interrupted. Returns: pandas.DataFrame: A DataFrame instance. @@ -124,7 +124,7 @@ def not_indexed( computation on the server. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. - is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is interrupted. Returns: pandas.DataFrame: A DataFrame instance. @@ -161,7 +161,7 @@ def for_items( of the items. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. - is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is interrupted. Returns: pandas.DataFrame: A DataFrame instance. @@ -210,7 +210,7 @@ def for_visualization( of the visualization. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. - is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is interrupted. Returns: pandas.DataFrame: A DataFrame instance. @@ -246,7 +246,7 @@ def for_created_visualization( created_visualizations_response (dict): Created visualization response. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. - is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is interrupted. Returns: pandas.DataFrame: A DataFrame instance. diff --git a/gooddata-pandas/gooddata_pandas/series.py b/gooddata-pandas/gooddata_pandas/series.py index c0d45047f..6833c1448 100644 --- a/gooddata-pandas/gooddata_pandas/series.py +++ b/gooddata-pandas/gooddata_pandas/series.py @@ -66,7 +66,7 @@ def indexed( on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. - is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is interrupted. Returns: pandas.Series: pandas series instance @@ -121,7 +121,7 @@ def not_indexed( Defaults to None. on_execution_submitted (Optional[Callable[[Execution], None]]): Callback to call when the execution was submitted to the backend. - is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is cancelled. + is_cancellable (bool, optional): Whether the execution should be cancelled when the connection is interrupted. Returns: pandas.Series: The resulting pandas Series instance. diff --git a/gooddata-sdk/gooddata_sdk/compute/service.py b/gooddata-sdk/gooddata_sdk/compute/service.py index e6c129519..1de3089d2 100644 --- a/gooddata-sdk/gooddata_sdk/compute/service.py +++ b/gooddata-sdk/gooddata_sdk/compute/service.py @@ -98,7 +98,7 @@ def build_exec_def_from_chat_result( Args: chat_result: ChatResult object containing visualization details from AI chat response is_cancellable (bool, optional): Whether the execution of this definition should be cancelled when - the connection is cancelled. + the connection is interrupted. Returns: ExecutionDefinition: Execution definition built from chat result visualization