This repository contains a Python CLI and Shiny application for converting NHP demand-model activity aggregated into functional areas into capacity estimates. It is a work in progress intended for internal use only.
Prerequisites for running this model are on the team wiki.
This package uses uv; the commands below were
verified with uv 0.12.1.
Run the complete capacity conversion pipeline for all sites or selected sites:
uv run --locked -m nhp.capacity_conversion GUID
uv run --locked -m nhp.capacity_conversion GUID --ip-sites ALL --op-sites SITEA,SITEB --aae-sites SITEARun a single activity type:
uv run --locked -m nhp.capacity_conversion.op GUID
uv run --locked -m nhp.capacity_conversion.aae GUID --sites SITEARunning the pipeline will create a results/GUID/RUNTIME folder, with a
capacity_conversion_results.xlsx file within it.
The application requires:
AZ_STORAGE_EP: Azure Blob Storage account endpoint.AZ_STORAGE_RESULTS: container containing functional aggregations.AZ_TABLE_ENDPOINT: Azure Table Storage account endpoint.CAPACITY_MODEL_VERSION: functional-aggregation catalogue partition and blob path version, such asdevorprod.TABLE_NAME: table containing functional-aggregation metadata.
FEEDBACK_FORM_URL is required. Set it to the src URL from the Microsoft Forms
embed code.
Deployment rejects a missing or invalid URL. The application still reports that
the form is unavailable if the runtime configuration is unexpectedly missing.
Azure authentication uses DefaultAzureCredential. The credential must have
read access to both the Table catalogue and Blob results container. For local
Azure CLI authentication, run az login when needed.
The local application does not load .env automatically. Start the development
server from the repository root, pointing uv to your .env file:
uv run --env-file .env --locked --group app shiny run --reload app.pyThe application queries the table partition configured by
CAPACITY_MODEL_VERSION. It presents permitted datasets, scenario_name
values and scenario_runtime model-run times, using the selected entity's
RowKey as the functional aggregation GUID. It loads OP, A&E, IP day-case, IP
maternity and IP wards aggregations, reshaping each across all sites before
capacity conversion. It displays their capacity summaries and includes all
five activity types in the Excel download.
On Posit Connect, nhp_provider_<dataset> grants access to one dataset, while
nhp_devs and nhp_power_users grant access to every available aggregation.
Unrecognised or absent Connect groups grant no dataset access. Local development
permits all available aggregations.
The Shiny dependencies are in the app dependency group. requirements.txt is
generated for Posit Connect and must not be edited manually.
Regenerate and validate the Connect requirements after changing dependencies:
uv lock --check
uv export --no-default-groups --group app --no-hashes --output-file requirements.txt
uv pip compile requirements.txt --output-file /tmp/nhp-capacity-connect-requirements.txtThe final command reproduces Connect's dependency-resolution step and must complete successfully.
Consult the
official Posit Connect publishing documentation
before using rsconnect.
The interactive deployment helper loads .env automatically. Values in .env
override variables already set in the current environment, making .env the
source of truth for deployment. .env is ignored by Git; never commit its
credentials. In addition to the application runtime variables above, set:
CONNECT_SERVER: the Posit Connect server URL.CONNECT_API_KEY: a Posit Connect API key with permission to publish.CONNECT_APP_ID: the existing content GUID, required only for a redeployment. This is not the numeric content ID.
From the repository root, start the deployment interface with:
uv run --locked --group dev scripts/deploy_shiny.pyChoose whether to create new content or replace an existing deployment. Before
deploying, the helper checks the required tools, bundle files, and environment
variables, reports whether each effective value came from .env or the current
environment, and rejects invalid HTTPS endpoints. It then verifies the Connect
server and asks for confirmation. It does not display environment variable
values or include the API key in subprocess arguments.
The Deploy Shiny application workflow redeploys the existing application to:
- the
devPosit Connect environment after a push tomain; and - the
prodPosit Connect environment when a GitHub release is published.
Configure GitHub Environments named dev and prod. Define all of the
following as environment secrets in each one so the same workflow can select
the target's configuration and GitHub can mask their values in workflow logs:
CONNECT_SERVERCONNECT_APP_ID(the existing content GUID, not the numeric content ID)CONNECT_API_KEYAZ_STORAGE_EPAZ_STORAGE_RESULTSAZ_TABLE_ENDPOINTCAPACITY_MODEL_VERSIONTABLE_NAMEFEEDBACK_FORM_URL
The API key must be able to publish the content identified by that environment's
CONNECT_APP_ID. The workflow creates a Python Shiny manifest.json, then uses
Posit's connect-publish action to replace that GUID. Runtime secrets are
passed through the action's CONNECT_ENV_SET_* interface; the API key is not
passed to the deployed application. The explicit manifest excludes .env, so
repository content cannot override GitHub secrets. Configure required reviewers
and deployment protection rules on the prod GitHub Environment.
Pull requests and deployments use the same reusable CI workflow for the
lockfile, generated requirements, formatting, lint, type, unit and browser
checks. Requiring its pull-request status check is optional; deployment always
runs the complete CI workflow again against the exact commit being deployed.
Deployments are serialized per environment, with up to 100 runs queued. GitHub
does not guarantee queue order, so production accepts only the highest stable
vMajor.Minor.Patch tag on main. A queued dev run stops before expensive CI
when a newer commit reaches main, then checks the tip again immediately before
deployment.
The deployment starts only after verification succeeds. The publish action
targets the existing content by GUID and waits for Connect's deployment task to
finish. A follow-up authenticated HTTPS request requires HTTP 200 and retries
transient failures; curl does not forward the API-key header to another host
when following redirects.
The action applies supplied runtime environment variables after the content deployment succeeds. The initial manual deployment must therefore configure the runtime environment. A later environment-update failure can leave the new bundle using the previous values even though the workflow reports failure, so test configuration changes in dev first and retain previous production values securely until the production workflow succeeds.
Both Connect applications must be created manually before the workflow's first run. The local helper remains the supported way to create that initial content.
The version guard deliberately prevents redeploying an older GitHub release. If
the latest production deployment is unhealthy, activate the previous known-good
bundle from the application's Content Bundles page in Connect. Restore any
previous runtime configuration separately because it is not stored in the
bundle. Then revert the faulty change on main and publish a new patch release;
for example, recover from v1.4.2 with v1.4.3 rather than rerunning v1.4.1.
After the initial deployment, set its Custom content URL under Settings → Manage access to:
/nhp/dev/capacity-conversion/
The development application is available at connect.strategyunitwm.nhs.uk/nhp/dev/capacity-conversion/.