py-lucidum 0.4.59__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.
- py_lucidum-0.4.59/.githooks/pre-commit +14 -0
- py_lucidum-0.4.59/.githooks/pre-push +14 -0
- py_lucidum-0.4.59/.github/dependabot.yml +8 -0
- py_lucidum-0.4.59/.github/workflows/ci.yml +45 -0
- py_lucidum-0.4.59/.github/workflows/release.yml +327 -0
- py_lucidum-0.4.59/.gitignore +27 -0
- py_lucidum-0.4.59/AGENTS.md +115 -0
- py_lucidum-0.4.59/DEVELOPMENT.md +612 -0
- py_lucidum-0.4.59/LICENSE +21 -0
- py_lucidum-0.4.59/PKG-INFO +519 -0
- py_lucidum-0.4.59/README.md +490 -0
- py_lucidum-0.4.59/REFACTOR.md +118 -0
- py_lucidum-0.4.59/datasets/monthly/2016-09.parquet +0 -0
- py_lucidum-0.4.59/datasets/monthly/2016-10.parquet +0 -0
- py_lucidum-0.4.59/datasets/monthly/2016-11.parquet +0 -0
- py_lucidum-0.4.59/datasets/monthly/2016-12.parquet +0 -0
- py_lucidum-0.4.59/datasets/monthly/2017-01.parquet +0 -0
- py_lucidum-0.4.59/datasets/monthly/2017-02.parquet +0 -0
- py_lucidum-0.4.59/datasets/monthly/2017-03.parquet +0 -0
- py_lucidum-0.4.59/datasets/motor_premiums.parquet +0 -0
- py_lucidum-0.4.59/docs/assets/line_and_bar.png +0 -0
- py_lucidum-0.4.59/docs/assets/postcode_area.png +0 -0
- py_lucidum-0.4.59/docs/assets/postcode_sector.png +0 -0
- py_lucidum-0.4.59/docs/assets/postcode_unit.png +0 -0
- py_lucidum-0.4.59/docs/specs/gbm-tool.md +140 -0
- py_lucidum-0.4.59/docs/specs/gbm-tool_plan.md +115 -0
- py_lucidum-0.4.59/docs/specs/glm-tabulations.md +106 -0
- py_lucidum-0.4.59/docs/telemetry-monitor.md +80 -0
- py_lucidum-0.4.59/favicon.ico +0 -0
- py_lucidum-0.4.59/pyproject.toml +53 -0
- py_lucidum-0.4.59/scripts/README.md +116 -0
- py_lucidum-0.4.59/scripts/build_uk_sector_adjacency.py +98 -0
- py_lucidum-0.4.59/scripts/bump_version.py +108 -0
- py_lucidum-0.4.59/scripts/release_artifacts.py +167 -0
- py_lucidum-0.4.59/scripts/run_browser_smoke.py +148 -0
- py_lucidum-0.4.59/scripts/run_tests.py +416 -0
- py_lucidum-0.4.59/scripts/uk_map/convert_gpkg_to_geojson.py +352 -0
- py_lucidum-0.4.59/specs/feature_spec.csv +20 -0
- py_lucidum-0.4.59/specs/filter_spec.csv +25 -0
- py_lucidum-0.4.59/specs/home_filter_spec.csv +93 -0
- py_lucidum-0.4.59/specs/kpi_spec.csv +10 -0
- py_lucidum-0.4.59/src/py_lucidum/__init__.py +15 -0
- py_lucidum-0.4.59/src/py_lucidum/__main__.py +4 -0
- py_lucidum-0.4.59/src/py_lucidum/_version.py +31 -0
- py_lucidum-0.4.59/src/py_lucidum/app/__init__.py +7 -0
- py_lucidum-0.4.59/src/py_lucidum/app/assets.py +30 -0
- py_lucidum-0.4.59/src/py_lucidum/app/context.py +14 -0
- py_lucidum-0.4.59/src/py_lucidum/app/factory.py +539 -0
- py_lucidum-0.4.59/src/py_lucidum/app/servers.py +265 -0
- py_lucidum-0.4.59/src/py_lucidum/app/telemetry.py +1227 -0
- py_lucidum-0.4.59/src/py_lucidum/cli.py +551 -0
- py_lucidum-0.4.59/src/py_lucidum/core/__init__.py +84 -0
- py_lucidum-0.4.59/src/py_lucidum/core/dataset.py +711 -0
- py_lucidum-0.4.59/src/py_lucidum/core/features.py +81 -0
- py_lucidum-0.4.59/src/py_lucidum/core/filters.py +37 -0
- py_lucidum-0.4.59/src/py_lucidum/core/kpis.py +78 -0
- py_lucidum-0.4.59/src/py_lucidum/core/metric_sources.py +220 -0
- py_lucidum-0.4.59/src/py_lucidum/core/metrics.py +285 -0
- py_lucidum-0.4.59/src/py_lucidum/core/schema.py +91 -0
- py_lucidum-0.4.59/src/py_lucidum/core/sql.py +9 -0
- py_lucidum-0.4.59/src/py_lucidum/core/workspace.py +79 -0
- py_lucidum-0.4.59/src/py_lucidum/demo.py +79 -0
- py_lucidum-0.4.59/src/py_lucidum/query.py +38 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/column-profile-tool.js +1152 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/dataset-viewer-tool.js +2277 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/gbm-evaluation-chart.js +502 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/gbm-feature-parameter-controls.js +471 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/gbm-model-navigator.js +188 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/gbm-shap-chart.js +772 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/gbm-shap-tool.js +1322 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/gbm-stacked-shap-chart.js +247 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/gbm-stacked-shap-tool.js +819 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/gbm-tab-orchestration.js +40 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/gbm-tool.js +3912 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/gbm-tree-viewer.js +1090 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/glm-formula-assist.js +278 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/glm-formula-builder.js +1239 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/glm-model-navigator.js +148 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/glm-tabulations.js +293 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/glm-tool.js +3230 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/histogram-tool.js +1090 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/line-bar-tool.js +5056 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/line-bar-two-feature-chart.js +1074 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/main.js +5171 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/model-tool-shell.js +120 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/api.js +56 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/echarts-gl.js +28 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/filter-expression.js +170 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/format.js +132 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/list-navigation.js +107 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/model-ui.js +232 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/schema.js +42 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/settings-strip.js +80 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/tabulator.js +41 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/timing.js +222 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/shared/tool-screen-nav.js +94 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/specifications-tool.js +1422 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app/uk-map-tool.js +2807 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app.css +11 -0
- py_lucidum-0.4.59/src/py_lucidum/static/app.js +3 -0
- py_lucidum-0.4.59/src/py_lucidum/static/index.html +679 -0
- py_lucidum-0.4.59/src/py_lucidum/static/monitor.css +829 -0
- py_lucidum-0.4.59/src/py_lucidum/static/monitor.html +270 -0
- py_lucidum-0.4.59/src/py_lucidum/static/monitor.js +765 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/column-profile.css +890 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/controls.css +1846 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/dataset-viewer.css +660 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/foundations.css +163 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/gbm.css +2852 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/glm.css +1811 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/histogram.css +350 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/line-bar.css +1068 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/model-shell.css +79 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/shell.css +634 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/specifications.css +314 -0
- py_lucidum-0.4.59/src/py_lucidum/static/styles/uk-map.css +1105 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/ace/LICENSE +24 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/ace/ace.js +22 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/ace/mode-r.js +7 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/ace/theme-monokai.js +7 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/ace/theme-textmate.js +7 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/d3/LICENSE +13 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/d3/d3.min.js +2 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/echarts/LICENSE +222 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/echarts/echarts.min.js +45 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/echarts-gl/LICENSE +29 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/echarts-gl/echarts-gl.min.js +1 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/leaflet/LICENSE +26 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/leaflet/images/layers-2x.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/leaflet/images/layers.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/leaflet/images/marker-icon-2x.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/leaflet/images/marker-icon.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/leaflet/images/marker-shadow.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/leaflet/leaflet.css +661 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/leaflet/leaflet.js +6 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/tabulator/LICENSE +21 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/tabulator/tabulator.min.css +2 -0
- py_lucidum-0.4.59/src/py_lucidum/static/vendor/tabulator/tabulator.min.js +3 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/__init__.py +3 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/column_profile/__init__.py +25 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/column_profile/query.py +652 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/column_profile/routes.py +44 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/dataset_viewer/__init__.py +24 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/dataset_viewer/query.py +109 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/dataset_viewer/routes.py +32 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/__init__.py +24 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/config.py +480 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/grid.py +364 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/interaction_group_model.py +641 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/jobs.py +329 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/routes.py +225 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/sample.py +189 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/shap.py +1197 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/sources.py +6 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/store.py +1115 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/tabulation.py +675 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/training.py +1832 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/trees.py +603 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/gbm/validation.py +1142 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/__init__.py +15 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/formula_assist.py +92 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/jobs.py +182 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/overlay.py +1758 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/overlay_worker.py +120 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/routes.py +257 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/store.py +612 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/tabulation.py +2416 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/tabulation_worker.py +70 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/terms.py +49 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/training.py +1730 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/validation.py +821 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/glm/worker.py +124 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/histogram/__init__.py +24 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/histogram/query.py +937 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/histogram/routes.py +28 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/line_bar/__init__.py +25 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/line_bar/favourites.py +550 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/line_bar/importance.py +217 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/line_bar/model_ratio.py +194 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/line_bar/query.py +3296 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/line_bar/routes.py +161 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/line_bar/two_feature.py +1016 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/registry.py +144 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/specifications/__init__.py +15 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/specifications/routes.py +49 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/specifications/service.py +487 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/__init__.py +24 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/query.py +661 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/routes.py +82 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/smoothing.py +371 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/.gitkeep +1 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/geodata/areas_MappaR.geojson +1 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/geodata/sector_adjacency.json +1 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/geodata/sector_smoothing_pools.parquet +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/geodata/sectors_MappaR.geojson +1 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/London.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/UK.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/map-controls/base-aerial.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/map-controls/base-blank.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/map-controls/base-dark.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/map-controls/base-esri.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/map-controls/base-light.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/map-controls/base-osm.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/map-controls/level-area.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/map-controls/level-sector.png +0 -0
- py_lucidum-0.4.59/src/py_lucidum/tools/uk_map/static/icons/map-controls/level-unit.png +0 -0
- py_lucidum-0.4.59/tests/test_browser_smoke.py +33793 -0
- py_lucidum-0.4.59/tests/test_cli.py +960 -0
- py_lucidum-0.4.59/tests/test_column_profile.py +496 -0
- py_lucidum-0.4.59/tests/test_dataset_viewer.py +234 -0
- py_lucidum-0.4.59/tests/test_demo_dataset.py +151 -0
- py_lucidum-0.4.59/tests/test_features.py +176 -0
- py_lucidum-0.4.59/tests/test_gbm.py +5292 -0
- py_lucidum-0.4.59/tests/test_glm.py +3863 -0
- py_lucidum-0.4.59/tests/test_histogram.py +406 -0
- py_lucidum-0.4.59/tests/test_line_bar.py +5096 -0
- py_lucidum-0.4.59/tests/test_model_denominators.py +446 -0
- py_lucidum-0.4.59/tests/test_parquet_folder.py +337 -0
- py_lucidum-0.4.59/tests/test_pipx_install.py +169 -0
- py_lucidum-0.4.59/tests/test_release_artifacts.py +111 -0
- py_lucidum-0.4.59/tests/test_specifications.py +504 -0
- py_lucidum-0.4.59/tests/test_static_assets.py +2814 -0
- py_lucidum-0.4.59/tests/test_telemetry.py +591 -0
- py_lucidum-0.4.59/tests/test_test_runner.py +292 -0
- py_lucidum-0.4.59/tests/test_uk_map.py +563 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
set -eu
|
|
4
|
+
|
|
5
|
+
repo_root=$(git rev-parse --show-toplevel)
|
|
6
|
+
python=${PY_LUCIDUM_TEST_PYTHON:-"$repo_root/.venv/bin/python"}
|
|
7
|
+
|
|
8
|
+
if ! command -v "$python" >/dev/null 2>&1; then
|
|
9
|
+
echo "Lucidum pre-commit: Python is not executable: $python" >&2
|
|
10
|
+
echo "Set PY_LUCIDUM_TEST_PYTHON to the test-environment Python path." >&2
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
exec "$python" "$repo_root/scripts/run_tests.py" precommit
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
set -eu
|
|
4
|
+
|
|
5
|
+
repo_root=$(git rev-parse --show-toplevel)
|
|
6
|
+
python=${PY_LUCIDUM_TEST_PYTHON:-"$repo_root/.venv/bin/python"}
|
|
7
|
+
|
|
8
|
+
if ! command -v "$python" >/dev/null 2>&1; then
|
|
9
|
+
echo "Lucidum pre-push: Python is not executable: $python" >&2
|
|
10
|
+
echo "Set PY_LUCIDUM_TEST_PYTHON to the test-environment Python path." >&2
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
exec "$python" "$repo_root/scripts/run_tests.py" prepush
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
17
|
+
cancel-in-progress: true
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
full-test-suite:
|
|
21
|
+
name: Full test suite
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
timeout-minutes: 30
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- name: Check out repository
|
|
27
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
28
|
+
with:
|
|
29
|
+
persist-credentials: false
|
|
30
|
+
|
|
31
|
+
- name: Set up Python
|
|
32
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
33
|
+
with:
|
|
34
|
+
python-version: "3.13"
|
|
35
|
+
cache: pip
|
|
36
|
+
cache-dependency-path: pyproject.toml
|
|
37
|
+
|
|
38
|
+
- name: Install test dependencies
|
|
39
|
+
run: |
|
|
40
|
+
python -m pip install --upgrade pip
|
|
41
|
+
python -m pip install -e ".[glm,gbm]" pytest playwright
|
|
42
|
+
python -m playwright install --with-deps chromium webkit
|
|
43
|
+
|
|
44
|
+
- name: Run complete test gate
|
|
45
|
+
run: python scripts/run_tests.py prepush
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
permissions: {}
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: release
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
validate:
|
|
16
|
+
name: Validate release tag
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
permissions:
|
|
19
|
+
actions: read
|
|
20
|
+
contents: read
|
|
21
|
+
outputs:
|
|
22
|
+
release_commit: ${{ steps.release.outputs.release_commit }}
|
|
23
|
+
version: ${{ steps.release.outputs.version }}
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- name: Check out tagged source
|
|
27
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
28
|
+
with:
|
|
29
|
+
fetch-depth: 0
|
|
30
|
+
persist-credentials: false
|
|
31
|
+
|
|
32
|
+
- name: Validate tag, version, branch, and CI
|
|
33
|
+
id: release
|
|
34
|
+
env:
|
|
35
|
+
GH_TOKEN: ${{ github.token }}
|
|
36
|
+
run: |
|
|
37
|
+
if [[ ! "$GITHUB_REF_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
38
|
+
echo "Release tag must use vMAJOR.MINOR.PATCH: $GITHUB_REF_NAME" >&2
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
if [[ "$(git cat-file -t "$GITHUB_REF_NAME")" != "tag" ]]; then
|
|
42
|
+
echo "Release tag must be annotated: $GITHUB_REF_NAME" >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
version="$(python - <<'PY'
|
|
47
|
+
import tomllib
|
|
48
|
+
from pathlib import Path
|
|
49
|
+
print(tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["project"]["version"])
|
|
50
|
+
PY
|
|
51
|
+
)"
|
|
52
|
+
if [[ "$GITHUB_REF_NAME" != "v$version" ]]; then
|
|
53
|
+
echo "Tag $GITHUB_REF_NAME does not match pyproject.toml version $version" >&2
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
release_commit="$(git rev-list -n 1 "$GITHUB_REF_NAME")"
|
|
58
|
+
git fetch --no-tags origin main
|
|
59
|
+
if ! git merge-base --is-ancestor "$release_commit" origin/main; then
|
|
60
|
+
echo "Tagged commit $release_commit is not on origin/main" >&2
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
conclusion="$(gh api -X GET \
|
|
65
|
+
"repos/$GITHUB_REPOSITORY/actions/workflows/ci.yml/runs" \
|
|
66
|
+
-f branch=main \
|
|
67
|
+
-f event=push \
|
|
68
|
+
-f head_sha="$release_commit" \
|
|
69
|
+
-f status=completed \
|
|
70
|
+
--jq '.workflow_runs[0].conclusion // ""')"
|
|
71
|
+
if [[ "$conclusion" != "success" ]]; then
|
|
72
|
+
echo "Tagged commit $release_commit does not have a successful main CI run" >&2
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
echo "release_commit=$release_commit" >> "$GITHUB_OUTPUT"
|
|
77
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
78
|
+
|
|
79
|
+
build:
|
|
80
|
+
name: Build and smoke-test distributions
|
|
81
|
+
needs: validate
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
timeout-minutes: 20
|
|
84
|
+
permissions:
|
|
85
|
+
contents: read
|
|
86
|
+
|
|
87
|
+
steps:
|
|
88
|
+
- name: Check out tagged source
|
|
89
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
90
|
+
with:
|
|
91
|
+
ref: ${{ needs.validate.outputs.release_commit }}
|
|
92
|
+
persist-credentials: false
|
|
93
|
+
|
|
94
|
+
- name: Set up Python
|
|
95
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
96
|
+
with:
|
|
97
|
+
python-version: "3.13"
|
|
98
|
+
cache: pip
|
|
99
|
+
cache-dependency-path: pyproject.toml
|
|
100
|
+
|
|
101
|
+
- name: Build and inspect distributions
|
|
102
|
+
env:
|
|
103
|
+
RELEASE_VERSION: ${{ needs.validate.outputs.version }}
|
|
104
|
+
run: |
|
|
105
|
+
python -m pip install --upgrade pip
|
|
106
|
+
python -m pip install build pipx twine
|
|
107
|
+
python -m build
|
|
108
|
+
python -m twine check dist/*
|
|
109
|
+
python scripts/release_artifacts.py inspect --version "$RELEASE_VERSION"
|
|
110
|
+
|
|
111
|
+
- name: Smoke-test the built wheel through pipx
|
|
112
|
+
env:
|
|
113
|
+
PY_LUCIDUM_EXPECTED_VERSION: ${{ needs.validate.outputs.version }}
|
|
114
|
+
PY_LUCIDUM_PIPX_SPEC: ${{ github.workspace }}/dist/py_lucidum-${{ needs.validate.outputs.version }}-py3-none-any.whl
|
|
115
|
+
run: python scripts/run_tests.py pipx
|
|
116
|
+
|
|
117
|
+
- name: Store distributions
|
|
118
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
119
|
+
with:
|
|
120
|
+
name: python-package-distributions
|
|
121
|
+
path: |
|
|
122
|
+
dist/*.whl
|
|
123
|
+
dist/*.tar.gz
|
|
124
|
+
if-no-files-found: error
|
|
125
|
+
retention-days: 14
|
|
126
|
+
|
|
127
|
+
- name: Store checksums
|
|
128
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
129
|
+
with:
|
|
130
|
+
name: python-package-checksums
|
|
131
|
+
path: dist/SHA256SUMS
|
|
132
|
+
if-no-files-found: error
|
|
133
|
+
retention-days: 14
|
|
134
|
+
|
|
135
|
+
draft-release:
|
|
136
|
+
name: Create draft GitHub release
|
|
137
|
+
needs:
|
|
138
|
+
- validate
|
|
139
|
+
- build
|
|
140
|
+
runs-on: ubuntu-latest
|
|
141
|
+
permissions:
|
|
142
|
+
contents: write
|
|
143
|
+
|
|
144
|
+
steps:
|
|
145
|
+
- name: Download distributions
|
|
146
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
147
|
+
with:
|
|
148
|
+
name: python-package-distributions
|
|
149
|
+
path: dist/
|
|
150
|
+
|
|
151
|
+
- name: Download checksums
|
|
152
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
153
|
+
with:
|
|
154
|
+
name: python-package-checksums
|
|
155
|
+
path: dist/
|
|
156
|
+
|
|
157
|
+
- name: Create draft and attach artifacts
|
|
158
|
+
env:
|
|
159
|
+
GH_TOKEN: ${{ github.token }}
|
|
160
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
161
|
+
run: |
|
|
162
|
+
if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
|
163
|
+
is_draft="$(gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --json isDraft --jq .isDraft)"
|
|
164
|
+
if [[ "$is_draft" != "true" ]]; then
|
|
165
|
+
echo "Release $RELEASE_TAG is already published" >&2
|
|
166
|
+
exit 1
|
|
167
|
+
fi
|
|
168
|
+
else
|
|
169
|
+
gh release create "$RELEASE_TAG" \
|
|
170
|
+
--repo "$GITHUB_REPOSITORY" \
|
|
171
|
+
--verify-tag \
|
|
172
|
+
--draft \
|
|
173
|
+
--generate-notes \
|
|
174
|
+
--title "py-lucidum $RELEASE_TAG"
|
|
175
|
+
fi
|
|
176
|
+
gh release upload "$RELEASE_TAG" dist/* \
|
|
177
|
+
--repo "$GITHUB_REPOSITORY" \
|
|
178
|
+
--clobber
|
|
179
|
+
|
|
180
|
+
publish-testpypi:
|
|
181
|
+
name: Publish to TestPyPI
|
|
182
|
+
needs:
|
|
183
|
+
- validate
|
|
184
|
+
- build
|
|
185
|
+
- draft-release
|
|
186
|
+
runs-on: ubuntu-latest
|
|
187
|
+
environment:
|
|
188
|
+
name: testpypi
|
|
189
|
+
url: https://test.pypi.org/p/py-lucidum
|
|
190
|
+
permissions:
|
|
191
|
+
id-token: write
|
|
192
|
+
|
|
193
|
+
steps:
|
|
194
|
+
- name: Download distributions
|
|
195
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
196
|
+
with:
|
|
197
|
+
name: python-package-distributions
|
|
198
|
+
path: dist/
|
|
199
|
+
|
|
200
|
+
- name: Publish distributions
|
|
201
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
202
|
+
with:
|
|
203
|
+
repository-url: https://test.pypi.org/legacy/
|
|
204
|
+
|
|
205
|
+
verify-testpypi:
|
|
206
|
+
name: Verify TestPyPI artifacts
|
|
207
|
+
needs:
|
|
208
|
+
- validate
|
|
209
|
+
- build
|
|
210
|
+
- publish-testpypi
|
|
211
|
+
runs-on: ubuntu-latest
|
|
212
|
+
permissions:
|
|
213
|
+
contents: read
|
|
214
|
+
|
|
215
|
+
steps:
|
|
216
|
+
- name: Check out release verifier
|
|
217
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
218
|
+
with:
|
|
219
|
+
ref: ${{ needs.validate.outputs.release_commit }}
|
|
220
|
+
persist-credentials: false
|
|
221
|
+
|
|
222
|
+
- name: Set up Python
|
|
223
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
224
|
+
with:
|
|
225
|
+
python-version: "3.13"
|
|
226
|
+
|
|
227
|
+
- name: Download distributions
|
|
228
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
229
|
+
with:
|
|
230
|
+
name: python-package-distributions
|
|
231
|
+
path: dist/
|
|
232
|
+
|
|
233
|
+
- name: Compare TestPyPI hashes
|
|
234
|
+
run: >-
|
|
235
|
+
python scripts/release_artifacts.py verify-index
|
|
236
|
+
--version "${{ needs.validate.outputs.version }}"
|
|
237
|
+
--repository-url https://test.pypi.org/pypi
|
|
238
|
+
|
|
239
|
+
publish-pypi:
|
|
240
|
+
name: Publish to PyPI
|
|
241
|
+
needs:
|
|
242
|
+
- validate
|
|
243
|
+
- build
|
|
244
|
+
- verify-testpypi
|
|
245
|
+
runs-on: ubuntu-latest
|
|
246
|
+
environment:
|
|
247
|
+
name: pypi
|
|
248
|
+
url: https://pypi.org/p/py-lucidum
|
|
249
|
+
permissions:
|
|
250
|
+
id-token: write
|
|
251
|
+
|
|
252
|
+
steps:
|
|
253
|
+
- name: Download distributions
|
|
254
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
255
|
+
with:
|
|
256
|
+
name: python-package-distributions
|
|
257
|
+
path: dist/
|
|
258
|
+
|
|
259
|
+
- name: Publish distributions
|
|
260
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
261
|
+
|
|
262
|
+
verify-pypi:
|
|
263
|
+
name: Verify PyPI installation
|
|
264
|
+
needs:
|
|
265
|
+
- validate
|
|
266
|
+
- build
|
|
267
|
+
- publish-pypi
|
|
268
|
+
runs-on: ubuntu-latest
|
|
269
|
+
timeout-minutes: 15
|
|
270
|
+
permissions:
|
|
271
|
+
contents: read
|
|
272
|
+
|
|
273
|
+
steps:
|
|
274
|
+
- name: Check out release verifier
|
|
275
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
276
|
+
with:
|
|
277
|
+
ref: ${{ needs.validate.outputs.release_commit }}
|
|
278
|
+
persist-credentials: false
|
|
279
|
+
|
|
280
|
+
- name: Set up Python
|
|
281
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
282
|
+
with:
|
|
283
|
+
python-version: "3.13"
|
|
284
|
+
|
|
285
|
+
- name: Download distributions
|
|
286
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
287
|
+
with:
|
|
288
|
+
name: python-package-distributions
|
|
289
|
+
path: dist/
|
|
290
|
+
|
|
291
|
+
- name: Compare PyPI hashes
|
|
292
|
+
run: >-
|
|
293
|
+
python scripts/release_artifacts.py verify-index
|
|
294
|
+
--version "${{ needs.validate.outputs.version }}"
|
|
295
|
+
--repository-url https://pypi.org/pypi
|
|
296
|
+
|
|
297
|
+
- name: Smoke-test installation from PyPI
|
|
298
|
+
env:
|
|
299
|
+
PY_LUCIDUM_EXPECTED_VERSION: ${{ needs.validate.outputs.version }}
|
|
300
|
+
PY_LUCIDUM_PIPX_SPEC: py-lucidum==${{ needs.validate.outputs.version }}
|
|
301
|
+
run: |
|
|
302
|
+
python -m pip install pipx
|
|
303
|
+
python scripts/run_tests.py pipx
|
|
304
|
+
|
|
305
|
+
publish-github-release:
|
|
306
|
+
name: Publish immutable GitHub release
|
|
307
|
+
needs:
|
|
308
|
+
- validate
|
|
309
|
+
- draft-release
|
|
310
|
+
- verify-pypi
|
|
311
|
+
runs-on: ubuntu-latest
|
|
312
|
+
permissions:
|
|
313
|
+
contents: write
|
|
314
|
+
|
|
315
|
+
steps:
|
|
316
|
+
- name: Publish the validated draft
|
|
317
|
+
env:
|
|
318
|
+
GH_TOKEN: ${{ github.token }}
|
|
319
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
320
|
+
run: |
|
|
321
|
+
gh release edit "$RELEASE_TAG" \
|
|
322
|
+
--repo "$GITHUB_REPOSITORY" \
|
|
323
|
+
--draft=false \
|
|
324
|
+
--latest
|
|
325
|
+
gh release view "$RELEASE_TAG" \
|
|
326
|
+
--repo "$GITHUB_REPOSITORY" \
|
|
327
|
+
--json isDraft,tagName,url
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Local development datasets and artifacts
|
|
2
|
+
datasets/*.csv
|
|
3
|
+
datasets/*.parquet
|
|
4
|
+
!datasets/motor_premiums.parquet
|
|
5
|
+
.lucidum/
|
|
6
|
+
local/
|
|
7
|
+
|
|
8
|
+
# Python/cache/build artifacts
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.venv
|
|
15
|
+
.venv/
|
|
16
|
+
.venv-linux
|
|
17
|
+
.venv-linux/
|
|
18
|
+
dist/
|
|
19
|
+
build/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
|
|
22
|
+
# OS/editor metadata
|
|
23
|
+
.DS_Store
|
|
24
|
+
|
|
25
|
+
# Generated README previews
|
|
26
|
+
README.html
|
|
27
|
+
README_files/
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Repository Guidance
|
|
2
|
+
|
|
3
|
+
This file gives future coding agents the shortest reliable path into `py_lucidum`.
|
|
4
|
+
|
|
5
|
+
## Start Here
|
|
6
|
+
|
|
7
|
+
- Read `README.md` for user-facing installation, launch commands, and current tool behavior.
|
|
8
|
+
- Read `DEVELOPMENT.md` before non-trivial changes. It is the durable maintainer context for architecture, behavior contracts, test commands, and commit rules.
|
|
9
|
+
- Read the `Releasing` section in `DEVELOPMENT.md` before packaging, tagging, or publishing a release.
|
|
10
|
+
- Current GBM product notes live in `docs/specs/gbm-tool.md`; `docs/specs/gbm-tool_plan.md` is historical implementation context only.
|
|
11
|
+
|
|
12
|
+
## Environment Notes
|
|
13
|
+
|
|
14
|
+
- This repo is normally driven from Positron, not the Codex app. Do not try to use the Codex in-app Browser plugin here; it will not be available. For browser verification, use the repo's Playwright smoke tooling, local `curl` checks, or a manually launched local browser session.
|
|
15
|
+
- To launch the committed multi-Parquet fixture from a source checkout, run `.venv/bin/lucidum datasets/monthly --port 8000`. Folder inputs are for non-modelling tools only; do not combine this fixture with `glm`, `gbm`, or `--tools all`.
|
|
16
|
+
|
|
17
|
+
## Shape Of The App
|
|
18
|
+
|
|
19
|
+
- `py_lucidum` is a local-first FastAPI + DuckDB app with a static ES-module frontend.
|
|
20
|
+
- Backend app setup lives under `src/py_lucidum/app/`; core dataset/query helpers live in `src/py_lucidum/core/`.
|
|
21
|
+
- Tool backends live under `src/py_lucidum/tools/`.
|
|
22
|
+
- Frontend shell and tool modules live under `src/py_lucidum/static/app/`.
|
|
23
|
+
- `src/py_lucidum/static/app/main.js` is the frontend coordinator only: schema/defaults, KPI controls, saved filters, sidebar/footer layout, tool selection, cross-tool invalidation, reload orchestration, and boot flow.
|
|
24
|
+
- Frontend tool ownership is split by file: `dataset-viewer-tool.js`, `column-profile-tool.js`, `line-bar-tool.js`, `histogram-tool.js`, `uk-map-tool.js`, `glm-tool.js`, `glm-formula-builder.js`, `glm-model-navigator.js`, `glm-tabulations.js`, `gbm-tool.js`, `gbm-feature-parameter-controls.js`, `gbm-evaluation-chart.js`, `gbm-model-navigator.js`, `gbm-tab-orchestration.js`, `gbm-shap-tool.js`, `gbm-shap-chart.js`, `gbm-stacked-shap-tool.js`, `gbm-stacked-shap-chart.js`, `gbm-tree-viewer.js`, and `model-tool-shell.js`.
|
|
25
|
+
- Import-safe shared frontend helpers live under `src/py_lucidum/static/app/shared/`: API calls, formatters, schema/source helpers, Tabulator lazy loading, and action timing.
|
|
26
|
+
- `src/py_lucidum/static/app.css` is only the stable linked CSS manifest. Actual app styles live under `src/py_lucidum/static/styles/` by shell/tool boundary.
|
|
27
|
+
- Vendored browser libraries live under `src/py_lucidum/static/vendor/`. ECharts and Leaflet load from `index.html` because default tools need them at startup; Ace, Tabulator, D3, and ECharts GL are lazy-loaded by the tools/views that need them.
|
|
28
|
+
|
|
29
|
+
## Read The Smallest Useful Surface
|
|
30
|
+
|
|
31
|
+
Use this map before opening large files:
|
|
32
|
+
|
|
33
|
+
- App boot, tool switching, shared sidebar/filter/KPI behavior: read `static/app/main.js`, `static/styles/shell.css`, and `static/styles/controls.css`.
|
|
34
|
+
- Shared API, formatting, schema/source helpers, or timing behavior: read the matching file in `static/app/shared/` first; only read tool modules if call-site behavior matters.
|
|
35
|
+
- Dataset Viewer frontend: read `static/app/dataset-viewer-tool.js` and `static/styles/dataset-viewer.css`; backend behavior is under `tools/dataset_viewer/`.
|
|
36
|
+
- Column Profile frontend: read `static/app/column-profile-tool.js` and `static/styles/column-profile.css`; backend behavior is under `tools/column_profile/`.
|
|
37
|
+
- Line/Bar frontend: read `static/app/line-bar-tool.js` and `static/styles/line-bar.css`; backend behavior is under `tools/line_bar/`.
|
|
38
|
+
- Histogram frontend: read `static/app/histogram-tool.js` and `static/styles/histogram.css`; backend behavior is under `tools/histogram/`.
|
|
39
|
+
- UK Mapping frontend: read `static/app/uk-map-tool.js` and `static/styles/uk-map.css`; backend behavior and map static assets are under `tools/uk_map/`.
|
|
40
|
+
- GLM frontend: read `static/app/glm-tool.js`, `static/app/glm-formula-builder.js`, `static/app/glm-model-navigator.js`, `static/app/glm-tabulations.js`, and `static/styles/glm.css`; backend behavior is under `tools/glm/`.
|
|
41
|
+
- GBM frontend: read `static/app/gbm-tool.js`, `static/app/gbm-feature-parameter-controls.js`, `static/app/gbm-evaluation-chart.js`, `static/app/gbm-model-navigator.js`, `static/app/gbm-tab-orchestration.js`, and `static/styles/gbm.css`; SHAP, Stacked SHAP, and tree UI have their own `gbm-shap-*`, `gbm-stacked-shap-*`, and `gbm-tree-viewer.js` files. Backend behavior is under `tools/gbm/`.
|
|
42
|
+
- Generic modelling shell: read `static/app/model-tool-shell.js` and `static/styles/model-shell.css`.
|
|
43
|
+
- CSS changes: read `static/app.css` only to confirm import order. Put shared primitives in `styles/foundations.css` or `styles/controls.css`; put tool-specific selectors in the tool-owned stylesheet.
|
|
44
|
+
- Static asset contract checks and frontend string contracts live in `tests/test_static_assets.py`; browser smoke coverage lives in `tests/test_browser_smoke.py`.
|
|
45
|
+
|
|
46
|
+
## GBM Notes
|
|
47
|
+
|
|
48
|
+
- GBM is opt-in via `--tools gbm` and optional Python dependencies from `pip install -e ".[gbm]"`.
|
|
49
|
+
- LightGBM with its Arrow bridge, Polars/PyArrow, pandas, and numpy must be imported lazily in GBM training paths, not at base app import time. Large GBM matrices flow from DuckDB to Polars to numeric Arrow; pandas remains for compact artifacts, tabulations, and exports.
|
|
50
|
+
- On macOS, LightGBM may need Homebrew `libomp`; missing native runtime errors should stay actionable rather than surfacing as server 500s.
|
|
51
|
+
- GBM and GLM artifacts are sidecars under `.lucidum/datasets/<dataset-slug>/<dataset-signature>/models/` beside the dataset folder and must not be committed.
|
|
52
|
+
- The GBM SHAP tab reads saved SHAP sidecars only. Keep SHAP aggregation in backend GBM modules, keep SHAP UI/chart code separate, return dense grids for 3D surfaces, and lazy-load vendored ECharts GL only for those surface plots.
|
|
53
|
+
|
|
54
|
+
## Before Committing
|
|
55
|
+
|
|
56
|
+
Use the tiered runner documented in `DEVELOPMENT.md`. The normal development
|
|
57
|
+
loop selects focused tests from staged, unstaged, and untracked changes, with a
|
|
58
|
+
safe fallback to the broad lane for shared or unknown files:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
.venv/bin/python scripts/run_tests.py changed
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The broad development lane runs syntax checks, non-browser modules, fast GLM
|
|
65
|
+
contracts, and excludes slow modelling process integration cases:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
.venv/bin/python scripts/run_tests.py dev
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Use `focus` or `browser` with a specific target when you need to override the
|
|
72
|
+
automatic selection. The fast commit gate runs staged and unstaged whitespace
|
|
73
|
+
checks, dynamic syntax checks, and the change-aware unit lane:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
.venv/bin/python scripts/run_tests.py precommit
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The complete push gate runs full unittest discovery and all browser smoke tests
|
|
80
|
+
after the same whitespace and syntax checks:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
.venv/bin/python scripts/run_tests.py prepush
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The versioned `.githooks/pre-commit` and `.githooks/pre-push` hooks run those
|
|
87
|
+
commands automatically after one-time setup with
|
|
88
|
+
`git config core.hooksPath .githooks`. Do not manually run `prepush`
|
|
89
|
+
immediately before a normal `git push`, and do not weaken or bypass either hook
|
|
90
|
+
merely to save time.
|
|
91
|
+
|
|
92
|
+
- Before every commit, unless the user explicitly says not to, run `.venv/bin/python scripts/bump_version.py patch`. Include `pyproject.toml` in the same commit and report the final version number.
|
|
93
|
+
|
|
94
|
+
Keep local datasets, `.lucidum/`, virtualenvs, caches, build artifacts, and generated README previews out of git.
|
|
95
|
+
|
|
96
|
+
## Before Releasing
|
|
97
|
+
|
|
98
|
+
- Release only a clean `main` commit whose hosted `CI` workflow passed. Run the
|
|
99
|
+
release-only pipx/build checks before tagging; the tag workflow repeats the
|
|
100
|
+
artifact and installed-wheel checks.
|
|
101
|
+
- Make all release-preparation changes in one commit when a specific next
|
|
102
|
+
version is required. Apply the normal version bump once before that commit,
|
|
103
|
+
then create an annotated `vMAJOR.MINOR.PATCH` tag whose value exactly matches
|
|
104
|
+
`project.version` in `pyproject.toml`.
|
|
105
|
+
- Do not create or push a release tag, publish or approve a production
|
|
106
|
+
deployment, move a released tag, or yank a release without explicit user
|
|
107
|
+
authorization. Repository changes alone do not imply permission for those
|
|
108
|
+
external actions.
|
|
109
|
+
- PyPI filenames and versions cannot be reused, and published immutable GitHub
|
|
110
|
+
release tags/assets cannot be edited. Fix a bad published release by yanking
|
|
111
|
+
it when appropriate and publishing a higher patch version; never rebuild or
|
|
112
|
+
repoint the old version.
|
|
113
|
+
- `py-lucidum==X.Y.Z` pins Lucidum only. It does not lock transitive dependency
|
|
114
|
+
versions; consuming projects should use their own lock or constraints file
|
|
115
|
+
when they need a reproducible complete environment.
|