openretailscience 0.46.0__tar.gz → 0.48.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 (342) hide show
  1. openretailscience-0.48.0/.env_sample +9 -0
  2. {openretailscience-0.46.0 → openretailscience-0.48.0}/.github/dependabot.yml +15 -0
  3. openretailscience-0.48.0/.github/workflows/bigquery-integration.yml +48 -0
  4. {openretailscience-0.46.0 → openretailscience-0.48.0}/.github/workflows/claude-code-review.yml +6 -2
  5. {openretailscience-0.46.0 → openretailscience-0.48.0}/.github/workflows/claude.yml +5 -4
  6. {openretailscience-0.46.0 → openretailscience-0.48.0}/.github/workflows/cloudflare-docs-preview.yml +18 -18
  7. openretailscience-0.48.0/.github/workflows/gh-page.yml +46 -0
  8. openretailscience-0.48.0/.github/workflows/oracle-integration.yml +46 -0
  9. openretailscience-0.48.0/.github/workflows/pyspark-integration.yml +36 -0
  10. openretailscience-0.48.0/.github/workflows/release.yml +206 -0
  11. openretailscience-0.48.0/.github/workflows/security-audit.yml +86 -0
  12. openretailscience-0.48.0/.github/workflows/slack-notifications.yml +110 -0
  13. {openretailscience-0.46.0 → openretailscience-0.48.0}/.github/workflows/snowflake-integration.yml +30 -40
  14. openretailscience-0.48.0/.github/workflows/sqlserver-integration.yml +64 -0
  15. openretailscience-0.48.0/.github/workflows/test.yml +104 -0
  16. {openretailscience-0.46.0 → openretailscience-0.48.0}/.gitignore +5 -0
  17. {openretailscience-0.46.0 → openretailscience-0.48.0}/.pre-commit-config.yaml +13 -11
  18. {openretailscience-0.46.0 → openretailscience-0.48.0}/CLAUDE.md +21 -3
  19. {openretailscience-0.46.0 → openretailscience-0.48.0}/CONTRIBUTING.md +13 -0
  20. {openretailscience-0.46.0 → openretailscience-0.48.0}/PKG-INFO +30 -13
  21. {openretailscience-0.46.0 → openretailscience-0.48.0}/README.md +25 -8
  22. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/analysis_modules.md +3 -3
  23. openretailscience-0.48.0/docs/api/experimental/clv.md +3 -0
  24. openretailscience-0.48.0/docs/api/experimental/metrics/distribution.md +5 -0
  25. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/examples/retention.ipynb +9 -11
  26. openretailscience-0.48.0/docs/experimental/clv.md +167 -0
  27. {openretailscience-0.46.0/docs → openretailscience-0.48.0/docs/experimental}/metrics.md +6 -2
  28. openretailscience-0.48.0/docs/gallery/index.md +156 -0
  29. openretailscience-0.48.0/docs/gallery/plots/customer_decision_hierarchy.ipynb +249 -0
  30. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/waterfall.ipynb +3 -3
  31. openretailscience-0.48.0/docs/getting_started/agent_skills.md +94 -0
  32. openretailscience-0.48.0/docs/getting_started/connecting_to_data.md +493 -0
  33. openretailscience-0.48.0/docs/getting_started/data_structures.md +274 -0
  34. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/getting_started/options_guide.md +5 -3
  35. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/overrides/home.html +1 -1
  36. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/scripts/regenerate_analysis_modules_svgs.py +5 -7
  37. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/stylesheets/extra.css +95 -0
  38. {openretailscience-0.46.0 → openretailscience-0.48.0}/mkdocs.yml +16 -4
  39. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/SKILL.md +105 -0
  40. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/references/analysis.md +105 -0
  41. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/references/clv.md +128 -0
  42. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/references/configuration.md +113 -0
  43. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/references/plotting.md +87 -0
  44. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_advanced_customization.py +29 -0
  45. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_area.py +79 -0
  46. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_bar.py +98 -0
  47. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_broken_timeline.py +74 -0
  48. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_clv_stats.py +80 -0
  49. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_cohort.py +50 -0
  50. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_cohort_analysis.py +56 -0
  51. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_composite_rank.py +35 -0
  52. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_connecting_to_data.py +167 -0
  53. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_cross_shop.py +43 -0
  54. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_customer_decision_hierarchy.py +108 -0
  55. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_filter_and_label.py +94 -0
  56. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_filter_by_periods.py +36 -0
  57. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_gain_loss.py +109 -0
  58. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_haversine_distance.py +78 -0
  59. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_heatmap.py +40 -0
  60. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_histogram.py +78 -0
  61. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_hml_segmentation.py +70 -0
  62. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_human_format.py +45 -0
  63. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_index.py +82 -0
  64. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_label_by_condition.py +87 -0
  65. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_line.py +56 -0
  66. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_nlr_segmentation.py +70 -0
  67. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_options.py +106 -0
  68. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_overlapping_periods.py +21 -0
  69. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_period_on_period.py +70 -0
  70. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_price.py +83 -0
  71. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_product_association.py +70 -0
  72. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_purchases_per_customer.py +42 -0
  73. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_revenue_tree.py +82 -0
  74. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_rfm_segmentation.py +89 -0
  75. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_scatter.py +103 -0
  76. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_segment_transaction_stats.py +102 -0
  77. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_set_axis_percent.py +68 -0
  78. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_threshold_segmentation.py +96 -0
  79. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_time.py +90 -0
  80. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_tree_diagram.py +115 -0
  81. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_trend_line.py +25 -0
  82. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_venn.py +69 -0
  83. openretailscience-0.48.0/openretailscience/.agents/skills/using-openretailscience/scripts/example_waterfall.py +61 -0
  84. openretailscience-0.48.0/openretailscience/__init__.py +1 -0
  85. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/analysis/cohort.py +62 -17
  86. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/analysis/composite_rank.py +22 -86
  87. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/analysis/cross_shop.py +9 -10
  88. openretailscience-0.48.0/openretailscience/analysis/customer.py +377 -0
  89. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/analysis/customer_decision_hierarchy.py +2 -4
  90. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/analysis/gain_loss.py +5 -5
  91. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/analysis/product_association.py +9 -13
  92. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/analysis/revenue_tree.py +8 -22
  93. openretailscience-0.48.0/openretailscience/core/__init__.py +1 -0
  94. openretailscience-0.48.0/openretailscience/core/validation.py +349 -0
  95. openretailscience-0.48.0/openretailscience/experimental/__init__.py +4 -0
  96. openretailscience-0.48.0/openretailscience/experimental/clv.py +483 -0
  97. {openretailscience-0.46.0/openretailscience → openretailscience-0.48.0/openretailscience/experimental}/metrics/distribution/acv.py +9 -13
  98. {openretailscience-0.46.0/openretailscience → openretailscience-0.48.0/openretailscience/experimental}/metrics/distribution/pct_of_stores.py +14 -15
  99. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/options.py +33 -16
  100. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/bar.py +31 -28
  101. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/broken_timeline.py +19 -43
  102. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/index.py +18 -19
  103. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/line.py +16 -13
  104. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/price.py +4 -1
  105. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/scatter.py +4 -1
  106. openretailscience-0.48.0/openretailscience/plots/styles/graph_utils.py +481 -0
  107. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/styles/styling_helpers.py +376 -77
  108. openretailscience-0.48.0/openretailscience/plots/styles/trend.py +506 -0
  109. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/waterfall.py +19 -13
  110. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/segmentation/nlr.py +26 -27
  111. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/segmentation/rfm.py +11 -15
  112. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/segmentation/segstats.py +188 -88
  113. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/segmentation/threshold.py +12 -21
  114. openretailscience-0.48.0/openretailscience/skills.py +425 -0
  115. openretailscience-0.48.0/openretailscience/utils/__init__.py +0 -0
  116. {openretailscience-0.46.0 → openretailscience-0.48.0}/options_template.toml +5 -0
  117. openretailscience-0.48.0/osv-scanner.toml +9 -0
  118. {openretailscience-0.46.0 → openretailscience-0.48.0}/pyproject.toml +27 -13
  119. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/analysis/test_cohort.py +109 -15
  120. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/analysis/test_composite_rank.py +22 -23
  121. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/analysis/test_cross_shop.py +2 -2
  122. openretailscience-0.48.0/tests/analysis/test_customer.py +522 -0
  123. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/analysis/test_customer_decision_hierarchy.py +24 -25
  124. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/analysis/test_gain_loss.py +1 -1
  125. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/analysis/test_revenue_tree.py +14 -12
  126. openretailscience-0.48.0/tests/core/__init__.py +0 -0
  127. openretailscience-0.48.0/tests/core/test_validation.py +331 -0
  128. {openretailscience-0.46.0/tests → openretailscience-0.48.0/tests/experimental}/metrics/distribution/test_acv.py +6 -6
  129. {openretailscience-0.46.0/tests → openretailscience-0.48.0/tests/experimental}/metrics/distribution/test_pct_of_stores.py +3 -3
  130. {openretailscience-0.46.0/tests → openretailscience-0.48.0/tests/experimental}/metrics/test_base.py +2 -2
  131. openretailscience-0.48.0/tests/experimental/test_clv.py +720 -0
  132. openretailscience-0.48.0/tests/integration/conftest.py +213 -0
  133. openretailscience-0.48.0/tests/integration/docker/README.md +61 -0
  134. openretailscience-0.48.0/tests/integration/docker/docker-compose.oracle.yml +24 -0
  135. openretailscience-0.48.0/tests/integration/docker/docker-compose.sqlserver.yml +23 -0
  136. openretailscience-0.48.0/tests/integration/test_clv_stats.py +133 -0
  137. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/styles/test_chart_chrome.py +263 -48
  138. openretailscience-0.48.0/tests/plots/styles/test_graph_utils.py +427 -0
  139. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/styles/test_styling_helper.py +118 -25
  140. openretailscience-0.48.0/tests/plots/styles/test_trend.py +625 -0
  141. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_bar.py +34 -7
  142. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_broken_timeline.py +33 -22
  143. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_index.py +47 -1
  144. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_line.py +15 -2
  145. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_price.py +15 -0
  146. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_scatter.py +29 -0
  147. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_waterfall.py +36 -1
  148. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/segmentation/test_nlr.py +22 -23
  149. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/segmentation/test_rfm.py +119 -74
  150. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/segmentation/test_segstats.py +374 -439
  151. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/segmentation/test_threshold.py +1 -1
  152. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/test_options.py +34 -13
  153. openretailscience-0.48.0/tests/test_skills.py +685 -0
  154. {openretailscience-0.46.0 → openretailscience-0.48.0}/uv.lock +1777 -1744
  155. openretailscience-0.46.0/.claude/commands/review-pr-tests.md +0 -607
  156. openretailscience-0.46.0/.env_sample +0 -9
  157. openretailscience-0.46.0/.github/workflows/bigquery-integration.yml +0 -67
  158. openretailscience-0.46.0/.github/workflows/gh-page.yml +0 -45
  159. openretailscience-0.46.0/.github/workflows/pyspark-integration.yml +0 -63
  160. openretailscience-0.46.0/.github/workflows/release.yml +0 -220
  161. openretailscience-0.46.0/.github/workflows/slack-notifications.yml +0 -103
  162. openretailscience-0.46.0/.github/workflows/test.yml +0 -75
  163. openretailscience-0.46.0/docs/api/metrics/distribution.md +0 -5
  164. openretailscience-0.46.0/docs/gallery/index.md +0 -212
  165. openretailscience-0.46.0/docs/gallery/plots/customer_decision_hierarchy.ipynb +0 -228
  166. openretailscience-0.46.0/openretailscience/__init__.py +0 -3
  167. openretailscience-0.46.0/openretailscience/analysis/customer.py +0 -237
  168. openretailscience-0.46.0/openretailscience/plots/styles/graph_utils.py +0 -923
  169. openretailscience-0.46.0/openretailscience/plugin.py +0 -238
  170. openretailscience-0.46.0/openretailscience/utils/validation.py +0 -45
  171. openretailscience-0.46.0/tests/analysis/test_customer.py +0 -203
  172. openretailscience-0.46.0/tests/integration/conftest.py +0 -47
  173. openretailscience-0.46.0/tests/plots/styles/test_graph_utils.py +0 -979
  174. openretailscience-0.46.0/tests/test_plugin.py +0 -367
  175. openretailscience-0.46.0/tests/utils/test_validation.py +0 -87
  176. {openretailscience-0.46.0 → openretailscience-0.48.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  177. {openretailscience-0.46.0 → openretailscience-0.48.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  178. {openretailscience-0.46.0 → openretailscience-0.48.0}/.markdownlint.json +0 -0
  179. {openretailscience-0.46.0 → openretailscience-0.48.0}/CNAME +0 -0
  180. {openretailscience-0.46.0 → openretailscience-0.48.0}/CODE_OF_CONDUCT.md +0 -0
  181. {openretailscience-0.46.0 → openretailscience-0.48.0}/LICENSE +0 -0
  182. {openretailscience-0.46.0 → openretailscience-0.48.0}/data/transactions.parquet +0 -0
  183. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/analysis/cohort.md +0 -0
  184. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/analysis/composite_rank.md +0 -0
  185. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/analysis/cross_shop.md +0 -0
  186. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/analysis/customer.md +0 -0
  187. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/analysis/customer_decision_hierarchy.md +0 -0
  188. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/analysis/gain_loss.md +0 -0
  189. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/analysis/haversine.md +0 -0
  190. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/analysis/product_association.md +0 -0
  191. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/analysis/revenue_tree.md +0 -0
  192. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/options.md +0 -0
  193. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/area.md +0 -0
  194. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/bar.md +0 -0
  195. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/broken_timeline.md +0 -0
  196. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/cohort.md +0 -0
  197. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/heatmap.md +0 -0
  198. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/histogram.md +0 -0
  199. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/index.md +0 -0
  200. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/line.md +0 -0
  201. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/period_on_period.md +0 -0
  202. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/price.md +0 -0
  203. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/scatter.md +0 -0
  204. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/styles/graph_utils.md +0 -0
  205. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/styles/styling_helpers.md +0 -0
  206. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/time.md +0 -0
  207. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/venn.md +0 -0
  208. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/plots/waterfall.md +0 -0
  209. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/segmentation/hml.md +0 -0
  210. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/segmentation/nlr.md +0 -0
  211. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/segmentation/rfm.md +0 -0
  212. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/segmentation/segstats.md +0 -0
  213. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/segmentation/threshold.md +0 -0
  214. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/utils/date.md +0 -0
  215. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/utils/filter_and_label.md +0 -0
  216. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/api/utils/label.md +0 -0
  217. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/analysis_modules/cohort.svg +0 -0
  218. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/analysis_modules/cross_shop.svg +0 -0
  219. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/analysis_modules/customer_decision_hierarchy.svg +0 -0
  220. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/analysis_modules/days_between_purchases.svg +0 -0
  221. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/analysis_modules/gain_loss.svg +0 -0
  222. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/analysis_modules/hml_segmentation.svg +0 -0
  223. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/analysis_modules/purchases_per_customer.svg +0 -0
  224. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/analysis_modules/revenue_tree.svg +0 -0
  225. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/analysis_modules/threshold_segmentation.svg +0 -0
  226. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/analysis_modules/transaction_churn.svg +0 -0
  227. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/features/charts.png +0 -0
  228. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/features/cross_shop.png +0 -0
  229. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/area_thumbnail.png +0 -0
  230. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/bar_thumbnail.png +0 -0
  231. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/broken_timeline_thumbnail.png +0 -0
  232. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/cohort_thumbnail.png +0 -0
  233. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/customer_decision_hierarchy_thumbnail.png +0 -0
  234. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/heatmap_thumbnail.png +0 -0
  235. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/histogram_thumbnail.png +0 -0
  236. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/index_thumbnail.png +0 -0
  237. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/line_thumbnail.png +0 -0
  238. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/period_on_period_thumbnail.png +0 -0
  239. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/price_thumbnail.png +0 -0
  240. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/revenue_tree_thumbnail.png +0 -0
  241. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/scatter_thumbnail.png +0 -0
  242. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/time_thumbnail.png +0 -0
  243. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/venn_thumbnail.png +0 -0
  244. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/gallery/waterfall_thumbnail.png +0 -0
  245. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/assets/images/logo.svg +0 -0
  246. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/examples/cross_shop.ipynb +0 -0
  247. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/examples/gain_loss.ipynb +0 -0
  248. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/examples/product_association.ipynb +0 -0
  249. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/examples/revenue_tree.ipynb +0 -0
  250. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/examples/segmentation.ipynb +0 -0
  251. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/area.ipynb +0 -0
  252. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/bar.ipynb +0 -0
  253. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/broken_timeline.ipynb +0 -0
  254. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/cohort.ipynb +0 -0
  255. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/heatmap.ipynb +0 -0
  256. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/histogram.ipynb +0 -0
  257. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/index_plot.ipynb +0 -0
  258. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/line.ipynb +0 -0
  259. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/period_on_period.ipynb +0 -0
  260. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/price.ipynb +0 -0
  261. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/revenue_tree.ipynb +0 -0
  262. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/scatter.ipynb +0 -0
  263. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/time.ipynb +0 -0
  264. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/gallery/plots/venn.ipynb +0 -0
  265. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/getting_started/installation.md +0 -0
  266. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/getting_started/styling_guide.ipynb +0 -0
  267. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/index.md +0 -0
  268. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/javascripts/mathjax.js +0 -0
  269. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/overrides/main.html +0 -0
  270. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/robots.txt +0 -0
  271. {openretailscience-0.46.0 → openretailscience-0.48.0}/docs/scripts/README.md +0 -0
  272. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/analysis/__init__.py +0 -0
  273. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/analysis/haversine.py +0 -0
  274. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/assets/fonts/Poppins-Bold.ttf +0 -0
  275. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/assets/fonts/Poppins-LightItalic.ttf +0 -0
  276. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/assets/fonts/Poppins-Medium.ttf +0 -0
  277. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/assets/fonts/Poppins-Regular.ttf +0 -0
  278. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/assets/fonts/Poppins-SemiBold.ttf +0 -0
  279. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/constants.py +0 -0
  280. {openretailscience-0.46.0/openretailscience → openretailscience-0.48.0/openretailscience/experimental}/metrics/__init__.py +0 -0
  281. {openretailscience-0.46.0/openretailscience → openretailscience-0.48.0/openretailscience/experimental}/metrics/base.py +0 -0
  282. {openretailscience-0.46.0/openretailscience → openretailscience-0.48.0/openretailscience/experimental}/metrics/distribution/__init__.py +0 -0
  283. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/__init__.py +0 -0
  284. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/area.py +0 -0
  285. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/cohort.py +0 -0
  286. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/heatmap.py +0 -0
  287. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/histogram.py +0 -0
  288. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/period_on_period.py +0 -0
  289. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/styles/__init__.py +0 -0
  290. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/styles/colors.py +0 -0
  291. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/styles/font_utils.py +0 -0
  292. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/time.py +0 -0
  293. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/tree_diagram.py +0 -0
  294. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/plots/venn.py +0 -0
  295. /openretailscience-0.46.0/openretailscience/segmentation/__init__.py → /openretailscience-0.48.0/openretailscience/py.typed +0 -0
  296. {openretailscience-0.46.0/openretailscience/utils → openretailscience-0.48.0/openretailscience/segmentation}/__init__.py +0 -0
  297. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/segmentation/hml.py +0 -0
  298. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/utils/date.py +0 -0
  299. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/utils/filter_and_label.py +0 -0
  300. {openretailscience-0.46.0 → openretailscience-0.48.0}/openretailscience/utils/label.py +0 -0
  301. {openretailscience-0.46.0 → openretailscience-0.48.0}/readme_assets/cross_shop_chart.png +0 -0
  302. {openretailscience-0.46.0 → openretailscience-0.48.0}/readme_assets/days_until_next_purchase.png +0 -0
  303. {openretailscience-0.46.0 → openretailscience-0.48.0}/readme_assets/gain_loss_chart.png +0 -0
  304. {openretailscience-0.46.0 → openretailscience-0.48.0}/readme_assets/logo.png +0 -0
  305. {openretailscience-0.46.0 → openretailscience-0.48.0}/readme_assets/logo.svg +0 -0
  306. {openretailscience-0.46.0 → openretailscience-0.48.0}/readme_assets/new_store_cannibalization.png +0 -0
  307. {openretailscience-0.46.0 → openretailscience-0.48.0}/taplo.toml +0 -0
  308. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/analysis/test_haversine.py +0 -0
  309. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/analysis/test_product_association.py +0 -0
  310. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_cohort_analysis.py +0 -0
  311. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_composite_rank.py +0 -0
  312. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_cross_shop.py +0 -0
  313. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_customer_decision_hierarchy.py +0 -0
  314. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_date.py +0 -0
  315. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_filter_and_label.py +0 -0
  316. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_haversine.py +0 -0
  317. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_hml_segmentation.py +0 -0
  318. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_product_association.py +0 -0
  319. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_revenue_tree.py +0 -0
  320. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_rfm_segmentation.py +0 -0
  321. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_segstats_segmentation.py +0 -0
  322. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/integration/test_threshold_segmentation.py +0 -0
  323. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/styles/test_color.py +0 -0
  324. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/styles/test_font_utils.py +0 -0
  325. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_area.py +0 -0
  326. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_cohort.py +0 -0
  327. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_heatmap.py +0 -0
  328. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_histogram.py +0 -0
  329. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_period_on_period.py +0 -0
  330. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_styling_options_integration.py +0 -0
  331. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_time.py +0 -0
  332. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_tree_diagram.py +0 -0
  333. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/plots/test_venn.py +0 -0
  334. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/segmentation/test_hml.py +0 -0
  335. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/toml_files/corrupt.toml +0 -0
  336. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/toml_files/invalid_option.toml +0 -0
  337. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/toml_files/test_options.toml +0 -0
  338. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/toml_files/valid.toml +0 -0
  339. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/utils/test_date.py +0 -0
  340. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/utils/test_filter_and_label.py +0 -0
  341. {openretailscience-0.46.0 → openretailscience-0.48.0}/tests/utils/test_label.py +0 -0
  342. {openretailscience-0.46.0 → openretailscience-0.48.0}/tox.ini +0 -0
@@ -0,0 +1,9 @@
1
+ GCP_PROJECT_ID=
2
+
3
+ # Snowflake CI Configuration (for integration tests)
4
+ SNOWFLAKE_CI_ACCOUNT=
5
+ SNOWFLAKE_CI_USER=
6
+ SNOWFLAKE_CI_WAREHOUSE=
7
+ SNOWFLAKE_CI_DATABASE=
8
+ SNOWFLAKE_CI_SCHEMA=
9
+ SNOWFLAKE_CI_PRIVATE_KEY_PATH=
@@ -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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
23
+ with:
24
+ persist-credentials: false
25
+ - name: Setup Python
26
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
27
+ with:
28
+ python-version: "3.11"
29
+
30
+ - name: Install uv
31
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
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
@@ -18,6 +18,9 @@ jobs:
18
18
  # github.event.pull_request.user.login == 'new-developer' ||
19
19
  # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
20
20
 
21
+ # Skip automated dependency PRs: Dependabot runs without access to secrets and
22
+ # claude-code-action refuses to run for bot actors, so the job can only ever fail.
23
+ if: github.actor != 'dependabot[bot]'
21
24
  runs-on: ubuntu-latest
22
25
  permissions:
23
26
  contents: read
@@ -27,13 +30,14 @@ jobs:
27
30
 
28
31
  steps:
29
32
  - name: Checkout repository
30
- uses: actions/checkout@v4
33
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
31
34
  with:
32
35
  fetch-depth: 1
36
+ persist-credentials: false
33
37
 
34
38
  - name: Run Claude Code Review
35
39
  id: claude-review
36
- uses: anthropics/claude-code-action@v1
40
+ uses: anthropics/claude-code-action@b76a0776ae74036e77cd11018083743453d7ad35 # v1.0.179
37
41
  with:
38
42
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39
43
  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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
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@b76a0776ae74036e77cd11018083743453d7ad35 # v1.0.179
96
97
  with:
97
98
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
98
99
 
@@ -22,23 +22,25 @@ jobs:
22
22
 
23
23
  steps:
24
24
  - name: Checkout repository
25
- uses: actions/checkout@v4
26
-
25
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
+ with:
27
+ persist-credentials: false
27
28
  - name: Setup Python
28
- uses: actions/setup-python@v5
29
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.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@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
35
+ with:
36
+ # Disabled: a shared cache feeding the preview deploy is a poisoning vector.
37
+ enable-cache: "false"
36
38
 
37
39
  - name: Install Dependencies
38
- run: uv sync
40
+ run: uv sync --locked
39
41
 
40
42
  - name: Build docs
41
- run: uv run mkdocs build
43
+ run: uv run mkdocs build --strict
42
44
 
43
45
  - name: Verify build output
44
46
  run: |
@@ -50,18 +52,16 @@ jobs:
50
52
 
51
53
  - name: Publish to Cloudflare Pages
52
54
  id: cloudflare-deploy
53
- uses: cloudflare/pages-action@v1
55
+ uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
54
56
  with:
55
57
  apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
56
58
  accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
57
- projectName: openretailscience-docs
58
- directory: site
59
59
  gitHubToken: ${{ secrets.GITHUB_TOKEN }}
60
- branch: ${{ github.head_ref || github.ref_name }}
60
+ command: pages deploy site --project-name=openretailscience-docs --branch=${{ github.head_ref || github.ref_name }}
61
61
 
62
62
  - name: Find existing comment
63
63
  if: github.event_name == 'pull_request'
64
- uses: peter-evans/find-comment@v3
64
+ uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
65
65
  id: find-comment
66
66
  with:
67
67
  issue-number: ${{ github.event.pull_request.number }}
@@ -70,7 +70,7 @@ jobs:
70
70
 
71
71
  - name: Create or update deployment comment
72
72
  if: github.event_name == 'pull_request'
73
- uses: peter-evans/create-or-update-comment@v4
73
+ uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
74
74
  with:
75
75
  comment-id: ${{ steps.find-comment.outputs.comment-id }}
76
76
  issue-number: ${{ github.event.pull_request.number }}
@@ -80,15 +80,15 @@ jobs:
80
80
 
81
81
  ✅ Preview deployed successfully!
82
82
 
83
- **Preview URL:** ${{ steps.cloudflare-deploy.outputs.url }}
84
- **Deployment ID:** ${{ steps.cloudflare-deploy.outputs.id }}
83
+ **Preview URL:** ${{ steps.cloudflare-deploy.outputs.deployment-url }}
84
+ **Deployment ID:** ${{ steps.cloudflare-deploy.outputs.pages-deployment-id }}
85
85
 
86
86
  ---
87
87
  *This preview will be updated automatically when you push new changes to this PR.*
88
88
 
89
89
  - name: Comment on deployment failure
90
90
  if: failure() && github.event_name == 'pull_request'
91
- uses: actions/github-script@v7
91
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
92
92
  with:
93
93
  script: |
94
94
  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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24
+ with:
25
+ persist-credentials: false
26
+ - name: Setup Python
27
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
28
+ with:
29
+ python-version: "3.11"
30
+ - name: Install uv
31
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
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,46 @@
1
+ name: Oracle 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 Oracle Integration Tests
13
+ runs-on: ubuntu-latest
14
+ timeout-minutes: 30
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
18
+ with:
19
+ persist-credentials: false
20
+ - name: Setup Python
21
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
22
+ with:
23
+ python-version: "3.11"
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
27
+ with:
28
+ enable-cache: true
29
+
30
+ - name: Install Dependencies
31
+ run: |
32
+ uv sync --locked
33
+
34
+ # Oracle 23ai Free (the supported LTS); the Compose file pins the gvenzl image.
35
+ - name: Start Oracle
36
+ run: |
37
+ docker compose -f tests/integration/docker/docker-compose.oracle.yml up -d --wait --wait-timeout 600
38
+
39
+ - name: Run Integration Tests
40
+ run: |
41
+ uv run pytest tests/integration -k "oracle" -v
42
+
43
+ - name: Stop Oracle
44
+ if: always()
45
+ run: |
46
+ docker compose -f tests/integration/docker/docker-compose.oracle.yml down -v
@@ -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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
18
+ with:
19
+ persist-credentials: false
20
+ - name: Setup Python
21
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
22
+ with:
23
+ python-version: "3.11"
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
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
@@ -0,0 +1,206 @@
1
+ name: PyPi Package Deploy
2
+ on:
3
+ workflow_dispatch:
4
+ inputs:
5
+ release:
6
+ type: choice
7
+ description: Select Release Type
8
+ default: "minor"
9
+ options:
10
+ - major
11
+ - minor
12
+ - patch
13
+ permissions:
14
+ contents: read
15
+ concurrency:
16
+ group: "release"
17
+ cancel-in-progress: false
18
+ jobs:
19
+ pre-commit:
20
+ name: Pre-Commit Checks
21
+ runs-on: ubuntu-latest
22
+ timeout-minutes: 10
23
+ steps:
24
+ - name: Checkout
25
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
+ with:
27
+ persist-credentials: false
28
+ - name: Setup Python
29
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
30
+ with:
31
+ python-version: "3.11"
32
+ - name: Install uv
33
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
34
+ with:
35
+ enable-cache: true
36
+ - name: Install dependencies
37
+ run: uv sync --locked
38
+ - name: Run Pre-commit
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
47
+
48
+ bigquery-integration-tests:
49
+ name: BigQuery Integration Tests
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 }}
54
+
55
+ pyspark-integration-tests:
56
+ name: PySpark Integration Tests
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 }}
70
+
71
+ sqlserver-integration-tests:
72
+ name: SQL Server Integration Tests
73
+ uses: ./.github/workflows/sqlserver-integration.yml
74
+
75
+ oracle-integration-tests:
76
+ name: Oracle Integration Tests
77
+ uses: ./.github/workflows/oracle-integration.yml
78
+
79
+ multi-python-tests:
80
+ name: Multi-Python Version Tests
81
+ runs-on: ubuntu-latest
82
+ timeout-minutes: 20
83
+ strategy:
84
+ fail-fast: false
85
+ matrix:
86
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
87
+ steps:
88
+ - name: Checkout code
89
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
90
+ with:
91
+ persist-credentials: false
92
+ - name: Set up Python ${{ matrix.python-version }}
93
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
94
+ with:
95
+ python-version: ${{ matrix.python-version }}
96
+ - name: Install uv
97
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
98
+ with:
99
+ enable-cache: true
100
+ - name: Install dependencies
101
+ run: |
102
+ uv sync --locked
103
+ - name: Run tests (excluding integration)
104
+ run: |
105
+ uv run pytest tests/ --ignore=tests/integration --tb=short -v
106
+
107
+ build:
108
+ name: Build
109
+ runs-on: ubuntu-latest
110
+ timeout-minutes: 15
111
+ needs: [security-audit, pre-commit, bigquery-integration-tests, pyspark-integration-tests, snowflake-integration-tests, sqlserver-integration-tests, oracle-integration-tests, multi-python-tests]
112
+ permissions:
113
+ contents: write
114
+ id-token: write
115
+ attestations: write
116
+ env:
117
+ CI_COMMIT_MESSAGE: Continuous Integration Version Bump ${{ inputs.release }}
118
+ steps:
119
+ # zizmor: ignore[artipacked] this job pushes the version-bump commit and
120
+ # tag, so it requires the GITHUB_TOKEN persisted by actions/checkout. Only
121
+ # dist/ is uploaded as an artifact, not .git/, so the token is not exposed.
122
+ - name: Checkout # zizmor: ignore[artipacked]
123
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
124
+ - name: Setup Python
125
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
126
+ with:
127
+ python-version: "3.11"
128
+ - name: Install uv
129
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
130
+ with:
131
+ enable-cache: true
132
+ - name: Install Dependencies
133
+ run: uv sync --locked --group dev
134
+ - name: Bump Version
135
+ run: |
136
+ uv run python - <<EOF
137
+ from tomlkit import parse, dumps
138
+ from tomlkit.toml_file import TOMLFile
139
+ file = TOMLFile("pyproject.toml")
140
+ doc = file.read()
141
+ version = doc["project"]["version"]
142
+ release = "${{ inputs.release }}"
143
+ major, minor, patch = map(int, version.split('.'))
144
+ if release == "major":
145
+ new_version = f"{major + 1}.0.0"
146
+ elif release == "minor":
147
+ new_version = f"{major}.{minor + 1}.0"
148
+ elif release == "patch":
149
+ new_version = f"{major}.{minor}.{patch + 1}"
150
+ doc["project"]["version"] = new_version
151
+ file.write(doc)
152
+ print(f"New Version: {new_version}")
153
+ EOF
154
+ echo "RELEASE_TAG=$(uv run python -c 'from tomlkit.toml_file import TOMLFile; print(TOMLFile("pyproject.toml").read()["project"]["version"])')" >> $GITHUB_ENV
155
+ - name: Build package
156
+ run: uv build
157
+ - name: Attest build provenance
158
+ uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
159
+ with:
160
+ subject-path: "dist/*"
161
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
162
+ with:
163
+ name: build
164
+ path: dist/
165
+ retention-days: 1
166
+ - name: GIT commit and tag
167
+ run: |
168
+ git config user.name 'github-actions[bot]'
169
+ git config user.email 'github-actions[bot]@users.noreply.github.com'
170
+ git add pyproject.toml
171
+ git commit -m "$CI_COMMIT_MESSAGE"
172
+ git push
173
+ echo "Tagging new version: $RELEASE_TAG"
174
+ git tag -a "$RELEASE_TAG" -m "Auto-generated tag"
175
+ git push origin "$RELEASE_TAG"
176
+ - name: Create release
177
+ env:
178
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
179
+ run: gh release create "$RELEASE_TAG" --generate-notes
180
+ pypi-publish:
181
+ name: Upload release to PyPI
182
+ runs-on: ubuntu-latest
183
+ timeout-minutes: 10
184
+ needs: build
185
+ environment:
186
+ name: production
187
+ url: "https://pypi.org/project/openretailscience"
188
+ permissions:
189
+ id-token: write
190
+ steps:
191
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
192
+ with:
193
+ name: build
194
+ path: dist
195
+ - name: Publish package distributions to PyPI
196
+ uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
197
+ with:
198
+ attestations: true
199
+ deploy-docs:
200
+ name: Deploy Documentation
201
+ needs: pypi-publish
202
+ permissions:
203
+ contents: read
204
+ pages: write
205
+ id-token: write
206
+ uses: ./.github/workflows/gh-page.yml
@@ -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 manual
16
+ # dispatches and release calls are hard gates. Detection of newly-disclosed CVEs
17
+ # against already-pinned dependencies (with no repo change) is left to Dependabot
18
+ # alerts and security updates, so no scheduled scan is needed here.
19
+
20
+ on:
21
+ pull_request:
22
+ types: [opened, synchronize]
23
+ paths:
24
+ - "uv.lock"
25
+ - "pyproject.toml"
26
+ - "osv-scanner.toml"
27
+ - ".github/workflows/security-audit.yml"
28
+ push:
29
+ branches: [main]
30
+ paths:
31
+ - "uv.lock"
32
+ - "pyproject.toml"
33
+ - "osv-scanner.toml"
34
+ - ".github/workflows/security-audit.yml"
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
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@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.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