Skip to content

Instantly share code, notes, and snippets.

@jsit
Last active February 16, 2023 19:19
Show Gist options
  • Save jsit/351f91dfec130e410911b8e6041b2c56 to your computer and use it in GitHub Desktop.
Save jsit/351f91dfec130e410911b8e6041b2c56 to your computer and use it in GitHub Desktop.
COVID-19 Levels widget for Scriptable
/**
* COVID-19 Tracker Widget
*
* Enter your county and state as "Example County, Connecticut" in the widget
* "Parameter". Be sure to include the word "County" in the county name.
*
* If unsure how the CDC spells your county, find values here:
* https://data.cdc.gov/Public-Health-Surveillance/United-States-COVID-19-Community-Levels-by-County/3nnm-4jni
*
* Script by Jay (@[email protected])
* https://gist.github.com/jsit/351f91dfec130e410911b8e6041b2c56
*
* This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
* International License.
* http://creativecommons.org/licenses/by-sa/4.0/
*/
// User will enter county as "County Name, State"
const params = args?.widgetParameter?.split(',') || [];
const county = params?.[0]?.trim() || 'Middlesex County';
const state = params?.[1]?.trim() || 'Massachusetts';
const communityUrl = `https://data.cdc.gov/resource/3nnm-4jni.json?county=${encodeURIComponent(
county
)}&state=${encodeURIComponent(state)}&$order=date_updated%20DESC`;
const transmissionUrl = `https://data.cdc.gov/resource/dt66-w6m6.json?county_name=${encodeURIComponent(
county
)}&state_name=${encodeURIComponent(state)}&$order=report_date%20DESC`;
const loadJson = async (url) => {
let req = new Request(url);
let json = await req.loadJSON();
return json;
};
const transmissionData = await loadJson(transmissionUrl);
const communityData = await loadJson(communityUrl);
const getTransmissionColor = (value) => {
switch (value) {
case 'high':
return Color.red();
case 'substantial':
return Color.orange();
case 'moderate':
return Color.yellow();
case 'low':
return Color.blue();
default:
return Color.dynamic(Color.white(), Color.black());
}
};
const getCommunityColor = (value) => {
switch (value) {
case 'High':
return Color.orange();
case 'Medium':
return Color.yellow();
case 'Low':
return Color.green();
default:
return Color.dynamic(Color.white(), Color.black());
}
};
const dateFormatter = new DateFormatter();
dateFormatter.useMediumDateStyle();
const dateColor = Color.dynamic(
new Color('#000000', 0.6),
new Color('#ffffff', 0.6)
);
const toTitleCase = (str) => {
return str.replace(/\w\S*/g, (txt) => {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
};
const errorWidget = () => {
let widget = new ListWidget();
widget.addText('Error loading county data. Please double-check spelling.');
return widget;
};
const createCommunityStack = (targetStack, communityData) => {
const communityStack = targetStack.addStack();
const {
covid_19_community_level: communityLevel,
date_updated: communityLevelDate,
covid_hospital_admissions_per_100k: hospitalAdmissions,
county_fips: countyCode,
} = communityData[0];
// Get next most recent record
const {covid_hospital_admissions_per_100k: hospitalAdmissionsPrevious} =
communityData.find(
(record) => record.date_updated !== communityLevelDate
) || {};
console.log(`hospitalAdmissions: ${hospitalAdmissions}`);
console.log(`hospitalAdmissionsPrevious: ${hospitalAdmissionsPrevious}`);
communityStack.layoutVertically();
const communityHeading = communityStack.addText('Community');
communityHeading.font = Font.subheadline();
communityHeading.textColor = Color.dynamic(Color.black(), Color.white());
const communityMeasure = communityStack.addStack();
communityMeasure.centerAlignContent();
communityMeasure.spacing = 4;
const communityTitle = communityMeasure.addText(toTitleCase(communityLevel));
communityTitle.font = Font.headline();
communityTitle.textColor = getCommunityColor(communityLevel);
communityTitle.url = `https://covid.cdc.gov/covid-data-tracker/#county-view?data-type=CommunityLevels&list_select_state=${encodeURIComponent(
state
)}&list_select_county=${countyCode}`;
// Only add a trend arrow if we have a current number and a previous number
if (hospitalAdmissions && hospitalAdmissionsPrevious) {
const isCommunityDeclining =
hospitalAdmissions - hospitalAdmissionsPrevious <= 0;
console.log(`hospital admissions declining?: ${isCommunityDeclining}`);
const communityTrendSymbol = isCommunityDeclining
? SFSymbol.named('arrow.down.right')
: SFSymbol.named('arrow.up.right');
communityTrendSymbol.applyBoldWeight();
const communityArrow = communityMeasure.addImage(
communityTrendSymbol.image
);
communityArrow.imageSize = new Size(12, 12);
communityArrow.tintColor = getCommunityColor(communityLevel);
}
const communityMeta = communityStack.addText(
dateFormatter.string(new Date(communityLevelDate))
);
communityMeta.font = Font.footnote();
communityMeta.textColor = dateColor;
return communityStack;
};
const createTransmissionStack = (targetStack, transmissionData) => {
const transmissionStack = targetStack.addStack();
transmissionStack.layoutVertically();
const {
community_transmission_level: transmissionLevel,
report_date: transmissionLevelDate,
percent_test_results_reported: percentPositive,
fips_code: countyCode,
} = transmissionData[0];
// Get next most recent record
const {percent_test_results_reported: percentPositivePrevious} =
transmissionData.find(
(record) => record.report_date !== transmissionLevelDate
) || {};
console.log(`percentPositive: ${percentPositive}`);
console.log(`percentPositivePrevious: ${percentPositivePrevious}`);
const transmissionHeading = transmissionStack.addText('Transmission');
transmissionHeading.font = Font.subheadline();
transmissionHeading.textColor = Color.dynamic(Color.black(), Color.white());
const transmissionMeasure = transmissionStack.addStack();
transmissionMeasure.centerAlignContent();
transmissionMeasure.spacing = 4;
const transmissionTitle = transmissionMeasure.addText(
`${toTitleCase(transmissionLevel)}`
);
transmissionTitle.font = Font.headline();
transmissionTitle.textColor = getTransmissionColor(transmissionLevel);
transmissionTitle.url = `https://covid.cdc.gov/covid-data-tracker/#county-view?data-type=Risk&list_select_state=${encodeURIComponent(
state
)}&list_select_county=${countyCode}`;
// Only add a trend arrow if we have a current number and a previous number
if (percentPositive && percentPositivePrevious) {
const isTransmissionDeclining =
percentPositive - percentPositivePrevious <= 0;
console.log(`transmission declining? ${isTransmissionDeclining}`);
const transmissionTrendSymbol = isTransmissionDeclining
? SFSymbol.named('arrow.down.right')
: SFSymbol.named('arrow.up.right');
transmissionTrendSymbol.applyBoldWeight();
const transmissionArrow = transmissionMeasure.addImage(
transmissionTrendSymbol.image
);
transmissionArrow.imageSize = new Size(12, 12);
transmissionArrow.tintColor = getTransmissionColor(transmissionLevel);
}
const transmissionMeta = transmissionStack.addText(
dateFormatter.string(new Date(`${transmissionLevelDate}T00:00`))
);
transmissionMeta.font = Font.footnote();
transmissionMeta.textColor = dateColor;
};
const createAccessoryRectangularWidget = (transmissionData, communityData) => {
let widget = new ListWidget();
const {covid_19_community_level: communityLevel} = communityData[0];
const {
community_transmission_level: transmissionLevel,
fips_code: countyCode,
} = transmissionData[0];
const communityTitle = widget.addText(`C: ${toTitleCase(communityLevel)}`);
communityTitle.url = `https://covid.cdc.gov/covid-data-tracker/#county-view?data-type=CommunityLevels&list_select_state=${encodeURIComponent(
state
)}&list_select_county=${countyCode}`;
const transmissionTitle = widget.addText(
`T: ${toTitleCase(transmissionLevel)}`
);
transmissionTitle.url = `https://covid.cdc.gov/covid-data-tracker/#county-view?data-type=Risk&list_select_state=${encodeURIComponent(
state
)}&list_select_county=${countyCode}`;
widget.addSpacer();
return widget;
};
const createHomeScreenWidgetSmall = (transmissionData, communityData) => {
let widget = new ListWidget();
console.log(transmissionData[0]);
console.log(communityData[0]);
widget.backgroundColor = Color.dynamic(Color.white(), Color.black());
widget.addSpacer();
createTransmissionStack(widget, transmissionData);
widget.addSpacer();
createCommunityStack(widget, communityData);
widget.addSpacer();
const widgetTitle = widget.addText(county);
widgetTitle.font = Font.caption2();
widgetTitle.textColor = dateColor;
widgetTitle.centerAlignText();
// Set URL to open when tapping widget.
widget.url = `https://covid.cdc.gov/covid-data-tracker/#county-view?data-type=Risk&list_select_state=${encodeURIComponent(
state
)}`;
return widget;
};
const createHomeScreenWidget = (transmissionData, communityData) => {
let widget = new ListWidget();
console.log(transmissionData[0]);
console.log(communityData[0]);
const widgetHeading = widget.addText('COVID-19 Levels');
widgetHeading.font = Font.headline();
widget.backgroundColor = Color.dynamic(Color.white(), Color.black());
widget.addSpacer(12);
const widgetStack = widget.addStack();
widgetStack.layoutHorizontally();
createTransmissionStack(widgetStack, transmissionData);
widgetStack.addSpacer();
createCommunityStack(widgetStack, communityData);
widgetStack.addSpacer();
widget.addSpacer();
const widgetTitle = widget.addText(`${county}, ${state}`);
widgetTitle.font = Font.caption1();
widgetTitle.textColor = dateColor;
widgetTitle.centerAlignText();
// Set URL to open when tapping widget.
widget.url = `https://covid.cdc.gov/covid-data-tracker/#county-view?data-type=Risk&list_select_state=${encodeURIComponent(
state
)}`;
return widget;
};
// Tell the widget on the Home Screen to show our ListWidget instance.
let widget;
if (transmissionData.length === 0 || communityData.length === 0) {
widget = errorWidget();
} else {
switch (config.widgetFamily) {
case 'accessoryRectangular':
widget = createAccessoryRectangularWidget(
transmissionData,
communityData
);
break;
case 'small':
widget = createHomeScreenWidgetSmall(transmissionData, communityData);
break;
default:
widget = createHomeScreenWidget(transmissionData, communityData);
break;
}
}
Script.setWidget(widget);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment