Skip to content

Instantly share code, notes, and snippets.

@Write

Write/README.md

Last active Mar 7, 2021
Embed
What would you like to do?
Scriptable Widget to track french vaccinations for COVID-19

Vaccin Tracker France

Suivez en temps réel l'évolution du vaccin contre la COVID-19 dans un mini Widget iOS grâce à Scriptable.
Supporte uniquement le widget de petite taille.

Source des données : https://covidtracker.fr/vaccintracker/ (Créateur du site : Guillaume Rozier)
Vaccintracker (le site) est rafraîchi 1 fois par jour à partir de source officielle

Capture d'écran

Claire Sombre

Fonctionnalités

  • Support du mode claire et sombre
  • Support uniquement la taille petite de widget
  • Définissez l'action de clique sur 'Run Script', cela permettera d'ouvrir https://covidtracker.fr/vaccintracker/ au clique sur le widget

Installation

Installez facilement le Script et tenez le à jour grâce à ScriptDude !
Une fois le script ScriptDude installé, vous pouvez facilement installer Vaccin Tracker France grâce au lien ci-dessous.

Download with ScriptDude

Lancez périodiquement le script ScriptDude dans Scriptable pour vérifier si il y a une mise à jour de Vaccin Tracker France

const CONFIG = {
dataURL: 'https://api.socialspill.com/covid',
sourceURL: 'https://covidtracker.fr/vaccintracker/',
alsoDrawState: undefined,
debug: false
};
const widgetBackgroundColor = Color.dynamic(new Color("#f2f2f2"), new Color("#1c1c1e"));
const fetchJSON = async (url) => {
const request = new Request(url);
const data = await request.loadJSON()
return data;
};
const drawError = (message) => {
const widget = new ListWidget();
let text = widget.addText(message);
text.font = Font.mediumSystemFont(16);
return widget;
};
async function drawRegion(view, title, { difference_to_the_previous_day: delta, vaccinated, percent_vaccinated: percent_vaccinated, total: population, update: update, update_hour: update_hour, source: source, url: url }) {
const stackTitle = view.addStack();
stackTitle.layoutHorizontally();
stackTitle.centerAlignContent();
stackTitle.spacing = 1;
let stackTitleText = stackTitle.addText(`🇫🇷 France`)
stackTitleText.font = Font.boldSystemFont(14);
stackTitleText.minimumScaleFactor = 1;
stackTitle.addSpacer(10)
let logoImage = stackTitle.addImage(await getImage("vac-logo.png"));
logoImage.imageSize = new Size(25, 25);
view.addSpacer(5)
const stack = view.addStack();
stack.layoutVertically();
stack.centerAlignContent();
stack.spacing = 1;
let stackText = stack.addText(`💉 +${new Intl.NumberFormat('fr-FR', { style: 'decimal' }).format(delta.toString())} en 24h`)
stackText.font = Font.mediumSystemFont(12);
stackText.lineLimit = 1
stackText.minimumScaleFactor = 0.5;
stack.addSpacer(4)
const stackTotalVacc = view.addStack();
stackTotalVacc.layoutVertically();
stackTotalVacc.centerAlignContent();
let stackTotalVaccText = stackTotalVacc.addText(`👪 ${new Intl.NumberFormat('fr-FR', { style: 'decimal' }).format(vaccinated.toString())} vaccinés`)
stackTotalVaccText.font = Font.mediumSystemFont(11)
stackTotalVaccText.lineLimit = 1
stackTotalVaccText.minimumScaleFactor = 0.5;
stackTotalVacc.addSpacer(8)
const stack2 = view.addStack();
stack2.layoutVertically();
stack2.centerAlignContent();
stack2.spacing = 1;
let stackText2 = stack2.addText(percent_vaccinated.toString() + "% pop. vaccinée")
stackText2.textColor = Color.dynamic(Color.gray(), Color.gray())
stackText2.font = Font.regularSystemFont(10);
stackText2.minimumScaleFactor = 1;
stack2.addSpacer(4);
const stack3 = view.addStack();
stack3.layoutVertically();
stack3.centerAlignContent();
stack3.spacing = 1;
let stackText3 = stack3.addText("Source : " + (source.toString() == "Estimation" ? "Éstimation" : source.toString().replace("Ministère de la Santé", "Ministère Santé")))
stackText3.textColor = Color.dynamic(Color.gray(), Color.gray())
stackText3.font = Font.regularSystemFont(9);
stackText3.minimumScaleFactor = 1;
stack3.addSpacer(4)
const stackUpdate = view.addStack();
stackUpdate.layoutVertically();
stackUpdate.centerAlignContent();
stackUpdate.spacing = 1;
let date = new Intl.DateTimeFormat('fr-FR', { month: 'long', day: "numeric" }).format(Date.parse(update))
let stackUpdateText = stackUpdate.addText("MàJ : " + date + " à " + update_hour)
stackUpdateText.textColor = Color.dynamic(Color.gray(), Color.gray())
stackUpdateText.font = Font.regularSystemFont(9);
stackUpdateText.minimumScaleFactor = 1;
const ctx = new DrawContext();
const size = new Size(58, 4);
ctx.size = size;
ctx.opaque = false;
ctx.respectScreenScale = true;
const backgroundColour = Dynamic.color(new Color('#f2f2f2'), new Color('#1c1c1e'))
return stack;
}
const extractData = (node, state) => {
// Quick validation
const fields = [
'difference_to_the_previous_day',
'vaccinated',
'total',
];
if (node == null) {
throw new Error(`missing node${state != null ? ' for state: ' + state : ''}`);
}
for (const field of fields) {
if (node[field] == null) {
throw new Error(`missing data: ${field}`);
}
}
return node;
};
const drawData = (data) => {
const widget = new ListWidget();
const padding = 16;
widget.setPadding(padding, padding, padding, padding);
widget.backgroundColor = widgetBackgroundColor;
drawRegion(widget, '🇫🇷 France', extractData(data));
return widget;
}
// Get images from iCloud or download them once
async function getImage(image) {
let fm = FileManager.local();
let dir = fm.documentsDirectory();
let path = fm.joinPath(dir, image);
if (fm.fileExists(path)) {
console.log("Image exist locally")
return fm.readImage(path);
} else {
// Download once
let imageUrl;
switch (image) {
case "vac-logo.png":
imageUrl = "https://i.imgur.com/ZsBNT8E.png";
break;
case "calendar-icon.png":
imageUrl = "https://i.imgur.com/Qp8CEFf.png";
break;
default:
console.log(`Sorry, couldn't find ${image}.`);
}
let req = new Request(imageUrl);
let loadedImage = await req.loadImage();
console.log("Downloading image...")
fm.writeImage(path, loadedImage);
return loadedImage;
}
}
const run = async () => {
try {
const url = `${CONFIG.dataURL}?nocache=${(new Date()).getTime()}`;
const data = await fetchJSON(url);
return drawData(data);
} catch (error) {
return drawError(error.message || 'Gasp! An unknown error!')
}
};
if (config.runsInWidget) {
let widget = await run()
Script.setWidget(widget)
Script.complete()
}
else if (CONFIG.debug) {
Script.complete();
const widget = await run()
await widget.presentSmall()
}
else {
const callback = new CallbackURL(CONFIG.sourceURL)
callback.open()
Script.complete()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment