Created
October 28, 2020 09:03
-
-
Save rflrkn/f8ec055954865f02be1212ab58366e08 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Variables used by Scriptable. | |
// These must be at the very top of the file. Do not edit. | |
// icon-color: purple; icon-glyph: magic; | |
// Licence: Robert Koch-Institut (RKI), dl-de/by-2-0 | |
/** | |
* MEDIUMWIDGET SUPPORT - Set Widgetparemeter to: NAME,51.123,6.7373 | |
*/ | |
const outputFields = 'GEN,cases,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 outputFieldsStates = 'Fallzahl,LAN_ew_GEN,cases7_bl_per_100k'; | |
const apiUrlStates = `https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/Coronaf%E4lle_in_den_Bundesl%E4ndern/FeatureServer/0/query?where=1%3D1&outFields=${outputFieldsStates}&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 LIMIT_RED = 50 | |
const LIMIT_ORANGE = 35 | |
const LIMIT_RED_COLOR = new Color('FF1A2A') | |
const LIMIT_ORANGE_COLOR = new Color('FFD800') | |
const LIMIT_GREEN_COLOR = new Color('00B32C') | |
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': 'NW', | |
'Rheinland-Pfalz': 'RP', | |
'Saarland': 'SL', | |
'Sachsen': 'SN', | |
'Sachsen-Anhalt': 'ST', | |
'Schleswig-Holstein': 'SH', | |
'Thüringen': 'TH' | |
}; | |
let MEDIUMWIDGET = false | |
let fixedCoordinates = [] | |
if (args.widgetParameter) { | |
fixedCoordinates = args.widgetParameter.split(",") | |
if (fixedCoordinates.length === 3) { | |
MEDIUMWIDGET = true | |
} | |
} else { // DEBUG MEDIUM WIDGET | |
// fixedCoordinates = ['DUS',51.2277,6.7734] | |
// MEDIUMWIDGET = true | |
} | |
let data = {} | |
let weekData = {} | |
const widget = await createWidget() | |
if (!config.runsInWidget) { | |
if (MEDIUMWIDGET) { | |
await widget.presentMedium() | |
} else { | |
await widget.presentSmall() | |
} | |
} | |
Script.setWidget(widget) | |
Script.complete() | |
async function createWidget(items) { | |
const _data = await getData() | |
const areaName = _data.areaName; | |
data[areaName] = _data | |
const list = new ListWidget() | |
const headerLabel = list.addStack() | |
headerLabel.useDefaultPadding() | |
headerLabel.centerAlignContent() | |
if (MEDIUMWIDGET) { | |
headerLabel.layoutHorizontally() | |
} else { | |
list.setPadding(10,15,10,10) | |
headerLabel.layoutVertically() | |
} | |
const header = headerLabel.addText("Corona-Info".toUpperCase()) | |
header.font = Font.mediumSystemFont(12) | |
if (data && typeof data[areaName] !== 'undefined') { | |
weekData[areaName] = saveLoadData(data[areaName], areaName) | |
if (!data[areaName].shouldCache) { | |
list.addSpacer(6) | |
const loadingIndicator = list.addText("Ort wird ermittelt...".toUpperCase()) | |
loadingIndicator.font = Font.mediumSystemFont(13) | |
loadingIndicator.textOpacity = 0.5 | |
} | |
if (MEDIUMWIDGET && typeof data[areaName] !== 'undefined') { | |
headerLabel.addSpacer() | |
createGerDailyCasesLabel(headerLabel, data[areaName], -1) | |
} | |
list.addSpacer(16) | |
// INCIDENCE | |
const incidenceLabel = list.addStack() | |
if (MEDIUMWIDGET) { | |
incidenceLabel.size = new Size(300, 90) | |
} | |
incidenceLabel.layoutHorizontally() | |
incidenceLabel.useDefaultPadding() | |
incidenceLabel.topAlignContent() | |
createIncidenceLabelBlock(incidenceLabel, data[areaName], weekData[areaName]) | |
const _dataF = await getData(true) | |
const areaNameF = _dataF.areaName; | |
data[areaNameF] = _dataF | |
if (MEDIUMWIDGET && typeof data[areaNameF] !== 'undefined') { | |
weekData[areaNameF] = saveLoadData(data[areaNameF], areaNameF) | |
incidenceLabel.addSpacer(10) | |
createIncidenceLabelBlock(incidenceLabel, data[areaNameF], weekData[areaNameF]) | |
} else if (typeof data[areaName] !== 'undefined') { | |
list.addSpacer() | |
createGerDailyCasesLabel(list, data[areaName]) | |
} | |
if (data[areaName].shouldCache) { | |
list.refreshAfterDate = new Date(Date.now() + 60 * 60 * 1000) | |
} | |
} else { | |
list.addSpacer() | |
const errorLabel = list.addText("Daten nicht verfügbar. \nWidget öffnen für reload...") | |
errorLabel.font = Font.mediumSystemFont(12) | |
errorLabel.textColor = Color.gray() | |
} | |
return list | |
} | |
async function getData(useFixedCoords = false) { | |
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, | |
cases: f.attributes.Fallzahl // ??? | |
}}) | |
const averageIncidence = incidencePerState.reduce((a, b) => a + b.incidence, 0) / incidencePerState.length | |
const location = await getLocation(useFixedCoords) | |
let data = await new Request(apiUrl(location)).loadJSON() | |
const attr = data.features[0].attributes | |
const res = { | |
incidence: parseFloat(attr.cases7_per_100k.toFixed(1)), | |
incidenceBL: parseFloat(attr.cases7_bl_per_100k.toFixed(1)), | |
areaName: attr.GEN, | |
areaCases: parseFloat(attr.cases.toFixed(1)), | |
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(useFixedCoords = false) { | |
try { | |
if (fixedCoordinates.length === 2) { | |
return { | |
latitude: parseFloat(fixedCoordinates[0]), | |
longitude: parseFloat(fixedCoordinates[1]) | |
} | |
} else if (useFixedCoords && fixedCoordinates.length === 3) { | |
return { | |
latitude: parseFloat(fixedCoordinates[1]), | |
longitude: parseFloat(fixedCoordinates[2]) | |
} | |
} else { | |
Location.setAccuracyToThreeKilometers() | |
return await Location.current() | |
} | |
} catch (e) { | |
return null; | |
} | |
} | |
// ABSOLUTE DAILY CASES GER LABEL | |
function createGerDailyCasesLabel(label, data, align = 0) { | |
const formatedCases = new Number(data.cases).toLocaleString('de-DE') | |
const labelUpdated = label.addText(`DE gesamt: +${formatedCases}↑\nAktualisiert: ${data.updated.substr(0, 10)}`) | |
if (align === -1) labelUpdated.rightAlignText() | |
labelUpdated.font = Font.systemFont(10) | |
labelUpdated.textColor = Color.gray() | |
} | |
function createIncidenceLabelBlock(labelBlock, data, weekData) { | |
const stack = labelBlock.addStack() | |
stack.layoutVertically() | |
stack.useDefaultPadding() | |
stack.topAlignContent() | |
// AREA CASES | |
const areaCasesLabel = stack.addText(getNewAreaCasesTrend(data, weekData)) | |
areaCasesLabel.font = Font.systemFont(10) | |
areaCasesLabel.leftAlignText() | |
areaCasesLabel.textColor = Color.white() | |
// MAIN ROW WITH INCIDENCE | |
const stackMainRow = stack.addStack() | |
stackMainRow.useDefaultPadding() | |
stackMainRow.centerAlignContent() | |
stackMainRow.size = (MEDIUMWIDGET) ? new Size(145, 30) : new Size(135, 30) | |
// MAIN INCIDENCE | |
let incidence = data.incidence >= 1000 ? Math.floor(data.incidence) : data.incidence; | |
const incidenceLabel = stackMainRow.addText('' + incidence) | |
incidenceLabel.font = Font.boldSystemFont(27) | |
incidenceLabel.leftAlignText(); | |
incidenceLabel.textColor = data.incidence >= LIMIT_RED ? LIMIT_RED_COLOR : data.incidence >= LIMIT_ORANGE ? LIMIT_ORANGE_COLOR : LIMIT_GREEN_COLOR | |
const incidenceTrend = getIncidenceTrend(data, weekData) | |
const incidenceLabelTrend = stackMainRow.addText(incidenceTrend) | |
incidenceLabelTrend.font = Font.boldSystemFont(27) | |
incidenceLabelTrend.leftAlignText(); | |
incidenceLabelTrend.textColor = (incidenceTrend === '↑') ? LIMIT_RED_COLOR : LIMIT_GREEN_COLOR | |
stackMainRow.addSpacer(5) | |
// BL INCIDENCE | |
const incidenceBLStack = stackMainRow.addStack(); | |
incidenceBLStack.addSpacer(1) | |
//incidenceBLStack.size = new Size(10,25) | |
incidenceBLStack.bottomAlignContent() | |
const incidenceBLLabel = incidenceBLStack.addText(data.incidenceBL + getIncidenceBLTrend(data, weekData) + "\n" + data.nameBL) | |
incidenceBLLabel.font = Font.mediumSystemFont(10) | |
incidenceBLLabel.textColor = new Color('eeeeee', 0.5) | |
stackMainRow.addSpacer() | |
const areanameLabel = stack.addText(data.areaName.toUpperCase()) | |
areanameLabel.font = Font.mediumSystemFont(14) | |
areanameLabel.lineLimit = 2 | |
} | |
function getIncidenceTrend(data, weekdata) { | |
let incidenceTrend = ' '; | |
if (typeof weekdata !== 'undefined' && Object.keys(weekdata).length > 0) { | |
const prevData = getDataForDate(weekdata); | |
if (prevData) { | |
incidenceTrend = (data.incidence < prevData.incidence) ? '↓' : '↑' | |
} | |
} | |
return incidenceTrend | |
} | |
function getNewAreaCasesTrend(data, weekdata) { | |
let newAreaCases = ''; | |
if (typeof weekdata !== 'undefined' && Object.keys(weekdata).length > 0) { | |
const prevData = getDataForDate(weekdata); | |
if (prevData && typeof prevData.areaCases !== 'undefined') { | |
newAreaCases += (data.areaCases < prevData.areaCases) ?'-' : '+' | |
newAreaCases += Math.abs(data.areaCases - prevData.areaCases) | |
newAreaCases += (data.areaCases > prevData.areaCases) ?'↑' : '↓' | |
} | |
} | |
return newAreaCases | |
} | |
function getIncidenceBLTrend(data, weekdata) { | |
let incidenceBLTrend = ' '; | |
if (typeof weekdata !== 'undefined' && Object.keys(weekdata).length > 0) { | |
const prevData = getDataForDate(weekdata); | |
if (prevData) { | |
incidenceBLTrend = (data.incidenceBL < prevData.incidenceBL) ? '↓' : '↑' | |
} | |
} | |
return incidenceBLTrend | |
} | |
function getDataForDate(weekdata, yesterday = true, datestr = '') { | |
let dateKey; | |
let dayOffset = 1 | |
const today = new Date(); | |
const todayDateKey = `${today.getDate()}.${today.getMonth() + 1}.${today.getFullYear()}` | |
if (typeof weekdata[todayDateKey] === 'undefined') { | |
dayOffset = 2 | |
} | |
if (yesterday) { | |
today.setDate(today.getDate() - dayOffset); | |
dateKey = `${today.getDate()}.${today.getMonth() + 1}.${today.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) | |
if (loadedData) { | |
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 | |
} | |
return {} | |
} | |
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 {}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment