churnkit 1.3.2a0__tar.gz → 1.3.2a2__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.
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/PKG-INFO +1 -1
- churnkit-1.3.2a2/apps/databricks_app/src/archetypes_view.py +445 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/data.py +151 -0
- churnkit-1.3.2a2/apps/databricks_app/src/playbooks_view.py +346 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/theme.css +241 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/causal_notebooks/c01_publish_definitions.ipynb +118 -6
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/causal_notebooks/c02_archetype_derivation.ipynb +43 -10
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/causal_notebooks/c03_approval_gate.ipynb +104 -6
- churnkit-1.3.2a2/causal_notebooks/c04_batch_inference.ipynb +549 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/causal_notebooks/c05_snapshot_and_dashboard.ipynb +153 -13
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/pyproject.toml +1 -1
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/__init__.py +1 -1
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/playbook_loader.py +9 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/schemas.py +10 -0
- churnkit-1.3.2a0/causal_notebooks/c04_batch_inference.ipynb +0 -368
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/.gitignore +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/LICENSE +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/README.md +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/accounts_view.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/archetype_view.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/auth.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/config.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/customer_profile.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/default_profile.css +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/default_profile.html +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/masthead.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/state.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/template.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/apps/databricks_app/src/treemap.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/causal_notebooks/_diagnostic_scoring_probe.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/constraints/.gitkeep +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/-1_parity_contract.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/-1_sample_datasets.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/00_start_here.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/01_data_discovery.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/01a_a_temporal_text_deep_dive.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/01a_temporal_deep_dive.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/01b_temporal_quality.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/01c_temporal_patterns.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/01d_event_aggregation.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/02_source_integrity.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/03_dataset_merge.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/04_column_deep_dive.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/04a_text_columns_deep_dive.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/05_relationship_analysis.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/06_feature_opportunities.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/07_modeling_readiness.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/08_baseline_experiments.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/09_business_alignment.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/10_spec_generation.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/11_scoring_validation.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/exploration_notebooks/12_view_documentation.ipynb +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/.claude/settings.local.json +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/README.md +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/build_framework_phase_map.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/data/create_snapshot.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/data/generate_edi_ticketing_dataset.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/data/generate_retail_dataset.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/data/generate_test_data.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/data/migrate_parquet_to_delta.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/data/migrate_to_temporal.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/databricks/build_wheel.sh +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/databricks/capture_runtime.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/databricks/dbr_init.sh +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/databricks/deploy_dev.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/databricks/generate_constraints.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/databricks/notebook_setup.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/experiments/capture_notebook_outputs.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/experiments/cell_profiling.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/experiments/compare_exploration_runs.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/experiments/compare_versions.sh +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/experiments/patch_dataset_config.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/generate_requirements.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/build_causal_notebooks.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/clean_notebook_outputs.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/export_tutorial_html.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/init_project.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/migrate_notebook_cell_ids.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/plotly_image_preprocessor.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/run_exploration.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/sync_notebooks.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/tag_framework_cells.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/tag_markdown_cells.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/notebooks/test_notebooks.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/release.sh +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/seed_playbooks_volume.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/templates/tutorial_html/conf.json +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/templates/tutorial_html/index.html.j2 +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/scripts/update_notebook_paths.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/active_dataset_store.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/analysis_context.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/column_describer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/dataset_fingerprinter.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/entity_timestamp_deriver.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/exploration_manager.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/explorer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/field_availability_audit.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/findings.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/intent_defaults.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/key_resolver.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/layered_recommendations.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/objective_support_communicator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/prediction_objective_detector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/project_context.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/recommendation_builder.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/recommendations.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/run_namespace.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/sampling.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/schema_report.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/service_unit_detector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/session.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/skip_logic.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/auto_explorer/snapshot_grid.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/business/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/business/ab_test_designer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/business/fairness_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/business/intervention_matcher.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/business/ratio_features.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/business/report_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/business/risk_profile.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/business/roi_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/calibration_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/cv_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/error_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/exploration_ledger.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/feature_provenance.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/feature_stability.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/leakage_detector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/model_diagnostics_report.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/noise_tester.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/overfitting_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/parity_report.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/diagnostics/segment_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/discovery/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/discovery/config_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/discovery/discovery_flow.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/discovery/type_inferencer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/interpretability/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/interpretability/cohort_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/interpretability/counterfactual.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/interpretability/individual_explainer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/interpretability/pdp_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/interpretability/shap_explainer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/jupyter_save_hook.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/notebook_html_exporter.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/notebook_progress.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/plotly_preprocessor.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/base.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/cleaning/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/cleaning/consistency.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/cleaning/deduplicate.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/cleaning/impute.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/cleaning/outlier.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/datetime/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/datetime/extract.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/encoding/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/encoding/categorical.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/pipeline.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/registry.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/selection/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/selection/drop_column.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/transform/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/transform/power.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/recommendations/transform/scale.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/visualization/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/visualization/attention_scorer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/visualization/chart_builder.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/visualization/column_paginator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/visualization/console.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/visualization/display.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/analysis/visualization/number_formatter.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/artifacts/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/artifacts/fit_artifact_registry.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/cli.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/compat/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/compat/bulk_profiling.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/compat/cell_profiling_hooks.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/compat/detection.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/compat/ops.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/compat/pandas_backend.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/compat/remote_path.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/compat/spark_backend.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/compat/timing.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/base.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/components/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/components/deployer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/components/explainer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/components/feature_eng.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/components/ingester.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/components/profiler.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/components/trainer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/components/transformer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/components/validator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/enums.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/orchestrator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/components/registry.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/config/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/config/column_config.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/config/experiments.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/config/pipeline_config.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/config/source_config.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/naming.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/utils/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/utils/leakage.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/utils/severity.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/core/utils/statistics.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/base.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/cell_builder.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/config.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/databricks_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/local_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/project_init.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/runner.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/scoring_replay.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/script_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/base_stage.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/causal_setup_cell.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s01_ingestion.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s02_profiling.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s03_cleaning.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s04_transformation.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s05_feature_engineering.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s06_feature_selection.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s07_model_training.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s08_deployment.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s09_monitoring.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s10_batch_inference.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s11_feature_store.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s_c01_publish_definitions.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s_c02_archetype_derivation.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s_c03_approval_gate.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s_c04_batch_inference.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_generator/stages/s_c05_snapshot_and_dashboard.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_merge/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_merge/cli.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_merge/config_merger.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_merge/config_parser.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_merge/conflict.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_merge/merge_engine.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_merge/merge_report.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_sync/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_sync/cell_id_standardizer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_sync/cell_types.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_sync/cli.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_sync/sync_engine.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/notebook_sync/sync_report.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/orchestration/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/orchestration/code_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/orchestration/context.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/orchestration/data_materializer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/orchestration/databricks_exporter.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/orchestration/doc_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/databricks_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/databricks_renderer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/exploration_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/findings_parser.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/generation_manifest.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/gold_transform_applicator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/llm_docs_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/models.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/override_merge.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/protocols.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/renderer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/pipeline_generator/user_extensions_emitter.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/spec_generator/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/generators/spec_generator/mlflow_pipeline_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/base.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/factory.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/feature_store/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/feature_store/base.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/feature_store/databricks.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/feature_store/feast_adapter.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/feature_store/local.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/mlflow/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/mlflow/base.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/mlflow/databricks.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/mlflow/experiment_tracker.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/mlflow/local.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/storage/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/storage/base.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/storage/databricks.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/adapters/storage/local.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/databricks_init.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/databricks_job_capture.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/feature_store/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/feature_store/definitions.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/feature_store/manager.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/feature_store/registry.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/iteration/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/iteration/context.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/iteration/feedback_collector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/iteration/orchestrator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/iteration/recommendation_tracker.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/iteration/signals.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/llm_context/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/llm_context/context_builder.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/llm_context/prompts.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/requirements_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/streaming/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/streaming/batch_integration.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/streaming/early_warning_model.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/streaming/event_schema.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/streaming/online_store_writer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/streaming/realtime_scorer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/streaming/trigger_engine.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/integrations/streaming/window_aggregator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/parity/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/parity/audit.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/parity/decorator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/parity/exploration_scan.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/parity/gaps.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/parity/kinds.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/parity/manifest.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/parity/production_scan.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/parity/schedule.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/parity/trace.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/api.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/dataset_resolution.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/decorator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/flags.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/harvest.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/logging.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/persistence.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/registry.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/replay.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/summary.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/runtime/validation.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/approval_gate.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/clusterer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/column_descriptions_writer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/dashboard_profile_override.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/dashboard_views.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/delta_writer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/derivation.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/feature_meta_writer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/archetype_context.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/business_phrase.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/discovery.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/enrichment_pipeline.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/feature_meta_builder.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/llm_prompt.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/markdown_bootstrap.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/predicate_prose.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/prose_backfill.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/quantile_phrasing.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/interpretation/sidecars.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/llm_namer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/playbook_mapper.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/policy_loader.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/population_stats.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/predicate_compiler.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/rule_extractor.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/run_context_writer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/seed_yamls/decision_policy.yaml +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/seed_yamls/response_schemas.yaml +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/seed_yamls/vocabularies.yaml +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/shap_runner.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/snapshot_writer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/sql/dashboard_views.sql +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/causal/top_drivers_writer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/cleaning/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/cleaning/base.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/cleaning/missing_handler.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/cleaning/outlier_handler.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/deployment/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/deployment/batch_scorer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/deployment/champion_challenger.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/deployment/model_registry.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/deployment/retraining_trigger.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/features/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/features/behavioral_features.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/features/customer_segmentation.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/features/feature_definitions.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/features/feature_engineer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/features/feature_manifest.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/features/feature_selector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/features/interaction_features.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/features/temporal_features.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/ingestion/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/ingestion/load_result.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/ingestion/loaders.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/ingestion/source_registry.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/lifecycle/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/lifecycle/config.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/lifecycle/enrich.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/lifecycle/validation.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/baseline_trainer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/cross_validator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/data_splitter.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/feature_profile.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/feature_scaler.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/feature_spec.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/hyperparameter_tuner.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/imbalance_handler.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/mlflow_logger.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/model_comparator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/model_evaluator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/shap_attribution.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/spark_baseline_trainer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/spark_classifier_wrapper.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/spark_feature_scaler.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/threshold_optimizer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/modeling/training_preparator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/monitoring/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/monitoring/alert_manager.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/monitoring/drift_detector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/monitoring/performance_monitor.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/preprocessing/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/preprocessing/transformer_manager.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/case_collision.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/categorical_distribution.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/categorical_target_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/column_profiler.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/distribution_analysis.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/drift_detector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/feature_capacity.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/pattern_analysis_config.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/profile_result.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/quality_checks.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/recommendation_filter.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/relationship_detector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/relationship_recommender.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/report_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/scd_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/segment_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/segment_aware_outlier.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/spark_segment_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/spark_temporal_feature_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/spark_temporal_feature_engineer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/spark_time_window_aggregator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/stats_helpers.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/target_level_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/target_validator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/temporal_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/temporal_coverage.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/temporal_feature_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/temporal_feature_engineer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/temporal_pattern_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/temporal_quality_checks.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/temporal_target_analyzer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/text_embedder.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/text_processor.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/text_reducer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/time_series_profiler.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/time_window_aggregator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/type_detector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/profiling/window_recommendation.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scd_history/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scd_history/augment.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scd_history/config.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scd_history/reconstruct.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scd_history/validation.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scoring/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scoring/batch_inference.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scoring/config.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scoring/data_loader.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scoring/exceptions.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/scoring/pipeline_discovery.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/access_guard.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/data_preparer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/point_in_time_join.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/point_in_time_registry.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/scenario_detector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/snapshot_manager.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/spark_temporal_merger.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/synthetic_coordinator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/temporal_merger.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/timestamp_discovery.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/temporal/timestamp_manager.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/transformation/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/transformation/binary_handler.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/transformation/categorical_encoder.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/transformation/datetime_transformer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/transformation/numeric_transformer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/transformation/pipeline.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/adversarial_scoring_validator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/business_sense_gate.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/data_quality_gate.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/data_validators.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/feature_quality_gate.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/gates.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/leakage_gate.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/model_validity_gate.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/pipeline_validation_runner.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/quality_scorer.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/rule_generator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/scoring_pipeline_validator.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/stages/validation/timeseries_detector.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/transforms/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/transforms/artifact_store.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/transforms/executor.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/transforms/fitted.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/transforms/ops.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/src/customer_retention/transforms/spark_ops.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/tests/exploration_notebooks/test_distributed_dtype_safety.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/tests/exploration_notebooks/test_nb02_case_collision_detection.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/tests/exploration_notebooks/test_nb06_ratio_registration.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/tests/exploration_notebooks/test_nb08_categorical_baseline.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/tests/exploration_notebooks/test_nb08_early_drops.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/tests/exploration_notebooks/test_nb10_run_pipeline_ordering.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/tests/scripts/__init__.py +0 -0
- {churnkit-1.3.2a0 → churnkit-1.3.2a2}/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.3.
|
|
3
|
+
Version: 1.3.2a2
|
|
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
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
"""Archetypes tab — master table of model-derived archetypes + readonly detail.
|
|
2
|
+
|
|
3
|
+
Master row click reveals a complex profile: summary header (cluster size,
|
|
4
|
+
mean churn, derivation), prose blocks (description, rationale), then the
|
|
5
|
+
top SHAP drivers split into positive (risk-pushing) and negative
|
|
6
|
+
(protective) lists. Each driver shows the readable business phrase on
|
|
7
|
+
the surface; an Expander reveals the raw technical metadata (column name,
|
|
8
|
+
mean SHAP, mean value, p25/p50/p75 from feature_thresholds, source
|
|
9
|
+
dataset / window from feature provenance).
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from html import escape
|
|
14
|
+
from typing import Any, Iterable
|
|
15
|
+
|
|
16
|
+
import pandas as pd
|
|
17
|
+
import streamlit as st
|
|
18
|
+
|
|
19
|
+
from . import data
|
|
20
|
+
|
|
21
|
+
_STATE_KEY = "arch_detail_id"
|
|
22
|
+
|
|
23
|
+
_DIRECTION_POSITIVE = "positive"
|
|
24
|
+
_DIRECTION_NEGATIVE = "negative"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _fmt_pct(v: Any) -> str:
|
|
28
|
+
try:
|
|
29
|
+
return f"{float(v) * 100:.1f}%"
|
|
30
|
+
except (TypeError, ValueError):
|
|
31
|
+
return "—"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _fmt_int(v: Any) -> str:
|
|
35
|
+
try:
|
|
36
|
+
if pd.isna(v):
|
|
37
|
+
return "—"
|
|
38
|
+
except (TypeError, ValueError):
|
|
39
|
+
pass
|
|
40
|
+
try:
|
|
41
|
+
return f"{int(v):,}"
|
|
42
|
+
except (TypeError, ValueError):
|
|
43
|
+
return "—"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _fmt_float(v: Any, *, precision: int = 3) -> str:
|
|
47
|
+
if v is None:
|
|
48
|
+
return "—"
|
|
49
|
+
try:
|
|
50
|
+
if pd.isna(v):
|
|
51
|
+
return "—"
|
|
52
|
+
except (TypeError, ValueError):
|
|
53
|
+
pass
|
|
54
|
+
try:
|
|
55
|
+
return f"{float(v):.{precision}f}"
|
|
56
|
+
except (TypeError, ValueError):
|
|
57
|
+
return "—"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _chip(label: str, value: str, *, accent: bool = False) -> str:
|
|
61
|
+
cls = "catalog-chip accent" if accent else "catalog-chip"
|
|
62
|
+
return (
|
|
63
|
+
f'<span class="{cls}">'
|
|
64
|
+
f'<span class="label">{escape(label)}</span>'
|
|
65
|
+
f'<span class="value">{escape(value)}</span>'
|
|
66
|
+
f'</span>'
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _prose_block(title: str, text: Any) -> str:
|
|
71
|
+
if text is None:
|
|
72
|
+
return ""
|
|
73
|
+
try:
|
|
74
|
+
if pd.isna(text):
|
|
75
|
+
return ""
|
|
76
|
+
except (TypeError, ValueError):
|
|
77
|
+
pass
|
|
78
|
+
s = str(text).strip()
|
|
79
|
+
if not s:
|
|
80
|
+
return ""
|
|
81
|
+
return (
|
|
82
|
+
'<section class="catalog-section">'
|
|
83
|
+
f'<h4>{escape(title)}</h4>'
|
|
84
|
+
f'<p class="prose">{escape(s)}</p>'
|
|
85
|
+
'</section>'
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _render_master_table(df: pd.DataFrame) -> int | None:
|
|
90
|
+
display = pd.DataFrame({
|
|
91
|
+
"Archetype": df["archetype_name"].astype(str),
|
|
92
|
+
"ID": df["archetype_id"].astype(str),
|
|
93
|
+
"Version": df["archetype_version"].astype(str),
|
|
94
|
+
"Cluster": df["cluster_size"].astype("Int64"),
|
|
95
|
+
"Mean churn": df["cluster_mean_churn_probability"],
|
|
96
|
+
"Stability": df.get("stability_vs_prior_version", pd.Series([None] * len(df))),
|
|
97
|
+
"Method": df["derivation_method"].astype(str),
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
col_config = {
|
|
101
|
+
"Archetype": st.column_config.TextColumn(width="medium"),
|
|
102
|
+
"ID": st.column_config.TextColumn(width="small", help="archetype_id"),
|
|
103
|
+
"Version": st.column_config.TextColumn(width="small"),
|
|
104
|
+
"Cluster": st.column_config.NumberColumn(format="%,d", width="small"),
|
|
105
|
+
"Mean churn": st.column_config.ProgressColumn(
|
|
106
|
+
format="%.2f", min_value=0.0, max_value=1.0, width="small"
|
|
107
|
+
),
|
|
108
|
+
"Stability": st.column_config.NumberColumn(format="%.2f", width="small",
|
|
109
|
+
help="Jaccard-like overlap with the same archetype in the prior version (1.0 = identical)"),
|
|
110
|
+
"Method": st.column_config.TextColumn(width="small"),
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
event = st.dataframe(
|
|
114
|
+
display,
|
|
115
|
+
use_container_width=True,
|
|
116
|
+
hide_index=True,
|
|
117
|
+
column_config=col_config,
|
|
118
|
+
on_select="rerun",
|
|
119
|
+
selection_mode="single-row",
|
|
120
|
+
height=420,
|
|
121
|
+
key="archetypes_master_table",
|
|
122
|
+
)
|
|
123
|
+
rows = (event.selection or {}).get("rows") if event else None
|
|
124
|
+
return rows[0] if rows else None
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# ---------------------------------------------------------------------------
|
|
128
|
+
# Feature struct helpers
|
|
129
|
+
# ---------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _as_dict(item: Any) -> dict[str, Any]:
|
|
133
|
+
"""Coerce a row of ``top_shap_features`` (struct or pyspark Row) to dict."""
|
|
134
|
+
if item is None:
|
|
135
|
+
return {}
|
|
136
|
+
if isinstance(item, dict):
|
|
137
|
+
return item
|
|
138
|
+
if hasattr(item, "asDict"):
|
|
139
|
+
try:
|
|
140
|
+
return item.asDict()
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
# plain object with attribute access (e.g. namedtuple-like)
|
|
144
|
+
out: dict[str, Any] = {}
|
|
145
|
+
for key in ("feature", "mean_shap", "mean_value", "direction"):
|
|
146
|
+
if hasattr(item, key):
|
|
147
|
+
out[key] = getattr(item, key)
|
|
148
|
+
return out
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _split_drivers(features: Any) -> tuple[list[dict], list[dict]]:
|
|
152
|
+
"""Return ``(positive, negative)`` SHAP-driver dicts, ordered by |mean_shap|."""
|
|
153
|
+
if features is None:
|
|
154
|
+
return [], []
|
|
155
|
+
try:
|
|
156
|
+
seq: Iterable[Any] = list(features)
|
|
157
|
+
except TypeError:
|
|
158
|
+
return [], []
|
|
159
|
+
if not seq:
|
|
160
|
+
return [], []
|
|
161
|
+
|
|
162
|
+
pos: list[dict] = []
|
|
163
|
+
neg: list[dict] = []
|
|
164
|
+
for raw in seq:
|
|
165
|
+
d = _as_dict(raw)
|
|
166
|
+
if not d.get("feature"):
|
|
167
|
+
continue
|
|
168
|
+
direction = (d.get("direction") or "").strip().lower()
|
|
169
|
+
mean_shap = d.get("mean_shap")
|
|
170
|
+
if direction == _DIRECTION_POSITIVE:
|
|
171
|
+
pos.append(d)
|
|
172
|
+
elif direction == _DIRECTION_NEGATIVE:
|
|
173
|
+
neg.append(d)
|
|
174
|
+
else:
|
|
175
|
+
# No explicit direction — fall back to sign of mean_shap.
|
|
176
|
+
try:
|
|
177
|
+
if mean_shap is not None and float(mean_shap) >= 0:
|
|
178
|
+
pos.append(d)
|
|
179
|
+
else:
|
|
180
|
+
neg.append(d)
|
|
181
|
+
except (TypeError, ValueError):
|
|
182
|
+
pos.append(d)
|
|
183
|
+
|
|
184
|
+
def _abs_shap(d: dict) -> float:
|
|
185
|
+
v = d.get("mean_shap")
|
|
186
|
+
try:
|
|
187
|
+
return abs(float(v))
|
|
188
|
+
except (TypeError, ValueError):
|
|
189
|
+
return 0.0
|
|
190
|
+
|
|
191
|
+
pos.sort(key=_abs_shap, reverse=True)
|
|
192
|
+
neg.sort(key=_abs_shap, reverse=True)
|
|
193
|
+
return pos, neg
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _coerce_map(item: Any) -> dict[str, Any]:
|
|
197
|
+
if item is None:
|
|
198
|
+
return {}
|
|
199
|
+
if isinstance(item, dict):
|
|
200
|
+
return item
|
|
201
|
+
if hasattr(item, "asDict"):
|
|
202
|
+
try:
|
|
203
|
+
return item.asDict()
|
|
204
|
+
except Exception:
|
|
205
|
+
return {}
|
|
206
|
+
return {}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _threshold_for(thresholds: dict, feature_name: str) -> dict[str, Any]:
|
|
210
|
+
raw = thresholds.get(feature_name)
|
|
211
|
+
if raw is None:
|
|
212
|
+
return {}
|
|
213
|
+
return _coerce_map(raw)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _phrase_lookup(phrases_df: pd.DataFrame) -> dict[str, dict[str, Any]]:
|
|
217
|
+
if phrases_df is None or phrases_df.empty:
|
|
218
|
+
return {}
|
|
219
|
+
out: dict[str, dict[str, Any]] = {}
|
|
220
|
+
for _, r in phrases_df.iterrows():
|
|
221
|
+
name = r.get("feature_name")
|
|
222
|
+
if not name:
|
|
223
|
+
continue
|
|
224
|
+
out[str(name)] = {
|
|
225
|
+
"business_phrase": r.get("business_phrase"),
|
|
226
|
+
"source_dataset": r.get("source_dataset"),
|
|
227
|
+
"aggregation_kind": r.get("aggregation_kind"),
|
|
228
|
+
"window_phrase": r.get("window_phrase"),
|
|
229
|
+
"polarity": r.get("polarity"),
|
|
230
|
+
}
|
|
231
|
+
return out
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _render_driver_row(
|
|
235
|
+
driver: dict,
|
|
236
|
+
*,
|
|
237
|
+
direction: str,
|
|
238
|
+
thresholds: dict,
|
|
239
|
+
phrases: dict[str, dict[str, Any]],
|
|
240
|
+
expander_key: str,
|
|
241
|
+
) -> None:
|
|
242
|
+
feature = str(driver.get("feature") or "")
|
|
243
|
+
phrase_meta = phrases.get(feature, {})
|
|
244
|
+
display_phrase = (
|
|
245
|
+
str(phrase_meta.get("business_phrase") or "").strip()
|
|
246
|
+
or feature
|
|
247
|
+
)
|
|
248
|
+
mean_shap = driver.get("mean_shap")
|
|
249
|
+
glyph = "▲" if direction == _DIRECTION_POSITIVE else "▼"
|
|
250
|
+
cls = "pos" if direction == _DIRECTION_POSITIVE else "neg"
|
|
251
|
+
|
|
252
|
+
# Summary row (always visible)
|
|
253
|
+
st.markdown(
|
|
254
|
+
f'<div class="feature-row {cls}">'
|
|
255
|
+
f'<span class="glyph">{glyph}</span>'
|
|
256
|
+
f'<span class="phrase">{escape(display_phrase)}</span>'
|
|
257
|
+
f'<span class="shap">SHAP {_fmt_float(mean_shap, precision=4)}</span>'
|
|
258
|
+
'</div>',
|
|
259
|
+
unsafe_allow_html=True,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Technical detail (click-to-expand)
|
|
263
|
+
threshold = _threshold_for(thresholds, feature)
|
|
264
|
+
with st.expander("technical detail", expanded=False):
|
|
265
|
+
meta_lines: list[str] = [f"**Column name** `{feature}`"]
|
|
266
|
+
mean_value = driver.get("mean_value")
|
|
267
|
+
if mean_value is not None:
|
|
268
|
+
meta_lines.append(f"**Cluster mean value** {_fmt_float(mean_value, precision=4)}")
|
|
269
|
+
if mean_shap is not None:
|
|
270
|
+
meta_lines.append(f"**Cluster mean SHAP** {_fmt_float(mean_shap, precision=6)}")
|
|
271
|
+
explicit_dir = driver.get("direction")
|
|
272
|
+
if explicit_dir:
|
|
273
|
+
meta_lines.append(f"**Direction** {explicit_dir}")
|
|
274
|
+
if threshold:
|
|
275
|
+
t_bits = []
|
|
276
|
+
for tk in ("p25", "p50", "p75"):
|
|
277
|
+
if tk in threshold and threshold[tk] is not None:
|
|
278
|
+
t_bits.append(f"{tk}={_fmt_float(threshold[tk], precision=3)}")
|
|
279
|
+
if t_bits:
|
|
280
|
+
meta_lines.append(f"**Cluster quantiles** {' · '.join(t_bits)}")
|
|
281
|
+
for key, label in (
|
|
282
|
+
("source_dataset", "Source dataset"),
|
|
283
|
+
("aggregation_kind", "Aggregation"),
|
|
284
|
+
("window_phrase", "Window"),
|
|
285
|
+
("polarity", "Polarity (training)"),
|
|
286
|
+
):
|
|
287
|
+
val = phrase_meta.get(key)
|
|
288
|
+
if val is None:
|
|
289
|
+
continue
|
|
290
|
+
try:
|
|
291
|
+
if pd.isna(val):
|
|
292
|
+
continue
|
|
293
|
+
except (TypeError, ValueError):
|
|
294
|
+
pass
|
|
295
|
+
s = str(val).strip()
|
|
296
|
+
if not s:
|
|
297
|
+
continue
|
|
298
|
+
meta_lines.append(f"**{label}** {s}")
|
|
299
|
+
st.markdown(" \n".join(meta_lines))
|
|
300
|
+
# The key is referenced to keep streamlit expander state stable per row.
|
|
301
|
+
_ = expander_key # noqa: F841 — kept for future per-row keying if needed
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def _render_drivers_section(
|
|
305
|
+
title: str,
|
|
306
|
+
drivers: list[dict],
|
|
307
|
+
*,
|
|
308
|
+
direction: str,
|
|
309
|
+
thresholds: dict,
|
|
310
|
+
phrases: dict[str, dict[str, Any]],
|
|
311
|
+
archetype_id: str,
|
|
312
|
+
) -> None:
|
|
313
|
+
if not drivers:
|
|
314
|
+
return
|
|
315
|
+
st.markdown(
|
|
316
|
+
f'<section class="catalog-section">'
|
|
317
|
+
f'<h4>{escape(title)} · {len(drivers)}</h4>'
|
|
318
|
+
'</section>',
|
|
319
|
+
unsafe_allow_html=True,
|
|
320
|
+
)
|
|
321
|
+
for i, d in enumerate(drivers):
|
|
322
|
+
_render_driver_row(
|
|
323
|
+
d,
|
|
324
|
+
direction=direction,
|
|
325
|
+
thresholds=thresholds,
|
|
326
|
+
phrases=phrases,
|
|
327
|
+
expander_key=f"{archetype_id}::{direction}::{i}",
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _render_detail(row: pd.Series, phrases: dict[str, dict[str, Any]]) -> None:
|
|
332
|
+
name = str(row.get("archetype_name") or row.get("archetype_id"))
|
|
333
|
+
aid = str(row.get("archetype_id"))
|
|
334
|
+
version = str(row.get("archetype_version"))
|
|
335
|
+
|
|
336
|
+
chips = [
|
|
337
|
+
_chip("ID", aid),
|
|
338
|
+
_chip("Version", version),
|
|
339
|
+
_chip("Cluster size", _fmt_int(row.get("cluster_size"))),
|
|
340
|
+
_chip("Mean churn", _fmt_pct(row.get("cluster_mean_churn_probability")), accent=True),
|
|
341
|
+
_chip("Method", str(row.get("derivation_method") or "—")),
|
|
342
|
+
]
|
|
343
|
+
stability = row.get("stability_vs_prior_version")
|
|
344
|
+
try:
|
|
345
|
+
if stability is not None and not pd.isna(stability):
|
|
346
|
+
chips.append(_chip("Stability vs prior", _fmt_float(stability, precision=2)))
|
|
347
|
+
except (TypeError, ValueError):
|
|
348
|
+
pass
|
|
349
|
+
model_name = row.get("model_name")
|
|
350
|
+
model_version = row.get("model_version")
|
|
351
|
+
if model_name:
|
|
352
|
+
mv = f"{model_name}"
|
|
353
|
+
if model_version:
|
|
354
|
+
mv = f"{mv} · v{model_version}"
|
|
355
|
+
chips.append(_chip("Model", mv))
|
|
356
|
+
|
|
357
|
+
sections_html = ''.join([
|
|
358
|
+
_prose_block("Description", row.get("archetype_description")),
|
|
359
|
+
_prose_block("Rationale", row.get("rationale")),
|
|
360
|
+
])
|
|
361
|
+
|
|
362
|
+
st.markdown(
|
|
363
|
+
f"""
|
|
364
|
+
<article class="catalog-detail">
|
|
365
|
+
<div class="eyebrow">Archetype · readonly</div>
|
|
366
|
+
<h3 class="title">{escape(name)}</h3>
|
|
367
|
+
<p class="subtitle">{escape(aid)} · v{escape(version)}</p>
|
|
368
|
+
<div class="chip-row">{''.join(chips)}</div>
|
|
369
|
+
{sections_html}
|
|
370
|
+
</article>
|
|
371
|
+
""",
|
|
372
|
+
unsafe_allow_html=True,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
pos, neg = _split_drivers(row.get("top_shap_features"))
|
|
376
|
+
thresholds = _coerce_map(row.get("feature_thresholds"))
|
|
377
|
+
|
|
378
|
+
if not pos and not neg:
|
|
379
|
+
st.markdown(
|
|
380
|
+
'<section class="catalog-section">'
|
|
381
|
+
'<h4>Driver features</h4>'
|
|
382
|
+
'<p class="prose"><em>No SHAP drivers recorded for this archetype.</em></p>'
|
|
383
|
+
'</section>',
|
|
384
|
+
unsafe_allow_html=True,
|
|
385
|
+
)
|
|
386
|
+
return
|
|
387
|
+
|
|
388
|
+
_render_drivers_section(
|
|
389
|
+
"Risk-driving features",
|
|
390
|
+
pos,
|
|
391
|
+
direction=_DIRECTION_POSITIVE,
|
|
392
|
+
thresholds=thresholds,
|
|
393
|
+
phrases=phrases,
|
|
394
|
+
archetype_id=aid,
|
|
395
|
+
)
|
|
396
|
+
_render_drivers_section(
|
|
397
|
+
"Protective features",
|
|
398
|
+
neg,
|
|
399
|
+
direction=_DIRECTION_NEGATIVE,
|
|
400
|
+
thresholds=thresholds,
|
|
401
|
+
phrases=phrases,
|
|
402
|
+
archetype_id=aid,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def render() -> None:
|
|
407
|
+
st.markdown(
|
|
408
|
+
'<p class="catalog-lead">'
|
|
409
|
+
'Behavioural clusters the model has identified in the current '
|
|
410
|
+
'feature space. Click a row to see the archetype’s description, '
|
|
411
|
+
'rationale, and the SHAP drivers — split into risk-driving '
|
|
412
|
+
'(pushes prediction toward churn) and protective. Expand any '
|
|
413
|
+
'driver for its technical detail.'
|
|
414
|
+
'</p>',
|
|
415
|
+
unsafe_allow_html=True,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
try:
|
|
419
|
+
df = data.archetype_catalog_all()
|
|
420
|
+
except Exception as exc:
|
|
421
|
+
st.error(f"Could not load archetype catalog: {exc}")
|
|
422
|
+
return
|
|
423
|
+
if df is None or df.empty:
|
|
424
|
+
st.info("No active archetypes — run c03 / c04 to derive and publish.")
|
|
425
|
+
return
|
|
426
|
+
|
|
427
|
+
try:
|
|
428
|
+
phrases_df = data.feature_business_phrases()
|
|
429
|
+
except Exception:
|
|
430
|
+
phrases_df = pd.DataFrame()
|
|
431
|
+
phrases = _phrase_lookup(phrases_df)
|
|
432
|
+
|
|
433
|
+
idx = _render_master_table(df)
|
|
434
|
+
if idx is None:
|
|
435
|
+
prior = st.session_state.get(_STATE_KEY)
|
|
436
|
+
if not prior:
|
|
437
|
+
return
|
|
438
|
+
match = df.index[df["archetype_id"] == prior]
|
|
439
|
+
if len(match) == 0:
|
|
440
|
+
return
|
|
441
|
+
idx = int(match[0])
|
|
442
|
+
else:
|
|
443
|
+
st.session_state[_STATE_KEY] = str(df.iloc[idx]["archetype_id"])
|
|
444
|
+
|
|
445
|
+
_render_detail(df.iloc[idx], phrases)
|
|
@@ -309,6 +309,157 @@ def load_template_html_from_uc(composite_name: Optional[str] = None) -> Optional
|
|
|
309
309
|
return str(html) if html else None
|
|
310
310
|
|
|
311
311
|
|
|
312
|
+
@st.cache_data(ttl=300, show_spinner=False)
|
|
313
|
+
def playbook_catalog_all() -> pd.DataFrame:
|
|
314
|
+
"""Latest-version row per playbook from ``playbook_catalog``.
|
|
315
|
+
|
|
316
|
+
The catalog is append-only — every YAML re-ingest writes a new
|
|
317
|
+
``(playbook_id, version)`` row. We project the most-recently-active
|
|
318
|
+
row per playbook so the master table shows one line per intervention
|
|
319
|
+
design. Eligible / recommended counts come from
|
|
320
|
+
``v_capacity_utilization`` for the latest scoring run; left-joined so
|
|
321
|
+
a freshly-loaded playbook with no scoring yet still renders.
|
|
322
|
+
"""
|
|
323
|
+
cfg = load_config()
|
|
324
|
+
return _query(cfg, f"""
|
|
325
|
+
WITH latest_pb AS (
|
|
326
|
+
SELECT
|
|
327
|
+
playbook_id,
|
|
328
|
+
version,
|
|
329
|
+
name,
|
|
330
|
+
description,
|
|
331
|
+
when_applicable,
|
|
332
|
+
cost_per_customer_default,
|
|
333
|
+
expected_uplift_pct_default,
|
|
334
|
+
outcome_windows_days,
|
|
335
|
+
outcome_definition_version,
|
|
336
|
+
time_zero_definition,
|
|
337
|
+
grace_period_days,
|
|
338
|
+
followup_start_rule,
|
|
339
|
+
followup_end_rule,
|
|
340
|
+
default_estimand,
|
|
341
|
+
analysis_population_rule,
|
|
342
|
+
active_from,
|
|
343
|
+
active_to,
|
|
344
|
+
ROW_NUMBER() OVER (
|
|
345
|
+
PARTITION BY playbook_id
|
|
346
|
+
ORDER BY active_from DESC NULLS LAST, version DESC
|
|
347
|
+
) AS rn
|
|
348
|
+
FROM {cfg.fqn_prefix}.playbook_catalog
|
|
349
|
+
),
|
|
350
|
+
capacity AS (
|
|
351
|
+
SELECT playbook_id,
|
|
352
|
+
SUM(eligible_count) AS eligible_count,
|
|
353
|
+
SUM(recommended_count) AS recommended_count,
|
|
354
|
+
SUM(holdout_count) AS holdout_count
|
|
355
|
+
FROM {cfg.fqn_prefix}.v_capacity_utilization
|
|
356
|
+
GROUP BY playbook_id
|
|
357
|
+
),
|
|
358
|
+
step_counts AS (
|
|
359
|
+
SELECT playbook_id, playbook_version, COUNT(*) AS step_count
|
|
360
|
+
FROM {cfg.fqn_prefix}.playbook_steps
|
|
361
|
+
GROUP BY playbook_id, playbook_version
|
|
362
|
+
)
|
|
363
|
+
SELECT
|
|
364
|
+
p.playbook_id,
|
|
365
|
+
p.version,
|
|
366
|
+
p.name,
|
|
367
|
+
p.description,
|
|
368
|
+
p.when_applicable,
|
|
369
|
+
p.cost_per_customer_default,
|
|
370
|
+
p.expected_uplift_pct_default,
|
|
371
|
+
p.outcome_windows_days,
|
|
372
|
+
p.outcome_definition_version,
|
|
373
|
+
p.time_zero_definition,
|
|
374
|
+
p.grace_period_days,
|
|
375
|
+
p.followup_start_rule,
|
|
376
|
+
p.followup_end_rule,
|
|
377
|
+
p.default_estimand,
|
|
378
|
+
p.analysis_population_rule,
|
|
379
|
+
p.active_from,
|
|
380
|
+
p.active_to,
|
|
381
|
+
COALESCE(c.eligible_count, 0) AS eligible_count,
|
|
382
|
+
COALESCE(c.recommended_count, 0) AS recommended_count,
|
|
383
|
+
COALESCE(c.holdout_count, 0) AS holdout_count,
|
|
384
|
+
COALESCE(sc.step_count, 0) AS step_count
|
|
385
|
+
FROM latest_pb p
|
|
386
|
+
LEFT JOIN capacity c ON c.playbook_id = p.playbook_id
|
|
387
|
+
LEFT JOIN step_counts sc ON sc.playbook_id = p.playbook_id
|
|
388
|
+
AND sc.playbook_version = p.version
|
|
389
|
+
WHERE p.rn = 1
|
|
390
|
+
ORDER BY COALESCE(c.recommended_count, 0) DESC, p.name ASC
|
|
391
|
+
""")
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
@st.cache_data(ttl=300, show_spinner=False)
|
|
395
|
+
def playbook_steps_for(playbook_id: str, version: str) -> pd.DataFrame:
|
|
396
|
+
"""Ordered step rows for one ``(playbook_id, version)`` pair."""
|
|
397
|
+
cfg = load_config()
|
|
398
|
+
return _query(cfg, f"""
|
|
399
|
+
SELECT step_id, step_sequence, step_name, action_type,
|
|
400
|
+
automation_level, owner_role, cadence_trigger,
|
|
401
|
+
cadence_relative_to, cadence_offset_days, cadence_condition,
|
|
402
|
+
timeout_days, response_schema_id, intensity_param_name,
|
|
403
|
+
skip_conditions, stop_conditions
|
|
404
|
+
FROM {cfg.fqn_prefix}.playbook_steps
|
|
405
|
+
WHERE playbook_id = :pid AND playbook_version = :ver
|
|
406
|
+
ORDER BY step_sequence ASC
|
|
407
|
+
""", {"pid": playbook_id, "ver": version})
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
@st.cache_data(ttl=300, show_spinner=False)
|
|
411
|
+
def archetype_catalog_all() -> pd.DataFrame:
|
|
412
|
+
"""Active archetypes from ``v_archetype_overview``.
|
|
413
|
+
|
|
414
|
+
One row per active archetype with cluster stats and the top-SHAP
|
|
415
|
+
driver array. The view is already filtered to ``status = 'active'``
|
|
416
|
+
so the master table reflects what's deployed against the latest
|
|
417
|
+
model build.
|
|
418
|
+
"""
|
|
419
|
+
cfg = load_config()
|
|
420
|
+
return _query(cfg, f"""
|
|
421
|
+
SELECT
|
|
422
|
+
archetype_id,
|
|
423
|
+
archetype_version,
|
|
424
|
+
archetype_name,
|
|
425
|
+
archetype_description,
|
|
426
|
+
rationale,
|
|
427
|
+
cluster_size,
|
|
428
|
+
cluster_mean_churn_probability,
|
|
429
|
+
top_shap_features,
|
|
430
|
+
feature_thresholds,
|
|
431
|
+
model_name,
|
|
432
|
+
model_version,
|
|
433
|
+
derivation_method,
|
|
434
|
+
stability_vs_prior_version,
|
|
435
|
+
status,
|
|
436
|
+
valid_from,
|
|
437
|
+
valid_to
|
|
438
|
+
FROM {cfg.fqn_prefix}.v_archetype_overview
|
|
439
|
+
ORDER BY cluster_mean_churn_probability DESC NULLS LAST, cluster_size DESC
|
|
440
|
+
""")
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
@st.cache_data(ttl=300, show_spinner=False)
|
|
444
|
+
def feature_business_phrases() -> pd.DataFrame:
|
|
445
|
+
"""``feature_name → business_phrase`` map for archetype detail.
|
|
446
|
+
|
|
447
|
+
Joined client-side onto the raw SHAP-feature list so the Archetypes
|
|
448
|
+
tab can show a readable phrase next to each technical column name.
|
|
449
|
+
Reads ``v_feature_provenance``; returns an empty DataFrame when the
|
|
450
|
+
view is missing.
|
|
451
|
+
"""
|
|
452
|
+
cfg = load_config()
|
|
453
|
+
try:
|
|
454
|
+
return _query(cfg, f"""
|
|
455
|
+
SELECT feature_name, business_phrase, source_dataset,
|
|
456
|
+
aggregation_kind, window_phrase, polarity
|
|
457
|
+
FROM {cfg.fqn_prefix}.v_feature_provenance
|
|
458
|
+
""")
|
|
459
|
+
except Exception:
|
|
460
|
+
return pd.DataFrame()
|
|
461
|
+
|
|
462
|
+
|
|
312
463
|
@st.cache_data(ttl=300, show_spinner=False)
|
|
313
464
|
def run_history() -> pd.DataFrame:
|
|
314
465
|
cfg = load_config()
|