Skip to content

Instantly share code, notes, and snippets.

@MatzeKitt
Last active June 6, 2023 18:12
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MatzeKitt/e9717fe547244ef58b09abea3c300230 to your computer and use it in GitHub Desktop.
Save MatzeKitt/e9717fe547244ef58b09abea3c300230 to your computer and use it in GitHub Desktop.
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: deep-gray; icon-glyph: magic;
// Licence: Robert Koch-Institut (RKI), dl-de/by-2-0
//
// -------------
// Configuration
// -------------
// whether a second graph with old data (-7 days) should be draw for comparison
const drawOldData = true;
// whether to shorten big numbers, e. g. 10.256 becomes 10,2k
const shortenBigNumbers = true;
// ---------------------------
// do not edit after this line
// ---------------------------
const DAY_IN_MICROSECONDS = 86400000;
const lineWeight = 2;
const vertLineWeight = .5;
const accentColor1 = new Color( '#33cc33', 1 );
const accentColor2 = Color.lightGray();
const widgetHeight = 338;
const widgetWidth = 720;
const graphLow = 280;
const graphHeight = 160;
const spaceBetweenDays = ( shortenBigNumbers ? 56 : 77 );
let drawContext = new DrawContext();
drawContext.size = new Size( widgetWidth, widgetHeight );
drawContext.opaque = false;
let widget = await createWidget();
widget.setPadding( 0, 0, 0, 0 );
widget.backgroundImage = ( drawContext.getImage() );
await widget.presentMedium();
Script.setWidget( widget );
Script.complete();
async function createWidget( items ) {
const list = new ListWidget();
const date = new Date();
date.setTime( ( date.getTime() - ( shortenBigNumbers ? 20 * DAY_IN_MICROSECONDS : 17 * DAY_IN_MICROSECONDS ) ) );
const minDate = ( '0' + ( date.getMonth() + 1 ) ).slice( -2 ) + '-' + ( '0' + date.getDate() ).slice( -2 ) + '-' + date.getFullYear();
const apiUrlData = `https://services7.arcgis.com/mOBPykOjAyBO2ZKk/ArcGIS/rest/services/Covid19_RKI_Sums/FeatureServer/0/query?where=Meldedatum+%3E+%27${ encodeURIComponent( minDate ) }%27&objectIds=&time=&resultType=none&outFields=*&returnIdsOnly=false&returnUniqueIdsOnly=false&returnCountOnly=false&returnDistinctValues=false&cacheHint=false&orderByFields=Meldedatum&groupByFieldsForStatistics=&outStatistics=&having=&resultOffset=&resultRecordCount=&sqlFormat=none&f=json&token=`;
date.setTime( ( date.getTime() + 7 * DAY_IN_MICROSECONDS ) );
const cityData = await new Request( apiUrlData ).loadJSON();
if ( ! cityData || ! cityData.features || ! cityData.features.length ) {
const errorList = new ListWidget();
errorList.backgroundColor = new Color( '#191a1d', 1 );
errorList.addText( 'Keine Statistik gefunden.' );
return errorList;
}
list.backgroundColor = new Color( '#191a1d', 1 );
drawContext.setTextColor( Color.white() );
drawContext.setFont( Font.mediumSystemFont( 26 ) );
drawContext.drawText( '🦠 Statistik'.toUpperCase() + ' deutschlandweit', new Point( 25, 25 ) );
drawContext.setTextAlignedCenter();
let data = {};
let oldData = {};
for ( const dataset of cityData.features ) {
if ( drawOldData && dataset.attributes.Meldedatum < date.getTime() ) {
// get old data
if ( typeof oldData[ dataset.attributes.Meldedatum ] === 'undefined' ) {
oldData[ dataset.attributes.Meldedatum ] = {
Meldedatum: dataset.attributes.Meldedatum,
AnzahlFall: 0,
};
}
oldData[ dataset.attributes.Meldedatum ].AnzahlFall += parseInt( dataset.attributes.AnzahlFall );
}
else if ( dataset.attributes.Meldedatum >= date.getTime() ) {
// get old data
if ( drawOldData && dataset.attributes.Meldedatum < date.getTime() + 5 * DAY_IN_MICROSECONDS ) {
if ( typeof oldData[ dataset.attributes.Meldedatum ] === 'undefined' ) {
oldData[ dataset.attributes.Meldedatum ] = {
Meldedatum: dataset.attributes.Meldedatum,
AnzahlFall: 0,
};
}
oldData[ dataset.attributes.Meldedatum ].AnzahlFall += parseInt( dataset.attributes.AnzahlFall );
}
// get current data
if ( typeof data[ dataset.attributes.Meldedatum ] === 'undefined' ) {
data[ dataset.attributes.Meldedatum ] = {
Meldedatum: dataset.attributes.Meldedatum,
AnzahlFall: 0,
};
}
data[ dataset.attributes.Meldedatum ].AnzahlFall += parseInt( dataset.attributes.AnzahlFall );
}
}
// get minimal value of current and old data
const currentDataData = Object.values( data );
const oldDataData = Object.values( oldData );
let currentMin, currentMax, oldMin, oldMax;
for ( let i = 0; i < currentDataData.length; i++ ) {
let aux = currentDataData[ i ].AnzahlFall;
currentMin = ( aux < currentMin || currentMin == undefined ? aux : currentMin );
currentMax = ( aux > currentMax || currentMax == undefined ? aux : currentMax );
}
for ( let i = 0; i < oldDataData.length; i++ ) {
let aux = oldDataData[ i ].AnzahlFall;
oldMin = ( aux < oldMin || oldMin == undefined ? aux : oldMin );
oldMax = ( aux > oldMax || oldMax == undefined ? aux : oldMax );
}
const min = currentMin <= oldMin ? currentMin : oldMin;
const max = currentMax >= oldMax ? currentMax : oldMax;
if ( drawOldData ) {
drawChart( oldDataData, 'old', min, max );
}
drawChart( currentDataData, 'current', min, max );
return list;
}
function drawChart( dataArray, chartType, min, max ) {
let diff = max - min;
const highestIndex = dataArray.length - 1;
for ( let i = 0, j = highestIndex; i < dataArray.length; i++, j-- ) {
const day = ( new Date( dataArray[ i ].Meldedatum ) ).getDate();
const dayOfWeek = ( new Date( dataArray[ i ].Meldedatum ) ).getDay();
const cases = dataArray[ i ].AnzahlFall;
const delta = ( cases - min ) / diff;
if ( i < highestIndex ) {
const nextCases = dataArray[ i + 1 ].AnzahlFall;
const nextDelta = ( nextCases - min ) / diff;
const point1 = new Point( spaceBetweenDays * i + 50, graphLow - ( graphHeight * delta ) );
const point2 = new Point( spaceBetweenDays * ( i + 1 ) + 50, graphLow - ( graphHeight * nextDelta ) );
if ( chartType === 'current' ) {
drawLine( point1, point2, lineWeight, accentColor1 );
}
else {
drawLine( point1, point2, 1, accentColor2 );
}
}
// Vertical Line
if ( chartType === 'current' ) {
const point1 = new Point( spaceBetweenDays * i + 50, graphLow - ( graphHeight * delta ) );
const point2 = new Point( spaceBetweenDays * i + 50, graphLow );
drawLine( point1, point2, vertLineWeight, accentColor2 );
let dayColor;
if ( dayOfWeek == 0 || dayOfWeek == 6 ) {
dayColor = accentColor2;
}
else {
dayColor = Color.white();
}
const casesRect = new Rect( spaceBetweenDays * i, ( graphLow - 40 ) - ( graphHeight * delta ), 100, 23 );
const dayRect = new Rect( spaceBetweenDays * i + 27, graphLow + 10, 50, 23 );
drawTextR( formatNumber( cases ), casesRect, dayColor, Font.systemFont( 22 ) );
drawTextR( day, dayRect, dayColor, Font.systemFont( 22 ) );
}
}
return min;
}
function drawTextR( text, rect, color, font ) {
drawContext.setFont( font );
drawContext.setTextColor( color );
drawContext.drawTextInRect( new String( text ).toString(), rect );
}
function drawLine( point1, point2, width, color ) {
const path = new Path();
path.move( point1 );
path.addLine( point2 );
drawContext.addPath( path );
drawContext.setStrokeColor( color );
drawContext.setLineWidth( width );
drawContext.strokePath();
}
function formatNumber( number ) {
let tooBig = false;
if ( shortenBigNumbers && number > 999 ) {
tooBig = true;
}
// replace dot by comma
number = number.toString().replace( '.', ',' );
// add thousands separator
number = number.replace( /\B(?=(\d{3})+(?!\d))/g, '.' );
if ( tooBig ) {
const thousandsSeparatorPosition = number.indexOf( '.' );
number = number.replace( '.', ',' );
number = number.substring( 0, thousandsSeparatorPosition + 2 ) + 'k';
}
return number;
}
@dennerforen
Copy link

Aus demselben Grund, aus dem auch die Daten des hier vorhandenen Charts für die vergangenen Tage nie 100 % korrekt sind: Die Daten sind nicht aktuell. Zudem war der 15. ein Sonntag, an dem generell weniger Daten an das RKI gemeldet werden.

Sorry, merke jetzt erst, das Du die Zahlen vom Vortag anzeigst, ich aber die 10.824 von heute lt rki gesucht habe.

Stehen morgen dann 10.8k dort, oder nimmst du die echten Tgeszahlen, ohne die Uberhangzahlen der nachträglichen Meldungen?

@MatzeKitt
Copy link
Author

Welche Daten genau da kommen, kann ich nicht sagen. Ich nehme die Daten, die ich per API bekomme. Diese Daten werden meines Wissens nach einmal täglich aktualisiert.

@dennerforen
Copy link

Ja, habe irgendwo gelesen, das es eine Gesamtzahl gibt, die nach Meldungen aus den Vortagen enthält und in der api zahlen von Erkrankt am oder getestet am veröffentlicht werden.

Aber da ja immer die Elbe datenbsis genommen wird, psst es ja pro Widget.

Ähnlich verwirrend ist auch das ein Widget den R4 wert nimmt 7nd ein anderes den R7 wert.

Copy link

ghost commented Nov 17, 2020

Klasse 👍🏻
Genau so ein Widget habe ich gesucht. Nur stimmen die Zahlen? Mir werden zB für den 15. und 16. 8,2k und 8,4k angezeigt.
Die Zahlen waren allerdings nie unter 10k 🤷🏻‍♂️

@dennerforen
Copy link

dennerforen commented Nov 17, 2020

Habe ich oben geschrieben. Einmal mit Nachmeldungen der Vortage und einmal nur die Tageszahlen.

Ist egal, weil die graphic ja auf der gleichen Datenbasis fußt und so der Trend korrekt ist.

dataset.attributes.Meldedatum

@MatzeKitt
Copy link
Author

Ich habe den Code eben aktualisiert. Neben einer Korrektur bei der Datumsberechnung wird nun eine zweite Linie gezeichnet, die immer die jeweils vorangegangene Woche darstellt. Das ist über const drawOldData = false; in Zeile 12 deaktivierbar, falls jemand es nicht möchte.

@dennerforen
Copy link

Danke, gefällt mir gut.

@MatzeKitt
Copy link
Author

Ich habe die Berechnung des Minimalwerts angepasst, sodass nun für beide Diagramme derselbe Minimalwert verwendet wird, sodass die Höhe der einzelnen Linien stimmt.

@MatzeKitt
Copy link
Author

Ich habe die Berechnung der Minimal- und Maximalwerte korrigiert, sodass nun sowohl die Daten der jeweils vorangegangenen Woche und der aktuellen Woche beachtet werden.

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