Skip to content

Instantly share code, notes, and snippets.

@drewkerr
Last active December 21, 2021 00:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drewkerr/a4df29873571c9f4e45afddf88d67faf to your computer and use it in GitHub Desktop.
Save drewkerr/a4df29873571c9f4e45afddf88d67faf to your computer and use it in GitHub Desktop.
COVID-19 Cases in Bendigo, Victoria, Australia
const url = 'https://covid-sheets-mirror.web.app/api?'
const sid = '1nUUU5zPRPlhAXM_-8R7lsMnAkK4jaebvIL5agAaKoXk'
function params(obj) {
return Object.entries(obj)
.map(([key, val]) => `${key}=${encodeURI(val)}`).join("&")
}
const cases = url + params({
cache: true,
sheet: sid,
range: 'Deaths Recoveries!A:H'
})
const state = url + params({
cache: true,
sheet: sid,
range: 'Daily Count States!A:E'
})
const local = url + params({
cache: true,
sheet: sid,
range: 'Victorian LGAs!A:D'
})
function parseCSV(str, len) {
let arr = [];
let quote = false;
let col, c;
for (let row = col = c = 0; c < str.length && row < len; c++) {
let cc = str[c], nc = str[c+1];
arr[row] = arr[row] || [];
arr[row][col] = arr[row][col] || '';
if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; }
if (cc == '"') { quote = !quote; continue; }
if (cc == ',' && !quote) { ++col; continue; }
if (cc == '\r' && nc == '\n' && !quote) { ++row; col = 0; ++c; continue; }
if (cc == '\n' && !quote) { ++row; col = 0; continue; }
if (cc == '\r' && !quote) { ++row; col = 0; continue; }
arr[row][col] += cc;
}
return arr;
}
function arrayHash(arr) {
let head = arr[0]
let body = arr.slice(1)
return body.map(row => {
return row.reduce((acc, v, i) => {
let key = head[i]
acc[key] = v
return acc
}, {})
})
}
async function getData(url, len) {
let req = new Request(url)
let txt = await req.loadString()
let csv = await parseCSV(txt, len)
return await arrayHash(csv)
}
let casesData = await getData(cases, 2)
let stateData = await getData(state, 28*8+1)
let localData = await getData(local, 80)
let vicData = stateData.filter(data => data["State/territory"] == "VIC")
let locData = localData.filter(data => data["LGA"] == "GREATER BENDIGO")
let graph = stateData.reduce((a, b) => {
let date = b["Date announced"]
let cases = parseInt(b["New cases"]) || 0
if (!a[date]) a[date] = cases
else a[date] += cases
return a
}, {})
const weekavg = (n) => Object.values(graph).slice(0 + n, 7 + n).reduce((a, b) => a + b, 0) / 7
let growth = weekavg(0) / weekavg(1)
let month = Object.values(graph).slice(0, 28)
let data = {
"Local active cases": locData[0]["Active Cases"],
"Local total cases": locData[0]["Total Cases"],
"Victoria new cases": vicData[0]["New cases"],
"Victoria total cases": vicData[0]["Cumulative confirmed"],
"Australia new cases": Object.values(graph)[0].toString(),
"Growth factor (7-day)": growth.toFixed(2),
"Australia total cases": casesData[0]["Confirmed (total)"],
"Australia total deaths": casesData[0]["Deceased"]
}
function columnGraph(data, width, height) {
let max = Math.max(...data)
let context = new DrawContext()
context.size = new Size(width, height)
context.opaque = false
context.setFillColor(Color.white())
data.forEach((value, index) => {
let w = width / (2 * data.length - 1)
let h = value / max * height
let x = width - (index * 2 + 1) * w
let y = height - h
let rect = new Rect(x, y, w, h)
context.fillRect(rect)
})
return context
}
function createWidget() {
let w = new ListWidget()
w.setPadding(16, 16, 16, 8)
function gradient(start, end) {
let startColor = new Color(start)
let endColor = new Color(end)
let gradient = new LinearGradient()
gradient.colors = [startColor, endColor]
gradient.locations = [0.0, 1]
w.backgroundGradient = gradient
}
if (growth < 1) gradient("37c25a", "1cb943")
else gradient("ee676c", "eb5056")
let head = `COVID-19 growth ${data["Growth factor (7-day)"]} `
let headText = w.addText(head)
headText.textColor = Color.white()
headText.textOpacity = 0.8
headText.textSize = 10
headText.rightAlignText()
let image = columnGraph(month, 400, 50).getImage()
w.addImage(image)
w.addSpacer()
let body = {
"BENDIGO": ` ${data["Local total cases"]} (${data["Local active cases"]} active)`,
"VICTORIA": ` ${data["Victoria total cases"]} (+${data["Victoria new cases"]})`,
"AUSTRALIA": ` ${data["Australia total cases"]} (+${data["Australia new cases"]})`
}
Object.entries(body).forEach(([key, value]) => {
let labelText = w.addText(key)
labelText.textColor = Color.white()
labelText.textOpacity = 0.6
labelText.textSize = 10
let bodyText = w.addText(value)
bodyText.textColor = Color.white()
bodyText.textSize = 14
})
return w
}
if (config.runsInWidget) {
let w = createWidget()
Script.setWidget(w)
Script.complete()
} else {
let table = new UITable()
let row = new UITableRow()
row.isHeader = true
row.addText('COVID-19 Statistics in Victoria')
table.addRow(row)
Object.entries(data).forEach(([key, value]) => {
let row = new UITableRow()
row.addText(key)
row.addText(value).rightAligned()
table.addRow(row)
})
if (config.runsWithSiri) {
Speech.speak(`There are ${data["Local active cases"]} active cases in Bendigo, and ${data["Victoria new cases"]} new cases in Victoria today.`)
}
table.present()
Script.complete()
}
if (config.runsInApp) {
let w = createWidget()
w.presentSmall()
Script.complete()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment