openretailscience 0.46.0__tar.gz → 0.47.0__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 (272) hide show
  1. {openretailscience-0.46.0 → openretailscience-0.47.0}/.github/dependabot.yml +15 -0
  2. openretailscience-0.47.0/.github/workflows/bigquery-integration.yml +48 -0
  3. {openretailscience-0.46.0 → openretailscience-0.47.0}/.github/workflows/claude-code-review.yml +3 -2
  4. {openretailscience-0.46.0 → openretailscience-0.47.0}/.github/workflows/claude.yml +5 -4
  5. {openretailscience-0.46.0 → openretailscience-0.47.0}/.github/workflows/cloudflare-docs-preview.yml +13 -12
  6. openretailscience-0.47.0/.github/workflows/gh-page.yml +46 -0
  7. openretailscience-0.47.0/.github/workflows/pyspark-integration.yml +36 -0
  8. {openretailscience-0.46.0 → openretailscience-0.47.0}/.github/workflows/release.yml +80 -102
  9. openretailscience-0.47.0/.github/workflows/security-audit.yml +86 -0
  10. openretailscience-0.47.0/.github/workflows/slack-notifications.yml +104 -0
  11. {openretailscience-0.46.0 → openretailscience-0.47.0}/.github/workflows/snowflake-integration.yml +30 -40
  12. {openretailscience-0.46.0 → openretailscience-0.47.0}/.github/workflows/test.yml +23 -19
  13. {openretailscience-0.46.0 → openretailscience-0.47.0}/.pre-commit-config.yaml +4 -0
  14. {openretailscience-0.46.0 → openretailscience-0.47.0}/CLAUDE.md +3 -2
  15. {openretailscience-0.46.0 → openretailscience-0.47.0}/PKG-INFO +3 -4
  16. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/getting_started/options_guide.md +1 -1
  17. openretailscience-0.47.0/openretailscience/__init__.py +1 -0
  18. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/analysis/cohort.py +5 -10
  19. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/analysis/composite_rank.py +19 -82
  20. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/analysis/cross_shop.py +3 -6
  21. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/analysis/customer.py +4 -18
  22. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/analysis/customer_decision_hierarchy.py +2 -4
  23. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/analysis/gain_loss.py +5 -5
  24. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/analysis/product_association.py +3 -6
  25. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/analysis/revenue_tree.py +8 -22
  26. openretailscience-0.47.0/openretailscience/core/__init__.py +1 -0
  27. openretailscience-0.47.0/openretailscience/core/validation.py +153 -0
  28. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/metrics/distribution/acv.py +5 -7
  29. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/metrics/distribution/pct_of_stores.py +9 -8
  30. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/bar.py +31 -28
  31. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/broken_timeline.py +19 -43
  32. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/index.py +15 -17
  33. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/line.py +16 -13
  34. openretailscience-0.47.0/openretailscience/plots/styles/graph_utils.py +423 -0
  35. openretailscience-0.47.0/openretailscience/plots/styles/trend.py +506 -0
  36. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/waterfall.py +5 -9
  37. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/segmentation/nlr.py +19 -17
  38. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/segmentation/rfm.py +8 -10
  39. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/segmentation/segstats.py +20 -26
  40. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/segmentation/threshold.py +5 -12
  41. openretailscience-0.47.0/openretailscience/utils/__init__.py +0 -0
  42. openretailscience-0.47.0/osv-scanner.toml +19 -0
  43. {openretailscience-0.46.0 → openretailscience-0.47.0}/pyproject.toml +7 -7
  44. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/analysis/test_cohort.py +10 -7
  45. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/analysis/test_composite_rank.py +25 -21
  46. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/analysis/test_cross_shop.py +2 -2
  47. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/analysis/test_customer_decision_hierarchy.py +24 -25
  48. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/analysis/test_gain_loss.py +1 -1
  49. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/analysis/test_revenue_tree.py +14 -12
  50. openretailscience-0.47.0/tests/core/__init__.py +0 -0
  51. openretailscience-0.47.0/tests/core/test_validation.py +179 -0
  52. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/metrics/distribution/test_acv.py +4 -4
  53. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/metrics/distribution/test_pct_of_stores.py +1 -1
  54. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/styles/test_chart_chrome.py +34 -35
  55. openretailscience-0.47.0/tests/plots/styles/test_graph_utils.py +363 -0
  56. openretailscience-0.47.0/tests/plots/styles/test_trend.py +625 -0
  57. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_bar.py +34 -7
  58. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_broken_timeline.py +33 -22
  59. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_index.py +47 -1
  60. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_line.py +15 -2
  61. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/segmentation/test_nlr.py +22 -11
  62. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/segmentation/test_rfm.py +122 -68
  63. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/segmentation/test_segstats.py +21 -1
  64. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/segmentation/test_threshold.py +1 -1
  65. {openretailscience-0.46.0 → openretailscience-0.47.0}/uv.lock +1613 -1605
  66. openretailscience-0.46.0/.github/workflows/bigquery-integration.yml +0 -67
  67. openretailscience-0.46.0/.github/workflows/gh-page.yml +0 -45
  68. openretailscience-0.46.0/.github/workflows/pyspark-integration.yml +0 -63
  69. openretailscience-0.46.0/.github/workflows/slack-notifications.yml +0 -103
  70. openretailscience-0.46.0/openretailscience/__init__.py +0 -3
  71. openretailscience-0.46.0/openretailscience/plots/styles/graph_utils.py +0 -923
  72. openretailscience-0.46.0/openretailscience/plugin.py +0 -238
  73. openretailscience-0.46.0/openretailscience/utils/validation.py +0 -45
  74. openretailscience-0.46.0/tests/plots/styles/test_graph_utils.py +0 -979
  75. openretailscience-0.46.0/tests/test_plugin.py +0 -367
  76. openretailscience-0.46.0/tests/utils/test_validation.py +0 -87
  77. {openretailscience-0.46.0 → openretailscience-0.47.0}/.claude/commands/review-pr-tests.md +0 -0
  78. {openretailscience-0.46.0 → openretailscience-0.47.0}/.env_sample +0 -0
  79. {openretailscience-0.46.0 → openretailscience-0.47.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  80. {openretailscience-0.46.0 → openretailscience-0.47.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  81. {openretailscience-0.46.0 → openretailscience-0.47.0}/.gitignore +0 -0
  82. {openretailscience-0.46.0 → openretailscience-0.47.0}/.markdownlint.json +0 -0
  83. {openretailscience-0.46.0 → openretailscience-0.47.0}/CNAME +0 -0
  84. {openretailscience-0.46.0 → openretailscience-0.47.0}/CODE_OF_CONDUCT.md +0 -0
  85. {openretailscience-0.46.0 → openretailscience-0.47.0}/CONTRIBUTING.md +0 -0
  86. {openretailscience-0.46.0 → openretailscience-0.47.0}/LICENSE +0 -0
  87. {openretailscience-0.46.0 → openretailscience-0.47.0}/README.md +0 -0
  88. {openretailscience-0.46.0 → openretailscience-0.47.0}/data/transactions.parquet +0 -0
  89. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/analysis_modules.md +0 -0
  90. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/analysis/cohort.md +0 -0
  91. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/analysis/composite_rank.md +0 -0
  92. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/analysis/cross_shop.md +0 -0
  93. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/analysis/customer.md +0 -0
  94. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/analysis/customer_decision_hierarchy.md +0 -0
  95. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/analysis/gain_loss.md +0 -0
  96. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/analysis/haversine.md +0 -0
  97. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/analysis/product_association.md +0 -0
  98. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/analysis/revenue_tree.md +0 -0
  99. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/metrics/distribution.md +0 -0
  100. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/options.md +0 -0
  101. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/area.md +0 -0
  102. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/bar.md +0 -0
  103. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/broken_timeline.md +0 -0
  104. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/cohort.md +0 -0
  105. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/heatmap.md +0 -0
  106. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/histogram.md +0 -0
  107. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/index.md +0 -0
  108. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/line.md +0 -0
  109. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/period_on_period.md +0 -0
  110. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/price.md +0 -0
  111. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/scatter.md +0 -0
  112. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/styles/graph_utils.md +0 -0
  113. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/styles/styling_helpers.md +0 -0
  114. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/time.md +0 -0
  115. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/venn.md +0 -0
  116. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/plots/waterfall.md +0 -0
  117. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/segmentation/hml.md +0 -0
  118. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/segmentation/nlr.md +0 -0
  119. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/segmentation/rfm.md +0 -0
  120. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/segmentation/segstats.md +0 -0
  121. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/segmentation/threshold.md +0 -0
  122. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/utils/date.md +0 -0
  123. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/utils/filter_and_label.md +0 -0
  124. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/api/utils/label.md +0 -0
  125. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/analysis_modules/cohort.svg +0 -0
  126. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/analysis_modules/cross_shop.svg +0 -0
  127. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/analysis_modules/customer_decision_hierarchy.svg +0 -0
  128. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/analysis_modules/days_between_purchases.svg +0 -0
  129. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/analysis_modules/gain_loss.svg +0 -0
  130. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/analysis_modules/hml_segmentation.svg +0 -0
  131. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/analysis_modules/purchases_per_customer.svg +0 -0
  132. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/analysis_modules/revenue_tree.svg +0 -0
  133. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/analysis_modules/threshold_segmentation.svg +0 -0
  134. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/analysis_modules/transaction_churn.svg +0 -0
  135. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/features/charts.png +0 -0
  136. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/features/cross_shop.png +0 -0
  137. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/area_thumbnail.png +0 -0
  138. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/bar_thumbnail.png +0 -0
  139. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/broken_timeline_thumbnail.png +0 -0
  140. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/cohort_thumbnail.png +0 -0
  141. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/customer_decision_hierarchy_thumbnail.png +0 -0
  142. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/heatmap_thumbnail.png +0 -0
  143. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/histogram_thumbnail.png +0 -0
  144. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/index_thumbnail.png +0 -0
  145. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/line_thumbnail.png +0 -0
  146. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/period_on_period_thumbnail.png +0 -0
  147. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/price_thumbnail.png +0 -0
  148. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/revenue_tree_thumbnail.png +0 -0
  149. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/scatter_thumbnail.png +0 -0
  150. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/time_thumbnail.png +0 -0
  151. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/venn_thumbnail.png +0 -0
  152. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/gallery/waterfall_thumbnail.png +0 -0
  153. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/assets/images/logo.svg +0 -0
  154. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/examples/cross_shop.ipynb +0 -0
  155. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/examples/gain_loss.ipynb +0 -0
  156. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/examples/product_association.ipynb +0 -0
  157. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/examples/retention.ipynb +0 -0
  158. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/examples/revenue_tree.ipynb +0 -0
  159. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/examples/segmentation.ipynb +0 -0
  160. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/index.md +0 -0
  161. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/area.ipynb +0 -0
  162. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/bar.ipynb +0 -0
  163. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/broken_timeline.ipynb +0 -0
  164. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/cohort.ipynb +0 -0
  165. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/customer_decision_hierarchy.ipynb +0 -0
  166. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/heatmap.ipynb +0 -0
  167. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/histogram.ipynb +0 -0
  168. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/index_plot.ipynb +0 -0
  169. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/line.ipynb +0 -0
  170. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/period_on_period.ipynb +0 -0
  171. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/price.ipynb +0 -0
  172. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/revenue_tree.ipynb +0 -0
  173. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/scatter.ipynb +0 -0
  174. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/time.ipynb +0 -0
  175. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/venn.ipynb +0 -0
  176. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/gallery/plots/waterfall.ipynb +0 -0
  177. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/getting_started/installation.md +0 -0
  178. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/getting_started/styling_guide.ipynb +0 -0
  179. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/index.md +0 -0
  180. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/javascripts/mathjax.js +0 -0
  181. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/metrics.md +0 -0
  182. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/overrides/home.html +0 -0
  183. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/overrides/main.html +0 -0
  184. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/robots.txt +0 -0
  185. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/scripts/README.md +0 -0
  186. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/scripts/regenerate_analysis_modules_svgs.py +0 -0
  187. {openretailscience-0.46.0 → openretailscience-0.47.0}/docs/stylesheets/extra.css +0 -0
  188. {openretailscience-0.46.0 → openretailscience-0.47.0}/mkdocs.yml +0 -0
  189. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/analysis/__init__.py +0 -0
  190. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/analysis/haversine.py +0 -0
  191. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/assets/fonts/Poppins-Bold.ttf +0 -0
  192. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/assets/fonts/Poppins-LightItalic.ttf +0 -0
  193. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/assets/fonts/Poppins-Medium.ttf +0 -0
  194. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/assets/fonts/Poppins-Regular.ttf +0 -0
  195. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/assets/fonts/Poppins-SemiBold.ttf +0 -0
  196. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/constants.py +0 -0
  197. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/metrics/__init__.py +0 -0
  198. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/metrics/base.py +0 -0
  199. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/metrics/distribution/__init__.py +0 -0
  200. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/options.py +0 -0
  201. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/__init__.py +0 -0
  202. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/area.py +0 -0
  203. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/cohort.py +0 -0
  204. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/heatmap.py +0 -0
  205. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/histogram.py +0 -0
  206. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/period_on_period.py +0 -0
  207. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/price.py +0 -0
  208. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/scatter.py +0 -0
  209. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/styles/__init__.py +0 -0
  210. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/styles/colors.py +0 -0
  211. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/styles/font_utils.py +0 -0
  212. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/styles/styling_helpers.py +0 -0
  213. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/time.py +0 -0
  214. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/tree_diagram.py +0 -0
  215. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/plots/venn.py +0 -0
  216. /openretailscience-0.46.0/openretailscience/segmentation/__init__.py → /openretailscience-0.47.0/openretailscience/py.typed +0 -0
  217. {openretailscience-0.46.0/openretailscience/utils → openretailscience-0.47.0/openretailscience/segmentation}/__init__.py +0 -0
  218. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/segmentation/hml.py +0 -0
  219. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/utils/date.py +0 -0
  220. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/utils/filter_and_label.py +0 -0
  221. {openretailscience-0.46.0 → openretailscience-0.47.0}/openretailscience/utils/label.py +0 -0
  222. {openretailscience-0.46.0 → openretailscience-0.47.0}/options_template.toml +0 -0
  223. {openretailscience-0.46.0 → openretailscience-0.47.0}/readme_assets/cross_shop_chart.png +0 -0
  224. {openretailscience-0.46.0 → openretailscience-0.47.0}/readme_assets/days_until_next_purchase.png +0 -0
  225. {openretailscience-0.46.0 → openretailscience-0.47.0}/readme_assets/gain_loss_chart.png +0 -0
  226. {openretailscience-0.46.0 → openretailscience-0.47.0}/readme_assets/logo.png +0 -0
  227. {openretailscience-0.46.0 → openretailscience-0.47.0}/readme_assets/logo.svg +0 -0
  228. {openretailscience-0.46.0 → openretailscience-0.47.0}/readme_assets/new_store_cannibalization.png +0 -0
  229. {openretailscience-0.46.0 → openretailscience-0.47.0}/taplo.toml +0 -0
  230. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/analysis/test_customer.py +0 -0
  231. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/analysis/test_haversine.py +0 -0
  232. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/analysis/test_product_association.py +0 -0
  233. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/conftest.py +0 -0
  234. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_cohort_analysis.py +0 -0
  235. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_composite_rank.py +0 -0
  236. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_cross_shop.py +0 -0
  237. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_customer_decision_hierarchy.py +0 -0
  238. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_date.py +0 -0
  239. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_filter_and_label.py +0 -0
  240. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_haversine.py +0 -0
  241. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_hml_segmentation.py +0 -0
  242. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_product_association.py +0 -0
  243. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_revenue_tree.py +0 -0
  244. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_rfm_segmentation.py +0 -0
  245. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_segstats_segmentation.py +0 -0
  246. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/integration/test_threshold_segmentation.py +0 -0
  247. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/metrics/test_base.py +0 -0
  248. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/styles/test_color.py +0 -0
  249. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/styles/test_font_utils.py +0 -0
  250. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/styles/test_styling_helper.py +0 -0
  251. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_area.py +0 -0
  252. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_cohort.py +0 -0
  253. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_heatmap.py +0 -0
  254. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_histogram.py +0 -0
  255. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_period_on_period.py +0 -0
  256. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_price.py +0 -0
  257. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_scatter.py +0 -0
  258. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_styling_options_integration.py +0 -0
  259. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_time.py +0 -0
  260. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_tree_diagram.py +0 -0
  261. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_venn.py +0 -0
  262. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/plots/test_waterfall.py +0 -0
  263. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/segmentation/test_hml.py +0 -0
  264. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/test_options.py +0 -0
  265. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/toml_files/corrupt.toml +0 -0
  266. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/toml_files/invalid_option.toml +0 -0
  267. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/toml_files/test_options.toml +0 -0
  268. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/toml_files/valid.toml +0 -0
  269. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/utils/test_date.py +0 -0
  270. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/utils/test_filter_and_label.py +0 -0
  271. {openretailscience-0.46.0 → openretailscience-0.47.0}/tests/utils/test_label.py +0 -0
  272. {openretailscience-0.46.0 → openretailscience-0.47.0}/tox.ini +0 -0
@@ -5,6 +5,8 @@ updates:
5
5
  schedule:
6
6
  interval: "weekly"
7
7
  open-pull-requests-limit: 5
8
+ cooldown:
9
+ default-days: 7
8
10
  commit-message:
9
11
  prefix: "chore(deps)"
10
12
  prefix-development: "chore(deps-dev)"
@@ -13,3 +15,16 @@ updates:
13
15
  dependency-type: "production"
14
16
  development-dependencies:
15
17
  dependency-type: "development"
18
+
19
+ - package-ecosystem: "github-actions"
20
+ directory: "/"
21
+ schedule:
22
+ interval: "weekly"
23
+ open-pull-requests-limit: 5
24
+ cooldown:
25
+ default-days: 7
26
+ commit-message:
27
+ prefix: "chore(deps)"
28
+ groups:
29
+ actions-minor-patch:
30
+ update-types: ["minor", "patch"]
@@ -0,0 +1,48 @@
1
+ name: BigQuery Integration Tests
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ workflow_call:
6
+ secrets:
7
+ GCP_SA_KEY:
8
+ required: true
9
+ GCP_PROJECT_ID:
10
+ required: true
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ integration-tests:
17
+ name: Run BigQuery Integration Tests
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 30
20
+ steps:
21
+ - name: Checkout
22
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
23
+ with:
24
+ persist-credentials: false
25
+ - name: Setup Python
26
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
27
+ with:
28
+ python-version: "3.11"
29
+
30
+ - name: Install uv
31
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
32
+ with:
33
+ enable-cache: true
34
+
35
+ - name: Install Dependencies
36
+ run: |
37
+ uv sync --locked
38
+
39
+ - name: Set up GCP Authentication
40
+ uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
41
+ with:
42
+ credentials_json: ${{ secrets.GCP_SA_KEY }}
43
+
44
+ - name: Run Integration Tests
45
+ env:
46
+ GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
47
+ run: |
48
+ uv run pytest tests/integration -k "bigquery" -v
@@ -27,13 +27,14 @@ jobs:
27
27
 
28
28
  steps:
29
29
  - name: Checkout repository
30
- uses: actions/checkout@v4
30
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
31
31
  with:
32
32
  fetch-depth: 1
33
+ persist-credentials: false
33
34
 
34
35
  - name: Run Claude Code Review
35
36
  id: claude-review
36
- uses: anthropics/claude-code-action@v1
37
+ uses: anthropics/claude-code-action@787c5a0ce96a9a6cfb050ea0c8f4c05f2447c251 # v1.0.133
37
38
  with:
38
39
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39
40
  track_progress: true
@@ -27,7 +27,7 @@ jobs:
27
27
  steps:
28
28
  - name: Check user permissions
29
29
  id: check_permissions
30
- uses: actions/github-script@v7
30
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
31
31
  with:
32
32
  script: |
33
33
  let actor;
@@ -60,7 +60,7 @@ jobs:
60
60
 
61
61
  - name: Comment on permission denied
62
62
  if: steps.check_permissions.outputs.has_permission == 'false'
63
- uses: actions/github-script@v7
63
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
64
64
  with:
65
65
  script: |
66
66
  let issueNumber;
@@ -85,14 +85,15 @@ jobs:
85
85
 
86
86
  - name: Checkout repository
87
87
  if: steps.check_permissions.outputs.has_permission == 'true'
88
- uses: actions/checkout@v4
88
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
89
89
  with:
90
90
  fetch-depth: 1
91
+ persist-credentials: false
91
92
 
92
93
  - name: Run Claude Code
93
94
  if: steps.check_permissions.outputs.has_permission == 'true'
94
95
  id: claude
95
- uses: anthropics/claude-code-action@v1
96
+ uses: anthropics/claude-code-action@787c5a0ce96a9a6cfb050ea0c8f4c05f2447c251 # v1.0.133
96
97
  with:
97
98
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
98
99
 
@@ -22,20 +22,21 @@ jobs:
22
22
 
23
23
  steps:
24
24
  - name: Checkout repository
25
- uses: actions/checkout@v4
26
-
25
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
26
+ with:
27
+ persist-credentials: false
27
28
  - name: Setup Python
28
- uses: actions/setup-python@v5
29
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
29
30
  with:
30
31
  python-version: "3.11"
31
32
 
32
- - name: Install uv Package
33
- run: |
34
- pip install --upgrade pip
35
- pip install uv==0.5.30
33
+ - name: Install uv
34
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
35
+ with:
36
+ enable-cache: true
36
37
 
37
38
  - name: Install Dependencies
38
- run: uv sync
39
+ run: uv sync --locked
39
40
 
40
41
  - name: Build docs
41
42
  run: uv run mkdocs build
@@ -50,7 +51,7 @@ jobs:
50
51
 
51
52
  - name: Publish to Cloudflare Pages
52
53
  id: cloudflare-deploy
53
- uses: cloudflare/pages-action@v1
54
+ uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1.5.0
54
55
  with:
55
56
  apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
56
57
  accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
@@ -61,7 +62,7 @@ jobs:
61
62
 
62
63
  - name: Find existing comment
63
64
  if: github.event_name == 'pull_request'
64
- uses: peter-evans/find-comment@v3
65
+ uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
65
66
  id: find-comment
66
67
  with:
67
68
  issue-number: ${{ github.event.pull_request.number }}
@@ -70,7 +71,7 @@ jobs:
70
71
 
71
72
  - name: Create or update deployment comment
72
73
  if: github.event_name == 'pull_request'
73
- uses: peter-evans/create-or-update-comment@v4
74
+ uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
74
75
  with:
75
76
  comment-id: ${{ steps.find-comment.outputs.comment-id }}
76
77
  issue-number: ${{ github.event.pull_request.number }}
@@ -88,7 +89,7 @@ jobs:
88
89
 
89
90
  - name: Comment on deployment failure
90
91
  if: failure() && github.event_name == 'pull_request'
91
- uses: actions/github-script@v7
92
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
92
93
  with:
93
94
  script: |
94
95
  github.rest.issues.createComment({
@@ -0,0 +1,46 @@
1
+ name: Page Deploy
2
+ on:
3
+ workflow_call:
4
+ workflow_dispatch:
5
+
6
+ permissions:
7
+ contents: read
8
+ pages: write
9
+ id-token: write
10
+
11
+ concurrency:
12
+ group: "pages"
13
+ cancel-in-progress: false
14
+ jobs:
15
+ docs:
16
+ environment:
17
+ name: github-pages
18
+ url: ${{ steps.deployment.outputs.page_url }}
19
+ runs-on: ubuntu-latest
20
+ timeout-minutes: 15
21
+ steps:
22
+ - name: Checkout
23
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
24
+ with:
25
+ persist-credentials: false
26
+ - name: Setup Python
27
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
28
+ with:
29
+ python-version: "3.11"
30
+ - name: Install uv
31
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
32
+ with:
33
+ enable-cache: true
34
+ - name: Install Dependencies
35
+ run: uv sync --locked
36
+ - name: Build docs
37
+ run: uv run mkdocs build
38
+ - name: Setup Pages
39
+ uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
40
+ - name: Upload artifact
41
+ uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
42
+ with:
43
+ path: "site/"
44
+ - name: Deploy to GitHub Pages
45
+ id: deployment
46
+ uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
@@ -0,0 +1,36 @@
1
+ name: PySpark Integration Tests
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ workflow_call:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ integration-tests:
12
+ name: Run PySpark Integration Tests
13
+ runs-on: ubuntu-latest
14
+ timeout-minutes: 30
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
18
+ with:
19
+ persist-credentials: false
20
+ - name: Setup Python
21
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
22
+ with:
23
+ python-version: "3.11"
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
27
+ with:
28
+ enable-cache: true
29
+
30
+ - name: Install Dependencies
31
+ run: |
32
+ uv sync --locked
33
+
34
+ - name: Run Integration Tests
35
+ run: |
36
+ uv run pytest tests/integration -k "pyspark" -v
@@ -11,9 +11,7 @@ on:
11
11
  - minor
12
12
  - patch
13
13
  permissions:
14
- contents: write
15
- deployments: write
16
- packages: write
14
+ contents: read
17
15
  concurrency:
18
16
  group: "release"
19
17
  cancel-in-progress: false
@@ -21,92 +19,79 @@ jobs:
21
19
  pre-commit:
22
20
  name: Pre-Commit Checks
23
21
  runs-on: ubuntu-latest
22
+ timeout-minutes: 10
24
23
  steps:
25
24
  - name: Checkout
26
- uses: actions/checkout@v4
25
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
26
+ with:
27
+ persist-credentials: false
27
28
  - name: Setup Python
28
- uses: actions/setup-python@v5
29
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
29
30
  with:
30
31
  python-version: "3.11"
31
- - name: Install UV
32
- run: |
33
- pip install --upgrade pip
34
- pip install uv==0.5.30
35
- uv sync
36
- - name: Install Pre-commit in uv environment
37
- run: uv run pip install pre-commit
32
+ - name: Install uv
33
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
34
+ with:
35
+ enable-cache: true
36
+ - name: Install dependencies
37
+ run: uv sync --locked
38
38
  - name: Run Pre-commit
39
- run: pre-commit run --all-files
39
+ run: uv run pre-commit run --all-files
40
+
41
+ security-audit:
42
+ name: Dependency Security Audit
43
+ uses: ./.github/workflows/security-audit.yml
44
+ permissions:
45
+ contents: read
46
+ security-events: write
40
47
 
41
48
  bigquery-integration-tests:
42
49
  name: BigQuery Integration Tests
43
- runs-on: ubuntu-latest
44
- steps:
45
- - name: Checkout
46
- uses: actions/checkout@v4
47
- - name: Setup Python
48
- uses: actions/setup-python@v5
49
- with:
50
- python-version: "3.11"
51
- - name: Install uv Package
52
- run: |
53
- pip install --upgrade pip
54
- pip install uv==0.5.30
55
- - name: Install Dependencies
56
- run: |
57
- uv sync
58
- - name: Set up GCP Authentication
59
- uses: google-github-actions/auth@v2
60
- with:
61
- credentials_json: ${{ secrets.GCP_SA_KEY }}
62
- - name: Run BigQuery Integration Tests
63
- env:
64
- GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
65
- run: |
66
- uv run pytest tests/integration -k "bigquery" -v
50
+ uses: ./.github/workflows/bigquery-integration.yml
51
+ secrets:
52
+ GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
53
+ GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
67
54
 
68
55
  pyspark-integration-tests:
69
56
  name: PySpark Integration Tests
70
- runs-on: ubuntu-latest
71
- steps:
72
- - name: Checkout
73
- uses: actions/checkout@v4
74
- - name: Setup Python
75
- uses: actions/setup-python@v5
76
- with:
77
- python-version: "3.11"
78
- - name: Install uv Package
79
- run: |
80
- pip install --upgrade pip
81
- pip install uv==0.5.30
82
- - name: Install Dependencies
83
- run: |
84
- uv sync
85
- - name: Run PySpark Integration Tests
86
- run: |
87
- uv run pytest tests/integration -k "pyspark" -v
57
+ uses: ./.github/workflows/pyspark-integration.yml
58
+
59
+ snowflake-integration-tests:
60
+ name: Snowflake Integration Tests
61
+ uses: ./.github/workflows/snowflake-integration.yml
62
+ secrets:
63
+ GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
64
+ GCP_INFRA_PROJECT_ID: ${{ secrets.GCP_INFRA_PROJECT_ID }}
65
+ SNOWFLAKE_CI_ACCOUNT: ${{ secrets.SNOWFLAKE_CI_ACCOUNT }}
66
+ SNOWFLAKE_CI_USER: ${{ secrets.SNOWFLAKE_CI_USER }}
67
+ SNOWFLAKE_CI_WAREHOUSE: ${{ secrets.SNOWFLAKE_CI_WAREHOUSE }}
68
+ SNOWFLAKE_CI_DATABASE: ${{ secrets.SNOWFLAKE_CI_DATABASE }}
69
+ SNOWFLAKE_CI_SCHEMA: ${{ secrets.SNOWFLAKE_CI_SCHEMA }}
88
70
 
89
71
  multi-python-tests:
90
72
  name: Multi-Python Version Tests
91
73
  runs-on: ubuntu-latest
74
+ timeout-minutes: 20
92
75
  strategy:
93
76
  fail-fast: false
94
77
  matrix:
95
78
  python-version: ["3.10", "3.11", "3.12", "3.13"]
96
79
  steps:
97
80
  - name: Checkout code
98
- uses: actions/checkout@v4
81
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
82
+ with:
83
+ persist-credentials: false
99
84
  - name: Set up Python ${{ matrix.python-version }}
100
- uses: actions/setup-python@v5
85
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
101
86
  with:
102
87
  python-version: ${{ matrix.python-version }}
103
- - name: Install UV
104
- run: |
105
- pip install --upgrade pip
106
- pip install uv==0.5.30
88
+ - name: Install uv
89
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
90
+ with:
91
+ enable-cache: true
107
92
  - name: Install dependencies
108
93
  run: |
109
- uv sync
94
+ uv sync --locked
110
95
  - name: Run tests (excluding integration)
111
96
  run: |
112
97
  uv run pytest tests/ --ignore=tests/integration --tb=short -v
@@ -114,23 +99,30 @@ jobs:
114
99
  build:
115
100
  name: Build
116
101
  runs-on: ubuntu-latest
117
- needs: [pre-commit, bigquery-integration-tests, pyspark-integration-tests, multi-python-tests]
102
+ timeout-minutes: 15
103
+ needs: [security-audit, pre-commit, bigquery-integration-tests, pyspark-integration-tests, snowflake-integration-tests, multi-python-tests]
104
+ permissions:
105
+ contents: write
106
+ id-token: write
107
+ attestations: write
118
108
  env:
119
109
  CI_COMMIT_MESSAGE: Continuous Integration Version Bump ${{ inputs.release }}
120
110
  steps:
121
- - name: Checkout
122
- uses: actions/checkout@v4
111
+ # zizmor: ignore[artipacked] this job pushes the version-bump commit and
112
+ # tag, so it requires the GITHUB_TOKEN persisted by actions/checkout. Only
113
+ # dist/ is uploaded as an artifact, not .git/, so the token is not exposed.
114
+ - name: Checkout # zizmor: ignore[artipacked]
115
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
123
116
  - name: Setup Python
124
- uses: actions/setup-python@v5
117
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
125
118
  with:
126
119
  python-version: "3.11"
127
- - name: Install uv Package
128
- run: |
129
- pip install --upgrade pip
130
- pip install build
131
- pip install uv==0.5.30
120
+ - name: Install uv
121
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
122
+ with:
123
+ enable-cache: true
132
124
  - name: Install Dependencies
133
- run: uv sync --group dev
125
+ run: uv sync --locked --group dev
134
126
  - name: Bump Version
135
127
  run: |
136
128
  uv run python - <<EOF
@@ -153,8 +145,12 @@ jobs:
153
145
  EOF
154
146
  echo "RELEASE_TAG=$(uv run python -c 'from tomlkit.toml_file import TOMLFile; print(TOMLFile("pyproject.toml").read()["project"]["version"])')" >> $GITHUB_ENV
155
147
  - name: Build package
156
- run: python -m build
157
- - uses: actions/upload-artifact@v4
148
+ run: uv build
149
+ - name: Attest build provenance
150
+ uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
151
+ with:
152
+ subject-path: "dist/*"
153
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
158
154
  with:
159
155
  name: build
160
156
  path: dist/
@@ -164,38 +160,19 @@ jobs:
164
160
  git config user.name 'github-actions[bot]'
165
161
  git config user.email 'github-actions[bot]@users.noreply.github.com'
166
162
  git add pyproject.toml
167
- git commit -m "${{ env.CI_COMMIT_MESSAGE }}"
163
+ git commit -m "$CI_COMMIT_MESSAGE"
168
164
  git push
169
165
  echo "Tagging new version: $RELEASE_TAG"
170
166
  git tag -a "$RELEASE_TAG" -m "Auto-generated tag"
171
167
  git push origin "$RELEASE_TAG"
172
- - name: "Create release"
173
- uses: actions/github-script@v7
174
- with:
175
- github-token: "${{ secrets.GITHUB_TOKEN }}"
176
- script: |
177
- if (!process.env.RELEASE_TAG) {
178
- core.setFailed("The environment variable RELEASE_TAG is not defined.");
179
- return;
180
- }
181
- try {
182
- const response = await github.rest.repos.createRelease({
183
- draft: false,
184
- generate_release_notes: true,
185
- name: process.env.RELEASE_TAG,
186
- owner: context.repo.owner,
187
- prerelease: false,
188
- repo: context.repo.repo,
189
- tag_name: process.env.RELEASE_TAG,
190
- });
191
- core.exportVariable('RELEASE_ID', response.data.id);
192
- core.exportVariable('RELEASE_UPLOAD_URL', response.data.upload_url);
193
- } catch (error) {
194
- core.setFailed(error.message);
195
- }
168
+ - name: Create release
169
+ env:
170
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
171
+ run: gh release create "$RELEASE_TAG" --generate-notes
196
172
  pypi-publish:
197
173
  name: Upload release to PyPI
198
174
  runs-on: ubuntu-latest
175
+ timeout-minutes: 10
199
176
  needs: build
200
177
  environment:
201
178
  name: production
@@ -203,13 +180,14 @@ jobs:
203
180
  permissions:
204
181
  id-token: write
205
182
  steps:
206
- - uses: actions/download-artifact@v4
183
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
207
184
  with:
208
185
  name: build
209
186
  path: dist
210
187
  - name: Publish package distributions to PyPI
211
- uses: pypa/gh-action-pypi-publish@release/v1
212
-
188
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
189
+ with:
190
+ attestations: true
213
191
  deploy-docs:
214
192
  name: Deploy Documentation
215
193
  needs: pypi-publish
@@ -0,0 +1,86 @@
1
+ name: Security Audit
2
+
3
+ # Scans the resolved dependency lockfile (uv.lock) for known vulnerabilities
4
+ # using OSV-Scanner. Because it reads uv.lock statically, it audits every pinned
5
+ # version across all supported Python versions (3.10-3.13) and platforms.
6
+ # Results are uploaded to GitHub code scanning (Security tab) as SARIF, so a
7
+ # newly-disclosed CVE in a pinned dependency surfaces as a failing check and a
8
+ # code-scanning alert rather than a passive Dependabot alert.
9
+ #
10
+ # Suppressions (each with a rationale, and an ignoreUntil expiry where a fix may
11
+ # yet appear) live in osv-scanner.toml at the repo root.
12
+ #
13
+ # On pull requests the scan runs only when dependencies or this workflow change,
14
+ # and is advisory (results still post to code scanning); pushes to main (same
15
+ # path filter) refresh the default-branch code-scanning state, and scheduled
16
+ # runs, manual dispatches, and release calls are hard gates.
17
+
18
+ on:
19
+ pull_request:
20
+ types: [opened, synchronize]
21
+ paths:
22
+ - "uv.lock"
23
+ - "pyproject.toml"
24
+ - "osv-scanner.toml"
25
+ - ".github/workflows/security-audit.yml"
26
+ push:
27
+ branches: [main]
28
+ paths:
29
+ - "uv.lock"
30
+ - "pyproject.toml"
31
+ - "osv-scanner.toml"
32
+ - ".github/workflows/security-audit.yml"
33
+ schedule:
34
+ - cron: "0 6 * * 1" # Mondays 06:00 UTC
35
+ workflow_dispatch:
36
+ workflow_call:
37
+
38
+ permissions: {}
39
+
40
+ concurrency:
41
+ group: "security-audit-${{ github.ref }}-${{ github.event_name }}"
42
+ cancel-in-progress: true
43
+
44
+ jobs:
45
+ osv-scan:
46
+ name: OSV-Scanner (uv.lock)
47
+ runs-on: ubuntu-latest
48
+ timeout-minutes: 10
49
+ permissions:
50
+ contents: read
51
+ security-events: write # upload SARIF to code scanning
52
+ steps:
53
+ - name: Checkout
54
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
55
+ with:
56
+ persist-credentials: false
57
+ - name: Run OSV-Scanner
58
+ id: osv
59
+ continue-on-error: true # gate is enforced explicitly below, after SARIF upload
60
+ uses: google/osv-scanner-action/osv-scanner-action@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8
61
+ with:
62
+ scan-args: |-
63
+ --config=osv-scanner.toml
64
+ --format=sarif
65
+ --output=results.sarif
66
+ --lockfile=uv.lock
67
+ - name: Upload results to code scanning
68
+ # Skip cleanly if the scanner errored before writing the SARIF, rather
69
+ # than erroring and being swallowed by continue-on-error.
70
+ if: always() && hashFiles('results.sarif') != ''
71
+ continue-on-error: true # never let SARIF upload (e.g. missing perms) break the gate
72
+ uses: github/codeql-action/upload-sarif@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
73
+ with:
74
+ sarif_file: results.sarif
75
+ category: osv-scanner
76
+ - name: Enforce gate
77
+ # Pull requests are advisory only (results still post to code scanning);
78
+ # every other trigger is a hard gate. Fail closed: block unless the scan
79
+ # completed successfully, so a failed, errored, or skipped scan never
80
+ # passes the gate. !cancelled() (rather than always()) avoids marking a
81
+ # concurrency-superseded run as failed; a real timeout still blocks
82
+ # downstream via the job's needs dependency.
83
+ if: ${{ !cancelled() && github.event_name != 'pull_request' && steps.osv.outcome != 'success' }}
84
+ run: |
85
+ echo "::error::OSV-Scanner did not complete cleanly (vulnerabilities found or scan failed) — see the SARIF results in the Security tab."
86
+ exit 1