Skip to content

Instantly share code, notes, and snippets.

@colinferm
Forked from marco79cgn/vaccination-stats.js
Last active August 25, 2021 11:18
Show Gist options
  • Save colinferm/e4b010891cdaa200cdaa7b69d0ba3a68 to your computer and use it in GitHub Desktop.
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
// 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