Last active
February 16, 2023 19:19
-
-
Save jsit/351f91dfec130e410911b8e6041b2c56 to your computer and use it in GitHub Desktop.
COVID-19 Levels widget for Scriptable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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