Skip to content

Instantly share code, notes, and snippets.

@barclay-reg
Forked from rphl/incidence.js
Last active November 3, 2020 14:52
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save barclay-reg/ff883a5f8a8fc99c14e19c841bcac2e0 to your computer and use it in GitHub Desktop.
Save barclay-reg/ff883a5f8a8fc99c14e19c841bcac2e0 to your computer and use it in GitHub Desktop.
COVID-19 Inzidenz-Widget für iOS innerhalb Deutschlands 🇩🇪 (Kreis/Stadt + Bundesland + Trend)
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: deep-green; icon-glyph: dove;
// Licence: Robert Koch-Institut (RKI), dl-de/by-2-0
const outputFields = 'GEN,cases_per_100k,cases7_per_100k,cases7_bl_per_100k,last_update,BL';
const apiUrl = (location) => `https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/RKI_Landkreisdaten/FeatureServer/0/query?where=1%3D1&outFields=${outputFields}&geometry=${location.longitude.toFixed(3)}%2C${location.latitude.toFixed(3)}&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelWithin&returnGeometry=false&outSR=4326&f=json`
const apiUrlStates = 'https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/Coronaf%E4lle_in_den_Bundesl%E4ndern/FeatureServer/0/query?where=1%3D1&outFields=LAN_ew_GEN,cases7_bl_per_100k&returnGeometry=false&outSR=4326&f=json'
const apiUrlNewCases = 'https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/RKI_COVID19/FeatureServer/0/query?f=json&where=NeuerFall%20IN(1%2C%20-1)&returnGeometry=false&spatialRel=esriSpatialRelIntersects&outFields=*&outStatistics=%5B%7B%22statisticType%22%3A%22sum%22%2C%22onStatisticField%22%3A%22AnzahlFall%22%2C%22outStatisticFieldName%22%3A%22value%22%7D%5D&resultType=standard&cacheHint=true'
const BUNDESLAENDER_SHORT = {
'Baden-Württemberg': 'BW',
'Bayern': 'BY',
'Berlin': 'BE',
'Brandenburg': 'BB',
'Bremen': 'HB',
'Hamburg': 'HH',
'Hessen': 'HE',
'Mecklenburg-Vorpommern': 'MV',
'Niedersachsen': 'NI',
'Nordrhein-Westfalen': 'NRW',
'Rheinland-Pfalz': 'RP',
'Saarland': 'SL',
'Sachsen': 'SN',
'Sachsen-Anhalt': 'ST',
'Schleswig-Holstein': 'SH',
'Thüringen': 'TH'
};
const widget = await createWidget()
if (!config.runsInWidget) {
await widget.presentSmall()
}
Script.setWidget(widget)
Script.complete()
async function createWidget(items) {
let locations
if(args.widgetParameter) {
locations = args.widgetParameter.split(";").map(parseInput)
} else {
Location.setAccuracyToThreeKilometers()
locations = [await Location.current()]
}
const list = new ListWidget()
const header = list.addText("🦠 Inzidenz".toUpperCase())
header.font = Font.mediumSystemFont(13)
let lastData
const spaceFor3 = locations.length == 3
list.addSpacer()
for (location of locations) {
const data = await getDataForLocation(location)
const weekData = saveLoadData(data, data.areaName)
if (data) {
lastData = data
if (!data.shouldCache) {
list.addSpacer(6)
const loadingIndicator = list.addText("Ort wird ermittelt...".toUpperCase())
loadingIndicator.font = Font.mediumSystemFont(13)
loadingIndicator.textOpacity = 0.5
}
// INCIDENCE
const locationStack = list.addStack()
locationStack.layoutHorizontally()
locationStack.centerAlignContent()
locationStack.useDefaultPadding()
// 1
data.incidence = (data.incidence >= 100) ? parseInt(data.incidence) : data.incidence
const label = locationStack.addText(data.incidence + '' + getIncidenceTrend(data, weekData))
label.font = Font.boldSystemFont( spaceFor3 ? 20: 28)
label.textColor = data.incidence >= 50 ? Color.red() : data.incidence >= 35 ? Color.orange() : Color.green()
// 2
blStack = locationStack.addStack()
blStack.layoutVertically()
const labelBL = blStack.addText(data.incidenceBL + getIncidenceBLTrend(data, weekData))
labelBL.font = Font.mediumSystemFont(spaceFor3 ? 8 : 10)
labelBL.textColor = Color.gray()
labelBlName = blStack.addText(data.nameBL)
labelBlName.font = Font.mediumSystemFont(spaceFor3 ? 8 : 10)
labelBlName.textColor = Color.gray()
// CITYNAME
const areaLabel = list.addText(data.areaName.toUpperCase())
areaLabel.font = Font.mediumSystemFont(spaceFor3 ? 8 : 12)
areaLabel.leftAlignText()
}
}
if (lastData) {
list.addSpacer()
// UPDATED
const labelUpdated = list.addText(`${lastData.updated.substr(0, 10)} ( +${lastData.cases} )`)
labelUpdated.font = Font.systemFont(10)
labelUpdated.textColor = Color.gray()
if (lastData.shouldCache) {
list.refreshAfterDate = new Date(Date.now() + 60 * 60 * 1000)
}
} else {
list.addSpacer()
list.addText("Daten nicht verfügbar")
}
return list
}
async function getDataForLocation(location) {
try {
let dataCases = await new Request(apiUrlNewCases).loadJSON()
const cases = dataCases.features[0].attributes.value
let dataStates = await new Request(apiUrlStates).loadJSON()
const incidencePerState = dataStates.features.map((f) => { return { BL: BUNDESLAENDER_SHORT[f.attributes.LAN_ew_GEN], incidence: f.attributes.cases7_bl_per_100k } })
const averageIncidence = incidencePerState.reduce((a, b) => a + b.incidence, 0) / incidencePerState.length
let data = await new Request(apiUrl(location)).loadJSON()
const attr = data.features[0].attributes
log("Data: " + JSON.stringify(data))
const res = {
incidence: parseFloat(attr.cases7_per_100k.toFixed(1)),
incidenceBL: parseFloat(attr.cases7_bl_per_100k.toFixed(1)),
areaName: attr.GEN,
nameBL: BUNDESLAENDER_SHORT[attr.BL],
shouldCache: true,
updated: attr.last_update,
incidencePerState: incidencePerState,
averageIncidence: parseFloat(averageIncidence.toFixed(1)),
cases: cases
}
return res
} catch (e) {
return null
}
}
async function getLocation() {
try {
if (args.widgetParameter) {
const fixedCoordinates = args.widgetParameter.split(",").map(parseFloat)
return {
latitude: fixedCoordinates[0],
longitude: fixedCoordinates[1]
}
} else {
Location.setAccuracyToThreeKilometers()
return await Location.current()
}
} catch (e) {
return null;
}
}
function getIncidenceTrend(data, weekdata) {
let incidenceTrend = ' ';
const prevData = getDataForDate(weekdata);
if (prevData) {
incidenceTrend = (data.incidence < prevData.incidence) ? '↓' : '↑'
}
return incidenceTrend
}
function getIncidenceBLTrend(data, weekdata) {
let incidenceBLTrend = ' ';
const prevData = getDataForDate(weekdata);
if (prevData) {
incidenceBLTrend = (data.incidenceBL < prevData.incidenceBL) ? '↓' : '↑'
}
return incidenceBLTrend
}
function getDataForDate(weekdata, yesterday = true, datestr = '') {
let dateKey;
if (yesterday) {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
dateKey = `${yesterday.getDate()}.${yesterday.getMonth() + 1}.${yesterday.getFullYear()}`
} else {
dateKey = datestr;
}
if (typeof weekdata[dateKey] !== 'undefined') {
return weekdata[dateKey]
}
return false
}
// LIMIT TO 7 DAYS
function saveLoadData (newData, suffix = '') {
const updated = newData.updated.substr(0, 10);
const loadedData = loadData(suffix)
loadedData[updated] = newData
const loadedDataKeys = Object.keys(loadedData);
const lastDaysKeys = loadedDataKeys.slice(Math.max(Object.keys(loadedData).length - 7, 0))
let loadedDataLimited = {}
lastDaysKeys.forEach(key => {
loadedDataLimited[key] = loadedData[key]
})
try {
let fm = FileManager.iCloud()
let path = fm.joinPath(fm.documentsDirectory(), 'covid19' + suffix + '.json')
fm.writeString(path, JSON.stringify(loadedDataLimited))
console.log('iCloud: save')
} catch (e) {
let fm = FileManager.local()
let path = fm.joinPath(fm.documentsDirectory(), 'covid19' + suffix + '.json')
fm.writeString(path, JSON.stringify(loadedDataLimited))
console.log('Local: save')
}
return loadedData
}
function parseInput (input) {
const fixedCoordinates = input.split(",")
return {
latitude: parseFloat(fixedCoordinates[0]),
longitude: parseFloat(fixedCoordinates[1]),
name: fixedCoordinates.length >= 2 ? fixedCoordinates[2] : null,
}
}
function loadData(suffix) {
try {
let fm = FileManager.iCloud()
let path = fm.joinPath(fm.documentsDirectory(), 'covid19' + suffix + '.json')
if (fm.fileExists(path)) {
let data = fm.readString(path)
console.log('iCloud: read')
return JSON.parse(data)
}
} catch (e) {
let fm = FileManager.local()
let path = fm.joinPath(fm.documentsDirectory(), 'covid19' + suffix + '.json')
if (fm.fileExists(path)) {
let data = fm.readString(path)
console.log('Local: read')
return JSON.parse(data)
}
}
return {};
}
@barclay-reg
Copy link
Author

barclay-reg commented Nov 1, 2020

@chimcen das ist jetzt gefixed. Ich hatte den Refresh-Intervall etwas zu groß :)

@chimcen
Copy link

chimcen commented Nov 1, 2020

Klasse, funzt. Danke.
Wenn Fu jetzt die Trendpfeile wie im Script von rphl übernehmen könntest (↑,↓,→ und mit rot/grün) dann wärs perfekt 🤩

@barclay-reg
Copy link
Author

@chimcen das mache ich mal, aber momentan zeigt ja eh alles nach oben und rot :/

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