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.
Files changed (102) hide show
  1. nhp_model-5.0.0/.coveragerc +3 -0
  2. nhp_model-5.0.0/.github/copilot-instructions.md +292 -0
  3. nhp_model-5.0.0/.github/dependabot.yml +16 -0
  4. nhp_model-5.0.0/.github/workflows/build_app.yaml +79 -0
  5. nhp_model-5.0.0/.github/workflows/build_container.yaml +48 -0
  6. nhp_model-5.0.0/.github/workflows/build_schema.yaml +57 -0
  7. nhp_model-5.0.0/.github/workflows/codecov.yaml +35 -0
  8. nhp_model-5.0.0/.github/workflows/deploy_dev.yaml +22 -0
  9. nhp_model-5.0.0/.github/workflows/deploy_docs.yaml +57 -0
  10. nhp_model-5.0.0/.github/workflows/deploy_pr.yaml +47 -0
  11. nhp_model-5.0.0/.github/workflows/deploy_release.yaml +35 -0
  12. nhp_model-5.0.0/.github/workflows/linting.yaml +37 -0
  13. nhp_model-5.0.0/.github/workflows/remove_untagged_container_images.yaml +28 -0
  14. nhp_model-5.0.0/.github/workflows/removed_closed_prs.yaml +34 -0
  15. nhp_model-5.0.0/.gitignore +258 -0
  16. nhp_model-5.0.0/.vscode/extensions.json +8 -0
  17. nhp_model-5.0.0/.vscode/launch.json +39 -0
  18. nhp_model-5.0.0/.vscode/settings.json +17 -0
  19. nhp_model-5.0.0/.vscode/tasks.json +17 -0
  20. nhp_model-5.0.0/CITATION.cff +26 -0
  21. nhp_model-5.0.0/CODEOWNERS +5 -0
  22. nhp_model-5.0.0/Dockerfile +42 -0
  23. nhp_model-5.0.0/LICENSE +21 -0
  24. nhp_model-5.0.0/PKG-INFO +19 -0
  25. nhp_model-5.0.0/codecov.yml +10 -0
  26. nhp_model-5.0.0/docs/gen_ref_pages.py +33 -0
  27. nhp_model-5.0.0/docs/index.md +33 -0
  28. nhp_model-5.0.0/mkdocs.yml +41 -0
  29. nhp_model-5.0.0/pyproject.toml +119 -0
  30. nhp_model-5.0.0/readme.md +61 -0
  31. nhp_model-5.0.0/setup.cfg +4 -0
  32. nhp_model-5.0.0/src/nhp/docker/__init__.py +1 -0
  33. nhp_model-5.0.0/src/nhp/docker/__main__.py +100 -0
  34. nhp_model-5.0.0/src/nhp/docker/config.py +77 -0
  35. nhp_model-5.0.0/src/nhp/docker/run.py +350 -0
  36. nhp_model-5.0.0/src/nhp/model/__init__.py +11 -0
  37. nhp_model-5.0.0/src/nhp/model/__main__.py +97 -0
  38. nhp_model-5.0.0/src/nhp/model/_version.py +24 -0
  39. nhp_model-5.0.0/src/nhp/model/aae.py +207 -0
  40. nhp_model-5.0.0/src/nhp/model/activity_resampling.py +282 -0
  41. nhp_model-5.0.0/src/nhp/model/data/__init__.py +8 -0
  42. nhp_model-5.0.0/src/nhp/model/data/data.py +93 -0
  43. nhp_model-5.0.0/src/nhp/model/data/local.py +126 -0
  44. nhp_model-5.0.0/src/nhp/model/data/reference/__init__.py +44 -0
  45. nhp_model-5.0.0/src/nhp/model/data/reference/hsa_split_normal_params.csv +145 -0
  46. nhp_model-5.0.0/src/nhp/model/data/reference/life_expectancy.csv +277 -0
  47. nhp_model-5.0.0/src/nhp/model/data/reference/variant_lookup.json +19 -0
  48. nhp_model-5.0.0/src/nhp/model/health_status_adjustment.py +227 -0
  49. nhp_model-5.0.0/src/nhp/model/helpers.py +37 -0
  50. nhp_model-5.0.0/src/nhp/model/inpatients.py +514 -0
  51. nhp_model-5.0.0/src/nhp/model/model.py +520 -0
  52. nhp_model-5.0.0/src/nhp/model/model_iteration.py +276 -0
  53. nhp_model-5.0.0/src/nhp/model/outpatients.py +263 -0
  54. nhp_model-5.0.0/src/nhp/model/params/__init__.py +71 -0
  55. nhp_model-5.0.0/src/nhp/model/params/__main__.py +55 -0
  56. nhp_model-5.0.0/src/nhp/model/params/params-sample.json +1549 -0
  57. nhp_model-5.0.0/src/nhp/model/params/params-schema.json +1417 -0
  58. nhp_model-5.0.0/src/nhp/model/results.py +337 -0
  59. nhp_model-5.0.0/src/nhp/model/run.py +193 -0
  60. nhp_model-5.0.0/src/nhp_model.egg-info/PKG-INFO +19 -0
  61. nhp_model-5.0.0/src/nhp_model.egg-info/SOURCES.txt +100 -0
  62. nhp_model-5.0.0/src/nhp_model.egg-info/dependency_links.txt +1 -0
  63. nhp_model-5.0.0/src/nhp_model.egg-info/requires.txt +12 -0
  64. nhp_model-5.0.0/src/nhp_model.egg-info/scm_file_list.json +96 -0
  65. nhp_model-5.0.0/src/nhp_model.egg-info/scm_version.json +8 -0
  66. nhp_model-5.0.0/src/nhp_model.egg-info/top_level.txt +1 -0
  67. nhp_model-5.0.0/tests/conftest.py +10 -0
  68. nhp_model-5.0.0/tests/e2e/test_run_model_snapshot/test_all_model_runs_default_.csv +64 -0
  69. nhp_model-5.0.0/tests/e2e/test_run_model_snapshot/test_all_model_runs_step_counts_.csv +1853 -0
  70. nhp_model-5.0.0/tests/e2e/test_run_model_snapshot/test_model_results_returns_expected_keys.yml +12 -0
  71. nhp_model-5.0.0/tests/e2e/test_run_model_snapshot.py +73 -0
  72. nhp_model-5.0.0/tests/integration/nhp/model/test_params_validation.py +19 -0
  73. nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_default_results_aae_.csv +9 -0
  74. nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_default_results_ip_.csv +52 -0
  75. nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_default_results_op_.csv +7 -0
  76. nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_returns_expected_aggregations.yml +26 -0
  77. nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_step_counts_aae_.csv +131 -0
  78. nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_step_counts_ip_.csv +1569 -0
  79. nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run/test_model_step_counts_op_.csv +149 -0
  80. nhp_model-5.0.0/tests/integration/nhp/model/test_single_model_run.py +47 -0
  81. nhp_model-5.0.0/tests/unit/nhp/docker/test___main__.py +238 -0
  82. nhp_model-5.0.0/tests/unit/nhp/docker/test_config.py +105 -0
  83. nhp_model-5.0.0/tests/unit/nhp/docker/test_run.py +548 -0
  84. nhp_model-5.0.0/tests/unit/nhp/model/__init__.py +0 -0
  85. nhp_model-5.0.0/tests/unit/nhp/model/data/test_data.py +68 -0
  86. nhp_model-5.0.0/tests/unit/nhp/model/data/test_local.py +195 -0
  87. nhp_model-5.0.0/tests/unit/nhp/model/data/test_reference.py +62 -0
  88. nhp_model-5.0.0/tests/unit/nhp/model/test___init__.py +5 -0
  89. nhp_model-5.0.0/tests/unit/nhp/model/test__main__.py +134 -0
  90. nhp_model-5.0.0/tests/unit/nhp/model/test_aae.py +249 -0
  91. nhp_model-5.0.0/tests/unit/nhp/model/test_activity_resampling.py +683 -0
  92. nhp_model-5.0.0/tests/unit/nhp/model/test_health_status_adjustment.py +472 -0
  93. nhp_model-5.0.0/tests/unit/nhp/model/test_helpers.py +32 -0
  94. nhp_model-5.0.0/tests/unit/nhp/model/test_inpatient_efficiencies.py +287 -0
  95. nhp_model-5.0.0/tests/unit/nhp/model/test_inpatients.py +693 -0
  96. nhp_model-5.0.0/tests/unit/nhp/model/test_model.py +915 -0
  97. nhp_model-5.0.0/tests/unit/nhp/model/test_model_iteration.py +522 -0
  98. nhp_model-5.0.0/tests/unit/nhp/model/test_outpatients.py +349 -0
  99. nhp_model-5.0.0/tests/unit/nhp/model/test_params.py +50 -0
  100. nhp_model-5.0.0/tests/unit/nhp/model/test_results.py +465 -0
  101. nhp_model-5.0.0/tests/unit/nhp/model/test_run.py +211 -0
  102. nhp_model-5.0.0/uv.lock +1520 -0
@@ -0,0 +1,3 @@
1
+ [run]
2
+ omit =
3
+ tests/*
@@ -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 }}