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.
Files changed (250) hide show
  1. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/PKG-INFO +3 -3
  2. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/pyproject.toml +3 -3
  3. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/arrow_convertor.py +88 -29
  4. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/data_access.py +20 -13
  5. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/dataframe.py +15 -6
  6. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/utils.py +52 -5
  7. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_dataframe_for_exec_def_arrow.py +32 -0
  8. gooddata_pandas-1.70.1.dev1/tests/utils/test_utils.py +72 -0
  9. gooddata_pandas-1.69.1.dev2/tests/utils/test_utils.py +0 -24
  10. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/.gitignore +0 -0
  11. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/.readthedocs.yaml +0 -0
  12. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/LICENSE.txt +0 -0
  13. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/MANIFEST.in +0 -0
  14. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/Makefile +0 -0
  15. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/README.md +0 -0
  16. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/_static/empty_file +0 -0
  17. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/_templates/class-template.rst +0 -0
  18. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/_templates/module-template.rst +0 -0
  19. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/api.rst +0 -0
  20. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/conf.py +0 -0
  21. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/examples.rst +0 -0
  22. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/index.rst +0 -0
  23. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/installation.rst +0 -0
  24. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/docs/requirements.txt +0 -0
  25. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/__init__.py +0 -0
  26. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/_version.py +0 -0
  27. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/arrow_types.py +0 -0
  28. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/good_pandas.py +0 -0
  29. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/py.typed +0 -0
  30. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/result_convertor.py +0 -0
  31. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/src/gooddata_pandas/series.py +0 -0
  32. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/__init__.py +0 -0
  33. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/conftest.py +0 -0
  34. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/__init__.py +0 -0
  35. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/conftest.py +0 -0
  36. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_c/dataframe.parquet +0 -0
  37. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_c/meta.json +0 -0
  38. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_c/result.arrow +0 -0
  39. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_cs/dataframe.parquet +0 -0
  40. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_cs/meta.json +0 -0
  41. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_cs/result.arrow +0 -0
  42. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_csy/dataframe.parquet +0 -0
  43. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_csy/meta.json +0 -0
  44. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_m_csy/result.arrow +0 -0
  45. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_cm/dataframe.parquet +0 -0
  46. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_cm/meta.json +0 -0
  47. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_cm/result.arrow +0 -0
  48. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csm/dataframe.parquet +0 -0
  49. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csm/meta.json +0 -0
  50. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csm/result.arrow +0 -0
  51. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csym/dataframe.parquet +0 -0
  52. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csym/meta.json +0 -0
  53. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_csym/result.arrow +0 -0
  54. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_m/dataframe.parquet +0 -0
  55. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_m/meta.json +0 -0
  56. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_r_m/result.arrow +0 -0
  57. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rc_m/dataframe.parquet +0 -0
  58. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rc_m/meta.json +0 -0
  59. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rc_m/result.arrow +0 -0
  60. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_m/dataframe.parquet +0 -0
  61. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_m/meta.json +0 -0
  62. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_m/result.arrow +0 -0
  63. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_ym/dataframe.parquet +0 -0
  64. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_ym/meta.json +0 -0
  65. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rcs_ym/result.arrow +0 -0
  66. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cm/dataframe.parquet +0 -0
  67. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cm/meta.json +0 -0
  68. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cm/result.arrow +0 -0
  69. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cym/dataframe.parquet +0 -0
  70. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cym/meta.json +0 -0
  71. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/dim_rs_cym/result.arrow +0 -0
  72. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/flat_attrs_and_metrics/dataframe.parquet +0 -0
  73. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/flat_attrs_and_metrics/meta.json +0 -0
  74. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/flat_attrs_and_metrics/result.arrow +0 -0
  75. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/manifest.json +0 -0
  76. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/metrics_only/dataframe.parquet +0 -0
  77. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/metrics_only/meta.json +0 -0
  78. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/metrics_only/result.arrow +0 -0
  79. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/single_metric_many_rows/dataframe.parquet +0 -0
  80. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/single_metric_many_rows/meta.json +0 -0
  81. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/single_metric_many_rows/result.arrow +0 -0
  82. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_grand/dataframe.parquet +0 -0
  83. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_grand/meta.json +0 -0
  84. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_grand/result.arrow +0 -0
  85. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub/dataframe.parquet +0 -0
  86. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub/meta.json +0 -0
  87. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub/result.arrow +0 -0
  88. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub_grand/dataframe.parquet +0 -0
  89. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub_grand/meta.json +0 -0
  90. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0_sub_grand/result.arrow +0 -0
  91. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1grand/dataframe.parquet +0 -0
  92. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1grand/meta.json +0 -0
  93. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1grand/result.arrow +0 -0
  94. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1sub/dataframe.parquet +0 -0
  95. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1sub/meta.json +0 -0
  96. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0grand_d1sub/result.arrow +0 -0
  97. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1grand/dataframe.parquet +0 -0
  98. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1grand/meta.json +0 -0
  99. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1grand/result.arrow +0 -0
  100. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1sub/dataframe.parquet +0 -0
  101. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1sub/meta.json +0 -0
  102. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d0sub_d1sub/result.arrow +0 -0
  103. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_grand/dataframe.parquet +0 -0
  104. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_grand/meta.json +0 -0
  105. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_grand/result.arrow +0 -0
  106. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub/dataframe.parquet +0 -0
  107. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub/meta.json +0 -0
  108. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub/result.arrow +0 -0
  109. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub_grand/dataframe.parquet +0 -0
  110. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub_grand/meta.json +0 -0
  111. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/tot_d1_sub_grand/result.arrow +0 -0
  112. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_both_dims/dataframe.parquet +0 -0
  113. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_both_dims/meta.json +0 -0
  114. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_both_dims/result.arrow +0 -0
  115. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_col_sum/dataframe.parquet +0 -0
  116. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_col_sum/meta.json +0 -0
  117. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_col_sum/result.arrow +0 -0
  118. {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
  119. {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
  120. {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
  121. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_row_sum/dataframe.parquet +0 -0
  122. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_row_sum/meta.json +0 -0
  123. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_grand_row_sum/result.arrow +0 -0
  124. {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
  125. {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
  126. {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
  127. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_multi_agg/dataframe.parquet +0 -0
  128. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_multi_agg/meta.json +0 -0
  129. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_multi_agg/result.arrow +0 -0
  130. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_col/dataframe.parquet +0 -0
  131. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_col/meta.json +0 -0
  132. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_col/result.arrow +0 -0
  133. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_row/dataframe.parquet +0 -0
  134. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_row/meta.json +0 -0
  135. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/arrow/totals_subtotal_row/result.arrow +0 -0
  136. {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
  137. {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
  138. {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
  139. {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
  140. {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
  141. {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
  142. {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
  143. {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
  144. {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
  145. {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
  146. {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
  147. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_one_dim1.yaml +0 -0
  148. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_one_dim2.yaml +0 -0
  149. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_totals1.yaml +0 -0
  150. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_totals2.yaml +0 -0
  151. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_totals3.yaml +0 -0
  152. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_totals4.yaml +0 -0
  153. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_two_dim1.yaml +0 -0
  154. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_two_dim2.yaml +0 -0
  155. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_exec_def_two_dim3.yaml +0 -0
  156. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_items.yaml +0 -0
  157. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_items_no_index.yaml +0 -0
  158. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_visualization.yaml +0 -0
  159. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_visualization_date.yaml +0 -0
  160. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/dataframe_for_visualization_no_index.yaml +0 -0
  161. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/empty_indexed_dataframe.yaml +0 -0
  162. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/empty_not_indexed_dataframe.yaml +0 -0
  163. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/filtered_empty_df.yaml +0 -0
  164. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/multi_index_filtered_metrics_and_label.yaml +0 -0
  165. {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
  166. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/multi_index_metrics.yaml +0 -0
  167. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/multi_index_metrics_and_label.yaml +0 -0
  168. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/not_indexed_filtered_metrics_and_labels.yaml +0 -0
  169. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/not_indexed_metrics.yaml +0 -0
  170. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/not_indexed_metrics_and_labels.yaml +0 -0
  171. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/simple_index_filtered_metrics_and_label.yaml +0 -0
  172. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/simple_index_metrics.yaml +0 -0
  173. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/simple_index_metrics_and_label.yaml +0 -0
  174. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/fixtures/simple_index_metrics_no_duplicate.yaml +0 -0
  175. {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
  176. {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
  177. {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
  178. {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
  179. {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
  180. {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
  181. {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
  182. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/campaign_spend.yaml +0 -0
  183. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/order_amount.yaml +0 -0
  184. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/percent_revenue.yaml +0 -0
  185. {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
  186. {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
  187. {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
  188. {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
  189. {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
  190. {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
  191. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue-clothing.yaml +0 -0
  192. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue-electronic.yaml +0 -0
  193. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue-home.yaml +0 -0
  194. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue-outdoor.yaml +0 -0
  195. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/revenue.yaml +0 -0
  196. {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
  197. {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
  198. {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
  199. {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
  200. {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
  201. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/analytics_model/metrics/total_revenue.yaml +0 -0
  202. {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
  203. {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
  204. {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
  205. {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
  206. {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
  207. {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
  208. {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
  209. {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
  210. {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
  211. {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
  212. {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
  213. {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
  214. {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
  215. {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
  216. {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
  217. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/datasets/campaign_channels.yaml +0 -0
  218. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/datasets/campaigns.yaml +0 -0
  219. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/datasets/customers.yaml +0 -0
  220. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/datasets/order_lines.yaml +0 -0
  221. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/datasets/products.yaml +0 -0
  222. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/load/ai/ldm/date_instances/date.yaml +0 -0
  223. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_dataframe_for_created_visualization.py +0 -0
  224. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_dataframe_for_exec_def.py +0 -0
  225. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_dataframe_for_insight.py +0 -0
  226. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_dataframe_for_items.py +0 -0
  227. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_indexed_dataframe.py +0 -0
  228. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/dataframe/test_not_indexed_dataframe.py +0 -0
  229. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/gd_test_config.yaml +0 -0
  230. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/good_pandas/__init__.py +0 -0
  231. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/good_pandas/profiles/profiles.yaml +0 -0
  232. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/good_pandas/test_good_pandas.py +0 -0
  233. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/overview.md +0 -0
  234. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/__init__.py +0 -0
  235. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/conftest.py +0 -0
  236. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/multi_index_filtered_series.yaml +0 -0
  237. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/multi_index_metric_series.yaml +0 -0
  238. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/not_indexed_filtered_metric_series.yaml +0 -0
  239. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/not_indexed_label_series.yaml +0 -0
  240. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/not_indexed_label_series_with_granularity.yaml +0 -0
  241. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/not_indexed_metric_series.yaml +0 -0
  242. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/not_indexed_metric_series_with_granularity.yaml +0 -0
  243. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/simple_index_filtered_series.yaml +0 -0
  244. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/simple_index_label_series.yaml +0 -0
  245. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/fixtures/simple_index_metric_series.yaml +0 -0
  246. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/test_indexed_series.py +0 -0
  247. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/series/test_not_indexed_series.py +0 -0
  248. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/utils/__init__.py +0 -0
  249. {gooddata_pandas-1.69.1.dev2 → gooddata_pandas-1.70.1.dev1}/tests/utils/fixtures/test_get_catalog_attributes_for_extract.yaml +0 -0
  250. {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.69.1.dev2
3
+ Version: 1.70.1.dev1
4
4
  Summary: GoodData Cloud to pandas
5
- Project-URL: Documentation, https://gooddata-pandas.readthedocs.io/en/v1.69.1.dev2
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.69.1.dev2
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.69.1.dev2"
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.69.1.dev2",
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.69.1.dev2"
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]
@@ -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
- row_type_vals = table.column(_COL_ROW_TYPE).to_pylist()
424
- grand_mask = pa.array([v == 2 for v in row_type_vals], type=pa.bool_())
425
- grand_total_rows = table.filter(grand_mask)
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
- data_and_sub_rows = table.filter(pa.array([v != 2 for v in row_type_vals], type=pa.bool_()))
429
- return pa.concat_tables([grand_total_rows, data_and_sub_rows])
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(table: pa.Table, execution_dims: list) -> list[list[int]]:
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 = _parse_schema_metadata(table)
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(table: pa.Table, execution_dims: list) -> list[list[int]]:
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 = _parse_schema_metadata(table)
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 = _get_row_types(table)
612
- total_row_idxs = [i for i, rt in enumerate(row_types) if rt != 0]
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
- for j, ref in enumerate(label_refs):
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
- display_vals = table.column(label_id).to_pylist()
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, is_data in zip(display_vals, data_row_mask) if is_data and isinstance(v, str)
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
- # Fallback: identity (primary label data not present in table)
669
- mapping = {v: v for v, is_data in zip(display_vals, data_row_mask) if is_data and isinstance(v, str)}
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 = _parse_schema_metadata(table)
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 = _parse_schema_metadata(table)
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"]
@@ -25,7 +25,7 @@ from gooddata_pandas.utils import (
25
25
  _str_to_obj_id,
26
26
  _to_attribute,
27
27
  _to_item,
28
- _typed_attribute_value,
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 _typed_result(attributes: list[CatalogAttribute], attribute: Attribute, result_values: list[Any]) -> list[Any]:
343
+ def _resolve_catalog_attribute(attributes: list[CatalogAttribute], attribute: Attribute) -> CatalogAttribute:
344
344
  """
345
- Internal function to convert result_values to proper data types.
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 for which the typed result will be computed.
350
- result_values (list[Any]): A list of raw values.
349
+ attribute (Attribute): The execution attribute to resolve.
351
350
 
352
351
  Returns:
353
- list[Any]: A list of converted values with proper data types.
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 [_typed_attribute_value(catalog_attribute, value) for value in result_values]
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
- attribute = index_to_attribute[idx_name]
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
- attribute = col_to_attribute[col]
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, list] = {}
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).to_pylist()
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
@@ -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) if execution_response is not None else []
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 AttributeConverterStore, DateConverter, DatetimeConverter, IntegerConverter
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
- IntegerConverter.set_external_fnc(lambda self, value: pandas.to_numeric(value))
30
- DateConverter.set_external_fnc(lambda self, value: pandas.to_datetime(value))
31
- DatetimeConverter.set_external_fnc(lambda self, value: pandas.to_datetime(value))
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"]