Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
6d0038a
Initial pre-processing script
david-mears-2 Aug 4, 2026
3b3e514
Add data inputs (examples) and output
david-mears-2 Aug 4, 2026
90d736c
Update pre-processing script to make use of tidyr and dplyr
david-mears-2 Aug 4, 2026
d929a23
Use a custom list of in-scope variants, per-STAVE release.
david-mears-2 Aug 4, 2026
3e78a93
Don't filter down STAVE data at all
david-mears-2 Aug 5, 2026
f247f1c
Extract R script from pre-processing draft md; use scripts dir for da…
david-mears-2 Aug 5, 2026
46631d6
Use command line args for R script
david-mears-2 Aug 5, 2026
2f56dbc
Potential fix for pull request finding
david-mears-2 Aug 5, 2026
263fc4a
mv .gitkeep
david-mears-2 Aug 5, 2026
965ed89
Small script refactors
david-mears-2 Aug 5, 2026
5b3ed13
Add README.md
david-mears-2 Aug 5, 2026
2c5b22c
Use cli package for messages and errors in process_stave.R
Copilot Aug 6, 2026
baaf842
Add script to generate example model output parquet files
david-mears-2 Aug 6, 2026
c97109e
Add metadata for model outputs
david-mears-2 Aug 6, 2026
7861d0c
Rename column variant_string to variant to match survey data parquet
david-mears-2 Aug 6, 2026
581b2e2
wip wip wip wip
david-mears-2 Aug 7, 2026
1ed17ca
Make versions soft-coded in metadata endpoint
david-mears-2 Aug 17, 2026
4b724b4
Make prevalences parquets include all ancestor region ids, not just r…
david-mears-2 Aug 17, 2026
db7bc7c
Implement prevalences endpoint
david-mears-2 Aug 17, 2026
fd6f9b6
Update example model outputs generator to (1) go back to 1970 more of…
david-mears-2 Aug 18, 2026
efe91a8
Add instrumentation for timing requests
david-mears-2 Aug 18, 2026
82f5dbf
Fix survey data requests scoped by admin0
david-mears-2 Aug 18, 2026
e8ec6c7
Response optimizations: columnar format and round high-precision floats
david-mears-2 Aug 19, 2026
c421893
Rows for survey data, columns for prevalences
david-mears-2 Aug 19, 2026
43eb383
Fix keys/names of rounded columns
david-mears-2 Aug 19, 2026
da405b1
Extract grout region metadata fetch into a startup script
david-mears-2 Aug 19, 2026
f766662
Extract some consts/types
david-mears-2 Aug 19, 2026
42fba7c
Extract some validators
david-mears-2 Aug 19, 2026
d30dd8c
Extract DuckDB connection
david-mears-2 Aug 19, 2026
2cc871a
Extract querying logic
david-mears-2 Aug 19, 2026
f3b1525
Refactor querying logic
david-mears-2 Aug 25, 2026
c7028a0
Move error handler call to correct place
david-mears-2 Aug 25, 2026
34708cb
Finish implementing query param validators and refactor them
david-mears-2 Aug 26, 2026
e6e4242
Tidy comments; extract todos into youtrack tickets
david-mears-2 Aug 26, 2026
49e5bb6
Reword variable names
david-mears-2 Aug 26, 2026
4436c71
Make node types package match canonical node version
david-mears-2 Aug 26, 2026
ef7b047
Apply tsconfig to scripts ts files too
david-mears-2 Aug 26, 2026
0f7bce3
Tests - iteration 0
david-mears-2 Aug 27, 2026
3518254
Tests - iteration 1 (refactor fixture generation)
david-mears-2 Aug 27, 2026
681f182
Remove fixture validation
david-mears-2 Aug 27, 2026
180fa58
Rename GHA workflow
david-mears-2 Aug 27, 2026
4cfec78
Small refactors
david-mears-2 Aug 27, 2026
8540cba
Start to type column names; simplify validator test mock
david-mears-2 Aug 27, 2026
96d0bea
Alphabetise column types and add unrequestable columns
david-mears-2 Aug 27, 2026
1a36c36
Give up having a subset of columns be requestable
david-mears-2 Aug 27, 2026
bc49b36
Lots of rewriting tests; remove admin0 format validation
david-mears-2 Aug 29, 2026
80d2947
Make exmaple model outputs generation script a bit more clear
david-mears-2 Aug 29, 2026
73c3b14
Remove request timing logger
david-mears-2 Aug 31, 2026
778bdc7
Update README.md
david-mears-2 Aug 31, 2026
1e988de
Fix lint command
david-mears-2 Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/lint.yml → .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: lint and type check
name: CI

on:
push:
Expand Down Expand Up @@ -29,3 +29,15 @@ jobs:
cache: npm
- run: npm ci
- run: npm run lint

test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm test
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.env
node_modules/
dist/
data/admin0-region-metadata.json
data/**/*.parquet
212 changes: 212 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# PARAmap API

This repo contains an API serving two kinds of data to be rendered by PARAmap:

1) data points of surveys of genetic markers, which come to us stored in the [STAVE](https://mrc-ide.github.io/STAVE/index.html) schema;
2) and a surface of model outputs imputed from the survey data, which are essentially interpolated prevalences of the difference genetic markers per region. These are provided at three levels of granularity: admin levels 0, 1, and 2.

In general, when new releases of model outputs or of survey data are created, this is treated additively: that is, we will intentionally keep around older versions. These may be requested using query parameters `model_release`/`data_release`. Each model release has a dependency on a specific data release.

## Endpoints

There are three endpoints, all read-only, which provide customisable slices of the data, which is read from parquet files.

The `/surveys` and `/prevalences` endpoints correspond to the two kinds of data referred to above. They share a common request format* whereby the query parameter `properties` specifies which parquet columns should be returned per entry, while several other query parameters are used to filter the data. To a first approximation, this is translated into an SQL query of the form `SELECT <properties> FROM <parquet file> WHERE <filters>`, though not all filters are expressible as `WHERE` clauses (e.g. they may instead entail reading a different source parquet file). By the use of these query parameters, we enable clients to flexibly thin the response sizes to precisely those rows and columns that are required.

*This is controlled by the const `endpointConfigs` in `dataHelpers.ts`.

1. /metadata

This endpoint returns:
- All available model releases
- The global/initial bounding box for the map
- Metadata pertaining to a specific model release (this is specified by an optional `model_release` parameter, which defaults to latest, as configured via `config.ts`):
- The model release label ('version')
- The corresponding data release for the model release
- The available genes for the model release, each with their available 'mutations' (encoding position and allele), and the range of dates for which prevalence is modelled for each mutation.

Example:

request:
`GET /metadata`

response:
```json
{
"model_releases": ["v1", "v2"],
"prevalences": {
"version": "v1",
"data_release": "v1.0.0",
"variants": [
{
"gene": "k13",
"mutations": [
{
"mutation": "469Y",
"date_range": {
"start": "2004-05-01",
"end": "2030-09-01",
},
},
{
"mutation": "469F",
"date_range": {
"start": "2004-05-01",
"end": "2030-09-01",
},
},
],
},
{
"gene": "crt",
"mutations": [
{
"mutation": "76K",
"date_range": {
"start": "2004-05-01",
"end": "2030-09-01",
},
},
],
}
]
},
"bounds": {
"min": {
"lng": -70.0635,
"lat": 12.4124
},
"max": {
"lng": -69.8654,
"lat": 12.624
}
},
}
```

2. /surveys

An endpoint for querying survey data, as stored in `/data/stave/<version>/survey_data.parquet`.

Note that this endpoint actually returns multiple entries per STAVE survey - that is, we have one entry per variant per STAVE survey. Thus these objects match the STAVE concept of a '[count](https://mrc-ide.github.io/STAVE/articles/howto_counts_table.html)' (which counts a particular mutation) a bit more closely than the concept of a '[survey](https://mrc-ide.github.io/STAVE/articles/howto_surveys_table.html)' (which would collect multiple genetic variants).

Example:

request:
```
GET /surveys?
&data_release=v1.0.0
&date_from=2010-01-01
&date_to=2010-02-01
&gene=k13
&mutation=469Y
&properties=survey_id,lat,lng,collection_day,denominator
```

response:
```json
[
{
"survey_id": "Dama_2017_Bamako_2014",
"lat": 12.612900,
"lng": -8.13560,
"collection_day": "2010-01-15",
"denominator": 130,
},
// ...
]
```

3. /prevalences

An endpoint for querying model outputs, as stored in `/data/model/<version>/admin<level>.parquet`.

The `admin_level` query parameter determines the granularity of the model outputs, while the query parameters `admin0`, `admin1` and `admin2` scope the results to a particular region. Thus for example, to request results within the `admin0` region of Mali (`MLI`), at the finest level of granularity:

request:
```
GET /prevalences?
&model_release=v2
&admin_level=2
&admin0=MLI
&gene=k13
&mutation=469Y
&date=2024-05-01
&properties=median,admin2
```

response:
```json
[
{
"admin2": "MLI.1.1_1",
"median": 0.76470588235
},
// ...
]
```


## First-time development set-up

1. Process STAVE data

```sh
Rscript ./scripts/process_stave.R 2026.03.17
```

2. Generate example model outputs

Currently, we generate example model outputs using a script. These example outputs are partly based on the (real) STAVE data.

```sh
Rscript scripts/create_example_model_outputs.R
```

3. Fetch admin0 region metadata from Grout

```sh
ts-node --esm scripts/fetch_admin0_region_metadata.ts
```

4. Optionally run tests

```sh
npm run test
```

5. Start the app

```sh
npm run dev
```

## How to update the data

NB The list of in-scope genes and mutations will vary over time, with model releases (rather than with STAVE data releases); thus it is not something to hard-code as a constant. Every model release has a dependency on one STAVE data release.

### STAVE data

When a new STAVE data release is provided, it should be given a version name e.g. "2026.03.17", and committed in `scripts/input/stave/<version>/stave_data.rds`. Then, run the [process_stave.R](./scripts/process_stave.R) script:

```sh
Rscript ./scripts/process_stave.R 2026.03.17
```

This will create `./data/stave/<version>/survey_data.parquet`.

### Model outputs

As mentioned above, early development has used example model outputs generated by a script. We will at some point have access to real model outputs. Once these are provided, we can get rid of the tooling that creates example model outputs. We may then still need to do some amount of transformation to wrangle the data into the preferred format or filetype; this transformation step should take the form of a new script, akin to `./scripts/process_stave.R`.

As things stand now, a file `./data/model/<version>/metadata.json` must be manually created, to document the dependency of the model outputs (example or real) on a particular STAVE release.

## Data schema details

### Genes and mutations

The [variantstring](https://github.com/mrc-ide/variantstring) format encodes genetic variants in three components: the gene, the locus (position), and the amino acid (sometimes written as "aa"). As far as the app is concerned, however, the variant is composed more simply of two parts: the gene (which exactly corresponds to the variantstring concept of a gene) and the mutation (which fuses the locus and amino acid. Technically we could be more accurate by calling this an 'allele' since 'mutation' implies deviation from a reference allele, and some of the variants are reference alleles). In pre-processing, derived columns for gene and mutation are appended to the STAVE data, to enable this data to be queried by variant.

### Dates

Model outputs ('prevalences') will be provided per-month, which we encode as the first of each month. Unlike prevalences, the dates of surveys are not snapped to the first of the month, but can be any day.
Empty file added data/model/.gitkeep
Empty file.
4 changes: 4 additions & 0 deletions data/model/2026.05.08/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"version": "2026.05.08",
"data_release": "2026.03.17"
}
Empty file added data/stave/.gitkeep
Empty file.
Loading