nhp-model 5.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nhp_model-5.0.0/.coveragerc +3 -0
- nhp_model-5.0.0/.github/copilot-instructions.md +292 -0
- nhp_model-5.0.0/.github/dependabot.yml +16 -0
- nhp_model-5.0.0/.github/workflows/build_app.yaml +79 -0
- nhp_model-5.0.0/.github/workflows/build_container.yaml +48 -0
- nhp_model-5.0.0/.github/workflows/build_schema.yaml +57 -0
- nhp_model-5.0.0/.github/workflows/codecov.yaml +35 -0
- nhp_model-5.0.0/.github/workflows/deploy_dev.yaml +22 -0
- nhp_model-5.0.0/.github/workflows/deploy_docs.yaml +57 -0
- nhp_model-5.0.0/.github/workflows/deploy_pr.yaml +47 -0
- nhp_model-5.0.0/.github/workflows/deploy_release.yaml +35 -0
- nhp_model-5.0.0/.github/workflows/linting.yaml +37 -0
- nhp_model-5.0.0/.github/workflows/remove_untagged_container_images.yaml +28 -0
- nhp_model-5.0.0/.github/workflows/removed_closed_prs.yaml +34 -0
- nhp_model-5.0.0/.gitignore +258 -0
- nhp_model-5.0.0/.vscode/extensions.json +8 -0
- nhp_model-5.0.0/.vscode/launch.json +39 -0
- nhp_model-5.0.0/.vscode/settings.json +17 -0
- nhp_model-5.0.0/.vscode/tasks.json +17 -0
- nhp_model-5.0.0/CITATION.cff +26 -0
- nhp_model-5.0.0/CODEOWNERS +5 -0
- nhp_model-5.0.0/Dockerfile +42 -0
- nhp_model-5.0.0/LICENSE +21 -0
- nhp_model-5.0.0/PKG-INFO +19 -0
- nhp_model-5.0.0/codecov.yml +10 -0
- nhp_model-5.0.0/docs/gen_ref_pages.py +33 -0
- nhp_model-5.0.0/docs/index.md +33 -0
- nhp_model-5.0.0/mkdocs.yml +41 -0
- nhp_model-5.0.0/pyproject.toml +119 -0
- nhp_model-5.0.0/readme.md +61 -0
- nhp_model-5.0.0/setup.cfg +4 -0
- nhp_model-5.0.0/src/nhp/docker/__init__.py +1 -0
- nhp_model-5.0.0/src/nhp/docker/__main__.py +100 -0
- nhp_model-5.0.0/src/nhp/docker/config.py +77 -0
- nhp_model-5.0.0/src/nhp/docker/run.py +350 -0
- nhp_model-5.0.0/src/nhp/model/__init__.py +11 -0
- nhp_model-5.0.0/src/nhp/model/__main__.py +97 -0
- nhp_model-5.0.0/src/nhp/model/_version.py +24 -0
- nhp_model-5.0.0/src/nhp/model/aae.py +207 -0
- nhp_model-5.0.0/src/nhp/model/activity_resampling.py +282 -0
- nhp_model-5.0.0/src/nhp/model/data/__init__.py +8 -0
- nhp_model-5.0.0/src/nhp/model/data/data.py +93 -0
- nhp_model-5.0.0/src/nhp/model/data/local.py +126 -0
- nhp_model-5.0.0/src/nhp/model/data/reference/__init__.py +44 -0
- nhp_model-5.0.0/src/nhp/model/data/reference/hsa_split_normal_params.csv +145 -0
- nhp_model-5.0.0/src/nhp/model/data/reference/life_expectancy.csv +277 -0
- nhp_model-5.0.0/src/nhp/model/data/reference/variant_lookup.json +19 -0
- nhp_model-5.0.0/src/nhp/model/health_status_adjustment.py +227 -0
- nhp_model-5.0.0/src/nhp/model/helpers.py +37 -0
- nhp_model-5.0.0/src/nhp/model/inpatients.py +514 -0
- nhp_model-5.0.0/src/nhp/model/model.py +520 -0
- nhp_model-5.0.0/src/nhp/model/model_iteration.py +276 -0
- nhp_model-5.0.0/src/nhp/model/outpatients.py +263 -0
- nhp_model-5.0.0/src/nhp/model/params/__init__.py +71 -0
- nhp_model-5.0.0/src/nhp/model/params/__main__.py +55 -0
- nhp_model-5.0.0/src/nhp/model/params/params-sample.json +1549 -0
- nhp_model-5.0.0/src/nhp/model/params/params-schema.json +1417 -0
- nhp_model-5.0.0/src/nhp/model/results.py +337 -0
- nhp_model-5.0.0/src/nhp/model/run.py +193 -0
- nhp_model-5.0.0/src/nhp_model.egg-info/PKG-INFO +19 -0
- nhp_model-5.0.0/src/nhp_model.egg-info/SOURCES.txt +100 -0
- nhp_model-5.0.0/src/nhp_model.egg-info/dependency_links.txt +1 -0
- nhp_model-5.0.0/src/nhp_model.egg-info/requires.txt +12 -0
- nhp_model-5.0.0/src/nhp_model.egg-info/scm_file_list.json +96 -0
- nhp_model-5.0.0/src/nhp_model.egg-info/scm_version.json +8 -0
- nhp_model-5.0.0/src/nhp_model.egg-info/top_level.txt +1 -0
- nhp_model-5.0.0/tests/conftest.py +10 -0
- nhp_model-5.0.0/tests/e2e/test_run_model_snapshot/test_all_model_runs_default_.csv +64 -0
- nhp_model-5.0.0/tests/e2e/test_run_model_snapshot/test_all_model_runs_step_counts_.csv +1853 -0
- nhp_model-5.0.0/tests/e2e/test_run_model_snapshot/test_model_results_returns_expected_keys.yml +12 -0
- nhp_model-5.0.0/tests/e2e/test_run_model_snapshot.py +73 -0
- nhp_model-5.0.0/tests/integration/nhp/model/test_params_validation.py +19 -0
- nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_default_results_aae_.csv +9 -0
- nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_default_results_ip_.csv +52 -0
- nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_default_results_op_.csv +7 -0
- nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_returns_expected_aggregations.yml +26 -0
- nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_step_counts_aae_.csv +131 -0
- nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_step_counts_ip_.csv +1569 -0
- nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_step_counts_op_.csv +149 -0
- nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run.py +47 -0
- nhp_model-5.0.0/tests/unit/nhp/docker/test___main__.py +238 -0
- nhp_model-5.0.0/tests/unit/nhp/docker/test_config.py +105 -0
- nhp_model-5.0.0/tests/unit/nhp/docker/test_run.py +548 -0
- nhp_model-5.0.0/tests/unit/nhp/model/__init__.py +0 -0
- nhp_model-5.0.0/tests/unit/nhp/model/data/test_data.py +68 -0
- nhp_model-5.0.0/tests/unit/nhp/model/data/test_local.py +195 -0
- nhp_model-5.0.0/tests/unit/nhp/model/data/test_reference.py +62 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test___init__.py +5 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test__main__.py +134 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_aae.py +249 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_activity_resampling.py +683 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_health_status_adjustment.py +472 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_helpers.py +32 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_inpatient_efficiencies.py +287 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_inpatients.py +693 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_model.py +915 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_model_iteration.py +522 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_outpatients.py +349 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_params.py +50 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_results.py +465 -0
- nhp_model-5.0.0/tests/unit/nhp/model/test_run.py +211 -0
- nhp_model-5.0.0/uv.lock +1520 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# NHP Model - Copilot Coding Agent Instructions
|
|
2
|
+
|
|
3
|
+
## Repository Overview
|
|
4
|
+
|
|
5
|
+
This is the **New Hospital Programme (NHP) Demand Model**, a Python package for healthcare activity prediction. The model provides modeling capabilities for inpatients, outpatients, and A&E (Accident & Emergency) services. It is built as a Python library using modern packaging tools and is deployed as both a Python package and a Docker container to Azure.
|
|
6
|
+
|
|
7
|
+
**Key Facts:**
|
|
8
|
+
- **Project Type:** Python package/library with Docker containerization
|
|
9
|
+
- **Python Version:** Requires Python 3.11 or higher (specified in pyproject.toml)
|
|
10
|
+
- **Package Manager:** `uv` (modern Python package manager from Astral)
|
|
11
|
+
- **Build System:** setuptools with setuptools-scm for versioning
|
|
12
|
+
- **Primary Language:** Python
|
|
13
|
+
- **Project Size:** Medium-sized Python project
|
|
14
|
+
- **Main Modules:** nhp.model (core model code), nhp.docker (Docker runtime)
|
|
15
|
+
|
|
16
|
+
## Environment Setup and Build Instructions
|
|
17
|
+
|
|
18
|
+
### Initial Setup
|
|
19
|
+
|
|
20
|
+
**ALWAYS start by installing uv and project dependencies:**
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Install uv using the recommended approach from Astral
|
|
24
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
25
|
+
|
|
26
|
+
# Install project dependencies (production only)
|
|
27
|
+
uv sync
|
|
28
|
+
|
|
29
|
+
# Install with dev dependencies for development/testing (RECOMMENDED for development)
|
|
30
|
+
uv sync --extra dev
|
|
31
|
+
|
|
32
|
+
# Install with docs dependencies for documentation
|
|
33
|
+
uv sync --extra docs
|
|
34
|
+
|
|
35
|
+
# Install multiple extras at once
|
|
36
|
+
uv sync --extra dev --extra docs
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Important:** The `uv sync` command only installs production dependencies. For development work (linting, testing), use `uv sync --extra dev` to install the dev dependencies.
|
|
40
|
+
|
|
41
|
+
**Python Version:** The project requires Python 3.11+. The CI uses Python 3.11 specifically via `uv python install` in workflows.
|
|
42
|
+
|
|
43
|
+
### Build Commands
|
|
44
|
+
|
|
45
|
+
**To build the package:**
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Standard build - creates wheel and source distribution
|
|
49
|
+
uv build
|
|
50
|
+
|
|
51
|
+
# Build for development (sets version to 0.dev0)
|
|
52
|
+
SETUPTOOLS_SCM_PRETEND_VERSION=0.dev0 uv build
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The build creates:
|
|
56
|
+
- `dist/nhp_model-<version>-py3-none-any.whl`
|
|
57
|
+
- `dist/nhp_model-<version>.tar.gz`
|
|
58
|
+
|
|
59
|
+
**Note:** The Dockerfile includes a TODO comment about forcing version numbers during Docker builds. Currently it uses `ENV SETUPTOOLS_SCM_PRETEND_VERSION=v0.0.0` as a workaround.
|
|
60
|
+
|
|
61
|
+
### Testing
|
|
62
|
+
|
|
63
|
+
**Unit Tests (ALWAYS run these before committing):**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Run all unit tests
|
|
67
|
+
uv run pytest tests/unit --verbose
|
|
68
|
+
|
|
69
|
+
# Run unit tests with coverage report
|
|
70
|
+
uv run pytest --cov=. tests/unit --ignore=tests --cov-branch --cov-report xml:coverage.xml
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Integration Tests:**
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Integration tests require test data in a specific format
|
|
77
|
+
# These are located in tests/integration/ but may require data setup
|
|
78
|
+
uv run pytest tests/integration --verbose
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**All unit tests must pass. Test failures are NOT acceptable.**
|
|
82
|
+
|
|
83
|
+
### Linting and Formatting
|
|
84
|
+
|
|
85
|
+
**ALWAYS run linting before committing. All linting checks MUST pass:**
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Run ruff linting check
|
|
89
|
+
uvx ruff check .
|
|
90
|
+
|
|
91
|
+
# Run ruff format check (no auto-formatting)
|
|
92
|
+
uvx ruff format --check .
|
|
93
|
+
|
|
94
|
+
# Auto-format code (if needed)
|
|
95
|
+
uvx ruff format .
|
|
96
|
+
|
|
97
|
+
# Run type checking with ty
|
|
98
|
+
uvx ty check .
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Linting Configuration:**
|
|
102
|
+
- Ruff config is in `pyproject.toml` under `[tool.ruff]`
|
|
103
|
+
- Line length: 100 characters
|
|
104
|
+
- Target Python version: 3.11
|
|
105
|
+
- Excludes: `notebooks/` directory
|
|
106
|
+
- Key rules: pydocstyle (D), pycodestyle (E/W), isort (I), pylint (PL), pandas-vet (PD), numpy (NPY), ruff-specific (RUF)
|
|
107
|
+
- Docstring convention: Google style
|
|
108
|
+
|
|
109
|
+
**The notebooks directory is excluded from linting and should not be linted.**
|
|
110
|
+
|
|
111
|
+
### Documentation
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Build documentation (requires docs dependencies)
|
|
115
|
+
uv run mkdocs build --clean
|
|
116
|
+
|
|
117
|
+
# Serve documentation locally
|
|
118
|
+
uv run mkdocs serve
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Documentation is deployed automatically to Connect via CI on main branch pushes.
|
|
122
|
+
|
|
123
|
+
### Running the Model
|
|
124
|
+
|
|
125
|
+
**Local execution:**
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Run with sample parameters (requires data in specified path)
|
|
129
|
+
uv run python -m nhp.model queue/params-sample.json -d data/synth --type all
|
|
130
|
+
|
|
131
|
+
# Run single model type
|
|
132
|
+
uv run python -m nhp.model queue/params-sample.json -d data --type ip # inpatients
|
|
133
|
+
uv run python -m nhp.model queue/params-sample.json -d data --type op # outpatients
|
|
134
|
+
uv run python -m nhp.model queue/params-sample.json -d data --type aae # A&E
|
|
135
|
+
|
|
136
|
+
# Run specific model iteration for debugging
|
|
137
|
+
uv run python -m nhp.model queue/params-sample.json -d data --model-run 1 --type ip
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Command-line arguments:**
|
|
141
|
+
- `params_file`: Path to JSON parameters file (default: `queue/params-sample.json`)
|
|
142
|
+
- `-d, --data-path`: Path to data directory (default: `data`)
|
|
143
|
+
- `-r, --model-run`: Which model iteration to run (default: 1)
|
|
144
|
+
- `-t, --type`: Model type - `all`, `ip`, `op`, or `aae` (default: `all`)
|
|
145
|
+
- `--save-full-model-results`: Save complete model results
|
|
146
|
+
|
|
147
|
+
**Data Requirements:**
|
|
148
|
+
The model expects data in parquet format organized by fiscal year and dataset:
|
|
149
|
+
- Format: `{data_path}/{file}/fyear={year}/dataset={dataset}/`
|
|
150
|
+
- Required files: `ip`, `op`, `aae`, `demographic_factors`, `birth_factors`, `hsa_activity_tables`, `hsa_gams` (pickle)
|
|
151
|
+
- Sample data location: `data/synth/` (synthetic dataset for testing - see GitHub issue #347)
|
|
152
|
+
|
|
153
|
+
## Project Structure
|
|
154
|
+
|
|
155
|
+
### Directory Layout
|
|
156
|
+
|
|
157
|
+
**Core Directories:**
|
|
158
|
+
- `.github/workflows/` - CI/CD pipelines (linting, codecov, build, deploy)
|
|
159
|
+
- `src/nhp/model/` - Core model: `__main__.py`, `model.py`, `inpatients.py`, `outpatients.py`, `aae.py`, `run.py`, `results.py`, `data/`
|
|
160
|
+
- `src/nhp/docker/` - Docker runtime with Azure Storage integration
|
|
161
|
+
- `tests/unit/` - Unit tests
|
|
162
|
+
- `tests/integration/` - Integration tests (require data)
|
|
163
|
+
- `docs/` - MkDocs documentation
|
|
164
|
+
- `notebooks/` - Databricks notebooks (excluded from linting)
|
|
165
|
+
- `queue/` - Parameter files (params-sample.json)
|
|
166
|
+
|
|
167
|
+
**Key Configuration Files:**
|
|
168
|
+
- `pyproject.toml` - Project metadata, dependencies, ruff/pytest/setuptools config
|
|
169
|
+
- `uv.lock` - Locked dependency versions (DO NOT modify manually)
|
|
170
|
+
- `params-schema.json` - JSON schema for model parameters (deployed to GitHub Pages)
|
|
171
|
+
|
|
172
|
+
### Architecture Overview
|
|
173
|
+
|
|
174
|
+
**Model Hierarchy:**
|
|
175
|
+
- `Model` (base class in model.py) - Common model functionality
|
|
176
|
+
- `InpatientsModel` - Inpatient demand modeling
|
|
177
|
+
- `OutpatientsModel` - Outpatient demand modeling
|
|
178
|
+
- `AaEModel` - A&E demand modeling
|
|
179
|
+
|
|
180
|
+
**Execution Flow:**
|
|
181
|
+
1. `__main__.py` parses CLI arguments and loads parameters
|
|
182
|
+
2. `run.py` orchestrates model execution (single or parallel runs)
|
|
183
|
+
3. `ModelIteration` runs a single model iteration
|
|
184
|
+
4. Results are aggregated and saved by `results.py`
|
|
185
|
+
|
|
186
|
+
**Data Loading:**
|
|
187
|
+
- Abstract `Data` interface allows multiple data sources
|
|
188
|
+
- `Local` loads from local parquet files
|
|
189
|
+
- `DatabricksNational` loads from Databricks (used in notebooks)
|
|
190
|
+
|
|
191
|
+
## CI/CD Validation Pipeline
|
|
192
|
+
|
|
193
|
+
### Pull Request Checks
|
|
194
|
+
|
|
195
|
+
**Every pull request triggers these workflows (ALL MUST PASS):**
|
|
196
|
+
|
|
197
|
+
1. **Linting** (`.github/workflows/linting.yaml`):
|
|
198
|
+
- `ruff check` - Code quality checks
|
|
199
|
+
- `ruff format --check` - Code formatting verification
|
|
200
|
+
- `ty check .` - Type checking
|
|
201
|
+
|
|
202
|
+
2. **Code Coverage** (`.github/workflows/codecov.yaml`):
|
|
203
|
+
- Runs unit tests with coverage
|
|
204
|
+
- Uploads to Codecov
|
|
205
|
+
- Requires passing tests
|
|
206
|
+
|
|
207
|
+
**IMPORTANT:** All linting and test checks must pass before merge. DO NOT skip or disable these checks.
|
|
208
|
+
|
|
209
|
+
### Main Branch / Release Workflows
|
|
210
|
+
|
|
211
|
+
On push to main or tags:
|
|
212
|
+
|
|
213
|
+
1. **build_app.yaml**: Builds Python wheel, uploads to Azure Storage and GitHub releases
|
|
214
|
+
2. **build_schema.yaml**: Deploys params-schema.json to GitHub Pages
|
|
215
|
+
3. **build_container.yaml**: Builds and pushes Docker image to GitHub Container Registry
|
|
216
|
+
4. **deploy_docs.yaml**: Builds and deploys MkDocs documentation to RStudio Connect
|
|
217
|
+
|
|
218
|
+
### Docker Deployment
|
|
219
|
+
|
|
220
|
+
The model is containerized using:
|
|
221
|
+
- Base image: `ghcr.io/astral-sh/uv:python3.11-alpine`
|
|
222
|
+
- Build args: `app_version`, `data_version`, `storage_account`
|
|
223
|
+
- Entry point: `python -m nhp.docker`
|
|
224
|
+
- Tags: `dev` (PRs), `v*.*.*` (releases), `latest` (latest release)
|
|
225
|
+
|
|
226
|
+
## Common Issues and Workarounds
|
|
227
|
+
|
|
228
|
+
**Known Issues:**
|
|
229
|
+
1. **Dockerfile Version**: Uses `ENV SETUPTOOLS_SCM_PRETEND_VERSION=v0.0.0` because setuptools-scm needs git metadata (TODO: build wheel and copy instead)
|
|
230
|
+
2. **Data Structure**: Model expects parquet files at `{data_path}/{file}/fyear={year}/dataset={dataset}/`. Missing files cause runtime errors.
|
|
231
|
+
3. **Notebooks**: `notebooks/` directory excluded from linting - don't lint these Databricks notebooks.
|
|
232
|
+
|
|
233
|
+
**Environment Variables (Docker):**
|
|
234
|
+
- `APP_VERSION`, `DATA_VERSION` (default: "dev")
|
|
235
|
+
- `STORAGE_ACCOUNT` (required for Azure), `BATCH_SIZE` (default: 16)
|
|
236
|
+
- `.env` file supported via python-dotenv for local development
|
|
237
|
+
|
|
238
|
+
## Testing Strategy
|
|
239
|
+
|
|
240
|
+
- **Unit Tests**: `tests/unit/` - Mock-based, parameterized. **ALWAYS run before committing.**
|
|
241
|
+
- **Integration Tests**: `tests/integration/` - Require properly formatted test data, test end-to-end runs
|
|
242
|
+
- **Test Organization**: pytest-mock for mocking, fixtures in `tests/conftest.py`
|
|
243
|
+
- **Coverage**: High coverage maintained via Codecov integration
|
|
244
|
+
|
|
245
|
+
## Best Practices for Coding Agents
|
|
246
|
+
|
|
247
|
+
1. **ALWAYS install dependencies first**: Run `uv sync --extra dev` before any development work.
|
|
248
|
+
|
|
249
|
+
2. **ALWAYS run linting before committing**: Run `uvx ruff check .` and `uvx ruff format --check .` - these MUST pass.
|
|
250
|
+
|
|
251
|
+
3. **ALWAYS run unit tests**: Run `uv run pytest tests/unit` before committing - all tests MUST pass.
|
|
252
|
+
|
|
253
|
+
4. **Follow Google docstring convention**: All public functions/classes must have Google-style docstrings (enforced by ruff).
|
|
254
|
+
|
|
255
|
+
5. **Respect line length**: Maximum 100 characters per line (ruff will enforce this).
|
|
256
|
+
|
|
257
|
+
6. **Don't modify notebooks**: The `notebooks/` directory is excluded from linting for a reason. These are Databricks notebooks with special formatting.
|
|
258
|
+
|
|
259
|
+
7. **Use uv for all Python commands**: Prefix commands with `uv run` to ensure correct virtual environment usage.
|
|
260
|
+
|
|
261
|
+
8. **Don't modify uv.lock manually**: Use `uv sync` to update dependencies.
|
|
262
|
+
|
|
263
|
+
9. **Test locally before pushing**: The CI checks are strict and will fail if linting/tests don't pass.
|
|
264
|
+
|
|
265
|
+
10. **Understand the data structure**: The model requires specific data formats. If testing model execution, ensure proper test data is available or use existing test fixtures.
|
|
266
|
+
|
|
267
|
+
## Quick Reference
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# Setup (production + dev dependencies)
|
|
271
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
272
|
+
uv sync --extra dev
|
|
273
|
+
|
|
274
|
+
# Lint (MUST pass)
|
|
275
|
+
uvx ruff check .
|
|
276
|
+
uvx ruff format --check .
|
|
277
|
+
|
|
278
|
+
# Test (MUST pass)
|
|
279
|
+
uv run pytest tests/unit --verbose
|
|
280
|
+
|
|
281
|
+
# Build
|
|
282
|
+
uv build
|
|
283
|
+
|
|
284
|
+
# Run model (requires data)
|
|
285
|
+
uv run python -m nhp.model queue/params-sample.json -d data --type all
|
|
286
|
+
|
|
287
|
+
# Build docs (requires docs extras)
|
|
288
|
+
uv sync --extra docs
|
|
289
|
+
uv run mkdocs build --clean
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**When in doubt, check the CI workflows in `.github/workflows/` - they define the exact validation steps used in the pipeline.**
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: "uv"
|
|
9
|
+
directory: "/"
|
|
10
|
+
schedule:
|
|
11
|
+
interval: "weekly"
|
|
12
|
+
|
|
13
|
+
groups:
|
|
14
|
+
all-dependencies:
|
|
15
|
+
patterns:
|
|
16
|
+
- "*"
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
on:
|
|
2
|
+
push:
|
|
3
|
+
branches:
|
|
4
|
+
- main
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-app:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: "Checkout GitHub Action"
|
|
13
|
+
uses: actions/checkout@v6
|
|
14
|
+
|
|
15
|
+
- name: Install the latest version of uv
|
|
16
|
+
uses: astral-sh/setup-uv@v7
|
|
17
|
+
with:
|
|
18
|
+
version: "latest"
|
|
19
|
+
enable-cache: true
|
|
20
|
+
cache-dependency-glob: "uv.lock"
|
|
21
|
+
|
|
22
|
+
- name: Build (release)
|
|
23
|
+
if: github.ref != 'refs/heads/main'
|
|
24
|
+
run: uv build
|
|
25
|
+
|
|
26
|
+
- name: Build (dev)
|
|
27
|
+
if: github.ref == 'refs/heads/main'
|
|
28
|
+
env:
|
|
29
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: 0.dev0
|
|
30
|
+
run: uv build
|
|
31
|
+
|
|
32
|
+
- name: Generate artifact
|
|
33
|
+
uses: actions/upload-artifact@v6
|
|
34
|
+
with:
|
|
35
|
+
name: dist-whl
|
|
36
|
+
path: dist/*.whl
|
|
37
|
+
|
|
38
|
+
upload-build-to-storage-account:
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
needs: ["build-app"]
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- name: Download build artifact
|
|
44
|
+
uses: actions/download-artifact@v8
|
|
45
|
+
with:
|
|
46
|
+
name: dist-whl
|
|
47
|
+
path: .
|
|
48
|
+
|
|
49
|
+
- name: Install Azure CLI
|
|
50
|
+
uses: Azure/setup-azd@v2
|
|
51
|
+
|
|
52
|
+
- name: Upload to blob storage
|
|
53
|
+
run: |
|
|
54
|
+
az storage blob upload \
|
|
55
|
+
--account-name ${{ secrets.NHP_STORAGE_ACCOUNT }} \
|
|
56
|
+
--container-name app \
|
|
57
|
+
--file $(ls *.whl) \
|
|
58
|
+
--sas-token "${{ secrets.APP_CONTAINER_SAS }}" \
|
|
59
|
+
--overwrite
|
|
60
|
+
|
|
61
|
+
add-build-to-release:
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
needs: ["build-app"]
|
|
64
|
+
permissions:
|
|
65
|
+
contents: write
|
|
66
|
+
|
|
67
|
+
steps:
|
|
68
|
+
- name: Download build artifact
|
|
69
|
+
uses: actions/download-artifact@v8
|
|
70
|
+
with:
|
|
71
|
+
name: dist-whl
|
|
72
|
+
path: .
|
|
73
|
+
- name: Upload artifact to the GitHub Release
|
|
74
|
+
uses: softprops/action-gh-release@v2
|
|
75
|
+
if: github.ref_type == 'tag'
|
|
76
|
+
with:
|
|
77
|
+
files: "*.whl"
|
|
78
|
+
env:
|
|
79
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: Build and push container image
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
docker-tag:
|
|
7
|
+
required: true
|
|
8
|
+
default: dev
|
|
9
|
+
type: string
|
|
10
|
+
app-version:
|
|
11
|
+
required: true
|
|
12
|
+
default: dev
|
|
13
|
+
type: string
|
|
14
|
+
data-version:
|
|
15
|
+
required: true
|
|
16
|
+
default: dev
|
|
17
|
+
type: string
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
build-container:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- name: "Checkout GitHub Action"
|
|
24
|
+
uses: actions/checkout@v6
|
|
25
|
+
|
|
26
|
+
- name: "Login to GitHub Container Registry"
|
|
27
|
+
uses: docker/login-action@v4
|
|
28
|
+
with:
|
|
29
|
+
registry: ghcr.io
|
|
30
|
+
username: ${{github.actor}}
|
|
31
|
+
password: ${{secrets.GITHUB_TOKEN}}
|
|
32
|
+
- name: Set up Docker Buildx
|
|
33
|
+
uses: docker/setup-buildx-action@v4
|
|
34
|
+
|
|
35
|
+
- name: "Build image"
|
|
36
|
+
uses: docker/build-push-action@v7
|
|
37
|
+
with:
|
|
38
|
+
context: .
|
|
39
|
+
tags: ${{ inputs.docker-tag }}
|
|
40
|
+
push: true
|
|
41
|
+
cache-from: type=gha
|
|
42
|
+
cache-to: type=gha,mode=max
|
|
43
|
+
platforms: linux/amd64
|
|
44
|
+
provenance: false
|
|
45
|
+
sbom: false
|
|
46
|
+
build-args: |
|
|
47
|
+
app_version=${{ inputs.app-version }}
|
|
48
|
+
data_version=${{ inputs.data-version }}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Deploy schema.json to GitHub Pages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
schema-tag:
|
|
7
|
+
required: true
|
|
8
|
+
default: dev
|
|
9
|
+
type: string
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
pages: write
|
|
13
|
+
id-token: write
|
|
14
|
+
contents: write
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout repository
|
|
21
|
+
uses: actions/checkout@v6
|
|
22
|
+
|
|
23
|
+
- name: Clone existing schemas branch content
|
|
24
|
+
run: |
|
|
25
|
+
git fetch --depth=1 origin schemas
|
|
26
|
+
git worktree add schemas schemas
|
|
27
|
+
|
|
28
|
+
- name: Copy schema to app version path
|
|
29
|
+
run: |
|
|
30
|
+
mkdir -p schemas/${{ inputs.schema-tag }}
|
|
31
|
+
sed '/$id/ s/dev/${{ inputs.schema-tag }}/' src/nhp/model/params/params-schema.json > schemas/${{ inputs.schema-tag }}/params-schema.json
|
|
32
|
+
|
|
33
|
+
- name: Commit the schema
|
|
34
|
+
run: |
|
|
35
|
+
git config user.name "github-actions[bot]"
|
|
36
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
37
|
+
pushd schemas
|
|
38
|
+
git add ${{ inputs.schema-tag }}/params-schema.json
|
|
39
|
+
git commit -m "adding schema for ${{ inputs.schema-tag }}" || echo "No changes to commit"
|
|
40
|
+
git push origin schemas
|
|
41
|
+
popd
|
|
42
|
+
|
|
43
|
+
- name: Upload to GitHub Pages
|
|
44
|
+
uses: actions/upload-pages-artifact@v4
|
|
45
|
+
with:
|
|
46
|
+
path: schemas
|
|
47
|
+
|
|
48
|
+
deploy:
|
|
49
|
+
needs: build
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
environment:
|
|
52
|
+
name: github-pages
|
|
53
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
54
|
+
steps:
|
|
55
|
+
- name: Deploy to GitHub Pages
|
|
56
|
+
id: deployment
|
|
57
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: CodeCov
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
run:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
defaults:
|
|
14
|
+
run:
|
|
15
|
+
shell: bash -l {0}
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v6
|
|
19
|
+
|
|
20
|
+
- name: Install the latest version of uv
|
|
21
|
+
uses: astral-sh/setup-uv@v7
|
|
22
|
+
with:
|
|
23
|
+
version: "latest"
|
|
24
|
+
activate-environment: true
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: uv sync --dev
|
|
28
|
+
|
|
29
|
+
- name: Generate Report
|
|
30
|
+
run: uv run pytest --cov=. tests/unit --ignore=tests --cov-branch --cov-report xml:coverage.xml
|
|
31
|
+
|
|
32
|
+
- name: Upload Coverage to Codecov
|
|
33
|
+
uses: codecov/codecov-action@v5
|
|
34
|
+
with:
|
|
35
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
on:
|
|
2
|
+
push:
|
|
3
|
+
branches:
|
|
4
|
+
- main
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
|
|
7
|
+
name: Deploy Dev
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
deploy-ghcr-dev:
|
|
11
|
+
uses: ./.github/workflows/build_container.yaml
|
|
12
|
+
with:
|
|
13
|
+
docker-tag: ghcr.io/the-strategy-unit/nhp_model:dev
|
|
14
|
+
app-version: dev
|
|
15
|
+
data-version: dev
|
|
16
|
+
secrets: inherit
|
|
17
|
+
|
|
18
|
+
deploy-dev-schema:
|
|
19
|
+
uses: ./.github/workflows/build_schema.yaml
|
|
20
|
+
with:
|
|
21
|
+
schema-tag: dev
|
|
22
|
+
secrets: inherit
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Deploy Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
pages: write
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: "pages"
|
|
14
|
+
cancel-in-progress: false
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v6
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v7
|
|
24
|
+
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
run: uv python install
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: uv sync --group docs
|
|
30
|
+
|
|
31
|
+
- name: Build documentation
|
|
32
|
+
run: uv run mkdocs build --clean
|
|
33
|
+
|
|
34
|
+
- name: Upload artifact
|
|
35
|
+
uses: actions/upload-artifact@v6
|
|
36
|
+
with:
|
|
37
|
+
name: site
|
|
38
|
+
path: ./site
|
|
39
|
+
|
|
40
|
+
deploy:
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
needs: build
|
|
43
|
+
steps:
|
|
44
|
+
- name: Download artifact
|
|
45
|
+
uses: actions/download-artifact@v8
|
|
46
|
+
with:
|
|
47
|
+
name: site
|
|
48
|
+
path: ./site
|
|
49
|
+
|
|
50
|
+
- name: Install uv (for rsconnect)
|
|
51
|
+
uses: astral-sh/setup-uv@v7
|
|
52
|
+
|
|
53
|
+
- name: Configure Connect
|
|
54
|
+
run: uvx rsconnect add -s ${{ secrets.RSCONNECT_URL }} -n connect -k ${{ secrets.RSCONNECT_API_KEY }}
|
|
55
|
+
|
|
56
|
+
- name: Deploy to Connect
|
|
57
|
+
run: uvx rsconnect deploy html site -a ${{ vars.CONNECT_DOCS_APP_ID }}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
on:
|
|
2
|
+
pull_request:
|
|
3
|
+
types: [opened, synchronize, reopened]
|
|
4
|
+
|
|
5
|
+
name: Deploy PR
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
deploy-ghcr-pr:
|
|
9
|
+
# only allow this workflow to run on PRs from *this* repository, not from forks
|
|
10
|
+
# this is to prevent the possibility of a malicious actor creating a PR from a fork that triggers this workflow and
|
|
11
|
+
# pushes a container image to our registry
|
|
12
|
+
if: ${{ github.actor != 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.repository }}
|
|
13
|
+
uses: ./.github/workflows/build_container.yaml
|
|
14
|
+
with:
|
|
15
|
+
docker-tag: ghcr.io/the-strategy-unit/nhp_model:pr-${{ github.event.number }}
|
|
16
|
+
app-version: dev
|
|
17
|
+
data-version: dev
|
|
18
|
+
secrets: inherit
|
|
19
|
+
|
|
20
|
+
add-comment-to-pr:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
needs: ["deploy-ghcr-pr"]
|
|
23
|
+
steps:
|
|
24
|
+
- name: Find Comment
|
|
25
|
+
uses: peter-evans/find-comment@v4
|
|
26
|
+
id: fc
|
|
27
|
+
with:
|
|
28
|
+
issue-number: ${{ github.event.pull_request.number }}
|
|
29
|
+
comment-author: "github-actions[bot]"
|
|
30
|
+
body-includes: "## ✅ A new build is available"
|
|
31
|
+
|
|
32
|
+
- name: Comment with container image link
|
|
33
|
+
if: github.event_name == 'pull_request'
|
|
34
|
+
uses: peter-evans/create-or-update-comment@v5
|
|
35
|
+
with:
|
|
36
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
37
|
+
comment-id: ${{ steps.fc.outputs.comment-id }}
|
|
38
|
+
issue-number: ${{ github.event.pull_request.number }}
|
|
39
|
+
body: |
|
|
40
|
+
## ✅ A new build is available.
|
|
41
|
+
|
|
42
|
+
You can use the following to use pull the image into your local environment:
|
|
43
|
+
|
|
44
|
+
``` bash
|
|
45
|
+
docker pull ghcr.io/the-strategy-unit/nhp_model:pr-${{ github.event.number }}
|
|
46
|
+
```
|
|
47
|
+
edit-mode: replace
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
on:
|
|
2
|
+
push:
|
|
3
|
+
tags:
|
|
4
|
+
- "v*.*.*"
|
|
5
|
+
|
|
6
|
+
name: Deploy Production
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
set-tag:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
outputs:
|
|
12
|
+
tag: ${{ steps.create-tag.outputs.TAG }}
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Create tag
|
|
16
|
+
id: create-tag
|
|
17
|
+
run: |
|
|
18
|
+
TAG=`echo ${{ github.ref_name }} | awk 'BEGIN { FS="."; } { print ""$1"."$2; }'`
|
|
19
|
+
echo "TAG=$TAG" >> $GITHUB_OUTPUT
|
|
20
|
+
|
|
21
|
+
deploy-ghcr-production:
|
|
22
|
+
needs: [set-tag]
|
|
23
|
+
uses: ./.github/workflows/build_container.yaml
|
|
24
|
+
with:
|
|
25
|
+
docker-tag: ghcr.io/the-strategy-unit/nhp_model:${{ needs.set-tag.outputs.tag }},ghcr.io/the-strategy-unit/nhp_model:latest
|
|
26
|
+
app-version: ${{ github.ref_name }}
|
|
27
|
+
data-version: ${{vars.data_version}}
|
|
28
|
+
secrets: inherit
|
|
29
|
+
|
|
30
|
+
deploy-schema:
|
|
31
|
+
needs: [set-tag]
|
|
32
|
+
|
|
33
|
+
uses: ./.github/workflows/build_schema.yaml
|
|
34
|
+
with:
|
|
35
|
+
schema-tag: ${{ needs.set-tag.outputs.tag }}
|