churnkit 1.2.8a6__tar.gz → 1.2.8a8__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 (504) hide show
  1. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/PKG-INFO +1 -1
  2. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/accounts_view.py +48 -2
  3. churnkit-1.2.8a8/apps/databricks_app/src/auth.py +71 -0
  4. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/data.py +166 -0
  5. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/pyproject.toml +1 -1
  6. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/__init__.py +1 -1
  7. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/databricks_renderer.py +43 -21
  8. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/.gitignore +0 -0
  9. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/LICENSE +0 -0
  10. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/README.md +0 -0
  11. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/__init__.py +0 -0
  12. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/archetype_view.py +0 -0
  13. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/config.py +0 -0
  14. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/customer_profile.py +0 -0
  15. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/default_profile.css +0 -0
  16. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/default_profile.html +0 -0
  17. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/masthead.py +0 -0
  18. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/state.py +0 -0
  19. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/template.py +0 -0
  20. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/theme.css +0 -0
  21. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/apps/databricks_app/src/treemap.py +0 -0
  22. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/causal_notebooks/c01_publish_definitions.ipynb +0 -0
  23. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/causal_notebooks/c02_archetype_derivation.ipynb +0 -0
  24. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/causal_notebooks/c03_approval_gate.ipynb +0 -0
  25. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/causal_notebooks/c04_batch_inference.ipynb +0 -0
  26. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/causal_notebooks/c05_snapshot_and_dashboard.ipynb +0 -0
  27. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/constraints/.gitkeep +0 -0
  28. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/-1_sample_datasets.ipynb +0 -0
  29. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/00_start_here.ipynb +0 -0
  30. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/01_data_discovery.ipynb +0 -0
  31. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/01a_a_temporal_text_deep_dive.ipynb +0 -0
  32. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/01a_temporal_deep_dive.ipynb +0 -0
  33. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/01b_temporal_quality.ipynb +0 -0
  34. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/01c_temporal_patterns.ipynb +0 -0
  35. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/01d_event_aggregation.ipynb +0 -0
  36. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/02_source_integrity.ipynb +0 -0
  37. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/03_dataset_merge.ipynb +0 -0
  38. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/04_column_deep_dive.ipynb +0 -0
  39. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/04a_text_columns_deep_dive.ipynb +0 -0
  40. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/05_relationship_analysis.ipynb +0 -0
  41. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/06_feature_opportunities.ipynb +0 -0
  42. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/07_modeling_readiness.ipynb +0 -0
  43. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/08_baseline_experiments.ipynb +0 -0
  44. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/09_business_alignment.ipynb +0 -0
  45. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/10_spec_generation.ipynb +0 -0
  46. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/11_scoring_validation.ipynb +0 -0
  47. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/exploration_notebooks/12_view_documentation.ipynb +0 -0
  48. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/.claude/settings.local.json +0 -0
  49. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/README.md +0 -0
  50. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/build_framework_phase_map.py +0 -0
  51. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/data/create_snapshot.py +0 -0
  52. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/data/generate_edi_ticketing_dataset.py +0 -0
  53. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/data/generate_retail_dataset.py +0 -0
  54. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/data/generate_test_data.py +0 -0
  55. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/data/migrate_parquet_to_delta.py +0 -0
  56. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/data/migrate_to_temporal.py +0 -0
  57. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/databricks/build_wheel.sh +0 -0
  58. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/databricks/capture_runtime.py +0 -0
  59. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/databricks/dbr_init.sh +0 -0
  60. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/databricks/deploy_dev.py +0 -0
  61. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/databricks/generate_constraints.py +0 -0
  62. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/databricks/notebook_setup.py +0 -0
  63. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/experiments/capture_notebook_outputs.py +0 -0
  64. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/experiments/cell_profiling.py +0 -0
  65. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/experiments/compare_exploration_runs.py +0 -0
  66. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/experiments/compare_versions.sh +0 -0
  67. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/experiments/patch_dataset_config.py +0 -0
  68. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/generate_requirements.py +0 -0
  69. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/build_causal_notebooks.py +0 -0
  70. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/clean_notebook_outputs.py +0 -0
  71. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/export_tutorial_html.py +0 -0
  72. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/init_project.py +0 -0
  73. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/migrate_notebook_cell_ids.py +0 -0
  74. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/plotly_image_preprocessor.py +0 -0
  75. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/run_exploration.py +0 -0
  76. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/sync_notebooks.py +0 -0
  77. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/tag_framework_cells.py +0 -0
  78. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/tag_markdown_cells.py +0 -0
  79. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/notebooks/test_notebooks.py +0 -0
  80. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/release.sh +0 -0
  81. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/seed_playbooks_volume.py +0 -0
  82. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/templates/tutorial_html/conf.json +0 -0
  83. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/templates/tutorial_html/index.html.j2 +0 -0
  84. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/scripts/update_notebook_paths.py +0 -0
  85. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/__init__.py +0 -0
  86. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/__init__.py +0 -0
  87. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/active_dataset_store.py +0 -0
  88. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/analysis_context.py +0 -0
  89. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/column_describer.py +0 -0
  90. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/dataset_fingerprinter.py +0 -0
  91. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/entity_timestamp_deriver.py +0 -0
  92. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/exploration_manager.py +0 -0
  93. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/explorer.py +0 -0
  94. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/field_availability_audit.py +0 -0
  95. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/findings.py +0 -0
  96. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/intent_defaults.py +0 -0
  97. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/key_resolver.py +0 -0
  98. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/layered_recommendations.py +0 -0
  99. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/objective_support_communicator.py +0 -0
  100. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/prediction_objective_detector.py +0 -0
  101. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/project_context.py +0 -0
  102. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/recommendation_builder.py +0 -0
  103. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/recommendations.py +0 -0
  104. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/run_namespace.py +0 -0
  105. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/sampling.py +0 -0
  106. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/schema_report.py +0 -0
  107. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/service_unit_detector.py +0 -0
  108. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/session.py +0 -0
  109. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/skip_logic.py +0 -0
  110. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/auto_explorer/snapshot_grid.py +0 -0
  111. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/business/__init__.py +0 -0
  112. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/business/ab_test_designer.py +0 -0
  113. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/business/fairness_analyzer.py +0 -0
  114. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/business/intervention_matcher.py +0 -0
  115. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/business/ratio_features.py +0 -0
  116. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/business/report_generator.py +0 -0
  117. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/business/risk_profile.py +0 -0
  118. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/business/roi_analyzer.py +0 -0
  119. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/__init__.py +0 -0
  120. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/calibration_analyzer.py +0 -0
  121. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/cv_analyzer.py +0 -0
  122. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/error_analyzer.py +0 -0
  123. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/exploration_ledger.py +0 -0
  124. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/feature_provenance.py +0 -0
  125. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/feature_stability.py +0 -0
  126. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/leakage_detector.py +0 -0
  127. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/model_diagnostics_report.py +0 -0
  128. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/noise_tester.py +0 -0
  129. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/overfitting_analyzer.py +0 -0
  130. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/parity_report.py +0 -0
  131. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/diagnostics/segment_analyzer.py +0 -0
  132. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/discovery/__init__.py +0 -0
  133. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/discovery/config_generator.py +0 -0
  134. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/discovery/discovery_flow.py +0 -0
  135. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/discovery/type_inferencer.py +0 -0
  136. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/interpretability/__init__.py +0 -0
  137. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/interpretability/cohort_analyzer.py +0 -0
  138. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/interpretability/counterfactual.py +0 -0
  139. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/interpretability/individual_explainer.py +0 -0
  140. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/interpretability/pdp_generator.py +0 -0
  141. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/interpretability/shap_explainer.py +0 -0
  142. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/jupyter_save_hook.py +0 -0
  143. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/notebook_html_exporter.py +0 -0
  144. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/notebook_progress.py +0 -0
  145. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/plotly_preprocessor.py +0 -0
  146. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/__init__.py +0 -0
  147. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/base.py +0 -0
  148. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/cleaning/__init__.py +0 -0
  149. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/cleaning/consistency.py +0 -0
  150. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/cleaning/deduplicate.py +0 -0
  151. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/cleaning/impute.py +0 -0
  152. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/cleaning/outlier.py +0 -0
  153. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/datetime/__init__.py +0 -0
  154. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/datetime/extract.py +0 -0
  155. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/encoding/__init__.py +0 -0
  156. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/encoding/categorical.py +0 -0
  157. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/pipeline.py +0 -0
  158. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/registry.py +0 -0
  159. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/selection/__init__.py +0 -0
  160. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/selection/drop_column.py +0 -0
  161. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/transform/__init__.py +0 -0
  162. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/transform/power.py +0 -0
  163. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/recommendations/transform/scale.py +0 -0
  164. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/visualization/__init__.py +0 -0
  165. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/visualization/attention_scorer.py +0 -0
  166. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/visualization/chart_builder.py +0 -0
  167. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/visualization/column_paginator.py +0 -0
  168. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/visualization/console.py +0 -0
  169. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/visualization/display.py +0 -0
  170. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/analysis/visualization/number_formatter.py +0 -0
  171. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/artifacts/__init__.py +0 -0
  172. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/artifacts/fit_artifact_registry.py +0 -0
  173. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/cli.py +0 -0
  174. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/__init__.py +0 -0
  175. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/compat/__init__.py +0 -0
  176. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/compat/bulk_profiling.py +0 -0
  177. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/compat/cell_profiling_hooks.py +0 -0
  178. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/compat/detection.py +0 -0
  179. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/compat/ops.py +0 -0
  180. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/compat/pandas_backend.py +0 -0
  181. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/compat/remote_path.py +0 -0
  182. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/compat/spark_backend.py +0 -0
  183. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/compat/timing.py +0 -0
  184. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/__init__.py +0 -0
  185. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/base.py +0 -0
  186. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/components/__init__.py +0 -0
  187. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/components/deployer.py +0 -0
  188. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/components/explainer.py +0 -0
  189. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/components/feature_eng.py +0 -0
  190. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/components/ingester.py +0 -0
  191. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/components/profiler.py +0 -0
  192. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/components/trainer.py +0 -0
  193. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/components/transformer.py +0 -0
  194. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/components/validator.py +0 -0
  195. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/enums.py +0 -0
  196. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/orchestrator.py +0 -0
  197. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/components/registry.py +0 -0
  198. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/config/__init__.py +0 -0
  199. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/config/column_config.py +0 -0
  200. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/config/experiments.py +0 -0
  201. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/config/pipeline_config.py +0 -0
  202. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/config/source_config.py +0 -0
  203. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/naming.py +0 -0
  204. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/utils/__init__.py +0 -0
  205. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/utils/leakage.py +0 -0
  206. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/utils/severity.py +0 -0
  207. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/core/utils/statistics.py +0 -0
  208. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/__init__.py +0 -0
  209. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/__init__.py +0 -0
  210. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/base.py +0 -0
  211. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/cell_builder.py +0 -0
  212. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/config.py +0 -0
  213. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/databricks_generator.py +0 -0
  214. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/local_generator.py +0 -0
  215. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/project_init.py +0 -0
  216. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/runner.py +0 -0
  217. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/scoring_replay.py +0 -0
  218. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/script_generator.py +0 -0
  219. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/__init__.py +0 -0
  220. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/base_stage.py +0 -0
  221. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/causal_setup_cell.py +0 -0
  222. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s01_ingestion.py +0 -0
  223. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s02_profiling.py +0 -0
  224. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s03_cleaning.py +0 -0
  225. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s04_transformation.py +0 -0
  226. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s05_feature_engineering.py +0 -0
  227. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s06_feature_selection.py +0 -0
  228. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s07_model_training.py +0 -0
  229. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s08_deployment.py +0 -0
  230. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s09_monitoring.py +0 -0
  231. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s10_batch_inference.py +0 -0
  232. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s11_feature_store.py +0 -0
  233. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s_c01_publish_definitions.py +0 -0
  234. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s_c02_archetype_derivation.py +0 -0
  235. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s_c03_approval_gate.py +0 -0
  236. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s_c04_batch_inference.py +0 -0
  237. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_generator/stages/s_c05_snapshot_and_dashboard.py +0 -0
  238. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_merge/__init__.py +0 -0
  239. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_merge/cli.py +0 -0
  240. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_merge/config_merger.py +0 -0
  241. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_merge/config_parser.py +0 -0
  242. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_merge/conflict.py +0 -0
  243. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_merge/merge_engine.py +0 -0
  244. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_merge/merge_report.py +0 -0
  245. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_sync/__init__.py +0 -0
  246. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_sync/cell_id_standardizer.py +0 -0
  247. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_sync/cell_types.py +0 -0
  248. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_sync/cli.py +0 -0
  249. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_sync/sync_engine.py +0 -0
  250. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/notebook_sync/sync_report.py +0 -0
  251. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/orchestration/__init__.py +0 -0
  252. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/orchestration/code_generator.py +0 -0
  253. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/orchestration/context.py +0 -0
  254. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/orchestration/data_materializer.py +0 -0
  255. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/orchestration/databricks_exporter.py +0 -0
  256. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/orchestration/doc_generator.py +0 -0
  257. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/__init__.py +0 -0
  258. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/databricks_generator.py +0 -0
  259. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/exploration_generator.py +0 -0
  260. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/findings_parser.py +0 -0
  261. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/generation_manifest.py +0 -0
  262. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/generator.py +0 -0
  263. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/gold_transform_applicator.py +0 -0
  264. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/llm_docs_generator.py +0 -0
  265. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/models.py +0 -0
  266. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/override_merge.py +0 -0
  267. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/protocols.py +0 -0
  268. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/renderer.py +0 -0
  269. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/pipeline_generator/user_extensions_emitter.py +0 -0
  270. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/spec_generator/__init__.py +0 -0
  271. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/generators/spec_generator/mlflow_pipeline_generator.py +0 -0
  272. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/__init__.py +0 -0
  273. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/__init__.py +0 -0
  274. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/base.py +0 -0
  275. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/factory.py +0 -0
  276. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/feature_store/__init__.py +0 -0
  277. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/feature_store/base.py +0 -0
  278. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/feature_store/databricks.py +0 -0
  279. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/feature_store/feast_adapter.py +0 -0
  280. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/feature_store/local.py +0 -0
  281. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/mlflow/__init__.py +0 -0
  282. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/mlflow/base.py +0 -0
  283. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/mlflow/databricks.py +0 -0
  284. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/mlflow/experiment_tracker.py +0 -0
  285. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/mlflow/local.py +0 -0
  286. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/storage/__init__.py +0 -0
  287. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/storage/base.py +0 -0
  288. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/storage/databricks.py +0 -0
  289. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/adapters/storage/local.py +0 -0
  290. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/databricks_init.py +0 -0
  291. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/databricks_job_capture.py +0 -0
  292. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/feature_store/__init__.py +0 -0
  293. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/feature_store/definitions.py +0 -0
  294. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/feature_store/manager.py +0 -0
  295. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/feature_store/registry.py +0 -0
  296. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/iteration/__init__.py +0 -0
  297. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/iteration/context.py +0 -0
  298. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/iteration/feedback_collector.py +0 -0
  299. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/iteration/orchestrator.py +0 -0
  300. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/iteration/recommendation_tracker.py +0 -0
  301. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/iteration/signals.py +0 -0
  302. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/llm_context/__init__.py +0 -0
  303. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/llm_context/context_builder.py +0 -0
  304. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/llm_context/prompts.py +0 -0
  305. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/requirements_generator.py +0 -0
  306. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/streaming/__init__.py +0 -0
  307. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/streaming/batch_integration.py +0 -0
  308. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/streaming/early_warning_model.py +0 -0
  309. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/streaming/event_schema.py +0 -0
  310. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/streaming/online_store_writer.py +0 -0
  311. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/streaming/realtime_scorer.py +0 -0
  312. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/streaming/trigger_engine.py +0 -0
  313. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/integrations/streaming/window_aggregator.py +0 -0
  314. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/__init__.py +0 -0
  315. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/api.py +0 -0
  316. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/dataset_resolution.py +0 -0
  317. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/decorator.py +0 -0
  318. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/flags.py +0 -0
  319. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/harvest.py +0 -0
  320. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/logging.py +0 -0
  321. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/persistence.py +0 -0
  322. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/registry.py +0 -0
  323. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/replay.py +0 -0
  324. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/summary.py +0 -0
  325. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/runtime/validation.py +0 -0
  326. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/__init__.py +0 -0
  327. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/__init__.py +0 -0
  328. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/approval_gate.py +0 -0
  329. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/clusterer.py +0 -0
  330. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/column_descriptions_writer.py +0 -0
  331. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/dashboard_profile_override.py +0 -0
  332. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/dashboard_views.py +0 -0
  333. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/delta_writer.py +0 -0
  334. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/derivation.py +0 -0
  335. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/feature_meta_writer.py +0 -0
  336. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/__init__.py +0 -0
  337. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/archetype_context.py +0 -0
  338. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/business_phrase.py +0 -0
  339. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/discovery.py +0 -0
  340. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/enrichment_pipeline.py +0 -0
  341. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/feature_meta_builder.py +0 -0
  342. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/llm_prompt.py +0 -0
  343. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/markdown_bootstrap.py +0 -0
  344. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/predicate_prose.py +0 -0
  345. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/prose_backfill.py +0 -0
  346. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/quantile_phrasing.py +0 -0
  347. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/interpretation/sidecars.py +0 -0
  348. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/llm_namer.py +0 -0
  349. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/playbook_loader.py +0 -0
  350. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/playbook_mapper.py +0 -0
  351. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/policy_loader.py +0 -0
  352. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/population_stats.py +0 -0
  353. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/predicate_compiler.py +0 -0
  354. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/rule_extractor.py +0 -0
  355. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/run_context_writer.py +0 -0
  356. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/schemas.py +0 -0
  357. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/seed_yamls/decision_policy.yaml +0 -0
  358. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/seed_yamls/response_schemas.yaml +0 -0
  359. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/seed_yamls/vocabularies.yaml +0 -0
  360. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/shap_runner.py +0 -0
  361. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/snapshot_writer.py +0 -0
  362. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/sql/dashboard_views.sql +0 -0
  363. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/causal/top_drivers_writer.py +0 -0
  364. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/cleaning/__init__.py +0 -0
  365. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/cleaning/base.py +0 -0
  366. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/cleaning/missing_handler.py +0 -0
  367. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/cleaning/outlier_handler.py +0 -0
  368. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/deployment/__init__.py +0 -0
  369. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/deployment/batch_scorer.py +0 -0
  370. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/deployment/champion_challenger.py +0 -0
  371. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/deployment/model_registry.py +0 -0
  372. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/deployment/retraining_trigger.py +0 -0
  373. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/features/__init__.py +0 -0
  374. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/features/behavioral_features.py +0 -0
  375. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/features/customer_segmentation.py +0 -0
  376. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/features/feature_definitions.py +0 -0
  377. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/features/feature_engineer.py +0 -0
  378. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/features/feature_manifest.py +0 -0
  379. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/features/feature_selector.py +0 -0
  380. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/features/interaction_features.py +0 -0
  381. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/features/temporal_features.py +0 -0
  382. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/ingestion/__init__.py +0 -0
  383. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/ingestion/load_result.py +0 -0
  384. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/ingestion/loaders.py +0 -0
  385. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/ingestion/source_registry.py +0 -0
  386. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/lifecycle/__init__.py +0 -0
  387. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/lifecycle/config.py +0 -0
  388. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/lifecycle/enrich.py +0 -0
  389. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/lifecycle/validation.py +0 -0
  390. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/__init__.py +0 -0
  391. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/baseline_trainer.py +0 -0
  392. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/cross_validator.py +0 -0
  393. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/data_splitter.py +0 -0
  394. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/feature_profile.py +0 -0
  395. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/feature_scaler.py +0 -0
  396. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/feature_spec.py +0 -0
  397. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/hyperparameter_tuner.py +0 -0
  398. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/imbalance_handler.py +0 -0
  399. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/mlflow_logger.py +0 -0
  400. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/model_comparator.py +0 -0
  401. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/model_evaluator.py +0 -0
  402. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/shap_attribution.py +0 -0
  403. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/spark_baseline_trainer.py +0 -0
  404. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/spark_classifier_wrapper.py +0 -0
  405. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/spark_feature_scaler.py +0 -0
  406. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/threshold_optimizer.py +0 -0
  407. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/modeling/training_preparator.py +0 -0
  408. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/monitoring/__init__.py +0 -0
  409. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/monitoring/alert_manager.py +0 -0
  410. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/monitoring/drift_detector.py +0 -0
  411. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/monitoring/performance_monitor.py +0 -0
  412. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/preprocessing/__init__.py +0 -0
  413. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/preprocessing/transformer_manager.py +0 -0
  414. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/__init__.py +0 -0
  415. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/categorical_distribution.py +0 -0
  416. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/categorical_target_analyzer.py +0 -0
  417. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/column_profiler.py +0 -0
  418. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/distribution_analysis.py +0 -0
  419. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/drift_detector.py +0 -0
  420. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/feature_capacity.py +0 -0
  421. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/pattern_analysis_config.py +0 -0
  422. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/profile_result.py +0 -0
  423. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/quality_checks.py +0 -0
  424. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/recommendation_filter.py +0 -0
  425. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/relationship_detector.py +0 -0
  426. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/relationship_recommender.py +0 -0
  427. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/report_generator.py +0 -0
  428. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/scd_analyzer.py +0 -0
  429. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/segment_analyzer.py +0 -0
  430. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/segment_aware_outlier.py +0 -0
  431. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/spark_segment_analyzer.py +0 -0
  432. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/spark_temporal_feature_analyzer.py +0 -0
  433. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/spark_temporal_feature_engineer.py +0 -0
  434. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/spark_time_window_aggregator.py +0 -0
  435. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/stats_helpers.py +0 -0
  436. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/target_level_analyzer.py +0 -0
  437. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/target_validator.py +0 -0
  438. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/temporal_analyzer.py +0 -0
  439. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/temporal_coverage.py +0 -0
  440. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/temporal_feature_analyzer.py +0 -0
  441. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/temporal_feature_engineer.py +0 -0
  442. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/temporal_pattern_analyzer.py +0 -0
  443. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/temporal_quality_checks.py +0 -0
  444. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/temporal_target_analyzer.py +0 -0
  445. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/text_embedder.py +0 -0
  446. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/text_processor.py +0 -0
  447. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/text_reducer.py +0 -0
  448. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/time_series_profiler.py +0 -0
  449. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/time_window_aggregator.py +0 -0
  450. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/type_detector.py +0 -0
  451. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/profiling/window_recommendation.py +0 -0
  452. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scd_history/__init__.py +0 -0
  453. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scd_history/augment.py +0 -0
  454. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scd_history/config.py +0 -0
  455. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scd_history/reconstruct.py +0 -0
  456. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scd_history/validation.py +0 -0
  457. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scoring/__init__.py +0 -0
  458. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scoring/batch_inference.py +0 -0
  459. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scoring/config.py +0 -0
  460. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scoring/data_loader.py +0 -0
  461. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scoring/exceptions.py +0 -0
  462. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/scoring/pipeline_discovery.py +0 -0
  463. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/__init__.py +0 -0
  464. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/access_guard.py +0 -0
  465. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/data_preparer.py +0 -0
  466. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/point_in_time_join.py +0 -0
  467. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/point_in_time_registry.py +0 -0
  468. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/scenario_detector.py +0 -0
  469. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/snapshot_manager.py +0 -0
  470. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/spark_temporal_merger.py +0 -0
  471. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/synthetic_coordinator.py +0 -0
  472. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/temporal_merger.py +0 -0
  473. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/timestamp_discovery.py +0 -0
  474. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/temporal/timestamp_manager.py +0 -0
  475. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/transformation/__init__.py +0 -0
  476. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/transformation/binary_handler.py +0 -0
  477. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/transformation/categorical_encoder.py +0 -0
  478. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/transformation/datetime_transformer.py +0 -0
  479. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/transformation/numeric_transformer.py +0 -0
  480. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/transformation/pipeline.py +0 -0
  481. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/__init__.py +0 -0
  482. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/adversarial_scoring_validator.py +0 -0
  483. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/business_sense_gate.py +0 -0
  484. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/data_quality_gate.py +0 -0
  485. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/data_validators.py +0 -0
  486. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/feature_quality_gate.py +0 -0
  487. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/gates.py +0 -0
  488. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/leakage_gate.py +0 -0
  489. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/model_validity_gate.py +0 -0
  490. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/pipeline_validation_runner.py +0 -0
  491. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/quality_scorer.py +0 -0
  492. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/rule_generator.py +0 -0
  493. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/scoring_pipeline_validator.py +0 -0
  494. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/stages/validation/timeseries_detector.py +0 -0
  495. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/transforms/__init__.py +0 -0
  496. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/transforms/artifact_store.py +0 -0
  497. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/transforms/executor.py +0 -0
  498. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/transforms/fitted.py +0 -0
  499. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/transforms/ops.py +0 -0
  500. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/src/customer_retention/transforms/spark_ops.py +0 -0
  501. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/tests/exploration_notebooks/test_distributed_dtype_safety.py +0 -0
  502. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/tests/exploration_notebooks/test_nb10_run_pipeline_ordering.py +0 -0
  503. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/tests/scripts/__init__.py +0 -0
  504. {churnkit-1.2.8a6 → churnkit-1.2.8a8}/tests/scripts/test_build_framework_phase_map.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: churnkit
3
- Version: 1.2.8a6
3
+ Version: 1.2.8a8
4
4
  Summary: Structured ML framework for customer churn prediction -- from exploration notebooks to production pipelines, locally or on Databricks.
5
5
  Project-URL: Homepage, https://github.com/aladjov/CR
6
6
  Project-URL: Documentation, https://github.com/aladjov/CR/wiki
@@ -6,9 +6,10 @@ available — click a header to resort.
6
6
  """
7
7
  from __future__ import annotations
8
8
 
9
+ import pandas as pd
9
10
  import streamlit as st
10
11
 
11
- from . import data, state
12
+ from . import auth, data, state
12
13
 
13
14
  _TIER_GLYPH = {"Low": "● ", "Medium": "● ", "High": "● "} # dot prefix, coloured via column_config? Streamlit doesn't style per-row — use unicode dots.
14
15
 
@@ -20,6 +21,25 @@ def _tier_label(tier) -> str:
20
21
  return f"{glyph} {tier}"
21
22
 
22
23
 
24
+ def _assignment_label(assigned_to, me: str | None) -> str:
25
+ """Render the ``assigned_to`` cell.
26
+
27
+ ``Mine`` is reserved for the current user so a CSM can scan their own
28
+ column at a glance; everyone else surfaces the local-part of their
29
+ email (``jane.doe`` from ``jane.doe@churnkit.com``) for compactness.
30
+ Returns an empty string for unassigned rows so the column visually
31
+ recedes when most rows are blank.
32
+ """
33
+ if assigned_to is None or (isinstance(assigned_to, float) and pd.isna(assigned_to)):
34
+ return ""
35
+ text = str(assigned_to).strip()
36
+ if not text:
37
+ return ""
38
+ if me and text.lower() == me.lower():
39
+ return "Mine"
40
+ return text.split("@", 1)[0] if "@" in text else text
41
+
42
+
23
43
  def render() -> None:
24
44
  pb = state.get("selected_playbook")
25
45
  ar = state.get("selected_archetype")
@@ -29,13 +49,34 @@ def render() -> None:
29
49
  st.info("No accounts in this scope.")
30
50
  return
31
51
 
52
+ # Left-merge the cached assignments frame so the In-scope table can
53
+ # surface "Mine" / owner handle / blank without modifying the upstream
54
+ # ``v_eligible_all_playbooks`` view. Empty assignments DataFrame leaves
55
+ # every row unassigned.
56
+ me = auth.current_user_email()
57
+ try:
58
+ assigns = data.assignments()
59
+ except Exception:
60
+ assigns = pd.DataFrame(columns=["entity_id", "assigned_to"])
61
+ if assigns is None or assigns.empty:
62
+ df = df.assign(assigned_to=None)
63
+ else:
64
+ df = df.merge(
65
+ assigns[["entity_id", "assigned_to"]],
66
+ on="entity_id",
67
+ how="left",
68
+ )
69
+
32
70
  display = df[[
33
71
  "entity_id", "churn_probability", "expected_loss", "value_at_risk",
34
72
  "risk_tier", "archetype_name",
35
73
  "policy_rank_among_eligible", "eligible_playbook_count",
36
- "recommended", "is_holdout",
74
+ "recommended", "is_holdout", "assigned_to",
37
75
  ]].copy()
38
76
  display["risk_tier"] = display["risk_tier"].map(_tier_label)
77
+ display["assigned_to"] = display["assigned_to"].map(
78
+ lambda v: _assignment_label(v, me)
79
+ )
39
80
 
40
81
  display = display.rename(columns={
41
82
  "entity_id": "Entity",
@@ -48,6 +89,7 @@ def render() -> None:
48
89
  "eligible_playbook_count": "Plays",
49
90
  "recommended": "Rec.",
50
91
  "is_holdout": "Holdout",
92
+ "assigned_to": "Assigned to",
51
93
  })
52
94
 
53
95
  col_config = {
@@ -61,6 +103,10 @@ def render() -> None:
61
103
  "Plays": st.column_config.NumberColumn(format="%d", width="small"),
62
104
  "Rec.": st.column_config.CheckboxColumn(width="small"),
63
105
  "Holdout": st.column_config.CheckboxColumn(width="small"),
106
+ "Assigned to": st.column_config.TextColumn(
107
+ width="small",
108
+ help="CSM who has self-assigned this account. 'Mine' = you.",
109
+ ),
64
110
  }
65
111
 
66
112
  event = st.dataframe(
@@ -0,0 +1,71 @@
1
+ """Identity helpers for the Streamlit app.
2
+
3
+ Databricks Apps inject the OAuth-authenticated user's identity into every
4
+ inbound request as ``X-Forwarded-*`` headers. Streamlit exposes those via
5
+ ``st.context.headers`` (>=1.37). The notebook-side ``get_current_username``
6
+ in ``customer_retention.analysis.auto_explorer.session`` is NOT reusable
7
+ here -- it reads ``DATABRICKS_USERNAME`` (set only inside notebook tasks)
8
+ and falls back to ``getpass.getuser`` (the App service principal, identical
9
+ for everyone). The App needs request-scoped resolution.
10
+
11
+ Resolution order for ``current_user_email``:
12
+ 1. ``X-Forwarded-Email`` request header (production)
13
+ 2. ``st.experimental_user.email`` (Streamlit-native fallback)
14
+ 3. ``CR_USER`` env var (local dev / tests)
15
+ 4. ``None`` (caller decides whether to block)
16
+
17
+ ``current_user_handle`` returns the local-part of the email (``jane.doe``
18
+ from ``jane.doe@churnkit.com``) for compact display in tables and button
19
+ labels. Falls back to ``getpass.getuser()`` when no email is resolvable so
20
+ local dev still works without setting any env vars.
21
+
22
+ Streamlit is imported lazily so this module loads cleanly in the test
23
+ venv (which intentionally omits the live-app dependencies).
24
+ """
25
+ from __future__ import annotations
26
+
27
+ import getpass
28
+ import os
29
+
30
+
31
+ def _from_request_header() -> str | None:
32
+ try:
33
+ import streamlit as st # noqa: PLC0415 - lazy so the module imports without streamlit
34
+ email = st.context.headers.get("X-Forwarded-Email")
35
+ except Exception:
36
+ return None
37
+ return email.strip().lower() if email else None
38
+
39
+
40
+ def _from_streamlit_user() -> str | None:
41
+ try:
42
+ import streamlit as st # noqa: PLC0415
43
+ email = getattr(st.experimental_user, "email", None)
44
+ except Exception:
45
+ return None
46
+ return email.strip().lower() if email else None
47
+
48
+
49
+ def _from_env() -> str | None:
50
+ raw = os.environ.get("CR_USER")
51
+ return raw.strip().lower() if raw else None
52
+
53
+
54
+ def current_user_email() -> str | None:
55
+ """Return the logged-in user's email, or ``None`` when not resolvable."""
56
+ return _from_request_header() or _from_streamlit_user() or _from_env()
57
+
58
+
59
+ def current_user_handle() -> str:
60
+ """Short display handle for the logged-in user.
61
+
62
+ ``jane.doe@churnkit.com`` -> ``jane.doe``. Falls back to the OS username
63
+ when no email is available so local dev without ``CR_USER`` still
64
+ renders something deterministic.
65
+ """
66
+ email = current_user_email()
67
+ if email and "@" in email:
68
+ return email.split("@", 1)[0]
69
+ if email:
70
+ return email
71
+ return getpass.getuser()
@@ -320,3 +320,169 @@ def run_history() -> pd.DataFrame:
320
320
  ORDER BY as_of_date DESC
321
321
  LIMIT 20
322
322
  """)
323
+
324
+
325
+ # ---------------------------------------------------------------------------
326
+ # Self-assignment — ``account_assignments`` Delta table
327
+ # ---------------------------------------------------------------------------
328
+ # A CSM clicks "Assign to me" on the L4 customer profile to claim ownership
329
+ # of an account. One row per assigned entity: no row means unassigned. The
330
+ # table is additive (no view modification, no schema migration) and lazy-
331
+ # created on first read or write so the dashboard can drop into existing
332
+ # deployments without an out-of-band DDL step.
333
+ #
334
+ # Race semantics: two CSMs clicking the same unassigned entity within the
335
+ # same second land on a Delta MERGE with ``WHEN NOT MATCHED THEN INSERT``,
336
+ # so exactly one INSERT wins and the loser's next rerun reads the winner's
337
+ # row and renders "Claimed by …". Unassign is restricted to the current
338
+ # owner -- you cannot clear someone else's claim from the UI.
339
+ _ASSIGNMENTS_TABLE = "account_assignments"
340
+
341
+ _TOGGLE_ASSIGNED = "assigned"
342
+ _TOGGLE_UNASSIGNED = "unassigned"
343
+ _TOGGLE_CLAIMED_BY_OTHER = "claimed_by_other"
344
+
345
+
346
+ def _assignments_fqn(cfg: AppConfig) -> str:
347
+ return f"{cfg.fqn_prefix}.{_ASSIGNMENTS_TABLE}"
348
+
349
+
350
+ def _ensure_assignments_table(cfg: AppConfig) -> None:
351
+ """Idempotent ``CREATE TABLE IF NOT EXISTS`` for the assignments table.
352
+
353
+ Runs once per process — the Streamlit session keeps a flag so we don't
354
+ fire DDL on every rerun. Safe to call concurrently: ``CREATE TABLE IF
355
+ NOT EXISTS`` is a no-op on a pre-existing table.
356
+ """
357
+ flag_key = f"_assignments_table_ready::{cfg.fqn_prefix}"
358
+ try:
359
+ if st.session_state.get(flag_key):
360
+ return
361
+ except Exception:
362
+ pass
363
+ with _connect(cfg) as conn:
364
+ cur = conn.cursor()
365
+ try:
366
+ cur.execute(f"""
367
+ CREATE TABLE IF NOT EXISTS {_assignments_fqn(cfg)} (
368
+ entity_id STRING NOT NULL,
369
+ assigned_to STRING NOT NULL,
370
+ assigned_at TIMESTAMP NOT NULL
371
+ ) USING DELTA
372
+ """)
373
+ finally:
374
+ cur.close()
375
+ try:
376
+ st.session_state[flag_key] = True
377
+ except Exception:
378
+ pass
379
+
380
+
381
+ @st.cache_data(ttl=10, show_spinner=False)
382
+ def assignments() -> pd.DataFrame:
383
+ """Return all current account assignments.
384
+
385
+ Two columns: ``entity_id`` and ``assigned_to`` (the owner's email).
386
+ Cached for 10s so the In-scope table stays fresh without hammering the
387
+ warehouse on every rerun. Bust the cache via ``data.assignments.clear()``
388
+ after a toggle so the UI reflects the change immediately.
389
+
390
+ Returns an empty DataFrame on the first call before any rows exist —
391
+ callers can safely left-merge against it.
392
+ """
393
+ cfg = load_config()
394
+ _ensure_assignments_table(cfg)
395
+ return _query(
396
+ cfg,
397
+ f"SELECT entity_id, assigned_to FROM {_assignments_fqn(cfg)}",
398
+ )
399
+
400
+
401
+ def toggle_assignment(entity_id: str, user_email: str) -> str:
402
+ """Self-assign or unassign ``entity_id`` for ``user_email``.
403
+
404
+ Returns one of:
405
+ - ``"assigned"`` — row was inserted (entity now owned by user)
406
+ - ``"unassigned"`` — row was deleted (user released their own claim)
407
+ - ``"claimed_by_other"`` — entity is owned by someone else; no DML
408
+
409
+ The MERGE-on-INSERT path protects against the read-then-write race
410
+ where another CSM claimed the entity between our SELECT and our
411
+ INSERT: ``WHEN NOT MATCHED`` collapses to a no-op and we surface
412
+ ``claimed_by_other`` on the next rerun via the cached read.
413
+ """
414
+ if not entity_id or not user_email:
415
+ raise ValueError("entity_id and user_email are required")
416
+ cfg = load_config()
417
+ _ensure_assignments_table(cfg)
418
+ fqn = _assignments_fqn(cfg)
419
+ user = user_email.strip().lower()
420
+
421
+ with _connect(cfg) as conn:
422
+ cur = conn.cursor()
423
+ try:
424
+ cur.execute(
425
+ f"SELECT assigned_to FROM {fqn} WHERE entity_id = :eid LIMIT 1",
426
+ {"eid": entity_id},
427
+ )
428
+ rows = cur.fetchall()
429
+ current = rows[0][0] if rows else None
430
+
431
+ if current is not None and current.strip().lower() != user:
432
+ return _TOGGLE_CLAIMED_BY_OTHER
433
+
434
+ if current is not None:
435
+ cur.execute(
436
+ f"DELETE FROM {fqn} WHERE entity_id = :eid AND assigned_to = :user",
437
+ {"eid": entity_id, "user": user},
438
+ )
439
+ return _TOGGLE_UNASSIGNED
440
+
441
+ # No prior row -- MERGE so a concurrent claim by another CSM
442
+ # cannot produce a duplicate row. The source row carries the
443
+ # candidate (entity, user, now()); the target only inserts when
444
+ # ``entity_id`` is still absent.
445
+ cur.execute(
446
+ f"""
447
+ MERGE INTO {fqn} t
448
+ USING (
449
+ SELECT :eid AS entity_id, :user AS assigned_to, current_timestamp() AS assigned_at
450
+ ) s
451
+ ON t.entity_id = s.entity_id
452
+ WHEN NOT MATCHED THEN INSERT (entity_id, assigned_to, assigned_at)
453
+ VALUES (s.entity_id, s.assigned_to, s.assigned_at)
454
+ """,
455
+ {"eid": entity_id, "user": user},
456
+ )
457
+
458
+ cur.execute(
459
+ f"SELECT assigned_to FROM {fqn} WHERE entity_id = :eid LIMIT 1",
460
+ {"eid": entity_id},
461
+ )
462
+ verify = cur.fetchall()
463
+ winner = verify[0][0] if verify else None
464
+ if winner is None:
465
+ return _TOGGLE_CLAIMED_BY_OTHER
466
+ if winner.strip().lower() == user:
467
+ return _TOGGLE_ASSIGNED
468
+ return _TOGGLE_CLAIMED_BY_OTHER
469
+ finally:
470
+ cur.close()
471
+
472
+
473
+ def assignment_for(entity_id: str) -> str | None:
474
+ """Return the current owner email for ``entity_id``, or ``None``.
475
+
476
+ Reads through the cached ``assignments()`` frame so the L4 button can
477
+ label itself ("Assign to me" / "Unassign" / "Claimed") without firing
478
+ its own SQL on every rerun.
479
+ """
480
+ if not entity_id:
481
+ return None
482
+ df = assignments()
483
+ if df is None or df.empty or "entity_id" not in df.columns:
484
+ return None
485
+ hit = df[df["entity_id"].astype(str) == str(entity_id)]
486
+ if hit.empty:
487
+ return None
488
+ return str(hit.iloc[0]["assigned_to"])
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "churnkit"
3
- version = "1.02.8a6"
3
+ version = "1.02.8a8"
4
4
  description = "Structured ML framework for customer churn prediction -- from exploration notebooks to production pipelines, locally or on Databricks."
5
5
  readme = "README.md"
6
6
  license = {text = "Apache-2.0"}
@@ -17,7 +17,7 @@ Main module categories:
17
17
  llm_context, iteration)
18
18
  """
19
19
 
20
- __version__ = "1.02.8a6"
20
+ __version__ = "1.02.8a8"
21
21
 
22
22
  # Environment utilities (always available)
23
23
  from .core.compat import (
@@ -30,10 +30,12 @@ def render_spark_step_call(step: TransformationStep) -> str:
30
30
 
31
31
 
32
32
  def _impute_null(col, p):
33
+ # Guard against recommendations that target a column not in this stage's
34
+ # input — same rationale as the per-column transforms below.
33
35
  value = p.get("value", 0)
34
36
  if isinstance(value, str):
35
- return f'df.fillna("{value}", subset=["{col}"])'
36
- return f'df.fillna({value}, subset=["{col}"])'
37
+ return f'(df.fillna("{value}", subset=["{col}"]) if "{col}" in df.columns else df)'
38
+ return f'(df.fillna({value}, subset=["{col}"]) if "{col}" in df.columns else df)'
37
39
 
38
40
 
39
41
  def _cap_outlier(col, p):
@@ -100,8 +102,9 @@ def _derived_ratio(col, p):
100
102
  num = p.get("numerator", "")
101
103
  den = p.get("denominator", "")
102
104
  return (
103
- f'df.withColumn("{col}", '
104
- f'F.col("{num}") / F.when(F.col("{den}") != 0, F.col("{den}")).otherwise(F.lit(None)))'
105
+ f'(df.withColumn("{col}", '
106
+ f'F.col("{num}") / F.when(F.col("{den}") != 0, F.col("{den}")).otherwise(F.lit(None))) '
107
+ f'if all(c in df.columns for c in ["{num}", "{den}"]) else df)'
105
108
  )
106
109
 
107
110
 
@@ -109,7 +112,10 @@ def _derived_interaction(col, p):
109
112
  features = p.get("features", [])
110
113
  col_a = features[0] if len(features) > 0 else p.get("col_a", "")
111
114
  col_b = features[1] if len(features) > 1 else p.get("col_b", "")
112
- return f'df.withColumn("{col}", F.col("{col_a}") * F.col("{col_b}"))'
115
+ return (
116
+ f'(df.withColumn("{col}", F.col("{col_a}") * F.col("{col_b}")) '
117
+ f'if all(c in df.columns for c in ["{col_a}", "{col_b}"]) else df)'
118
+ )
113
119
 
114
120
 
115
121
  def _derived_composite(col, p):
@@ -117,23 +123,31 @@ def _derived_composite(col, p):
117
123
  if not columns:
118
124
  raise ValueError(f"Composite derived column '{col}' requires non-empty 'columns' parameter")
119
125
  expr_parts = " + ".join(f'F.col("{c}")' for c in columns)
120
- return f'df.withColumn("{col}", ({expr_parts}) / {len(columns)})'
126
+ cols_repr = "[" + ", ".join(f'"{c}"' for c in columns) + "]"
127
+ return (
128
+ f'(df.withColumn("{col}", ({expr_parts}) / {len(columns)}) '
129
+ f'if all(c in df.columns for c in {cols_repr}) else df)'
130
+ )
121
131
 
122
132
 
123
133
  def _segment_aware_cap(col, p):
124
134
  n_segments = p.get("n_segments", 2)
125
- return f'_segment_aware_cap(df, "{col}", n_segments={n_segments})'
135
+ return (
136
+ f'(_segment_aware_cap(df, "{col}", n_segments={n_segments}) '
137
+ f'if "{col}" in df.columns else df)'
138
+ )
126
139
 
127
140
 
128
141
  def _zero_inflation_handling(col, _p):
129
142
  return (
130
- f'df.withColumn("{col}_is_zero", F.when(F.col("{col}") == 0, 1).otherwise(0))'
131
- f'.withColumn("{col}_log", F.when(F.col("{col}") > 0, F.log1p(F.col("{col}"))).otherwise(0))'
143
+ f'(df.withColumn("{col}_is_zero", F.when(F.col("{col}") == 0, 1).otherwise(0))'
144
+ f'.withColumn("{col}_log", F.when(F.col("{col}") > 0, F.log1p(F.col("{col}"))).otherwise(0)) '
145
+ f'if "{col}" in df.columns else df)'
132
146
  )
133
147
 
134
148
 
135
149
  def _cap_then_log(col, _p):
136
- return f'_cap_then_log(df, "{col}")'
150
+ return f'(_cap_then_log(df, "{col}") if "{col}" in df.columns else df)'
137
151
 
138
152
 
139
153
  def _type_cast(col, p):
@@ -208,10 +222,11 @@ def _derived_recency(col, p):
208
222
  return f'df # silver_derived recency {col!r}: source unresolvable'
209
223
  src = sources[0]
210
224
  return (
211
- f'df.withColumn("{col}", '
225
+ f'(df.withColumn("{col}", '
212
226
  f'F.when(F.col("{src}").isNotNull() & F.col("as_of_date").isNotNull(), '
213
227
  f'F.datediff(F.col("as_of_date"), F.to_date(F.col("{src}"))).cast("double"))'
214
- f'.otherwise(F.lit(None)))'
228
+ f'.otherwise(F.lit(None))) '
229
+ f'if "{src}" in df.columns else df)'
215
230
  )
216
231
 
217
232
 
@@ -221,10 +236,11 @@ def _derived_duration(col, p):
221
236
  return f'df # silver_derived duration {col!r}: source unresolvable'
222
237
  a, b = sources[0], sources[1]
223
238
  return (
224
- f'df.withColumn("{col}", '
239
+ f'(df.withColumn("{col}", '
225
240
  f'F.when(F.col("{a}").isNotNull() & F.col("{b}").isNotNull(), '
226
241
  f'F.datediff(F.to_date(F.col("{b}")), F.to_date(F.col("{a}"))).cast("double"))'
227
- f'.otherwise(F.lit(None)))'
242
+ f'.otherwise(F.lit(None))) '
243
+ f'if all(c in df.columns for c in ["{a}", "{b}"]) else df)'
228
244
  )
229
245
 
230
246
 
@@ -243,12 +259,13 @@ def _derived_cyclical(col, p):
243
259
  return f'df # silver_derived cyclical {col!r}: source unresolvable'
244
260
  src = sources[0]
245
261
  return (
246
- f'df.withColumn("{src}_month_sin", '
262
+ f'(df.withColumn("{src}_month_sin", '
247
263
  f'F.when(F.col("{src}").isNotNull(), '
248
264
  f'F.sin(2 * 3.141592653589793 * F.month(F.col("{src}")) / 12)).otherwise(F.lit(None)))'
249
265
  f'.withColumn("{src}_month_cos", '
250
266
  f'F.when(F.col("{src}").isNotNull(), '
251
- f'F.cos(2 * 3.141592653589793 * F.month(F.col("{src}")) / 12)).otherwise(F.lit(None)))'
267
+ f'F.cos(2 * 3.141592653589793 * F.month(F.col("{src}")) / 12)).otherwise(F.lit(None))) '
268
+ f'if "{src}" in df.columns else df)'
252
269
  )
253
270
 
254
271
 
@@ -258,10 +275,11 @@ def _derived_tenure(col, p):
258
275
  return f'df # silver_derived tenure {col!r}: source unresolvable'
259
276
  src = sources[0]
260
277
  return (
261
- f'df.withColumn("{col}", '
278
+ f'(df.withColumn("{col}", '
262
279
  f'F.when(F.col("{src}").isNotNull() & F.col("as_of_date").isNotNull(), '
263
280
  f'(F.datediff(F.col("as_of_date"), F.to_date(F.col("{src}"))) / 365.25).cast("double"))'
264
- f'.otherwise(F.lit(None)))'
281
+ f'.otherwise(F.lit(None))) '
282
+ f'if "{src}" in df.columns else df)'
265
283
  )
266
284
 
267
285
 
@@ -276,12 +294,16 @@ def _derived_extraction(col, p):
276
294
  if not sources:
277
295
  return f'df # silver_derived extraction {col!r}: source unresolvable'
278
296
  src = sources[0]
297
+ # Three-way fall-through: passthrough if `{col}` is already present
298
+ # (landing emitted it), derive from `{src}` when present, otherwise no-op
299
+ # so a missing source from a phantom rec doesn't crash the stage.
279
300
  return (
280
- f'df if "{col}" in df.columns else '
281
- f'df.withColumn("{col}", '
301
+ f'(df if "{col}" in df.columns else '
302
+ f'(df.withColumn("{col}", '
282
303
  f'F.when(F.col("{src}").isNotNull(), '
283
304
  f'F.when(F.dayofweek(F.to_date(F.col("{src}"))).isin(1, 7), 1.0).otherwise(0.0))'
284
- f'.otherwise(F.lit(None)))'
305
+ f'.otherwise(F.lit(None))) '
306
+ f'if "{src}" in df.columns else df))'
285
307
  )
286
308
 
287
309
 
File without changes
File without changes
File without changes
File without changes