Public
Edited
Mar 15, 2022
1 fork
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function validate() {
const input = document.getElementById('searchTerm');
if (!schoolNames.includes(input.value)) {
input.setCustomValidity('"' + input.value + '" is not in our list of Chicago public schools.');
} else {
input.setCustomValidity('');
}
}
Insert cell
Insert cell
chatter = new Object({
"hed": "Daily COVID-19 case counts in Chicago public schools",
"subhed": "Search for a school to see COVID-19 case counts over time",
"src": "Chicago Public Schools",
"note": "Reporting lags may result in under-counts for recent dates.",
"credit": "Charmaine Runes/WBEZ"
})
Insert cell
viewof selectedSchool = Inputs.select(schoolNames, {sort: true, value: "CPS"})

//Inputs.text({name: 'searchTerm', datalist: schoolNames, placeholder:"Enter school name", value: "CPS", autocomplete: true, submit: true})
Insert cell
selectedSchool
Insert cell
Insert cell
singleAnnotations = [
{date: "2022-03-15", text: "Masks optional →"}
]
Insert cell
rangeAnnotations = [
{startDate: "2021-11-24", endDate: "2021-11-28", text: "Thanksgiving"},
{startDate: "2021-12-18", endDate: "2022-01-02", text: "Winter break"},
{startDate: "2022-01-05", endDate: "2022-01-11", text: "CPS shutdown"}
]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function getTopResults(results, originalList) {

let textContainer = html`<div>${style()}</div>`;
if (results.length != originalList.length) {
let topThree = results.slice(0,3);

for (let result of topThree) {
let currentSchool = result["School Site"].trim();
let text = html`<p>
<span style='font-weight:bold;margin-bottom:0;color:#5f2b7b'>${result["School Site"].trim()}</span>
<br>
<span style='font-size:12px;margin-top:0;color:#636466'>${result["City"].trim()}, ${result["County Name"]} County</span>
<br>
<span style='font-size:14px'><b>Source:</b> ${result["Case Source"]}</span>
<br>
<span style='font-size:14px'><b>Count:</b> ${result["Case Count"]}</span>
</p>
`
textContainer.appendChild(text)
}
} else {
let text = html`<p></p>`
textContainer.appendChild(text);
}
return textContainer;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof searchSchools = Inputs.search(mergedIDPHDataForTable, {label: 'School', placeholder: 'e.g., Lane Tech', query: 'LANE TECH HS'})
Insert cell
viewof searchResults = Inputs.table(searchSchools)
Insert cell
viewof searchAllSchools = Inputs.text({
label: 'School',
placeholder: 'e.g., Lane Tech',
datalist: Array.from(mergedIDPHData.keys()).sort()
})

//viewof searchAllSchools = Inputs.search(mergedIDPHDataForTable, {label: 'School', placeholder: 'e.g., Lane Tech'})
Insert cell
viewof searchAllResults = Inputs.table(mergedIDPHDataForTable.filter(school => school.School == searchAllSchools))
Insert cell
viewof autoSelectSchool = autoSelect({
options: Array.from(mergedIDPHData.keys()).sort(),
placeholder: "e.g., Lane Tech",
value: ""
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function findCPS() {
// Get list of Cook County school names from IDPH data
let cookCountySchools = [];
Array.from(mergedIDPHDataForTable.filter(school => school.CountyName == 'Cook')).forEach(school => cookCountySchools.push(school['School']));

// Get list of CPS school names
let cpsSchools = [];
cpsCasesData.forEach(school => cpsSchools.push(school['School']));

// Find potential matches
let potentialMatches = []
cpsSchools.sort().forEach(cpsSchool => {
for (let cookCountySchool of cookCountySchools) {
if (cookCountySchool.toUpperCase().includes(cpsSchool)) {
potentialMatches.push({
"CPS name": cpsSchool,
"IDPH name": cookCountySchool,
"CPS cases": mergedIDPHDataForTable.filter(school => school.School == cpsSchool)[0]['Confirmed Cases'],
"IDPH city": mergedIDPHDataForTable.filter(school => school.School == cookCountySchool)[0]['City'],
"IDPH cases (potential exposures)": mergedIDPHDataForTable.filter(school => school.School == cookCountySchool)[0]['CaseCountDefinition'],
"IDPH cases (outbreaks)": mergedIDPHDataForTable.filter(school => school.School == cookCountySchool)[0]['CaseCount'],
"IDPH latitude": mergedIDPHDataForTable.filter(school => school.School == cookCountySchool)[0]['Latitude'],
"IDPH longitude": mergedIDPHDataForTable.filter(school => school.School == cookCountySchool)[0]['Longitude']
})
}
}
})
return potentialMatches //[cookCountySchools.sort(), cpsSchools.sort()]
}
Insert cell
potentialMatches = findCPS()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
outbreaksDataClean = schoolOutbreaksData.cOVIDSchoolOutbreaks.map(school => renameKeys(cleanOutbreakFields, school))
Insert cell
countyOutbreaksData = d3.json(countyOutbreaksURL)
Insert cell
Insert cell
potentialExposuresDataClean = schoolPotentialExposuresData.schoolCases.map(school => renameKeys(cleanExposureFields, school))
Insert cell
Insert cell
Insert cell
mergedIDPHData.get('LANE TECH HS (Chicago Public Schools)')
Insert cell
Insert cell
mergedIDPHDataForTable.filter(school => school.School == 'LANE TECH HS')[0]
Insert cell
Insert cell
Insert cell
function serialize (data) {
let parser = new json2csv.Parser();
let csv = parser.parse(data);
return csv
}
Insert cell
Insert cell
Insert cell
function getDataRange(updatedAsOf, difference) {

const lastUpdated = ''.concat(updatedAsOf.year + '/' + updatedAsOf.month + '/' + updatedAsOf.day)

const date = new Date();
date.setDate(date.getDate(updatedAsOf) - difference);

const dateString = date.toLocaleDateString(); // month/day/year
const dateObj = new Object({"year": '', "month": '', "day": ''})
dateObj.year = dateString.split("/")[2]
dateObj.month = dateString.split("/")[0]
dateObj.day = dateString.split("/")[1]

const dateFormatted = `${convertMonth[dateObj.month]} ${dateObj.day}` //, ${dateObj.year}`

return `${dateFormatted} to ${outbreaksLastUpdated}`;
}
Insert cell
Insert cell
Insert cell
lastUpdatedIDPH = schoolOutbreaksData['lastUpdatedDate']
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function checkIfInDateRange(begin, end, reportDate) {
var range = Date.parse(end) - Date.parse(begin)
return null
}
Insert cell
schoolVarNames = ["School", "SchoolName", "SchoolSite"]
Insert cell
function cleanTitleCaseString(string) {
// splits and rejoins a string based on uppercase characters
// e.g. "ThisIsALongString" -> "This Is A Long String"
return string.match(/([A-Z]?[^A-Z]*)/g).slice(0,-1).join(" ")
}
Insert cell
// Need to abstract using cleanTitleString() and loop through / map keys
cleanOutbreakFields = new Object({
'Address': 'Address',
'City': 'City',
'SchoolSite': 'School Site',
'CaseCategory': 'Case Category',
'CaseSource': 'Case Source',
'SchoolCategory': 'School Category',
'CountyName': 'County Name',
'CaseCount': 'Case Count',
'reportDate': 'report Date',
'dateReported': 'date Reported'
})
Insert cell
cleanExposureFields = new Object({
'SchoolName': 'School Name',
'CountyName': 'County Name',
'CaseCountDefinition': 'Case Count Definition',
'Longitude': 'Longitude',
'Latitude': 'Latitude',
'ReportDate': 'Report Date'
})
Insert cell
function renameKeys(keysMap, obj) {
return Object.keys(obj).reduce(
(acc, key) => ({
...acc,
...{ [keysMap[key] || key]: obj[key] }
}),
{}
)
};
Insert cell
function findCorrectSchoolVar(properties) {
// Assumes that there is only one correct school variable name
for (const [index, name] of schoolVarNames.entries()) {
if (properties.includes(name)) {
return schoolVarNames[index]
}
}
}

Insert cell
function mergeData(listOfArrays) {
// Takes a list of arrays, where each Object in each array represents a unique school
// Returns a compiled array of Objects, where each Object represents a unique school
var mergedData = new Map()

for (let [i, array] of listOfArrays.entries()) {
var schoolVarName = findCorrectSchoolVar(Object.getOwnPropertyNames(array[0]))

for (let school of array) {
var schoolName = school[schoolVarName]
if (Object.getOwnPropertyNames(school).includes("CountyName")) {
var county = `(${school['CountyName']} County)`
} else if (i == 2) { // i.e., it's the CPS dataset
var county = "(Chicago Public Schools)"
} else {
var county = ""
}

// Use both school name and county, since school name alone is not a unique identifier
var id = `${schoolName} ${county}`
var attributes = new Map()
attributes.set('School', schoolName)
for (var [property, val] of Object.entries(school)) {
if (property != schoolVarName) {
attributes.set(property, val)
}
}
if (Array.from(mergedData.keys()).includes(id)) {
var initialAttributes = mergedData.get(id)
mergedData.set(id, new Map([...initialAttributes, ...attributes]))
} else {
mergedData.set(id, attributes)
}
}
}
return mergedData
}
Insert cell
function convertMapForTable(mapOfSchools) {
return Array.from(mapOfSchools.values()).map(school => Object.fromEntries(school))
}
Insert cell
function renderSchoolProperties(school) {

const propertiesToExclude = ["School", "CountyName", "Latitude", "Longitude", "ReportDate", "Address", "City", "reportDate"]
const outerContainer = html`<div>${style()}</div>`
const updatedNames = new Map([
["School", ""],
["CaseCountDefinition", "Potential exposures"],
["CaseCount", "Outbreak status"],
["CaseCategory", "Cases among"],
["CaseSource", "Source"],
["CountyName", "County"],
["SchoolCategory", "School type"],
["Confirmed Cases", "Confirmed cases"],
["Close Contacts Identified", "Close contacts identified"]
])

if (Array.from(mergedIDPHData.keys()).includes(autoSelectSchool)) { // searchAllSchools)) {
// typing function keeps lagging because it's checking against an array?
// use for now: jashenkas inputs -> https://observablehq.com/@jashkenas/inputs#textDemo
let properties = mergedIDPHData.get(school)
renderSchoolAndCounty(properties, outerContainer)
properties.forEach((val, key) => {
if (propertiesToExclude.includes(key)) {
//skip
} else {
renderProperty(updatedNames.get(key), val, outerContainer)
}
})
}

return outerContainer

}
Insert cell
function renderSchoolAndCounty(schoolProperties, container) {

// Similar to renderProperty() but takes a Map of properties
const schoolName = schoolProperties.get("School")
const schoolText = html`<h3 style='color:${wbezColors.get("black")}'>${schoolName}</h3>`
container.appendChild(schoolText)
// Assume CPS naming convention for schools with uppercase is consistent
var countyName = schoolProperties.get('CountyName')
if (schoolName != schoolName.toUpperCase() && countyName == "") {
// no county name since this is IDPH data that's missing a county, potentially a charter school with multiple locations
} else {
if (schoolName == schoolName.toUpperCase() && countyName == undefined) {
var countyName = "Chicago Public Schools"
} else if (schoolName != schoolName.toUpperCase() && countyName == "") {
var countyName = schoolProperties.get('CountyName') + " County"
}
const countyText = html`<p class='dataviz-copy' style='color:${wbezColors.get("black")}'>${countyName}</h4>`
container.appendChild(countyText)
}

}
Insert cell
function renderProperty(key, val, outerContainer) {
const container = html`<div>${style()}</div>`
const keyText = html`<h4 style='color:${wbezColors.get("royalpurple")}'>${key}</h4>`
const valText = html`<p class='dataviz-copy' style='color:${wbezColors.get("mediumgray")}'>${val}</p>`

container.appendChild(keyText)
container.appendChild(valText)
outerContainer.appendChild(container)
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
json2csv = require("json2csv")
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more