-
-
Save colinferm/e4b010891cdaa200cdaa7b69d0ba3a68 to your computer and use it in GitHub Desktop.
A Scriptable widget that shows the amount of people who have received the corona vaccination in Germany
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
// Version 1.1.2 | |
// Mit Erstimpfungen und vollständigem Schutz sowie Fix für Herdenimmunitätsdatum | |
// | |
// Mit Caching und Fallback | |
const cacheMinutes = 60; // 60 min | |
const today = new Date(); | |
let neededTotalVaccinations; | |
let result; | |
let resultDe; | |
let width = 100; | |
const h = 5; | |
const vaccineStatus = "https://rki-vaccination-data.vercel.app/api/v2"; | |
let widget = new ListWidget(); | |
widget.setPadding(8, 10, 0, 10); | |
await getNumbers(); | |
await createWidget(); | |
Script.setWidget(widget); | |
Script.complete(); | |
if (config.runsInApp) { | |
widget.presentSmall(); | |
} | |
async function createWidget() { | |
var parentStack = widget; | |
var superStack; | |
superStack = widget.addStack(); | |
superStack.layoutHorizontally(); | |
parentStack = superStack.addStack(); | |
parentStack.layoutVertically(); | |
createInfoStack(parentStack, resultDe, "Ersten Impfdosen:"); | |
superStack.addSpacer(4); | |
var rightStack = superStack.addStack(); | |
rightStack.layoutVertically(); | |
} | |
async function createInfoStack(parentStack, data, vacText) { | |
//console.log(data); | |
//console.log(JSON.stringify(data, null, 2)); | |
const vaccine = data.vaccinatedAtLeastOnce; | |
const upperStack = parentStack.addStack(); | |
upperStack.layoutHorizontally(); | |
//console.log(vaccine.quote); | |
const upperTextStack = upperStack.addStack(); | |
upperTextStack.layoutVertically(); | |
let staticText1 = upperTextStack.addText("Verabreichte"); | |
staticText1.font = Font.semiboldRoundedSystemFont(10); | |
let staticText2 = upperTextStack.addText(vacText); | |
staticText2.font = Font.semiboldRoundedSystemFont(10); | |
upperStack.addSpacer(); | |
let logoImage = upperStack.addImage(await getImage("vac-logo.png")); | |
logoImage.imageSize = new Size(30, 30); | |
parentStack.addSpacer(4); | |
var amountPerCent = 0; | |
if (vaccine.quote) { | |
amountPerCent = round(vaccine.quote, 1); | |
} else { | |
amountPerCent = round( | |
100 / neededTotalVaccinations * vaccine.doses, | |
1 | |
); | |
} | |
let amountText = parentStack.addText( | |
vaccine.doses.toLocaleString() + " (" + amountPerCent.toLocaleString() + "%)" | |
); | |
amountText.font = Font.boldSystemFont(13); | |
amountText.textColor = new Color("#00a86b"); | |
amountText.minimumScaleFactor = 0.8 | |
let dailyVac = await calculateDailyVac(vaccine); | |
let description3 = parentStack.addText("(YD. Ø: " + dailyVac.toLocaleString() +")"); | |
description3.font = Font.mediumSystemFont(9); | |
parentStack.addSpacer(4); | |
let progressStack = parentStack.addStack(); | |
progressStack.layoutVertically(); | |
let progressNumberStack = parentStack.addStack(); | |
progressNumberStack.layoutHorizontally(); | |
const progressText0 = progressNumberStack.addText("0%"); | |
progressText0.font = Font.mediumSystemFont(8); | |
progressNumberStack.addSpacer(); | |
const progressText70 = progressNumberStack.addText("70%"); | |
progressText70.font = Font.mediumSystemFont(8); | |
const progressBar = await createProgress(vaccine.doses); | |
progressStack.addImage(progressBar); | |
parentStack.addSpacer(7); | |
let calendarStack = parentStack.addStack(); | |
const calendarImage = calendarStack.addImage(await getImage("calendar-icon.png")); | |
calendarImage.imageSize = new Size(26, 26); | |
calendarStack.addSpacer(6); | |
let calendarTextStack = calendarStack.addStack(); | |
calendarTextStack.layoutVertically(); | |
calendarTextStack.addSpacer(0); | |
// calculate date | |
var estimatedDate = new Date(); | |
let remainingDays = await calculateRemainingDays(vaccine); | |
estimatedDate.setDate(new Date().getDate() + remainingDays); | |
let description = calendarTextStack.addText("Herdenimmunität:"); | |
description.font = Font.mediumSystemFont(10); | |
const description2 = calendarTextStack.addText(estimatedDate.toLocaleDateString()); | |
description2.font = Font.boldSystemFont(10); | |
parentStack.addSpacer(4) | |
const lastUpdateDate = new Date(result.lastUpdate); | |
let lastUpdatedText = parentStack.addText("Stand: " + lastUpdateDate.toLocaleDateString()); | |
lastUpdatedText.font = Font.mediumMonospacedSystemFont(8); | |
lastUpdatedText.textOpacity = 0.7; | |
lastUpdatedText.centerAlignText() | |
console.log("Done!"); | |
} | |
// get images from iCloud or download them once | |
async function getImage(image) { | |
let fm = FileManager.local(); | |
let dir = fm.documentsDirectory(); | |
let path = fm.joinPath(dir, image); | |
if (fm.fileExists(path)) { | |
return fm.readImage(path); | |
} else { | |
// download once | |
let imageUrl; | |
switch (image) { | |
case "vac-logo.png": | |
imageUrl = "https://i.imgur.com/ZsBNT8E.png"; | |
break; | |
case "calendar-icon.png": | |
imageUrl = "https://i.imgur.com/Qp8CEFf.png"; | |
break; | |
default: | |
console.log(`Sorry, couldn't find ${image}.`); | |
} | |
let req = new Request(imageUrl); | |
let loadedImage = await req.loadImage(); | |
fm.writeImage(path, loadedImage); | |
return loadedImage; | |
} | |
} | |
async function getNumbers() { | |
// Set up the file manager. | |
const files = FileManager.local(); | |
// Set up cache | |
const cachePath = files.joinPath( | |
files.cacheDirectory(), | |
"api-cache-covid-vaccine-numbers-mopo" | |
); | |
const cacheExists = files.fileExists(cachePath); | |
//const cacheExists = false; | |
const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0; | |
// Get Data | |
try { | |
// If cache exists and it's been less than 60 minutes since last request, use cached data. | |
if ( | |
cacheExists && | |
today.getTime() - cacheDate.getTime() < cacheMinutes * 60 * 1000 | |
) { | |
console.log("Get from Cache"); | |
result = JSON.parse(files.readString(cachePath)); | |
} else { | |
console.log("Get from API"); | |
const req2 = new Request(vaccineStatus); | |
result = await req2.loadJSON(); | |
console.log("Write Data to Cache"); | |
try { | |
files.writeString(cachePath, JSON.stringify(result)); | |
} catch (e) { | |
console.log("Creating Cache failed!"); | |
console.log(e); | |
} | |
} | |
} catch (e) { | |
console.error(e); | |
if (cacheExists) { | |
console.log("Get from Cache"); | |
result = JSON.parse(files.readString(cachePath)); | |
} else { | |
console.log("No fallback to cache possible. Due to missing cache."); | |
} | |
} | |
//console.log(JSON.stringify(result, null, 2)); | |
await setTotalVacNoForGermany(result); | |
} | |
async function setTotalVacNoForGermany(result) { | |
resultDe = result.data[result.data.length - 1]; | |
neededTotalVaccinations = (resultDe.inhabitants * .7); | |
return resultDe; | |
} | |
async function createProgress(currentVacNo) { | |
const context = new DrawContext(); | |
context.size = new Size(width, h); | |
context.opaque = false; | |
context.respectScreenScale = true; | |
context.setFillColor(new Color("#d2d2d7")); | |
const path = new Path(); | |
path.addRoundedRect(new Rect(0, 0, width, h), 3, 2); | |
context.addPath(path); | |
context.fillPath(); | |
context.setFillColor(new Color("#00a86b")); | |
const path1 = new Path(); | |
const path1width = | |
(width * currentVacNo) / neededTotalVaccinations > width | |
? width | |
: (width * currentVacNo) / neededTotalVaccinations; | |
path1.addRoundedRect(new Rect(0, 0, path1width, h), 3, 2); | |
context.addPath(path1); | |
context.fillPath(); | |
return context.getImage(); | |
} | |
async function calculateDailyVac(vaccine) { | |
return vaccine.differenceToThePreviousDay; | |
} | |
async function calculateRemainingDays(vaccine) { | |
console.log("\nNeeded: " + neededTotalVaccinations + "\nTotal: " + vaccine.doses + "\nRemaining: " + (neededTotalVaccinations - vaccine.doses)); | |
const daily = await calculateDailyVac(vaccine); | |
const daysRemaining = Math.round( | |
(neededTotalVaccinations - vaccine.doses) / daily | |
); | |
console.log("Remaining Days: " + daysRemaining); | |
return daysRemaining; | |
} | |
function round(value, decimals) { | |
return Number(Math.round(value + "e" + decimals) + "e-" + decimals); | |
} | |
116433395.39999999 | |
53530526 | |
// | |
// Bitte bis zum Ende kopieren |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment