Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
f3fe115
spacing fix in `MyPlots.jsx`
emprzy Apr 7, 2026
b078e76
add backend processing for NSSP data
emprzy Apr 15, 2026
5417be9
remove vestigal things
emprzy Apr 15, 2026
29857ee
add NSSP to `build-deploy.yml`
emprzy Apr 15, 2026
28ed02e
urgent fix
emprzy Apr 15, 2026
76f2a76
Update `build-deploy.yml` to have NSSP processing
emprzy Apr 15, 2026
67391d6
python and parity test fix
emprzy Apr 17, 2026
6d80215
ultra basic NSSP pathway (view selector shows NSSP)
emprzy Apr 22, 2026
e0d50e0
scoring- test with scroring utils
jcblemai Apr 28, 2026
723e1bd
Merge pull request #105 from ACCIDDA/minviews-gridc
jcblemai Apr 28, 2026
bd04043
wip NSSP front end
emprzy May 5, 2026
1cad87b
functional data pipeline for NSSP
emprzy May 5, 2026
8457fea
add map functionality
emprzy May 5, 2026
24d8881
add new `location_info.json` file that will help with frontend proces…
emprzy May 5, 2026
0aee521
gray out states/counties for which there is no data
emprzy May 5, 2026
98d5295
remove map when you get to county level
emprzy May 5, 2026
78652c5
fix issue of switching between views if location isnt in the new view
emprzy May 6, 2026
3413de5
better location persistence behavior across views
emprzy May 6, 2026
51fb633
add emily test tournament
emprzy May 7, 2026
fe179df
Update tournament.js
emprzy May 7, 2026
a77d856
Update tournament.js
emprzy May 7, 2026
a1fd318
Merge pull request #106 from ACCIDDA/toy-tournament
emprzy May 7, 2026
1d8f299
fix tournament being editable + option to hide year
jcblemai May 12, 2026
22f3550
Merge branch 'ACCIDDA:main' into main
jcblemai May 12, 2026
88c1f7a
further fix
jcblemai May 12, 2026
98191fe
Merge branch 'main' of https://github.com/ACCIDDA/RespiLens-staging
jcblemai May 12, 2026
091f832
basic visualization for NSSP view
emprzy May 12, 2026
563c7cb
more simplified visualization presentation
emprzy May 12, 2026
11a4c00
basic MyPlots compatability
emprzy May 12, 2026
e1258f4
fix MyPlots headers to be pretty and useful
emprzy May 18, 2026
afca278
basic nssp overview "plot"
emprzy May 18, 2026
cf721f7
NSSP overview "plot" is flexible with location selector
emprzy May 18, 2026
a34691e
front page load is always default loc (US)
emprzy May 18, 2026
51740b4
show "All" data for states that only have "All" data
emprzy May 18, 2026
a090a8a
fix title of only "All" nssp states
emprzy May 18, 2026
926ad90
more harmonized myplots display name
emprzy May 18, 2026
b2fd00b
standardize state map sizes
emprzy May 18, 2026
0ac7185
About NSSP overlay
emprzy May 19, 2026
bb0da44
data mapping bug fix!!!
emprzy May 19, 2026
c70e26a
Merge branch 'main' into add-nssp
emprzy May 19, 2026
b5fdda8
fix issues with merge conflicts
emprzy May 19, 2026
6989abf
Merge pull request #101 from ACCIDDA/add-nssp
emprzy May 19, 2026
0b7f021
attempt to fix forecastle resubmission bug
emprzy May 19, 2026
23e9e84
Merge pull request #108 from ACCIDDA/fix-forecastle
emprzy May 19, 2026
8c10491
attempted new forecastle challenge
emprzy May 20, 2026
5b8ccb6
bug fixes? merge to test...
emprzy May 20, 2026
21f91fe
lint compliance
emprzy May 20, 2026
f5dd7eb
Merge pull request #109 from ACCIDDA/ah-forecastle
emprzy May 20, 2026
39205b5
fix forecastle tournament score not displaying
emprzy May 20, 2026
42b7e8c
Merge pull request #110 from ACCIDDA/forecastle-bug-fixes
emprzy May 20, 2026
a238fe3
fix mispellings pointed out by @O957
emprzy May 20, 2026
877d316
Merge pull request #111 from ACCIDDA/fix-typos
emprzy May 20, 2026
992c4d0
new banner for nssp
emprzy May 20, 2026
740a42f
remove `MEtroCastLink`
emprzy May 20, 2026
00d25e8
Merge pull request #112 from ACCIDDA/nssp-announcement
emprzy May 20, 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
3 changes: 2 additions & 1 deletion .github/workflows/build-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ jobs:
--rsv-hub-path $GITHUB_WORKSPACE/rsv-forecast-hub \
--covid-hub-path $GITHUB_WORKSPACE/covid19-forecast-hub \
--flu-metrocast-hub-path $GITHUB_WORKSPACE/flu-metrocast \
--NHSN
--NHSN \
--NSSP

- name: Upload all processed data as a single artifact
uses: actions/upload-artifact@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/parity-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install pandas jsonschema
pip install pandas jsonschema requests

- name: Set up R
uses: r-lib/actions/setup-r@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pandas pytest jsonschema
pip install pandas pytest jsonschema requests

- name: Run processor unit tests
run: python -m pytest tests/test_processors.py
39 changes: 39 additions & 0 deletions .github/workflows/scoringutils-wis-parity.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Scoringutils WIS Parity

on:
pull_request:
push:
branches:
- main

jobs:
scoringutils-wis-parity:
runs-on: ubuntu-latest
defaults:
run:
working-directory: app

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
cache: 'npm'
cache-dependency-path: app/package-lock.json

- name: Set up R
uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true

- name: Install R packages
run: Rscript -e 'install.packages("scoringutils", repos = "https://cloud.r-project.org")'

- name: Install app dependencies
run: npm ci

- name: Run scoringutils WIS parity test
run: npm run test:scoringutils
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
Authors: **Emily Przykucki**, Joseph Lemaitre, and others within ACCIDDA, the Atlantic Coast Center for Infectious Disease Dynamics and Analytics.

* **stable version** https://www.RespiLens.com
* **new features are developped on** https://staging.RespiLens.com, [GitHub](https://github.com/ACCIDDA/RespiLens-staging)
* **new features are developed on** https://staging.RespiLens.com, [GitHub](https://github.com/ACCIDDA/RespiLens-staging)

RespiLens is a responsive web app to visualize respiratory disease forecasts in the US, focused on accessibility for state health departments and the general public. The RSV, COVID-19 and flu views pull from CDC forecast hubs (collectively known as the Hubverse: [rsv-forecast-hub](https://github.com/CDCgov/rsv-forecast-hub), [covid19-forecast-hub](https://github.com/CDCgov/covid19-forecast-hub), and [FluSight-forecast-hub](https://github.com/cdcepi/FluSight-forecast-hub)), and consolidate pathogen data by location and date into one user-friendly plot. While other visualization tools for FluSight/RSV/COVID-19 Hubverse data may exist, many of them are geared towards academics instead of state health departments and the public, making them less accessible to people who need them. Our goal is to make a dashboard with these users in mind. Presently, RespiLens offers these features:
- **Ability to link a certain view to a URL to share a forecast.** Select a pathogen, a location, and date(s), and then click the "Share View" button to copy the URL with your settings.
- Site rebuilt daily, with new data Hub produced weekly during Hub forecasting seasons (year-round for COVID-19)
- Ability to choose any number of dates to visualize on your plot
- Ability to choose any number of contributing models to visualize on your plot
- A view with National Healthcare Safety Network data, plotting almost 300 data categories (e.g., Number of Adult COVID-19 Admissions, or Percent Inpatient Beds Occupied by RSV Patients)
- **Forecastle**, a daily disease forecasting game, where you can attempt to predict hospitalizations counts for a specific senario and then get scored against participating models.
- **Forecastle**, a daily disease forecasting game, where you can attempt to predict hospitalizations counts for a specific scenario and then get scored against participating models.
- **MyRespiLens**, where you can visualize your own data for a specific location without the file leaving your machine.


Expand Down
4 changes: 3 additions & 1 deletion app/.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Tournament API Configuration
# Replace with your deployed Google Apps Script Web App URL
# See TOURNAMENT_SETUP.md for deployment instructions
VITE_TOURNAMENT_API_URL=https://script.google.com/macros/s/AKfycbwB7LnE8DSk9S7ACLs20j65iB-9ryCXAiih2FlMwpeWDDE4pLZ1zF3RQilfrm6_byLU7w/exec
VITE_EMILY_TOURNAMENT_API_URL=https://script.google.com/macros/s/AKfycby_pE9-KoA_bWjv9xIzNC1DF8jIrMPbQJ3I9P62RafivdQaHujnX2539tYFZtrn-nGRpw/exec
VITE_EPIDEMICS10_TOURNAMENT_API_URL=https://script.google.com/macros/s/AKfycbwB7LnE8DSk9S7ACLs20j65iB-9ryCXAiih2FlMwpeWDDE4pLZ1zF3RQilfrm6_byLU7w/exec
VITE_ALLHANDS_CHALLENGE_API_URL=https://script.google.com/macros/s/AKfycbzGBcCDAnAgnbCcyw0zSHSTNjlfgdiO5HyNCYn5gNGxhAjypgUcoufUTx2E7X1IOXyb/exec
1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"build": "vite build",
"build:staging": "vite build --mode staging",
"lint": "eslint .",
"test:scoringutils": "node scripts/test-scoringutils-wis.mjs",
"preview": "vite preview",
"format": "prettier . --write",
"format:check": "prettier . --check --log-level warn",
Expand Down
1 change: 1 addition & 0 deletions app/public/maps/nssp/us-counties-10m.topo.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions app/public/maps/nssp/us-counties-metadata.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions app/public/maps/nssp/us-states-10m.topo.json

Large diffs are not rendered by default.

158 changes: 158 additions & 0 deletions app/scripts/test-scoringutils-wis.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";

import { calculateWIS } from "../src/utils/forecastleScoring.js";
import { calculateWIS as calculateSharedWIS } from "../src/lib/forecast-components/scoring.js";

const rscriptCandidates = [process.env.SCORINGUTILS_RSCRIPT, "Rscript"].filter(
Boolean,
);

const findScoringutilsRscript = () => {
for (const rscript of rscriptCandidates) {
const check = spawnSync(
rscript,
[
"-e",
"quit(status = !requireNamespace('scoringutils', quietly = TRUE))",
],
{ encoding: "utf8" },
);
if (check.status === 0) {
return rscript;
}
}
return null;
};

const rscript = findScoringutilsRscript();

if (!rscript) {
if (process.env.CI) {
throw new Error("No Rscript with scoringutils found.");
} else {
console.log(
"Skipping scoringutils WIS parity test: no Rscript with scoringutils found.",
);
process.exit(0);
}
}

const cases = [
{
observed: 100,
median: 105,
lower50: 90,
upper50: 115,
lower95: 80,
upper95: 140,
},
{
observed: 70,
median: 105,
lower50: 90,
upper50: 115,
lower95: 80,
upper95: 140,
},
{
observed: 160,
median: 105,
lower50: 90,
upper50: 115,
lower95: 80,
upper95: 140,
},
];

const rProgram = `
library(scoringutils)
cases <- list(
list(observed=100, predicted=c(80, 90, 105, 115, 140)),
list(observed=70, predicted=c(80, 90, 105, 115, 140)),
list(observed=160, predicted=c(80, 90, 105, 115, 140))
)
quantile_level <- c(0.025, 0.25, 0.5, 0.75, 0.975)
for (case in cases) {
result <- wis(
observed = case$observed,
predicted = case$predicted,
quantile_level = quantile_level,
separate_results = TRUE
)
cat(sprintf(
"%.12f,%.12f,%.12f,%.12f\\n",
result$wis,
result$dispersion,
result$underprediction,
result$overprediction
))
}
`;

const result = spawnSync(rscript, ["-e", rProgram], { encoding: "utf8" });

if (result.status !== 0) {
throw new Error(
`scoringutils parity reference failed:\n${result.stderr || result.stdout}`,
);
}

const expected = result.stdout
.trim()
.split("\n")
.map((line) => {
const [wis, dispersion, underprediction, overprediction] = line
.split(",")
.map(Number);
return { wis, dispersion, underprediction, overprediction };
});

const assertClose = (actual, expectedValue, label) => {
assert.ok(
Math.abs(actual - expectedValue) < 1e-9,
`${label}: expected ${expectedValue}, got ${actual}`,
);
};

cases.forEach((testCase, index) => {
const forecastle = calculateWIS(
testCase.observed,
testCase.median,
testCase.lower50,
testCase.upper50,
testCase.lower95,
testCase.upper95,
);
const shared = calculateSharedWIS(
testCase.observed,
testCase.median,
testCase.lower50,
testCase.upper50,
testCase.lower95,
testCase.upper95,
);

for (const implementation of [forecastle, shared]) {
assertClose(implementation.wis, expected[index].wis, `case ${index} wis`);
assertClose(
implementation.dispersion,
expected[index].dispersion,
`case ${index} dispersion`,
);
assertClose(
implementation.underprediction,
expected[index].underprediction,
`case ${index} underprediction`,
);
assertClose(
implementation.overprediction,
expected[index].overprediction,
`case ${index} overprediction`,
);
}
});

assert.equal(calculateWIS(100, NaN, 90, 115, 80, 140), null);

console.log(`scoringutils WIS parity passed using ${rscript}`);
38 changes: 38 additions & 0 deletions app/src/components/DataVisualizationContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,44 @@ const DataVisualizationContainer = ({ disableSeo = false }) => {
</>
),
},
nsspall: {
title: (
<Group gap="sm">
<Title order={4}>
National Syndromic Surveillance Program (NSSP)
</Title>
</Group>
),
buttonLabel: "About NSSP Data",
content: (
<>
<p>
Data for the RespiLens NSSP view comes from the CDC's{" "}
<a
href="https://www.cdc.gov/nssp/index.html"
target="_blank"
rel="noopener noreferrer"
>
National Syndromic Surveillance Program,
</a>{" "}
which integrates data from many sources to help consolidate
complete, accurate, and timely health data. Syndromic surveillance
is a collaborative effort among local and state health departments,
CDC, and partners.
</p>
<div>
<Title order={4} mb="xs">
Data
</Title>
<p>
RespiLens' display of NSSP data shows percent of hospital visits
attributable to RSV, COVID-19, and influenza across 50 states +
Washington D.C.; where 25 states have county-level visualization.
</p>
</div>
</>
),
},
};

const currentAboutConfig = aboutHubConfig[viewType];
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/Documentation.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ const Documentation = () => {
successfully build a RespiLens projections JSON file. The
conversion pipeline's shortlist of requirements is below. Please
note that if you pulled your data directly from a Hubverse hub{" "}
<code>.csv</code> file, it likley already conforms to conversion
<code>.csv</code> file, it likely already conforms to conversion
requirements and you can skip to the next step!
</Text>
<Text>
Expand Down
Loading
Loading