v2 vaccination widget

This commit is contained in:
Benny 2021-06-13 17:01:01 +02:00
parent d964ab5c5f
commit aeef5a36d6
7 changed files with 117 additions and 68 deletions

View file

@ -18,10 +18,6 @@ Enter the name of you state in the Widget-Parameter:
![Setup](https://raw.githubusercontent.com/ThisIsBenny/iOS-Widgets/main/number-of-covild-19-vaccinations/setup.jpeg) ![Setup](https://raw.githubusercontent.com/ThisIsBenny/iOS-Widgets/main/number-of-covild-19-vaccinations/setup.jpeg)
If you like to show the total values in million instead of thousand, you can add an additional 1 to the Widget-Parameter, which has to be separated by a comma: `Nordrhein-Westfalen,1`. If you do not want to specify a state, you only have to enter a `1`
![Setup](https://raw.githubusercontent.com/ThisIsBenny/iOS-Widgets/main/number-of-covild-19-vaccinations/alternative.jpeg)
## Notice ## Notice
Source of the data is the RKI: https://www.rki.de/DE/Content/InfAZ/N/Neuartiges_Coronavirus/Daten/Impfquoten-Tab.html Source of the data is the RKI: https://www.rki.de/DE/Content/InfAZ/N/Neuartiges_Coronavirus/Daten/Impfquoten-Tab.html

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View file

@ -1,14 +1,18 @@
// Variables used by Scriptable. // Variables used by Scriptable.
// These must be at the very top of the file. Do not edit. // These must be at the very top of the file. Do not edit.
// icon-color: red; icon-glyph: syringe; // icon-color: red; icon-glyph: syringe;
/************** /**************
Version 1.2.0
Version 2.0.0
Changelog: Changelog:
v2.0.0
- Show "at least one" and "fully" vaccination in Medium and small widget
- Upgrade to v2 of the API
- Switch to "Mio." numbers instead of "Tsd.
v1.2.0 v1.2.0
- Large Widget: write percentage to the bar and show total numbers - Large Widget: write percentage to the bar and show total numbers
- Allow to change sorting my add a field namen into sortBy variable: quote, vaccinated, total - Allow to change sorting my add a field namen into sortBy variable
v1.1.1 v1.1.1
- Cache path changed - Cache path changed
- Allow force Update of the data - Allow force Update of the data
@ -32,9 +36,10 @@ Changelog:
// How many minutes should the cache be valid // How many minutes should the cache be valid
let cacheMinutes = 4 * 60 let cacheMinutes = 4 * 60
// enter the name of the field which shoul be used for sorting in the large widget list. // enter the path of the field which should be used for sorting in the large widget list.
// e.g. 'quote' or 'vaccinated'. Default: State name // e.g. 'vaccinatedAtLeastOnce.quote' or 'vaccinatedAtLeastOnce.doses'. Default: State name
const sortBy = '' const sortBy = ''
const sortDirection = '' // asc or desc. Default: asc
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
////////////////////////// Dev Settings //////////////////////// ////////////////////////// Dev Settings ////////////////////////
@ -48,26 +53,20 @@ config.widgetFamily = config.widgetFamily || 'large'
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
let widgetInputRAW = args.widgetParameter let widgetInputRAW = args.widgetParameter
let widgetInput, selectedState, altUnits let widgetInput, selectedState
if (widgetInputRAW !== null && widgetInputRAW !== "") { if (widgetInputRAW !== null && widgetInputRAW !== "") {
widgetInput = widgetInputRAW.toString().split(",") selectedState = widgetInputRAW.toString()
if(widgetInput.length === 1 && widgetInput[0].trim() === '1') {
altUnits = true
selectedState = undefined
} else {
selectedState = widgetInput[0].trim()
altUnits = widgetInput[1] ? true : false
}
if (/^(Baden-Württemberg|Bayern|Berlin|Brandenburg|Bremen|Hamburg|Hessen|Mecklenburg-Vorpommern|Niedersachsen|Nordrhein-Westfalen|Rheinland-Pfalz|Saarland|Sachsen|Sachsen-Anhalt|Schleswig-Holstein|Thüringen)$/.test(selectedState) === false && selectedState !== '' && selectedState !== undefined) { if (/^(Baden-Württemberg|Bayern|Berlin|Brandenburg|Bremen|Hamburg|Hessen|Mecklenburg-Vorpommern|Niedersachsen|Nordrhein-Westfalen|Rheinland-Pfalz|Saarland|Sachsen|Sachsen-Anhalt|Schleswig-Holstein|Thüringen)$/.test(selectedState) === false && selectedState !== '' && selectedState !== undefined) {
throw new Error('Kein gültiges Bundesland. Bitte prüfen Sie die Eingabe.') throw new Error('Kein gültiges Bundesland. Bitte prüfen Sie die Eingabe.')
} }
} }
const altUnits = true
const maximumFractionDigits = altUnits ? 1 : 0 const maximumFractionDigits = 1
const fontSize = 9 const fontSize = 9
const fontSize2 = 12 const fontSize2 = 12
const fontSize3 = 7
const spacing = 5 const spacing = 5
const width = 100 const width = 100
@ -86,6 +85,17 @@ if (args.queryParameters.forceUpdate) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function fetchFromObject(obj, prop) {
if(typeof obj === 'undefined') {
return false;
}
let _index = prop.indexOf('.')
if(_index > -1) {
return fetchFromObject(obj[prop.substring(0, _index)], prop.substr(_index + 1));
}
return obj[prop];
}
function creatProgress(percentage) { function creatProgress(percentage) {
const context = new DrawContext() const context = new DrawContext()
context.size = new Size(width, h) context.size = new Size(width, h)
@ -120,12 +130,12 @@ function creatProgress(percentage) {
context.setTextAlignedCenter() context.setTextAlignedCenter()
context.setTextColor(Color.white()) context.setTextColor(Color.white())
context.setFont(Font.systemFont(fontSize - 1)) context.setFont(Font.systemFont(fontSize - 1))
context.drawTextInRect(`${percentage.toLocaleString(Device.language())}%`, backgroundReact) context.drawTextInRect(`${percentage.toLocaleString(Device.language())}`, backgroundReact)
return context.getImage() return context.getImage()
} }
function getDiagram(percentage) { function getDiagram(percentage, percentage2) {
function drawArc(ctr, rad, w, deg) { function drawArc(ctr, rad, w, deg) {
bgx = ctr.x - rad bgx = ctr.x - rad
bgy = ctr.y - rad bgy = ctr.y - rad
@ -162,10 +172,10 @@ function getDiagram(percentage) {
} }
const canvas = new DrawContext() const canvas = new DrawContext()
const canvSize = 200 const canvSize = 200
const canvTextSize = 36 const canvTextSize = 20
const canvWidth = 10 const canvWidth = 10
const canvRadius = 80 const canvRadius = 85
canvas.opaque = false canvas.opaque = false
canvas.size = new Size(canvSize, canvSize) canvas.size = new Size(canvSize, canvSize)
@ -177,6 +187,12 @@ function getDiagram(percentage) {
canvWidth, canvWidth,
Math.floor(percentage * 3.6) Math.floor(percentage * 3.6)
) )
drawArc(
new Point(canvSize / 2, canvSize / 2),
canvRadius - 15,
canvWidth,
Math.floor(percentage2 * 3.6)
)
const canvTextRect = new Rect( const canvTextRect = new Rect(
0, 0,
@ -187,7 +203,7 @@ function getDiagram(percentage) {
canvas.setTextAlignedCenter() canvas.setTextAlignedCenter()
canvas.setTextColor(Color.gray()) canvas.setTextColor(Color.gray())
canvas.setFont(Font.boldSystemFont(canvTextSize)) canvas.setFont(Font.boldSystemFont(canvTextSize))
canvas.drawTextInRect(`${percentage.toLocaleString(Device.language())}%`, canvTextRect) canvas.drawTextInRect(`${Math.round(percentage).toLocaleString(Device.language())}% / ${Math.round(percentage2).toLocaleString(Device.language())}%`, canvTextRect)
return canvas.getImage() return canvas.getImage()
} }
@ -198,7 +214,7 @@ var today = new Date()
const files = FileManager.local() const files = FileManager.local()
// Set up cache // Set up cache
const cachePath = files.joinPath(files.cacheDirectory(), "widget-vaccination") const cachePath = files.joinPath(files.cacheDirectory(), "widget-vaccination-v2")
const cacheExists = files.fileExists(cachePath) const cacheExists = files.fileExists(cachePath)
const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0 const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0
@ -213,7 +229,7 @@ try {
lastUpdate = cacheDate lastUpdate = cacheDate
} else { } else {
console.log("Get from API") console.log("Get from API")
const req = new Request('https://rki-vaccination-data.vercel.app/api') const req = new Request('https://rki-vaccination-data.vercel.app/api/v2')
result = await req.loadJSON() result = await req.loadJSON()
lastUpdate = today lastUpdate = today
console.log("Write Data to Cache") console.log("Write Data to Cache")
@ -234,6 +250,8 @@ try {
console.log("No fallback to cache possible. Due to missing cache.") console.log("No fallback to cache possible. Due to missing cache.")
} }
} }
const germany = result.data.find((e) => e.name === "Deutschland")
const states = result.data.filter((e) => e.isState)
if (debug) { if (debug) {
console.log(JSON.stringify(result, null, 2)) console.log(JSON.stringify(result, null, 2))
@ -269,27 +287,33 @@ if (config.widgetFamily === 'large') {
stack.layoutVertically() stack.layoutVertically()
stack.spacing = spacing stack.spacing = spacing
const list = Object.entries(result.states).sort((a, b) => { const list = states.sort((a, b) => {
if(sortBy && a[1][sortBy] !== undefined && b[1][sortBy] !== undefined) { const aValue = fetchFromObject(a, sortBy)
return a[1][sortBy] > b[1][sortBy] const bValue = fetchFromObject(b, sortBy)
if(sortBy && aValue !== undefined && bValue !== undefined) {
if (sortDirection === "" || sortDirection === "asc") {
return aValue > bValue
} else {
return aValue < bValue
}
} else { } else {
return a[0].localeCompare(b[0]) return a.name.localeCompare(b.name)
} }
}) })
for (const [key, value] of list) { for (const value of list) {
const row = stack.addStack() const row = stack.addStack()
row.layoutHorizontally() row.layoutHorizontally()
const stateText = row.addText(key) const stateText = row.addText(value.name)
stateText.font = Font.mediumSystemFont(fontSize) stateText.font = Font.mediumSystemFont(fontSize)
stateText.lineLimit = 1 stateText.lineLimit = 1
row.addSpacer() row.addSpacer()
const quoteText = row.addText(`${parseInt(value.vaccinated).toLocaleString(Device.language())}`) const quoteText = row.addText(`${parseInt(value.vaccinatedAtLeastOnce.doses).toLocaleString(Device.language())}`)
quoteText.font = Font.systemFont(fontSize) quoteText.font = Font.systemFont(fontSize)
row.addSpacer(4) row.addSpacer(4)
const progressBar = row.addImage(creatProgress(value.quote)) const progressBar = row.addImage(creatProgress(value.vaccinatedAtLeastOnce.quote))
progressBar.imageSize = new Size(width, h) progressBar.imageSize = new Size(width, h)
} }
@ -301,11 +325,11 @@ if (config.widgetFamily === 'large') {
stateText.font = Font.boldSystemFont(fontSize + 1) stateText.font = Font.boldSystemFont(fontSize + 1)
row.addSpacer() row.addSpacer()
const quoteText = row.addText(`${parseInt(result.vaccinated).toLocaleString(Device.language())}`) const quoteText = row.addText(`${parseInt(germany.vaccinatedAtLeastOnce.doses).toLocaleString(Device.language())}`)
quoteText.font = Font.boldSystemFont(fontSize + 1) quoteText.font = Font.boldSystemFont(fontSize + 1)
row.addSpacer(4) row.addSpacer(4)
const progressBar = row.addImage(creatProgress(result.quote)) const progressBar = row.addImage(creatProgress(germany.vaccinatedAtLeastOnce.quote))
progressBar.imageSize = new Size(width, h) progressBar.imageSize = new Size(width, h)
widget.addSpacer(0) widget.addSpacer(0)
@ -313,64 +337,81 @@ if (config.widgetFamily === 'large') {
const row = widget.addStack() const row = widget.addStack()
row.layoutHorizontally() row.layoutHorizontally()
if (selectedState) { if (selectedState) {
const state = states.find((e) => e.name === selectedState)
const column = row.addStack() const column = row.addStack()
column.layoutVertically() column.layoutVertically()
//column.addSpacer(2)
column.centerAlignContent() column.centerAlignContent()
const imageStack1 = column.addStack() const imageStack1 = column.addStack()
imageStack1.layoutHorizontally() imageStack1.layoutHorizontally()
imageStack1.addSpacer() imageStack1.addSpacer()
imageStack1.addImage(getDiagram(result.states[selectedState].quote)); imageStack1.addImage(getDiagram(state.vaccinatedAtLeastOnce.quote, state.fullyVaccinated.quote));
imageStack1.addSpacer() imageStack1.addSpacer()
column.addSpacer(2) column.addSpacer(5)
// Total Numbers // Total Numbers
let total1 = result.states[selectedState].total / 1000 let total1 = state.inhabitants / 1000
let total1unit = altUnits ? " Tsd." : "t" let total1unit = " Tsd."
// if total is a million or more, format as millions and not thousands // if total is a million or more, format as millions and not thousands
if ( altUnits && result.states[selectedState].total > 999999 ){ if ( altUnits && state.inhabitants > 999999 ){
total1 = result.states[selectedState].total / 1000000 total1 = state.inhabitants / 1000000
total1unit = " Mio." total1unit = " Mio."
} }
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
// vaccinated nunbers // vaccinated nunbers
let vaccinated1 let vaccinated1
let vaccinated1unit = altUnits ? " Tsd." : "t" let vaccinated1unit = " Tsd."
if ( altUnits && result.states[selectedState].vaccinated > 999999){ if (altUnits && state.vaccinatedAtLeastOnce.doses > 999999){
vaccinated1 = result.states[selectedState].vaccinated / 1000000 vaccinated1 = state.vaccinatedAtLeastOnce.doses / 1000000
vaccinated1unit = " Mio." vaccinated1unit = " Mio."
} }
else if ( result.states[selectedState].vaccinated > 999 ) { else if (state.vaccinatedAtLeastOnce.doses > 999 ) {
vaccinated1 = result.states[selectedState].vaccinated / 1000 vaccinated1 = state.vaccinatedAtLeastOnce.doses / 1000
} else { } else {
vaccinated1 = result.states[selectedState].vaccinated vaccinated1 = state.vaccinatedAtLeastOnce.doses
vaccinated1unit = '' vaccinated1unit = ''
} }
let vaccinated1b
let vaccinated1bunit = " Tsd."
if (altUnits && state.fullyVaccinated.doses > 999999){
vaccinated1b = state.fullyVaccinated.doses / 1000000
vaccinated1bunit = " Mio."
}
else if ( state.fullyVaccinated.doses > 999 ) {
vaccinated1b = state.fullyVaccinated.doses / 1000
} else {
vaccinated1b = state.fullyVaccinated.doses
vaccinated1bunit = ''
}
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
if (maximumFractionDigits === 0) { if (maximumFractionDigits === 0) {
total1 = parseInt(total1) total1 = parseInt(total1)
vaccinated1 = parseInt(vaccinated1) vaccinated1 = parseInt(vaccinated1)
vaccinated1b = parseInt(vaccinated1b)
} }
const numbersText1Stack = column.addStack() const numbersText1Stack = column.addStack()
numbersText1Stack.layoutHorizontally() numbersText1Stack.layoutHorizontally()
numbersText1Stack.addSpacer() numbersText1Stack.addSpacer()
const textString1 = `${ const textString1 = `${
parseFloat(vaccinated1) parseFloat(vaccinated1)
.toLocaleString(Device.language(), {maximumFractionDigits: maximumFractionDigits}) .toLocaleString(Device.language(), {maximumFractionDigits: maximumFractionDigits})
}${vaccinated1unit} von ${ }${vaccinated1unit} / ${
parseFloat(vaccinated1b)
.toLocaleString(Device.language(), {maximumFractionDigits: maximumFractionDigits})
}${vaccinated1bunit} von ${
parseFloat(total1) parseFloat(total1)
.toLocaleString(Device.language(), {maximumFractionDigits: maximumFractionDigits}) .toLocaleString(Device.language(), {maximumFractionDigits: maximumFractionDigits})
}${total1unit}` }${total1unit}`
const numbersText1 = numbersText1Stack.addText(textString1) const numbersText1 = numbersText1Stack.addText(textString1)
numbersText1.font = Font.systemFont(fontSize2) numbersText1.font = Font.systemFont(fontSize3)
numbersText1.centerAlignText()
numbersText1Stack.addSpacer() numbersText1Stack.addSpacer()
const stateText1Stack = column.addStack() const stateText1Stack = column.addStack()
@ -386,36 +427,45 @@ if (config.widgetFamily === 'large') {
if (!selectedState || config.widgetFamily == 'medium') { if (!selectedState || config.widgetFamily == 'medium') {
const column2 = row.addStack() const column2 = row.addStack()
column2.layoutVertically() column2.layoutVertically()
//column2.addSpacer(2)
column2.centerAlignContent() column2.centerAlignContent()
const imageStack2 = column2.addStack() const imageStack2 = column2.addStack()
imageStack2.layoutHorizontally() imageStack2.layoutHorizontally()
imageStack2.addSpacer() imageStack2.addSpacer()
imageStack2.addImage(getDiagram(result.quote)); imageStack2.addImage(getDiagram(germany.vaccinatedAtLeastOnce.quote, germany.fullyVaccinated.quote));
imageStack2.addSpacer() imageStack2.addSpacer()
column2.addSpacer(5)
// Total numbers // Total numbers
let total2 = (result.total / 1000).toFixed(0) let total2 = (germany.inhabitants / 1000).toFixed(0)
let total2unit = altUnits ? " Tsd." : "t" let total2unit = " Tsd."
// if total is a million or more, format as millions and not thousands // if total is a million or more, format as millions and not thousands
if ( altUnits && result.total > 999999 ){ if (altUnits && germany.inhabitants > 999999 ){
total2 = result.total / 1000000 total2 = germany.inhabitants / 1000000
total2unit = " Mio." total2unit = " Mio."
} }
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
// vaccinated numbers // vaccinated numbers
let vaccinated2 = result.vaccinated / 1000 let vaccinated2 = germany.vaccinatedAtLeastOnce.doses / 1000
let vaccinated2unit = altUnits ? " Tsd." : "t" let vaccinated2unit = " Tsd."
if ( altUnits && result.vaccinated > 999999 ){ if ( altUnits && germany.vaccinatedAtLeastOnce.doses > 999999 ){
vaccinated2 = result.vaccinated / 1000000 vaccinated2 = germany.vaccinatedAtLeastOnce.doses / 1000000
vaccinated2unit = " Mio." vaccinated2unit = " Mio."
} }
let vaccinated2b = germany.fullyVaccinated.doses / 1000
let vaccinated2bunit = " Tsd."
if (altUnits && germany.fullyVaccinated.doses > 999999 ){
vaccinated2b = germany.fullyVaccinated.doses / 1000000
vaccinated2bunit = " Mio."
}
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
if (maximumFractionDigits === 0) { if (maximumFractionDigits === 0) {
total2 = parseInt(total2) total2 = parseInt(total2)
vaccinated2 = parseInt(vaccinated2) vaccinated2 = parseInt(vaccinated2)
vaccinated2b = parseInt(vaccinated2b)
} }
const numbersText2Stack = column2.addStack() const numbersText2Stack = column2.addStack()
@ -426,13 +476,16 @@ if (config.widgetFamily === 'large') {
const textString2 = `${ const textString2 = `${
parseFloat(vaccinated2) parseFloat(vaccinated2)
.toLocaleString(Device.language(), {maximumFractionDigits: maximumFractionDigits}) .toLocaleString(Device.language(), {maximumFractionDigits: maximumFractionDigits})
}${vaccinated2unit} von ${ }${vaccinated2unit} / ${
parseFloat(vaccinated2b)
.toLocaleString(Device.language(), {maximumFractionDigits: maximumFractionDigits})
}${vaccinated2bunit} von ${
parseFloat(total2) parseFloat(total2)
.toLocaleString(Device.language(), {maximumFractionDigits: maximumFractionDigits}) .toLocaleString(Device.language(), {maximumFractionDigits: maximumFractionDigits})
}${total2unit}` }${total2unit}`
const numbersText2 = numbersText2Stack.addText(textString2) const numbersText2 = numbersText2Stack.addText(textString2)
numbersText2.font = Font.systemFont(fontSize2) numbersText2.font = Font.systemFont(fontSize3)
numbersText2Stack.addSpacer() numbersText2Stack.addSpacer()
const stateText2Stack = column2.addStack() const stateText2Stack = column2.addStack()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB