Skip to content

Instantly share code, notes, and snippets.

@soc201
Forked from marco79cgn/vaccination-stats.js
Created February 5, 2021 23:26
Show Gist options
  • Save soc201/6bacb8f59d58968af5e2af3d47598f30 to your computer and use it in GitHub Desktop.
Save soc201/6bacb8f59d58968af5e2af3d47598f30 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
// Mit Caching und Fallback
const cacheMinutes = 60; // 60 min
const today = new Date();
const neededTotalVaccinations = 116400000;
let result;
let resultDe;
let width = 100;
const h = 5;
let widget = new ListWidget();
widget.setPadding(8, 10, 0, 10);
widget.url =
"https://interaktiv.morgenpost.de/corona-virus-karte-infektionen-deutschland-weltweit/";
await getNumbers();
await createWidget();
Script.setWidget(widget);
Script.complete();
if (config.runsInApp) {
widget.presentSmall();
}
async function createWidget() {
const upperStack = widget.addStack();
upperStack.layoutHorizontally();
const upperTextStack = upperStack.addStack();
upperTextStack.layoutVertically();
let staticText1 = upperTextStack.addText("Verabreichte");
staticText1.font = Font.semiboldRoundedSystemFont(11);
let staticText2 = upperTextStack.addText("Impfdosen:");
staticText2.font = Font.semiboldRoundedSystemFont(11);
upperStack.addSpacer();
let logoImage = upperStack.addImage(await getImage("vac-logo.png"));
logoImage.imageSize = new Size(30, 30);
widget.addSpacer(4);
const amountPerCent = round(
100 / neededTotalVaccinations * resultDe.cumsum_latest,
1
);
let amountText = widget.addText(
resultDe.cumsum_latest.toLocaleString() + " (" + amountPerCent.toLocaleString() + "%)"
);
amountText.font = Font.boldSystemFont(13);
amountText.textColor = new Color("#00a86b");
amountText.minimumScaleFactor = 0.8
let description3 = widget.addText("(7T. Ø: " + calculateDailyVac().toLocaleString() +")");
description3.font = Font.mediumSystemFont(9);
widget.addSpacer(4);
let progressStack = widget.addStack();
progressStack.layoutVertically();
let progressNumberStack = widget.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);
progressStack.addImage(createProgress(resultDe.cumsum_latest));
widget.addSpacer(7);
let calendarStack = widget.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();
estimatedDate.setDate(new Date().getDate() + calculateRemainingDays());
let description = calendarTextStack.addText("Herdenimmunität:");
description.font = Font.mediumSystemFont(10);
const description2 = calendarTextStack.addText(
estimatedDate.toLocaleDateString()
);
description2.font = Font.boldSystemFont(10);
widget.addSpacer(4)
const lastUpdateDate = new Date(resultDe.date);
let lastUpdatedText = widget.addText(
"Stand: " + lastUpdateDate.toLocaleDateString()
);
lastUpdatedText.font = Font.mediumMonospacedSystemFont(8);
lastUpdatedText.textOpacity = 0.7;
lastUpdatedText.centerAlignText()
}
// 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 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(
"https://interaktiv.morgenpost.de/data/corona/rki-vaccinations.json"
);
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.");
}
}
await setTotalVacNoForGermany(result);
}
async function setTotalVacNoForGermany(result) {
for (var i = result.length - 1; i >= 0; i--) {
let currentItem = result[i];
if (currentItem["id"] === "de") {
resultDe = currentItem;
}
}
}
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();
}
function calculateDailyVac() {
const latestVacAmount = resultDe.cumsum_latest;
const vacAmount7DaysAgo = resultDe.cumsum_7_days_ago;
const dailyVacAmount = Math.round((latestVacAmount - vacAmount7DaysAgo) / 7);
return dailyVacAmount;
}
function calculateRemainingDays() {
const daysRemaining = Math.round(
(neededTotalVaccinations - resultDe.cumsum_latest) / calculateDailyVac()
);
return daysRemaining;
}
function round(value, decimals) {
return Number(Math.round(value + "e" + decimals) + "e-" + decimals);
}
//
// Bitte bis zum Ende kopieren
//
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment