Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A Scriptable widget that shows the amount of people who have received the corona vaccination in Germany
const version = 'v1.5'
const cacheMinutes = 60 // 60 min
const today = new Date();
let result
let widget = new ListWidget()
widget.setPadding(10, 15, 10, 10)
await getNumbers()
await createWidget()
Script.setWidget(widget)
Script.complete()
if (config.runsInApp) {
widget.presentSmall()
}
async function createWidget() {
let param
let bundesland
if (config.runsInApp) {
param = 'BY';
}
else {
param = args.widgetParameter;
}
switch (param) {
case 'BW':
bundesland = 'de.bw';
break;
case 'BY':
bundesland = 'de.by';
break;
case 'BE':
bundesland = 'de.be';
break;
case 'BB':
bundesland = 'de.bb';
break;
case 'HB':
bundesland = 'de.hb';
break;
case 'HH':
bundesland = 'de.hh';
break;
case 'HE':
bundesland = 'de.he';
break;
case 'MV':
bundesland = 'de.mv';
break;
case 'NI':
bundesland = 'de.ni';
break;
case 'NRW':
bundesland = 'de.nw';
break;
case 'RP':
bundesland = 'de.rp';
break;
case 'SL':
bundesland = 'de.sl';
break;
case 'SN':
bundesland = 'de.sn';
break;
case 'ST':
bundesland = 'de.st';
break;
case 'SH':
bundesland = 'de.sh';
break;
case 'TH':
bundesland = 'de.th';
break;
default:
bundesland = 'default';
}
if (bundesland === 'default') {
throw new Error('Kein gültiges Bundesland.')
}
for (let i = 1; i < result.length; i++) {
if (result[i][0] == 'de') {
de_data = result[i]
}
if (result[i][0] == bundesland) {
state_data = result[i]
}
}
const header = result[0]
// console.log(header)
const id = header.indexOf('id')
const date = header.indexOf('date')
const vaccinations1 = header.indexOf('vaccinations1')
const vaccinations1_quota = header.indexOf('vaccinations1_quota')
const vaccinations2 = header.indexOf('vaccinations2')
const vaccinations2_quota = header.indexOf('vaccinations2_quota')
const vaccinations3 = header.indexOf('vaccinations3')
const vaccinations3_quota = header.indexOf('vaccinations3_quota')
const vaccinations4 = header.indexOf('vaccinations4')
const vaccinations4_quota = header.indexOf('vaccinations4_quota')
result_de = {
id: de_data[id],
date: de_data[date],
vaccinations1: Number(de_data[vaccinations1]),
vaccinations1_quota: Number(de_data[vaccinations1_quota]),
vaccinations2: Number(de_data[vaccinations2]),
vaccinations2_quota: Number(de_data[vaccinations2_quota]),
vaccinations3: Number(de_data[vaccinations3]),
vaccinations3_quota: Number(de_data[vaccinations3_quota]),
vaccinations4: Number(de_data[vaccinations4]),
vaccinations4_quota: Number(de_data[vaccinations4_quota])
};
console.log('DE date: ' + result_de.date)
result_state = {
id: state_data[id],
date: state_data[date],
vaccinations1: Number(state_data[vaccinations1]),
vaccinations1_quota: Number(state_data[vaccinations1_quota]),
vaccinations2: Number(state_data[vaccinations2]),
vaccinations2_quota: Number(state_data[vaccinations2_quota]),
vaccinations3: Number(state_data[vaccinations3]),
vaccinations3_quota: Number(state_data[vaccinations3_quota]),
vaccinations4: Number(state_data[vaccinations4]),
vaccinations4_quota: Number(state_data[vaccinations4_quota])
};
console.log('State date: ' + result_state.date)
const upperStack = widget.addStack()
upperStack.layoutHorizontally()
let staticText1 = upperStack.addText("Impfungen DE 💉");
staticText1.font = Font.boldSystemFont(12);
staticText1.textColor = new Color("#00a86b");
widget.addSpacer(5)
let amountText1 = widget.addText("① " + result_de.vaccinations1.toLocaleString() + " (" + result_de.vaccinations1_quota.toLocaleString() + "%)")
amountText1.font = Font.boldSystemFont(9)
widget.addSpacer(1)
let amountText2 = widget.addText("② " + result_de.vaccinations2.toLocaleString() + " (" + result_de.vaccinations2_quota.toLocaleString() + "%)")
amountText2.font = Font.boldSystemFont(9)
widget.addSpacer(1)
let amountText3 = widget.addText("③ " + result_de.vaccinations3.toLocaleString() + " (" + result_de.vaccinations3_quota.toLocaleString() + "%)")
amountText3.font = Font.boldSystemFont(9)
widget.addSpacer(1)
let amountText4 = widget.addText("④ " + result_de.vaccinations4.toLocaleString() + " (" + result_de.vaccinations4_quota.toLocaleString() + "%)")
amountText4.font = Font.boldSystemFont(9)
widget.addSpacer(5)
let staticText2 = widget.addText("Impfungen " + param + " 💉")
staticText2.font = Font.boldSystemFont(12);
staticText2.textColor = new Color("#00a86b");
widget.addSpacer(2)
let amountText5 = widget.addText("① " + result_state.vaccinations1.toLocaleString() + " (" + result_state.vaccinations1_quota.toLocaleString() + "%)")
amountText5.font = Font.boldSystemFont(9)
widget.addSpacer(1)
let amountText6 = widget.addText("② " + result_state.vaccinations2.toLocaleString() + " (" + result_state.vaccinations2_quota.toLocaleString() + "%)")
amountText6.font = Font.boldSystemFont(9)
widget.addSpacer(1)
let amountText7 = widget.addText("③ " + result_state.vaccinations3.toLocaleString() + " (" + result_state.vaccinations3_quota.toLocaleString() + "%)")
amountText7.font = Font.boldSystemFont(9)
widget.addSpacer(1)
let amountText8 = widget.addText("④ " + result_state.vaccinations4.toLocaleString() + " (" + result_state.vaccinations4_quota.toLocaleString() + "%)")
amountText8.font = Font.boldSystemFont(9)
widget.addSpacer(5)
const lastUpdateDate = new Date(result_de.date)
let day = lastUpdateDate.getDate()
let month = lastUpdateDate.getMonth() + 1
let year = lastUpdateDate.getFullYear()
if (day<10)
{
day = "0" + day
}
if (month<10)
{
month = "0" + month
}
let lastUpdatedText = widget.addText("Stand: " + day + "." + month + "." + year)
lastUpdatedText.textColor = Color.gray()
lastUpdatedText.font = Font.mediumSystemFont(8)
}
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-" + version) // ggfs. namen anpassen
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_string = files.readString(cachePath)
} else {
console.log("Get from API")
const req2 = new Request('https://cdn.fnki.de/corona-data/vaccinations.rki.tsv')
result_string = await req2.loadString()
console.log("Write Data to Cache")
try {
files.writeString(cachePath, result_string)
} catch (e) {
console.log("Creating Cache failed!")
console.log(e)
}
}
} catch (e) {
console.error(e)
if (cacheExists) {
console.log("Get from Cache")
result_string = files.readString(cachePath)
} else {
console.log("No fallback to cache possible. Due to missing cache.")
}
}
const columns = result_string.split(/\r?\n/)
result = columns.map(col => col.split('\t'))
}
//
// Please copy everything until the end
//
@thorstenleidl
Copy link
Author

thorstenleidl commented Jan 6, 2021

Ja genau, danke. NRW funktioniert bei mir auch. Könntest du das Skript nochmal komplett neu rein kopieren? Und könntest du prüfen, dass bei dem Kürzel kein Leerzeichen dabei ist?

Habe ich gemacht, leider ohne Erfolg. Ich erhalte außerdem noch diese Fehlermeldung.

3C0C9412-EB7E-4702-8064-21F1B3B736F9

Okay, das ist merkwürdig, die Meldung hatte ich bei mir bisher noch nie und in Zeile 19 stand bei Revision 12 eigentlich gar kein Code. Ich könnte mir aber den Hintergrund vorstellen. Lokal bei mir hatte ich noch ein paar kleine Änderungen erstellt, welche ich soeben als Revision 13 hochgeladen habe. Könntest du das bisherige Skript und das Widget löschen und anschließend mit dem neuen Code beides neu erstellen?

@jnhllTM4
Copy link

jnhllTM4 commented Jan 6, 2021

Könntest du das bisherige Skript und das Widget löschen und anschließend mit dem neuen Code beides neu erstellen?

Funktioniert! Vielen Dank! Tolles Script! :-)

@thorstenleidl
Copy link
Author

thorstenleidl commented Jan 6, 2021

Könntest du das bisherige Skript und das Widget löschen und anschließend mit dem neuen Code beides neu erstellen?

Funktioniert! Vielen Dank! Tolles Script! :-)

Freut mich zu hören, ich hab es ja nur geforkt ;)

@thorstenleidl
Copy link
Author

thorstenleidl commented Jan 14, 2021

Revision 14:

  • Impfquote durch 2 geteilt, damit 2 Impfungen pro Person berücksichtigt werden, da diese von den RKI Daten bisher nicht ganz heraus zu lesen war

@grisu112
Copy link

grisu112 commented Jan 23, 2021

Hallo, mir gefällt diese Variante sehr gut. Mich persönlich interessiert allerdings nur die 2. Impfung, da nur das eine wirkliche Aussagekraft hat wie ich finde. Aber das muss ja jeder selber wissen. Nun werden ja (zu mindestens teilweise) auch Daten zur zweiten Impfung in der API geliefert. Als ich das umstellen wollte sind mir einige Unregelmäsigkeiten aufgefallen.
Zum einen hatte ich mich gewundert warum das Widget die Gesamtzahl nicht mehr zuverlässig aktualisiert.
Du nutzt aktuell die Variable "vaccinated". Die beinhaltet aber nur die 1. Impfung. Es gibt jetzt noch "sum_vaccine_doses" in dem auch die Zweitimpfungen enthalten sind. Außerdem gibt es unter "2nd_vaccination" jetzt Werte für die 2. Impfung. Hinzu kommt, das sich der Wert "quote" nur auf "vaccinated" bezieht, also auf die Erstimpfung. Im letzten Update hattest du den Wert einfach durch zwei geteilt, was somit leider nicht passt. Selbst wenn sich das auf die Gesamtimpfungen bezieht wäre das ja auch keine sichere Quote, weil man nicht weis wieviele davon Erst- oder Zweitimpfungen sind.

Ich finde da ist aktuell noch ziemlich Chaos und es fehlen auch noch Werte. Daher hab ich das jetzt für mich erstmal wie folgt gelöst. (Wie eingangs erwähnt nur noch bezogen auf 2. Impfung). Anzeige der Werte aus der API für die Zweitimpfung, Und eigene Errechnung der Quote, weil diese aktuell noch nicht geliefert wird. Vielleicht kann das noch jemand gebrauchen:

Zeile 89:
let staticText = widget.addText("Impfungen DE:")
ersetz durch:
let staticText = widget.addText("2. Impfung DE:")

Zeile 96:
let staticText2 = widget.addText("Impfungen " + param + ":")
ersetzt durch:
let staticText2 = widget.addText("2. Impfung " + param + ":")

Zeile 92:
let amountText = widget.addText(result.vaccinated.toLocaleString() + " (" + (result.quote / 2).toFixed(2) + "%)")
ersetzt durch:
let amountText = widget.addText(result["2nd_vaccination"].vaccinated.toLocaleString() + " (" + (result["2nd_vaccination"].vaccinated / result.total * 100 ).toFixed(2) + "%)")

Zeile 99:
let amountText2 = widget.addText(result.states[bundesland].vaccinated.toLocaleString() + " (" + (result.states[bundesland].quote / 2).toFixed(2) + "%)")
Ersetzt durch:
let amountText2 = widget.addText(result.states[bundesland]["2nd_vaccination"].vaccinated.toLocaleString() + " (" + (result.states[bundesland]["2nd_vaccination"].vaccinated / result.states[bundesland].total * 100).toFixed(2) + "%)")

@thorstenleidl
Copy link
Author

thorstenleidl commented Jan 23, 2021

Hallo, mir gefällt diese Variante sehr gut. Mich persönlich interessiert allerdings nur die 2. Impfung, da nur das eine wirkliche Aussagekraft hat wie ich finde. Aber das muss ja jeder selber wissen. Nun werden ja (zu mindestens teilweise) auch Daten zur zweiten Impfung in der API geliefert. Als ich das umstellen wollte sind mir einige Unregelmäsigkeiten aufgefallen.
Zum einen hatte ich mich gewundert warum das Widget die Gesamtzahl nicht mehr zuverlässig aktualisiert.
Du nutzt aktuell die Variable "vaccinated". Die beinhaltet aber nur die 1. Impfung. Es gibt jetzt noch "sum_vaccine_doses" in dem auch die Zweitimpfungen enthalten sind. Außerdem gibt es unter "2nd_vaccination" jetzt Werte für die 2. Impfung. Hinzu kommt, das sich der Wert "quote" nur auf "vaccinated" bezieht, also auf die Erstimpfung. Im letzten Update hattest du den Wert einfach durch zwei geteilt, was somit leider nicht passt. Selbst wenn sich das auf die Gesamtimpfungen bezieht wäre das ja auch keine sichere Quote, weil man nicht weis wieviele davon Erst- oder Zweitimpfungen sind.

Ich finde da ist aktuell noch ziemlich Chaos und es fehlen auch noch Werte. Daher hab ich das jetzt für mich erstmal wie folgt gelöst. (Wie eingangs erwähnt nur noch bezogen auf 2. Impfung). Anzeige der Werte aus der API für die Zweitimpfung, Und eigene Errechnung der Quote, weil diese aktuell noch nicht geliefert wird. Vielleicht kann das noch jemand gebrauchen:

Zeile 89:
let staticText = widget.addText("Impfungen DE:")
ersetz durch:
let staticText = widget.addText("2. Impfung DE:")

Zeile 96:
let staticText2 = widget.addText("Impfungen " + param + ":")
ersetzt durch:
let staticText2 = widget.addText("2. Impfung " + param + ":")

Zeile 92:
let amountText = widget.addText(result.vaccinated.toLocaleString() + " (" + (result.quote / 2).toFixed(2) + "%)")
ersetzt durch:
let amountText = widget.addText(result["2nd_vaccination"].vaccinated.toLocaleString() + " (" + (result["2nd_vaccination"].vaccinated / result.total * 100 ).toFixed(2) + "%)")

Zeile 99:
let amountText2 = widget.addText(result.states[bundesland].vaccinated.toLocaleString() + " (" + (result.states[bundesland].quote / 2).toFixed(2) + "%)")
Ersetzt durch:
let amountText2 = widget.addText(result.states[bundesland]["2nd_vaccination"].vaccinated.toLocaleString() + " (" + (result.states[bundesland]["2nd_vaccination"].vaccinated / result.states[bundesland].total * 100).toFixed(2) + "%)")

Hi,
ist mir alles bewusst, anfangs waren die Daten noch nicht vorhanden und ich habe aktuell kaum Zeit das umzubauen. Werde es so bald wie möglich nachholen ;)

@thorstenleidl
Copy link
Author

thorstenleidl commented Jan 23, 2021

Da die Datenbasis überarbeitet wurde habe ich diese in Rev16 im Widget angepasst und leicht umgebaut:

FFF109D4-E560-4D8B-852F-4FA591F45342

@lululasse
Copy link

lululasse commented Nov 25, 2021

Hi Thorsten, könntest Du eine dritte Zeile einbinden mit de Angabe der Booster Impfungen?

@thorstenleidl
Copy link
Author

thorstenleidl commented Nov 30, 2021

Hi @lululasse, dazu muss ich auf die neue API umbauen (https://rki-vaccination-data.vercel.app/api/v2), bei der die Ebenen neu strukturiert wurden, was ein wenig Aufwand mit sich bringt. Sobald ich dazu komme poste ich es hier, kann aber nicht versprechen, dass das bald ist.

@thorstenleidl
Copy link
Author

thorstenleidl commented Nov 30, 2021

@lululasse, es hat mir keine Ruhe gelassen :) Version 17 ist online mit Erst-, Zweit- und Booster Impfung aus der neuen API:

5CDE272D-299E-4BC5-B486-719AC42F605F

@cl4udiu5
Copy link

cl4udiu5 commented Nov 30, 2021

895237F0-F1CB-401F-BD16-5758B1466B11

Parameter: BW

@thorstenleidl
Copy link
Author

thorstenleidl commented Nov 30, 2021

Hi @cl4udiu5, ich vermute ein Copy/Paste Fehler? In Zeile 111 steht kein Code, ich hab es eben mit Parameter BW nochmal neu eingerichtet bei mir, hat alles funktioniert. Code nochmal komplett kopieren und den alten vorher löschen, dann sollte es gehen.

@lululasse
Copy link

lululasse commented Nov 30, 2021

Hi Thorsten, danke für Deinen Einsatz!
Bekomme auch die Fehlermeldung, aber für line 107:50. Habe HE für Hessen eingegeben.

@lululasse
Copy link

lululasse commented Nov 30, 2021

Bei BY gleiche Fehlermeldung.

@thorstenleidl
Copy link
Author

thorstenleidl commented Nov 30, 2021

Hi @lululasse,
sehr merkwürdig, dann kann es vermutlich nur noch der Cache sein. Kannst du bitte folgendes testen:

  1. Scriptable App öffnen -> Zeile 1 den Cache von 60 auf 0 stellen
  2. Script in der App ausführen
  3. Wenn das erwartete Widget kommt danach wieder von 0 auf 60 stellen
  4. Auf dem Homescreen dauert es ein wenig bis es sich danach aktualisiert.

Alternative zum Workaround wäre 60 Minuten warten :)

Ich teste es parallel nochmal auf einem anderen iOS Gerät.

@thorstenleidl
Copy link
Author

thorstenleidl commented Nov 30, 2021

Ich habe in Rev. 18 noch eine Anpassung vorgenommen, damit es beim Update auf die neue API nicht zu Fehlern kommt.
-> Name des Cache Path auf v2 angepasst

@lululasse, @cl4udiu5: das sollte euer Problem beheben, einfach nochmal neu kopieren.

@lululasse
Copy link

lululasse commented Nov 30, 2021

Bingo! Das hat es gebracht! Du hast es echt drauf! Danke!

@cl4udiu5
Copy link

cl4udiu5 commented Nov 30, 2021

Danke Thorsten. Jetzt geht’s.

@Lord-N1KON
Copy link

Lord-N1KON commented Jan 19, 2022

E99ACE39-9555-4F72-98AC-B15AD9216677

hat hier vllt jmd. eine Lösung ??

@thorstenleidl
Copy link
Author

thorstenleidl commented Jan 19, 2022

Hi @Lord-N1KON,

die API ist down: https://rki-vaccination-data.vercel.app/api/v2

636D6154-9C87-4432-85C7-DAA992BFD15C

Einfach mal den Tag abwarten, vielleicht kommt’s später noch.

@Lord-N1KON
Copy link

Lord-N1KON commented Jan 19, 2022

Ahh Ok Danke für die Info.
ist schon seit Gestern 🙈 naja mal abwarten

@thorstenleidl
Copy link
Author

thorstenleidl commented Jan 19, 2022

@Lord-N1KON: funktioniert wieder

@lululasse
Copy link

lululasse commented May 6, 2022

Letzte Update war am 28.4. Ist die API kaputt. Kann man da was machen?

@thorstenleidl
Copy link
Author

thorstenleidl commented May 6, 2022

Hi @lululasse,
ich sehe gerade, dass die API vermutlich down ist oder ganz abgeschaltet wurde. Das Script von dem ich geforkt habe nutzt mittlerweile eine andere API, aber auch dort sind die Daten zuletzt am 27.04. aktualisiert:

Script:
https://gist.github.com/marco79cgn/b5f291d6242a2c530e56c748f1ae7f2c

API:
https://interaktiv.morgenpost.de/data/corona/rki-vaccinations-latest.json

API result:
image

Sobald ich eine funktionierende Datenquelle finde kann ich es umbauen.

@lululasse
Copy link

lululasse commented May 6, 2022

Das wäre super! Vielen Dank im Voraus!

@lululasse
Copy link

lululasse commented May 25, 2022

Hi Thorsten, hast Du schon eine neue Datenquelle gefunden?

@thorstenleidl
Copy link
Author

thorstenleidl commented May 25, 2022

Hi @lululasse,
nein bislang leider nicht.

@lululasse
Copy link

lululasse commented Jun 16, 2022

Hi Thorsten, hast Du das Thema noch auf dem Radar? Auch noch die 4.Impfung. Danke für eine Info!

@thorstenleidl
Copy link
Author

thorstenleidl commented Jun 16, 2022

Hi @lululasse,

Version 1.5 ist online:

F6CD5110-C8F1-4D15-9AC5-E748117F0F8E

@lululasse
Copy link

lululasse commented Jun 17, 2022

Super! Vielen Dank! Funktioniert einwandfrei!

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