Skip to content

Instantly share code, notes, and snippets.

@princemaxwell
Forked from marco79cgn/vaccination-stats.js
Created May 20, 2021 11:55
Show Gist options
  • Save princemaxwell/ecc91bc0b51511462912a2b5edbf9691 to your computer and use it in GitHub Desktop.
Save princemaxwell/ecc91bc0b51511462912a2b5edbf9691 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.1
// 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();
const neededTotalVaccinations = 83200000;
let result;
let resultDe;
let width = 100;
const h = 5;
// Colors of light and dark numbers - change them to fit your needs
const colorLightGreen = new Color("#47b881")
const colorDarkGreen = new Color("#00783e")
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(5);
const amountFirstPerCent = round(
(100 / neededTotalVaccinations) * (resultDe.cumsum_latest - resultDe.cumsum2_latest),
1
);
const amountSecondPerCent = round(
(100 / neededTotalVaccinations) * resultDe.cumsum2_latest,
1
);
let amountFirstText = widget.addText(
(resultDe.cumsum_latest - resultDe.cumsum2_latest).toLocaleString() +
" | " +
amountFirstPerCent.toLocaleString() +
"%"
);
amountFirstText.font = Font.boldSystemFont(12);
amountFirstText.textColor = colorLightGreen;
amountFirstText.minimumScaleFactor = 0.8;
// amountFirstText.rightAlignText()
let amountSecondText = widget.addText(
resultDe.cumsum2_latest.toLocaleString() +
" | " +
amountSecondPerCent.toLocaleString() +
"%"
);
amountSecondText.font = Font.boldSystemFont(12);
amountSecondText.textColor = colorDarkGreen;
amountSecondText.minimumScaleFactor = 0.8;
// amountSecondText.rightAlignText();
widget.addSpacer(4);
let progressStack = widget.addStack();
progressStack.layoutVertically();
let progressNumberStack = widget.addStack();
progressNumberStack.layoutHorizontally();
let description3 = progressNumberStack.addText(
"(7T. Ø: " + calculateDailyVac().toLocaleString() + ")"
);
description3.font = Font.mediumSystemFont(9);
progressNumberStack.addSpacer();
const progressText70 = progressNumberStack.addText("83,2 Mio");
progressText70.font = Font.mediumSystemFont(9);
progressStack.addImage(createProgress((resultDe.cumsum_latest - resultDe.cumsum2_latest), resultDe.cumsum2_latest));
widget.addSpacer(8);
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(2);
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 local storage 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-vaccination.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") {
console.log("result de: " + JSON.stringify(currentItem))
resultDe = currentItem;
}
}
}
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();
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();
}
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(
(116400000 - 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