Skip to content

Instantly share code, notes, and snippets.

@ffittschen
Last active November 7, 2020 17:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ffittschen/ba6c2fc5af4065e34de0200f349a4b77 to your computer and use it in GitHub Desktop.
Save ffittschen/ba6c2fc5af4065e34de0200f349a4b77 to your computer and use it in GitHub Desktop.
COVID-19 Test Result Widget for DoctorBox (Scriptable)
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: red; icon-glyph: microscope;
/**
* Parameters:
*
* TEST_ID: The test number (9 or 10 digit number below barcode)
* DAY: The day of your birthdate.
* MONTH: The month of your birthdate.
* YEAR: The year of your birthdate.
*
*
* Format:
*
* TEST_ID,DAY,MONTH,YEAR
*
*
* Examples:
*
* 0123456789,31,03,1984
*/
const RESULT_URL = `https://www.doctorbox.de/covid19.jsp`
const API_URL = `https://www.doctorbox.de/covid19-result.jsp`
const COLOR_RED = new Color('f6000f')
const COLOR_ORANGE = new Color('ff7927')
const COLOR_GREEN = new Color('1CC747')
const TEST_RESULT_XPATH = `document.evaluate('//*[@id="covid19-2"]//h2', document, null, XPathResult.STRING_TYPE, null).stringValue`
let widgetConfiguration = {}
if (args.widgetParameter) {
widgetConfiguration = parseInput(args.widgetParameter)
} else {
// widgetConfiguration = { covid19ID: "0123456789", day: "31", month: "03", year: "1984" }
}
class TestResultWidget {
async init() {
const widget = await this.createWidget()
if (!config.runsInWidget) {
await widget.presentSmall()
}
Script.setWidget(widget)
Script.complete()
}
async createWidget() {
const list = new ListWidget()
list.spacing = 8
list.url = RESULT_URL
list.setPadding(10,10,10,10)
const headerRow = list.addStack()
headerRow.spacing = 2
addLabelTo(headerRow, "🧪", Font.mediumSystemFont(16))
addLabelTo(headerRow, "Test Result", Font.mediumSystemFont(14))
headerRow.addSpacer()
const dataResponse = await getTestResult(widgetConfiguration)
if (dataResponse.status === 200) {
const resultRow = list.addStack()
addResultBlockTo(resultRow, dataResponse.data)
list.refreshAfterDate = new Date(Date.now() + 60 * 60 * 1000) // Refresh after 1h
} else {
list.addSpacer()
let errorBox = list.addStack()
addLabelTo(errorBox, "⚡️ Daten konnten nicht geladen werden. \nWidget öffnen für reload.", Font.mediumSystemFont(10), COLOR_ORANGE)
list.addSpacer()
}
return list
}
}
class DataResponse {
constructor(data, status = 200) {
this.data = data
this.status = status
}
}
// MARK: Formatting
function addLabelTo(view, text, font = false, textColor = false) {
const label = view.addText('' + text)
if (font) label.font = font
if (textColor) label.textColor = (typeof textColor === 'string') ? new Color(textColor) : textColor
return label
}
function addResultBlockTo(view, data) {
const resultBlockBox = view.addStack()
resultBlockBox.backgroundColor = new Color('cccccc', 0.1)
resultBlockBox.spacing = 2
resultBlockBox.cornerRadius = 14
resultBlockBox.setPadding(8,8,8,8)
addResult(resultBlockBox, data)
resultBlockBox.addSpacer()
return resultBlockBox
}
function addResult(view, data) {
let formatter = new DateFormatter()
formatter.useShortDateStyle()
formatter.useShortTimeStyle()
const dateString = formatter.string(new Date())
const resultBox = view.addStack()
resultBox.spacing = 2
resultBox.layoutVertically()
addLabelTo(resultBox, dateString, Font.mediumSystemFont(10), new Color('888888'))
addLabelTo(resultBox, data.text, Font.mediumSystemFont(18), data.color)
resultBox.addSpacer()
}
// MARK: Fetching Data
async function getTestResult(widgetConfiguration) {
const formParams = Object.keys(widgetConfiguration).map((key) => {
return encodeURIComponent(key) + '=' + encodeURIComponent(widgetConfiguration[key])
}).join('&')
try {
let request = new Request(API_URL)
request.method = "POST"
request.headers = {
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8"
}
request.body = formParams
let webView = new WebView()
await webView.loadRequest(request)
const xPathResult = await webView.evaluateJavaScript(TEST_RESULT_XPATH)
const normalizedResponse = xPathResult
.trim()
.replace(/(\r\n|\n|\r)/gm," ") // Replace any newlines
.replace(/\s+/g," ") // Replace extra white space
.replace(/\s?\(.*?\)/gm, "") // Remove test number
const parsedResponse = parseResponse(normalizedResponse)
return new DataResponse(parsedResponse)
} catch(e) { console.warn(e) }
return new DataResponse({}, 404)
}
// MARK: Parsing
function parseInput (input) {
const configArray = input.split(",")
const config = {
covid19ID: configArray[0],
day: configArray[1],
month: configArray[2],
year: configArray[3]
}
return config
}
function parseResponse (response) {
const resultPrefix = "Ihr Ergebnis"
let result = {
text: "",
color: new Color('999999')
}
if (response.includes("kein Testergebnis")) {
result.text = "No result yet"
} else if (response.startsWith(resultPrefix) && response.includes("negativ")) {
result.text = "Negative"
result.color = COLOR_GREEN
} else if (response.startsWith(resultPrefix) && response.includes("positiv")) {
result.text = "Positive"
result.color = COLOR_RED
} else if (response.startsWith(resultPrefix)) {
result.text = response
result.color = COLOR_ORANGE
} else {
console.warn("Could not parse response: " + response)
result.text = "Could not parse response"
result.color = COLOR_ORANGE
}
return result
}
await new TestResultWidget().init()
@ffittschen
Copy link
Author

ffittschen commented Nov 7, 2020

COVID-19 Test Result Widget for DoctorBox (Scriptable)

hero_banner

This Widget will periodically check the DoctorBox website for the results of a COVID-19 test. The Widget can be configured with your test number and birthdate by passing the following parameters to the Scriptable widget:

Parameters
TEST_ID: The test number (9 or 10 digit number below barcode)
DAY: The day of your birthdate.
MONTH: The month of your birthdate.
YEAR: The year of your birthdate.

Format
TEST_ID,DAY,MONTH,YEAR

Example
0123456789,31,03,1984

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