gooddata-pandas 1.69.1.dev2__tar.gz → 1.70.1.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/PKG-INFO +3 -3
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/pyproject.toml +3 -3
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/arrow_convertor.py +88 -29
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/data_access.py +20 -13
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/dataframe.py +15 -6
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/utils.py +52 -5
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_dataframe_for_exec_def_arrow.py +32 -0
- gooddata_pandas-1.70.1.dev1/tests/utils/test_utils.py +72 -0
- gooddata_pandas-1.69.1.dev2/tests/utils/test_utils.py +0 -24
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/.gitignore +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/.readthedocs.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/LICENSE.txt +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/MANIFEST.in +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/Makefile +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/README.md +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/_static/empty_file +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/_templates/class-template.rst +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/_templates/module-template.rst +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/api.rst +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/conf.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/examples.rst +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/index.rst +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/installation.rst +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/requirements.txt +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/__init__.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/_version.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/arrow_types.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/good_pandas.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/py.typed +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/result_convertor.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/series.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/__init__.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/conftest.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/__init__.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/conftest.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_c/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_c/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_c/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_cs/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_cs/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_cs/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_csy/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_csy/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_csy/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_cm/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_cm/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_cm/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csm/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csm/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csm/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csym/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csym/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csym/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_m/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_m/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_m/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rc_m/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rc_m/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rc_m/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_m/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_m/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_m/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_ym/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_ym/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_ym/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cm/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cm/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cm/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cym/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cym/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cym/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/flat_attrs_and_metrics/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/flat_attrs_and_metrics/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/flat_attrs_and_metrics/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/manifest.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/metrics_only/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/metrics_only/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/metrics_only/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/single_metric_many_rows/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/single_metric_many_rows/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/single_metric_many_rows/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_grand/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_grand/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_grand/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub_grand/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub_grand/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub_grand/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1grand/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1grand/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1grand/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1sub/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1sub/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1sub/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1grand/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1grand/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1grand/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1sub/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1sub/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1sub/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_grand/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_grand/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_grand/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub_grand/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub_grand/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub_grand/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_both_dims/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_both_dims/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_both_dims/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_col_sum/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_col_sum/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_col_sum/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_col_two_row_labels/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_col_two_row_labels/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_col_two_row_labels/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_row_sum/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_row_sum/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_row_sum/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_row_two_col_labels/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_row_two_col_labels/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_row_two_col_labels/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_multi_agg/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_multi_agg/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_multi_agg/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_col/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_col/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_col/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_row/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_row/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_row/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/two_dim_metrics_in_cols/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/two_dim_metrics_in_cols/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/two_dim_metrics_in_cols/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/two_dim_metrics_in_rows/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/two_dim_metrics_in_rows/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/two_dim_metrics_in_rows/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/wide_few_rows_many_cols/dataframe.parquet +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/wide_few_rows_many_cols/meta.json +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/wide_few_rows_many_cols/result.arrow +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_bytes_limits_failure.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_dimensions_limits_failure.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_one_dim1.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_one_dim2.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_totals1.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_totals2.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_totals3.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_totals4.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_two_dim1.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_two_dim2.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_two_dim3.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_items.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_items_no_index.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_visualization.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_visualization_date.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_visualization_no_index.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/empty_indexed_dataframe.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/empty_not_indexed_dataframe.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/filtered_empty_df.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/multi_index_filtered_metrics_and_label.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/multi_index_filtered_metrics_and_label_reuse.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/multi_index_metrics.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/multi_index_metrics_and_label.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/not_indexed_filtered_metrics_and_labels.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/not_indexed_metrics.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/not_indexed_metrics_and_labels.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/simple_index_filtered_metrics_and_label.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/simple_index_metrics.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/simple_index_metrics_and_label.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/simple_index_metrics_no_duplicate.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/analytical_dashboards/b2f2d436-9831-4fe0-81df-8c59fd33242b.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/filter_contexts/4717b786-2c2e-4596-adb5-4f8b0ab0e19a.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/filter_contexts/9f2b1f37-befa-450e-a6cc-dc576845c412.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/amount_of_active_customers.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/amount_of_orders.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/amount_of_top_customers.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/amount_of_valid_orders.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/campaign_spend.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/order_amount.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/percent_revenue.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/percent_revenue_from_top_10_customers.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/percent_revenue_from_top_10_percent_customers.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/percent_revenue_from_top_10_percent_products.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/percent_revenue_from_top_10_products.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/percent_revenue_in_category.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/percent_revenue_per_product.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue-clothing.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue-electronic.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue-home.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue-outdoor.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue_per_customer.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue_per_dollar_spent.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue_top_10.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue_top_10_percent.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/total_revenue-no_filters.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/total_revenue.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/campaign_spend.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/customers_trend.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/percent_revenue_per_product_by_customer_and_category.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/percentage_of_customers_by_region.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/product_breakdown.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/product_categories_pie_chart.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/product_revenue_comparison-over_previous_period.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/product_saleability.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/revenue_and_quantity_by_product_and_category.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/revenue_by_category_trend.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/revenue_by_product.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/revenue_per_usd_vs_spend_by_campaign.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/revenue_trend.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/top_10_customers.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/visualization_objects/top_10_products.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/datasets/campaign_channels.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/datasets/campaigns.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/datasets/customers.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/datasets/order_lines.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/datasets/products.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/date_instances/date.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_dataframe_for_created_visualization.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_dataframe_for_exec_def.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_dataframe_for_insight.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_dataframe_for_items.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_indexed_dataframe.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_not_indexed_dataframe.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/gd_test_config.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/good_pandas/__init__.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/good_pandas/profiles/profiles.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/good_pandas/test_good_pandas.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/overview.md +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/__init__.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/conftest.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/multi_index_filtered_series.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/multi_index_metric_series.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/not_indexed_filtered_metric_series.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/not_indexed_label_series.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/not_indexed_label_series_with_granularity.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/not_indexed_metric_series.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/not_indexed_metric_series_with_granularity.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/simple_index_filtered_series.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/simple_index_label_series.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/simple_index_metric_series.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/test_indexed_series.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/test_not_indexed_series.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/utils/__init__.py +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/utils/fixtures/test_get_catalog_attributes_for_extract.yaml +0 -0
- {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tox.ini +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gooddata-pandas
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.70.1.dev1
|
|
4
4
|
Summary: GoodData Cloud to pandas
|
|
5
|
-
Project-URL: Documentation, https://gooddata-pandas.readthedocs.io/en/v1.
|
|
5
|
+
Project-URL: Documentation, https://gooddata-pandas.readthedocs.io/en/v1.70.1.dev1
|
|
6
6
|
Project-URL: Source, https://github.com/gooddata/gooddata-python-sdk
|
|
7
7
|
Author-email: GoodData <support@gooddata.com>
|
|
8
8
|
License-Expression: MIT
|
|
@@ -20,7 +20,7 @@ Classifier: Topic :: Scientific/Engineering
|
|
|
20
20
|
Classifier: Topic :: Software Development
|
|
21
21
|
Classifier: Typing :: Typed
|
|
22
22
|
Requires-Python: >=3.10
|
|
23
|
-
Requires-Dist: gooddata-sdk~=1.
|
|
23
|
+
Requires-Dist: gooddata-sdk~=1.70.1.dev1
|
|
24
24
|
Requires-Dist: pandas<3.0.0,>=2.0.0
|
|
25
25
|
Provides-Extra: arrow
|
|
26
26
|
Requires-Dist: orjson>=3.11.0; extra == 'arrow'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# (C) 2025 GoodData Corporation
|
|
2
2
|
[project]
|
|
3
3
|
name = "gooddata-pandas"
|
|
4
|
-
version = "1.
|
|
4
|
+
version = "1.70.1.dev1"
|
|
5
5
|
description = "GoodData Cloud to pandas"
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
license = "MIT"
|
|
@@ -29,7 +29,7 @@ keywords = [
|
|
|
29
29
|
]
|
|
30
30
|
requires-python = ">=3.10"
|
|
31
31
|
dependencies = [
|
|
32
|
-
"gooddata-sdk~=1.
|
|
32
|
+
"gooddata-sdk~=1.70.1.dev1",
|
|
33
33
|
"pandas>=2.0.0,<3.0.0",
|
|
34
34
|
]
|
|
35
35
|
classifiers = [
|
|
@@ -50,7 +50,7 @@ classifiers = [
|
|
|
50
50
|
arrow = ["pyarrow>=23.0.1", "orjson>=3.11.0"]
|
|
51
51
|
|
|
52
52
|
[project.urls]
|
|
53
|
-
Documentation = "https://gooddata-pandas.readthedocs.io/en/v1.
|
|
53
|
+
Documentation = "https://gooddata-pandas.readthedocs.io/en/v1.70.1.dev1"
|
|
54
54
|
Source = "https://github.com/gooddata/gooddata-python-sdk"
|
|
55
55
|
|
|
56
56
|
[dependency-groups]
|
{gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/arrow_convertor.py
RENAMED
|
@@ -4,14 +4,16 @@ from __future__ import annotations
|
|
|
4
4
|
import logging
|
|
5
5
|
from typing import Callable
|
|
6
6
|
|
|
7
|
+
import numpy
|
|
7
8
|
import orjson
|
|
8
9
|
import pandas
|
|
9
|
-
from gooddata_sdk.type_converter import AttributeConverterStore
|
|
10
|
+
from gooddata_sdk.type_converter import AttributeConverterStore, DateConverter, DatetimeConverter
|
|
10
11
|
|
|
11
12
|
from gooddata_pandas.arrow_types import TypesMapper
|
|
12
13
|
|
|
13
14
|
try:
|
|
14
15
|
import pyarrow as pa
|
|
16
|
+
import pyarrow.compute as pc
|
|
15
17
|
except ImportError as _exc:
|
|
16
18
|
raise ImportError(
|
|
17
19
|
"pyarrow is required for Arrow support. Install it with: pip install gooddata-pandas[arrow]"
|
|
@@ -96,9 +98,30 @@ def convert_label_values(label_id: str, values: list, model_labels: dict) -> lis
|
|
|
96
98
|
Returns:
|
|
97
99
|
Converted list, or the original *values* object when no conversion is needed.
|
|
98
100
|
"""
|
|
101
|
+
# pick the converter for this label's granularity (None for plain text attributes)
|
|
99
102
|
converter = _get_date_converter_for_label(label_id, model_labels)
|
|
100
103
|
if converter is None:
|
|
101
104
|
return values
|
|
105
|
+
|
|
106
|
+
if isinstance(converter, (DateConverter, DatetimeConverter)):
|
|
107
|
+
# Date/datetime granularity is costly, so vectorize it in three steps:
|
|
108
|
+
|
|
109
|
+
# 1) parse each raw string into a datetime.date/datetime with the cheap
|
|
110
|
+
# per-value to_type() (handles partial dates like "2023" or "2023-01");
|
|
111
|
+
# keep None as None. This step stays per-value so that pandas
|
|
112
|
+
# does not re-infer the date format from the raw strings in step 2.
|
|
113
|
+
typed = [converter.to_type(v) if v is not None else None for v in values]
|
|
114
|
+
|
|
115
|
+
# 2) convert the whole column to Timestamps in one vectorized call (this
|
|
116
|
+
# single call replaces N per-value ones; None becomes NaT here).
|
|
117
|
+
converted = pandas.to_datetime(typed)
|
|
118
|
+
|
|
119
|
+
# 3) rebuild the list, restoring None wherever the input was None so that NaT
|
|
120
|
+
# does not leak into the output.
|
|
121
|
+
return [None if orig is None else c for orig, c in zip(values, converted)]
|
|
122
|
+
|
|
123
|
+
# WEEK / QUARTER (StringConverter) and integer granularities are cheap per value
|
|
124
|
+
# and could change type subtly if batched, so convert them one by one (None kept).
|
|
102
125
|
return [converter.to_external_type(v) if v is not None else None for v in values]
|
|
103
126
|
|
|
104
127
|
|
|
@@ -420,16 +443,23 @@ def reorder_grand_totals(
|
|
|
420
443
|
return table
|
|
421
444
|
if _COL_ROW_TYPE not in table.schema.names:
|
|
422
445
|
return table
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
446
|
+
|
|
447
|
+
# uses pyarrow.compute to run these in a vectorized way.
|
|
448
|
+
# the fill_null ensures that nulls are still marked as non-totals.
|
|
449
|
+
# (pyarrow.compute functions are generated at runtime, so ty cannot see them.)
|
|
450
|
+
row_type_col = table.column(_COL_ROW_TYPE)
|
|
451
|
+
is_grand_total = pc.fill_null(pc.equal(row_type_col, 2), False) # ty: ignore[unresolved-attribute]
|
|
452
|
+
grand_total_rows = table.filter(is_grand_total)
|
|
426
453
|
if grand_total_rows.num_rows == 0:
|
|
427
454
|
return table
|
|
428
|
-
|
|
429
|
-
|
|
455
|
+
|
|
456
|
+
not_grand_total = pc.fill_null(pc.not_equal(row_type_col, 2), True) # ty: ignore[unresolved-attribute]
|
|
457
|
+
return pa.concat_tables([grand_total_rows, table.filter(not_grand_total)])
|
|
430
458
|
|
|
431
459
|
|
|
432
|
-
def compute_column_totals_indexes(
|
|
460
|
+
def compute_column_totals_indexes(
|
|
461
|
+
table: pa.Table, execution_dims: list, schema_meta: dict | None = None
|
|
462
|
+
) -> list[list[int]]:
|
|
433
463
|
"""
|
|
434
464
|
Compute column_totals_indexes compatible with DataFrameMetadata from an Arrow table.
|
|
435
465
|
|
|
@@ -445,7 +475,9 @@ def compute_column_totals_indexes(table: pa.Table, execution_dims: list) -> list
|
|
|
445
475
|
grand_total_* fields become output rows and are already covered by
|
|
446
476
|
compute_row_totals_indexes. Returns [] in that case.
|
|
447
477
|
"""
|
|
448
|
-
schema_meta
|
|
478
|
+
if schema_meta is None:
|
|
479
|
+
schema_meta = _parse_schema_metadata(table)
|
|
480
|
+
|
|
449
481
|
xtab_meta = schema_meta[_META_XTAB]
|
|
450
482
|
is_transposed = schema_meta[_META_VIEW]["isTransposed"]
|
|
451
483
|
|
|
@@ -518,7 +550,9 @@ def compute_column_totals_indexes(table: pa.Table, execution_dims: list) -> list
|
|
|
518
550
|
return result
|
|
519
551
|
|
|
520
552
|
|
|
521
|
-
def compute_row_totals_indexes(
|
|
553
|
+
def compute_row_totals_indexes(
|
|
554
|
+
table: pa.Table, execution_dims: list, schema_meta: dict | None = None
|
|
555
|
+
) -> list[list[int]]:
|
|
522
556
|
"""
|
|
523
557
|
Compute row_totals_indexes compatible with DataFrameMetadata from an Arrow table.
|
|
524
558
|
|
|
@@ -534,7 +568,9 @@ def compute_row_totals_indexes(table: pa.Table, execution_dims: list) -> list[li
|
|
|
534
568
|
Total rows are grand_total_N fields. A field is total at level j only when
|
|
535
569
|
j >= len(gdc["label_values"]), i.e. that label level is being aggregated.
|
|
536
570
|
"""
|
|
537
|
-
schema_meta
|
|
571
|
+
if schema_meta is None:
|
|
572
|
+
schema_meta = _parse_schema_metadata(table)
|
|
573
|
+
|
|
538
574
|
xtab_meta = schema_meta[_META_XTAB]
|
|
539
575
|
is_transposed = schema_meta[_META_VIEW]["isTransposed"]
|
|
540
576
|
|
|
@@ -608,8 +644,8 @@ def compute_row_totals_indexes(table: pa.Table, execution_dims: list) -> list[li
|
|
|
608
644
|
else:
|
|
609
645
|
# Output rows are Arrow rows; every total row (row_type != 0) is listed
|
|
610
646
|
# in the total-indexes for every attribute level.
|
|
611
|
-
row_types =
|
|
612
|
-
total_row_idxs =
|
|
647
|
+
row_types = table.column(_COL_ROW_TYPE).to_numpy(zero_copy_only=False)
|
|
648
|
+
total_row_idxs = numpy.nonzero(row_types != 0)[0].tolist()
|
|
613
649
|
|
|
614
650
|
result = []
|
|
615
651
|
for header in row_dim.get("headers", []):
|
|
@@ -643,30 +679,47 @@ def _compute_primary_labels_from_inline(
|
|
|
643
679
|
"""
|
|
644
680
|
result: dict[int, dict[str, str]] = {}
|
|
645
681
|
label_meta = xtab_meta.get("labelMetadata", {})
|
|
646
|
-
row_types = _get_row_types(table)
|
|
647
|
-
data_row_mask = [rt == 0 for rt in row_types]
|
|
648
682
|
|
|
649
|
-
|
|
683
|
+
# Project to only the columns this function reads - __row_type plus the label
|
|
684
|
+
# and primary-label columns - before filtering. Filtering the whole table would
|
|
685
|
+
# also copy every metric column for the data rows even though only these
|
|
686
|
+
# attribute columns are ever read below.
|
|
687
|
+
# table.select is zero-copy, so the subsequent filter copies just these columns.
|
|
688
|
+
needed_cols = [_COL_ROW_TYPE]
|
|
689
|
+
for ref in label_refs:
|
|
650
690
|
info = label_meta.get(ref, {})
|
|
651
691
|
label_id = label_ref_to_id.get(ref, info.get("labelId", ""))
|
|
652
692
|
primary_label_id = info.get("primaryLabelId", label_id)
|
|
693
|
+
for col in (label_id, primary_label_id):
|
|
694
|
+
if col in table.schema.names and col not in needed_cols:
|
|
695
|
+
needed_cols.append(col)
|
|
696
|
+
|
|
697
|
+
projected = table.select(needed_cols)
|
|
698
|
+
|
|
699
|
+
# Extract the data rows (row_type == 0) once and reuse. Only filter when totals
|
|
700
|
+
# are actually present - otherwise the projected table already is the data rows.
|
|
701
|
+
# (pyarrow.compute functions are generated at runtime, so ty cannot see them.)
|
|
702
|
+
row_type_col = projected.column(_COL_ROW_TYPE)
|
|
703
|
+
has_total_rows = pc.any(pc.not_equal(row_type_col, 0)).as_py() # ty: ignore[unresolved-attribute]
|
|
704
|
+
data_row_mask = pc.equal(row_type_col, 0) # ty: ignore[unresolved-attribute]
|
|
705
|
+
data_rows = projected.filter(data_row_mask) if has_total_rows else projected
|
|
653
706
|
|
|
654
|
-
|
|
707
|
+
for j, ref in enumerate(label_refs):
|
|
708
|
+
info = label_meta.get(ref, {})
|
|
709
|
+
label_id = label_ref_to_id.get(ref, info.get("labelId", ""))
|
|
710
|
+
primary_label_id = info.get("primaryLabelId", label_id)
|
|
655
711
|
|
|
656
|
-
if label_id == primary_label_id:
|
|
712
|
+
if label_id == primary_label_id or primary_label_id not in table.schema.names:
|
|
713
|
+
# identity (or fallback when the primary column is absent): map each
|
|
714
|
+
# distinct display value to itself.
|
|
657
715
|
mapping: dict[str, str] = {
|
|
658
|
-
v: v for v
|
|
659
|
-
}
|
|
660
|
-
elif primary_label_id in table.schema.names:
|
|
661
|
-
primary_vals = table.column(primary_label_id).to_pylist()
|
|
662
|
-
mapping = {
|
|
663
|
-
p: d
|
|
664
|
-
for p, d, is_data in zip(primary_vals, display_vals, data_row_mask)
|
|
665
|
-
if is_data and isinstance(p, str) and isinstance(d, str)
|
|
716
|
+
v: v for v in data_rows.column(label_id).unique().to_pylist() if isinstance(v, str)
|
|
666
717
|
}
|
|
667
718
|
else:
|
|
668
|
-
#
|
|
669
|
-
|
|
719
|
+
# primary != display: map each primary value to its display value
|
|
720
|
+
primary_vals = data_rows.column(primary_label_id).to_pylist()
|
|
721
|
+
display_vals = data_rows.column(label_id).to_pylist()
|
|
722
|
+
mapping = {p: d for p, d in zip(primary_vals, display_vals) if isinstance(p, str) and isinstance(d, str)}
|
|
670
723
|
|
|
671
724
|
result[j] = mapping
|
|
672
725
|
return result
|
|
@@ -709,6 +762,7 @@ def _compute_primary_labels_from_fields(
|
|
|
709
762
|
|
|
710
763
|
def compute_primary_labels(
|
|
711
764
|
table: pa.Table,
|
|
765
|
+
schema_meta: dict | None = None,
|
|
712
766
|
) -> tuple[dict[int, dict[str, str]], dict[int, dict[str, str]]]:
|
|
713
767
|
"""
|
|
714
768
|
Compute primary_labels_from_index and primary_labels_from_columns from an Arrow table.
|
|
@@ -719,7 +773,9 @@ def compute_primary_labels(
|
|
|
719
773
|
Returns:
|
|
720
774
|
(primary_labels_from_index, primary_labels_from_columns)
|
|
721
775
|
"""
|
|
722
|
-
schema_meta
|
|
776
|
+
if schema_meta is None:
|
|
777
|
+
schema_meta = _parse_schema_metadata(table)
|
|
778
|
+
|
|
723
779
|
xtab_meta = schema_meta[_META_XTAB]
|
|
724
780
|
is_transposed = schema_meta[_META_VIEW]["isTransposed"]
|
|
725
781
|
|
|
@@ -750,6 +806,7 @@ def convert_arrow_table_to_dataframe(
|
|
|
750
806
|
types_mapper: TypesMapper = TypesMapper.DEFAULT,
|
|
751
807
|
custom_mapping: dict | None = None,
|
|
752
808
|
label_overrides: dict | None = None,
|
|
809
|
+
schema_meta: dict | None = None,
|
|
753
810
|
) -> pandas.DataFrame:
|
|
754
811
|
"""
|
|
755
812
|
Convert a pyarrow Table returned by the GoodData /binary execution endpoint
|
|
@@ -800,7 +857,9 @@ def convert_arrow_table_to_dataframe(
|
|
|
800
857
|
else:
|
|
801
858
|
raise ValueError("Unknown types_mapper value")
|
|
802
859
|
|
|
803
|
-
schema_meta
|
|
860
|
+
if schema_meta is None:
|
|
861
|
+
schema_meta = _parse_schema_metadata(table)
|
|
862
|
+
|
|
804
863
|
xtab_meta = schema_meta[_META_XTAB]
|
|
805
864
|
model_meta = schema_meta[_META_MODEL]
|
|
806
865
|
is_transposed = schema_meta[_META_VIEW]["isTransposed"]
|
{gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/data_access.py
RENAMED
|
@@ -25,7 +25,7 @@ from gooddata_pandas.utils import (
|
|
|
25
25
|
_str_to_obj_id,
|
|
26
26
|
_to_attribute,
|
|
27
27
|
_to_item,
|
|
28
|
-
|
|
28
|
+
_typed_attribute_values,
|
|
29
29
|
get_catalog_attributes_for_extract,
|
|
30
30
|
)
|
|
31
31
|
|
|
@@ -340,22 +340,21 @@ def _find_attribute(attributes: list[CatalogAttribute], id_obj: IdObjType) -> Un
|
|
|
340
340
|
return None
|
|
341
341
|
|
|
342
342
|
|
|
343
|
-
def
|
|
343
|
+
def _resolve_catalog_attribute(attributes: list[CatalogAttribute], attribute: Attribute) -> CatalogAttribute:
|
|
344
344
|
"""
|
|
345
|
-
|
|
345
|
+
Find the CatalogAttribute matching the given execution attribute.
|
|
346
346
|
|
|
347
347
|
Args:
|
|
348
348
|
attributes (list[CatalogAttribute]): The catalog of attributes.
|
|
349
|
-
attribute (Attribute): The attribute
|
|
350
|
-
result_values (list[Any]): A list of raw values.
|
|
349
|
+
attribute (Attribute): The execution attribute to resolve.
|
|
351
350
|
|
|
352
351
|
Returns:
|
|
353
|
-
|
|
352
|
+
CatalogAttribute: The matching catalog attribute.
|
|
354
353
|
"""
|
|
355
354
|
catalog_attribute = _find_attribute(attributes, attribute.label)
|
|
356
355
|
if catalog_attribute is None:
|
|
357
356
|
raise ValueError(f"Unable to find attribute {attribute.label} in catalog")
|
|
358
|
-
return
|
|
357
|
+
return catalog_attribute
|
|
359
358
|
|
|
360
359
|
|
|
361
360
|
def _extract_from_attributes_and_maybe_metrics(
|
|
@@ -399,6 +398,16 @@ def _extract_from_attributes_and_maybe_metrics(
|
|
|
399
398
|
index_to_attribute = {index_name: exec_def.attributes[i] for index_name, i in safe_index_to_attr_idx.items()}
|
|
400
399
|
col_to_attribute = {col: exec_def.attributes[i] for col, i in col_to_attr_idx.items()}
|
|
401
400
|
|
|
401
|
+
# resolve the matching CatalogAttribute for each index / attribute column once:
|
|
402
|
+
# it does not change during the batch iteration
|
|
403
|
+
index_to_catalog_attribute = {
|
|
404
|
+
index_name: _resolve_catalog_attribute(attributes, attribute)
|
|
405
|
+
for index_name, attribute in index_to_attribute.items()
|
|
406
|
+
}
|
|
407
|
+
col_to_catalog_attribute = {
|
|
408
|
+
col: _resolve_catalog_attribute(attributes, attribute) for col, attribute in col_to_attribute.items()
|
|
409
|
+
}
|
|
410
|
+
|
|
402
411
|
# datastructures to return
|
|
403
412
|
index: dict[str, list[Any]] = {idx_name: [] for idx_name in safe_index_to_attr_idx}
|
|
404
413
|
data: dict[str, list[Any]] = {col: [] for col in cols}
|
|
@@ -406,13 +415,11 @@ def _extract_from_attributes_and_maybe_metrics(
|
|
|
406
415
|
while True:
|
|
407
416
|
for idx_name in index:
|
|
408
417
|
rs = result.get_all_header_values(attribute_dim, safe_index_to_attr_idx[idx_name])
|
|
409
|
-
|
|
410
|
-
index[idx_name] += _typed_result(attributes, attribute, rs)
|
|
418
|
+
index[idx_name] += _typed_attribute_values(index_to_catalog_attribute[idx_name], rs)
|
|
411
419
|
for col in cols:
|
|
412
420
|
if col in col_to_attr_idx:
|
|
413
421
|
rs = result.get_all_header_values(attribute_dim, col_to_attr_idx[col])
|
|
414
|
-
|
|
415
|
-
data[col] += _typed_result(attributes, attribute, rs)
|
|
422
|
+
data[col] += _typed_attribute_values(col_to_catalog_attribute[col], rs)
|
|
416
423
|
elif col_to_metric_idx[col] < len(result.data):
|
|
417
424
|
data[col] += result.data[col_to_metric_idx[col]]
|
|
418
425
|
if result.is_complete(attribute_dim):
|
|
@@ -455,11 +462,11 @@ def _extract_from_arrow(
|
|
|
455
462
|
metric_dim_idx_to_field = build_metric_field_index(table)
|
|
456
463
|
model_labels = read_model_labels(table)
|
|
457
464
|
|
|
458
|
-
data: dict[str,
|
|
465
|
+
data: dict[str, Any] = {}
|
|
459
466
|
for col in cols:
|
|
460
467
|
if col in col_to_metric_idx:
|
|
461
468
|
field_name = metric_dim_idx_to_field[col_to_metric_idx[col]]
|
|
462
|
-
data[col] = table.column(field_name).
|
|
469
|
+
data[col] = table.column(field_name).to_numpy(zero_copy_only=False)
|
|
463
470
|
else:
|
|
464
471
|
attr = exec_def.attributes[col_to_attr_idx[col]]
|
|
465
472
|
label_id = attr.label.id
|
{gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/dataframe.py
RENAMED
|
@@ -25,6 +25,7 @@ from gooddata_pandas.data_access import compute_and_extract
|
|
|
25
25
|
|
|
26
26
|
try:
|
|
27
27
|
from gooddata_pandas.arrow_convertor import (
|
|
28
|
+
_parse_schema_metadata,
|
|
28
29
|
compute_column_totals_indexes,
|
|
29
30
|
compute_primary_labels,
|
|
30
31
|
compute_row_totals_indexes,
|
|
@@ -469,16 +470,18 @@ class DataFrameFactory:
|
|
|
469
470
|
call site.
|
|
470
471
|
"""
|
|
471
472
|
table = reorder_grand_totals(table, grand_totals_position)
|
|
473
|
+
schema_meta = _parse_schema_metadata(table)
|
|
472
474
|
df = convert_arrow_table_to_dataframe(
|
|
473
475
|
table,
|
|
474
476
|
self_destruct=self._arrow_config.self_destruct,
|
|
475
477
|
types_mapper=self._arrow_config.types_mapper,
|
|
476
478
|
custom_mapping=self._arrow_config.custom_mapping,
|
|
477
479
|
label_overrides=label_overrides,
|
|
480
|
+
schema_meta=schema_meta,
|
|
478
481
|
)
|
|
479
|
-
row_totals_indexes = compute_row_totals_indexes(table, exec_response.dimensions)
|
|
480
|
-
column_totals_indexes = compute_column_totals_indexes(table, exec_response.dimensions)
|
|
481
|
-
primary_labels_from_index, primary_labels_from_columns = compute_primary_labels(table)
|
|
482
|
+
row_totals_indexes = compute_row_totals_indexes(table, exec_response.dimensions, schema_meta=schema_meta)
|
|
483
|
+
column_totals_indexes = compute_column_totals_indexes(table, exec_response.dimensions, schema_meta=schema_meta)
|
|
484
|
+
primary_labels_from_index, primary_labels_from_columns = compute_primary_labels(table, schema_meta=schema_meta)
|
|
482
485
|
metadata = DataFrameMetadata(
|
|
483
486
|
row_totals_indexes=row_totals_indexes,
|
|
484
487
|
column_totals_indexes=column_totals_indexes,
|
|
@@ -588,22 +591,28 @@ class DataFrameFactory:
|
|
|
588
591
|
label_overrides = {}
|
|
589
592
|
|
|
590
593
|
table = reorder_grand_totals(table, grand_totals_position)
|
|
594
|
+
# parse the schema metadata once and share it across the sibling functions
|
|
595
|
+
# below; each of them would otherwise re-parse it from the table
|
|
596
|
+
schema_meta = _parse_schema_metadata(table)
|
|
591
597
|
df = convert_arrow_table_to_dataframe(
|
|
592
598
|
table,
|
|
593
599
|
self_destruct=self._arrow_config.self_destruct,
|
|
594
600
|
types_mapper=self._arrow_config.types_mapper,
|
|
595
601
|
custom_mapping=self._arrow_config.custom_mapping,
|
|
596
602
|
label_overrides=label_overrides,
|
|
603
|
+
schema_meta=schema_meta,
|
|
597
604
|
)
|
|
598
605
|
row_totals_indexes = (
|
|
599
|
-
compute_row_totals_indexes(table, execution_response.dimensions)
|
|
606
|
+
compute_row_totals_indexes(table, execution_response.dimensions, schema_meta=schema_meta)
|
|
607
|
+
if execution_response is not None
|
|
608
|
+
else []
|
|
600
609
|
)
|
|
601
610
|
column_totals_indexes = (
|
|
602
|
-
compute_column_totals_indexes(table, execution_response.dimensions)
|
|
611
|
+
compute_column_totals_indexes(table, execution_response.dimensions, schema_meta=schema_meta)
|
|
603
612
|
if execution_response is not None
|
|
604
613
|
else []
|
|
605
614
|
)
|
|
606
|
-
primary_labels_from_index, primary_labels_from_columns = compute_primary_labels(table)
|
|
615
|
+
primary_labels_from_index, primary_labels_from_columns = compute_primary_labels(table, schema_meta=schema_meta)
|
|
607
616
|
metadata = DataFrameMetadata(
|
|
608
617
|
row_totals_indexes=row_totals_indexes,
|
|
609
618
|
column_totals_indexes=column_totals_indexes,
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import hashlib
|
|
5
5
|
import uuid
|
|
6
|
-
from typing import Any, Union
|
|
6
|
+
from typing import Any, Callable, Union
|
|
7
7
|
|
|
8
8
|
import pandas
|
|
9
9
|
from gooddata_sdk import (
|
|
@@ -16,7 +16,13 @@ from gooddata_sdk import (
|
|
|
16
16
|
VisualizationAttribute,
|
|
17
17
|
VisualizationMetric,
|
|
18
18
|
)
|
|
19
|
-
from gooddata_sdk.type_converter import
|
|
19
|
+
from gooddata_sdk.type_converter import (
|
|
20
|
+
AttributeConverterStore,
|
|
21
|
+
Converter,
|
|
22
|
+
DateConverter,
|
|
23
|
+
DatetimeConverter,
|
|
24
|
+
IntegerConverter,
|
|
25
|
+
)
|
|
20
26
|
from gooddata_sdk.utils import filter_for_attributes_labels
|
|
21
27
|
from pandas import Index, MultiIndex
|
|
22
28
|
|
|
@@ -25,10 +31,25 @@ DataItemDef = Union[Attribute, Metric, ObjId, str]
|
|
|
25
31
|
IndexDef = Union[LabelItemDef, dict[str, LabelItemDef]]
|
|
26
32
|
ColumnsDef = dict[str, DataItemDef]
|
|
27
33
|
|
|
34
|
+
# Maps SDK attribute converters to the pandas function that converts their parsed
|
|
35
|
+
# (already-typed) values into the external pandas type. These pandas functions are
|
|
36
|
+
# vectorized: they accept a single value OR a whole column. This lets the JSON
|
|
37
|
+
# extraction path convert an entire attribute column in one call instead of once per
|
|
38
|
+
# value - see _typed_attribute_values.
|
|
39
|
+
_ATTRIBUTE_EXTERNAL_CONVERSIONS: dict[type[Converter], Callable[[Any], Any]] = {
|
|
40
|
+
IntegerConverter: pandas.to_numeric,
|
|
41
|
+
DateConverter: pandas.to_datetime,
|
|
42
|
+
DatetimeConverter: pandas.to_datetime,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
28
46
|
# register external pandas types to converters
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
def _external_conversion(conversion_function: Callable[[Any], Any]) -> Callable[[object, Any], Any]:
|
|
48
|
+
return lambda instance, value: conversion_function(value)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
for _converter_cls, _conversion_function in _ATTRIBUTE_EXTERNAL_CONVERSIONS.items():
|
|
52
|
+
_converter_cls.set_external_fnc(_external_conversion(_conversion_function))
|
|
32
53
|
|
|
33
54
|
|
|
34
55
|
def get_catalog_attributes_for_extract(
|
|
@@ -169,6 +190,32 @@ def _typed_attribute_value(ct_attr: CatalogAttribute, value: Any) -> Any:
|
|
|
169
190
|
return converter.to_external_type(value)
|
|
170
191
|
|
|
171
192
|
|
|
193
|
+
def _typed_attribute_values(ct_attr: CatalogAttribute, values: list[Any]) -> list[Any]:
|
|
194
|
+
"""
|
|
195
|
+
Batch equivalent of _typed_attribute_value: convert a whole attribute value column
|
|
196
|
+
to its external type in a single vectorized call rather than once per value.
|
|
197
|
+
|
|
198
|
+
Calling pandas.to_datetime / pandas.to_numeric per scalar is a well-known
|
|
199
|
+
performance pitfall; here each raw value is parsed with the (cheap) per-value
|
|
200
|
+
to_type() and the pandas conversion is then applied once to the whole column.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
ct_attr (CatalogAttribute): The catalog attribute.
|
|
204
|
+
values (list[Any]): The values to convert.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
list[Any]: The converted values, in the same order.
|
|
208
|
+
"""
|
|
209
|
+
converter = AttributeConverterStore.find_converter(ct_attr.dataset.dataset_type, ct_attr.granularity)
|
|
210
|
+
typed_values = [converter.to_type(value) for value in values]
|
|
211
|
+
|
|
212
|
+
conversion_function = _ATTRIBUTE_EXTERNAL_CONVERSIONS.get(type(converter))
|
|
213
|
+
if conversion_function is None:
|
|
214
|
+
return typed_values
|
|
215
|
+
|
|
216
|
+
return list(conversion_function(typed_values))
|
|
217
|
+
|
|
218
|
+
|
|
172
219
|
def make_pandas_index(index: dict) -> Union[Index, MultiIndex] | None:
|
|
173
220
|
"""
|
|
174
221
|
Create a pandas index or multi-index based on the input index dictionary.
|
|
@@ -206,6 +206,38 @@ def test_primary_labels_from_inline_fallback_identity() -> None:
|
|
|
206
206
|
assert result == {0: {"New York": "New York", "Los Angeles": "Los Angeles"}}
|
|
207
207
|
|
|
208
208
|
|
|
209
|
+
def test_primary_labels_from_inline_ignores_metric_columns_and_total_rows() -> None:
|
|
210
|
+
"""Regression guard for the column-projection optimization.
|
|
211
|
+
|
|
212
|
+
_compute_primary_labels_from_inline projects to only the label/primary-label
|
|
213
|
+
columns before filtering to data rows. This table adds several unrelated
|
|
214
|
+
metric/grand-total columns plus a total row, with primaryLabelId != labelId,
|
|
215
|
+
so a projection that dropped a needed label column (or a filter that leaked
|
|
216
|
+
the total row) would produce a wrong mapping or raise. The separate-column
|
|
217
|
+
test above cannot catch that: its table has no extra columns to drop and no
|
|
218
|
+
total row to exclude.
|
|
219
|
+
"""
|
|
220
|
+
table = pa.table(
|
|
221
|
+
{
|
|
222
|
+
"__row_type": pa.array([0, 0, 2], type=pa.int8()), # last row is a total
|
|
223
|
+
"display_label": pa.array(["New York", "Los Angeles", "sum"], type=pa.string()),
|
|
224
|
+
"primary_label": pa.array(["ny", "la", "sum"], type=pa.string()),
|
|
225
|
+
"metric_group_1": pa.array([1.0, 2.0, 3.0], type=pa.float64()),
|
|
226
|
+
"metric_group_2": pa.array([4.0, 5.0, 6.0], type=pa.float64()),
|
|
227
|
+
"grand_total_1": pa.array([7.0, 8.0, 9.0], type=pa.float64()),
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
xtab_meta = {"labelMetadata": {"l0": {"labelId": "display_label", "primaryLabelId": "primary_label"}}}
|
|
231
|
+
result = _compute_primary_labels_from_inline(
|
|
232
|
+
table,
|
|
233
|
+
label_refs=["l0"],
|
|
234
|
+
label_ref_to_id={"l0": "display_label"},
|
|
235
|
+
xtab_meta=xtab_meta,
|
|
236
|
+
)
|
|
237
|
+
# total row excluded; primary -> display for the data rows only
|
|
238
|
+
assert result == {0: {"ny": "New York", "la": "Los Angeles"}}
|
|
239
|
+
|
|
240
|
+
|
|
209
241
|
# ---------------------------------------------------------------------------
|
|
210
242
|
# _compute_primary_labels_from_fields — non-string skip branch
|
|
211
243
|
# ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# (C) 2025 GoodData Corporation
|
|
2
|
+
import types
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pandas
|
|
6
|
+
from gooddata_pandas.utils import _typed_attribute_values, get_catalog_attributes_for_extract
|
|
7
|
+
from gooddata_sdk import (
|
|
8
|
+
Attribute,
|
|
9
|
+
GoodDataSdk,
|
|
10
|
+
)
|
|
11
|
+
from tests_support.vcrpy_utils import get_vcr
|
|
12
|
+
|
|
13
|
+
gd_vcr = get_vcr()
|
|
14
|
+
|
|
15
|
+
_current_dir = Path(__file__).parent.absolute()
|
|
16
|
+
_fixtures_dir = _current_dir / "fixtures"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@gd_vcr.use_cassette(str(_fixtures_dir / "test_get_catalog_attributes_for_extract.yaml"))
|
|
20
|
+
def test_get_catalog_attributes_for_extract(test_config):
|
|
21
|
+
sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"])
|
|
22
|
+
workspace_id = "demo"
|
|
23
|
+
attributes = [Attribute(local_id="0", label="campaign_name"), Attribute(local_id="1", label="region")]
|
|
24
|
+
catalog_attributes = get_catalog_attributes_for_extract(sdk, workspace_id, attributes, character_limit=28)
|
|
25
|
+
assert len(catalog_attributes) == 2
|
|
26
|
+
assert [ca.id for ca in catalog_attributes] == ["campaign_name", "region"]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# _typed_attribute_values — JSON-path attribute value conversion
|
|
31
|
+
#
|
|
32
|
+
# The JSON path converts a whole attribute column in one vectorized pandas call
|
|
33
|
+
# instead of once per value. These pin the resulting values (the behaviour the
|
|
34
|
+
# per-value path produced before the change); nothing else covers this function -
|
|
35
|
+
# the JSON exec_def cassettes only contain text attributes (granularity: null).
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _date_catalog_attribute(granularity: str) -> types.SimpleNamespace:
|
|
40
|
+
"""Minimal stand-in exposing the two fields _typed_attribute_values reads."""
|
|
41
|
+
return types.SimpleNamespace(
|
|
42
|
+
dataset=types.SimpleNamespace(dataset_type="DATE"),
|
|
43
|
+
granularity=granularity,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_typed_attribute_values_batches_dates_to_timestamps():
|
|
48
|
+
"""DAY/MONTH/YEAR granularities convert the whole column to pandas.Timestamp."""
|
|
49
|
+
assert _typed_attribute_values(_date_catalog_attribute("DAY"), ["2023-01-15", "2024-06-30"]) == [
|
|
50
|
+
pandas.Timestamp("2023-01-15"),
|
|
51
|
+
pandas.Timestamp("2024-06-30"),
|
|
52
|
+
]
|
|
53
|
+
# partial dates (year-only / year-month) still parse to the period start
|
|
54
|
+
assert _typed_attribute_values(_date_catalog_attribute("YEAR"), ["2023", "2024"]) == [
|
|
55
|
+
pandas.Timestamp("2023-01-01"),
|
|
56
|
+
pandas.Timestamp("2024-01-01"),
|
|
57
|
+
]
|
|
58
|
+
assert _typed_attribute_values(_date_catalog_attribute("MONTH"), ["2023-01", "2023-03"]) == [
|
|
59
|
+
pandas.Timestamp("2023-01-01"),
|
|
60
|
+
pandas.Timestamp("2023-03-01"),
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_typed_attribute_values_week_and_quarter_stay_strings():
|
|
65
|
+
"""WEEK/QUARTER use a string converter (no external pandas fn) — values unchanged."""
|
|
66
|
+
assert _typed_attribute_values(_date_catalog_attribute("WEEK"), ["2025-1", "2025-49"]) == ["2025-1", "2025-49"]
|
|
67
|
+
assert _typed_attribute_values(_date_catalog_attribute("QUARTER"), ["2025-1", "2025-4"]) == ["2025-1", "2025-4"]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_typed_attribute_values_empty_list():
|
|
71
|
+
"""Empty column returns an empty list without error."""
|
|
72
|
+
assert _typed_attribute_values(_date_catalog_attribute("DAY"), []) == []
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# (C) 2025 GoodData Corporation
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
from gooddata_pandas.utils import get_catalog_attributes_for_extract
|
|
5
|
-
from gooddata_sdk import (
|
|
6
|
-
Attribute,
|
|
7
|
-
GoodDataSdk,
|
|
8
|
-
)
|
|
9
|
-
from tests_support.vcrpy_utils import get_vcr
|
|
10
|
-
|
|
11
|
-
gd_vcr = get_vcr()
|
|
12
|
-
|
|
13
|
-
_current_dir = Path(__file__).parent.absolute()
|
|
14
|
-
_fixtures_dir = _current_dir / "fixtures"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@gd_vcr.use_cassette(str(_fixtures_dir / "test_get_catalog_attributes_for_extract.yaml"))
|
|
18
|
-
def test_get_catalog_attributes_for_extract(test_config):
|
|
19
|
-
sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"])
|
|
20
|
-
workspace_id = "demo"
|
|
21
|
-
attributes = [Attribute(local_id="0", label="campaign_name"), Attribute(local_id="1", label="region")]
|
|
22
|
-
catalog_attributes = get_catalog_attributes_for_extract(sdk, workspace_id, attributes, character_limit=28)
|
|
23
|
-
assert len(catalog_attributes) == 2
|
|
24
|
-
assert [ca.id for ca in catalog_attributes] == ["campaign_name", "region"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/_templates/class-template.rst
RENAMED
|
File without changes
|
{gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/_templates/module-template.rst
RENAMED
|
File without changes
|