Skip to content

Instantly share code, notes, and snippets.

@rafaelmaeuer
Forked from marco79cgn/vaccination-stats.js
Last active April 22, 2023 13:23
Show Gist options
  • Save rafaelmaeuer/2321d1543c8e846b13fe38ea0b61b683 to your computer and use it in GitHub Desktop.
Save rafaelmaeuer/2321d1543c8e846b13fe38ea0b61b683 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.4.3
// 30.05.2022
//
// Mit Caching und Fallback
// Neue API mit TSV-Parsing
// Dark-Mode Unterstützung
const resetCache = false;
const cacheMinutes = 60;
const today = new Date();
const neededTotalVaccinations = 83200000;
let result;
let resultDe;
let width = 100;
const h = 5;
const colorLightGreen = new Color("#47b881")
const colorDarkGreen = new Color("#00783e")
let widget = new ListWidget();
widget.setPadding(6, 12, 8, 12);
widget.url =
"https://interaktiv.morgenpost.de/corona-impfungen-deutschland-bundeslaender-weltweit/";
await getNumbers();
await createWidget();
Script.setWidget(widget);
Script.complete();
if (config.runsInApp) {
widget.presentSmall();
}
async function createWidget() {
// Add background color
widget.backgroundColor = new Color("ffffff");
widget.backgroundColor = Color.dynamic(widget.backgroundColor, new Color("1c1c1c"));
widget.addSpacer();
const dataFirst = {
text: "Erstimpfung",
quota: resultDe.vaccinations1_quota,
vaccination: resultDe.vaccinations1,
progress: createProgress(resultDe.vaccinations1),
};
createProgressStack(dataFirst);
const dataSecond = {
text: "Zweitimpfung",
quota: resultDe.vaccinations2_quota,
vaccination: resultDe.vaccinations2,
progress: createProgress(resultDe.vaccinations2),
};
createProgressStack(dataSecond);
const dataThird = {
text: "Booster",
quota: resultDe.vaccinations4_quota + " | " + resultDe.vaccinations3_quota,
vaccination: resultDe.vaccinations4 + " | " + resultDe.vaccinations3,
progress: createProgress(resultDe.vaccinations3, resultDe.vaccinations4),
};
createProgressStack(dataThird);
widget.addSpacer(3);
let calendarStack = widget.addStack();
const date = resultDe.date.split('-'); // destructure date
const lastUpdateDate = new Date(date[0], date[1]-1, date[2]); // fixes -1 day
const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
let description = widget.addText("Stand: " + lastUpdateDate.toLocaleDateString('de-DE', options));
description.centerAlignText();
description.font = Font.mediumSystemFont(9);
}
function createProgressStack(data) {
let firstDoseStack = widget.addStack();
firstDoseStack.layoutHorizontally();
let amountFirstText = firstDoseStack.addText(data.text);
amountFirstText.font = Font.boldSystemFont(12);
firstDoseStack.addSpacer();
let percentFirstText = firstDoseStack.addText(data.quota + "%");
percentFirstText.font = Font.boldSystemFont(12);
widget.addSpacer(2);
let progressStack = widget.addStack();
progressStack.layoutVertically();
let progressNumberStack = widget.addStack();
progressNumberStack.layoutHorizontally();
const progressText = progressNumberStack.addText(numberWithDots(data.vaccination));
progressText.font = Font.mediumSystemFont(10);
progressStack.addImage(data.progress);
widget.addSpacer(3);
}
// get images from local storage or download them once (currently not used)
//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;
// }
//}
function numberWithDots(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}
async function getNumbers() {
// Set up the file manager.
const files = FileManager.local();
// Set up cache
const cachePath = files.joinPath(
files.cacheDirectory(),
"api-cache-covid-vaccination-numbers-mopo-tsv"
);
const cacheExists = files.fileExists(cachePath);
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 && !resetCache &&
today.getTime() - cacheDate.getTime() < cacheMinutes * 60 * 1000
) {
console.log("Get from Cache");
result = files.readString(cachePath);
} else {
console.log("Get from API");
const req2 = new Request(
"https://cdn.fnki.de/corona-data/vaccinations.rki.tsv"
);
result = await req2.loadString();
console.log("Write Data to Cache");
try {
files.writeString(cachePath, result);
} catch (e) {
console.log("Creating Cache failed!");
console.log(e);
}
}
} catch (e) {
console.error(e);
if (cacheExists) {
console.log("Get from Cache");
result = files.readString(cachePath);
} else {
console.log("No fallback to cache possible. Due to missing cache.");
}
}
await setTotalVacNoForGermany(result);
}
async function setTotalVacNoForGermany(result) {
const cols = result.split(/\r?\n/);
const data = cols.map(col => col.split('\t'));
data.pop(); // remove last (empty) element
const name = data[0];
const vals = data[data.length -1];
const date = name.indexOf('date');
const vac1 = name.indexOf('vaccinations1');
const vac2 = name.indexOf('vaccinations2');
const vac3 = name.indexOf('vaccinations3');
const vac4 = name.indexOf('vaccinations4');
const vac1Quota = name.indexOf('vaccinations1_quota');
const vac2Quota = name.indexOf('vaccinations2_quota');
const vac3Quota = name.indexOf('vaccinations3_quota');
const vac4Quota = name.indexOf('vaccinations4_quota');
resultDe = {
date: vals[date],
vaccinations1: vals[vac1],
vaccinations1_quota: vals[vac1Quota],
vaccinations2: vals[vac2],
vaccinations2_quota: vals[vac2Quota],
vaccinations3: vals[vac3],
vaccinations3_quota: vals[vac3Quota],
vaccinations4: vals[vac4],
vaccinations4_quota: vals[vac4Quota],
};
}
function createProgress(firstVacNo, secondVacNo) {
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(colorLightGreen);
const path1 = new Path();
const path1width =
(width * firstVacNo) / neededTotalVaccinations > width
? width
: (width * firstVacNo) / neededTotalVaccinations;
path1.addRoundedRect(new Rect(0, 0, path1width, h), 3, 2);
context.addPath(path1);
context.fillPath();
if (secondVacNo) {
const path2 = new Path();
const path2width =
(width * secondVacNo) / neededTotalVaccinations > width
? width
: (width * secondVacNo) / neededTotalVaccinations;
path2.addRoundedRect(new Rect(0, 0, path2width, h), 3, 2);
context.addPath(path2);
context.setFillColor(colorDarkGreen)
context.fillPath();
}
return context.getImage();
}
//
// Bitte bis zum Ende kopieren
//
@dennerforen
Copy link

RKI hat Daten eingestellt

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment