← All entries
Web app

Leave Dashboard

Month calendar of who is out, a build-your-own person calendar, and a team-risk heatmap with a live coverage threshold — plus add-a-person via prefilled PR.

Web app leavecalendarteam-riskxlsxreactpng-export

◎ What it solves

Frees leave data that only existed as spreadsheet cell colours — so "who's out that week" and "is this team covered" become answerable instead of eyeballed.

⚠ Limitations & Risks

Every catalog entry must acknowledge at least one limitation or risk.

What it solves

The leave tracker is a spreadsheet where the status is the cell’s fill colour. Not a word in the cell — the colour is the data. That is perfectly readable to a human scrolling the sheet, and completely invisible to everything else.

The consequence is sharper than it first sounds: a CSV export of that spreadsheet comes out blank. There is no text to export. So the data cannot be queried, summarised, joined to teams, or put in front of anyone who doesn’t have the file open. Every question — who is out the week of the 14th, is this team thin enough to worry about — gets answered by eye, by one person, again each time someone asks.

This reads the fill colours directly out of the .xlsx (stdlib zipfile + xml.etree, no dependencies) and turns them into real records. Once leave is data rather than formatting, the month calendar, the per-person picker and the team-risk heatmap with its live coverage threshold are just views over it.

Leave stored as cell colour versus leave parsed into data

Before: a spreadsheet where each person’s leave is a coloured cell, and exporting it to CSV produces empty rows because the cells contain no text. After: the fill colours are parsed into records, which drive a coverage view where one team crosses the risk threshold.

BEFORE — colour IS the dataMTWTFAliceBobDan🟥 out 🟦 PTO 🟨 holiday — no text in any cellexport to CSVleave.csvname,mon,tue,wed,thu,friAlice,,,,,Bob,,,,,⚠ blank — there was never any textAFTER — colour parsed into dataread fillszipfile + XMLrecordsteam coverage — out %IngestPortalDSEthreshold⚠ thin

The spreadsheet on the left is not missing data — it is storing it as formatting, which is why the export is empty. Parsing the fills turns leave into records, and coverage questions become a view rather than a squint.

What it does

Makes a colour-coded leave-tracker spreadsheet legible and answerable:

Where the data comes from

The Leave Tracker composite action parses the committed .xlsx — reading cell fill colours, not text — into leaves_<slug>.{csv,json} plus a leave_coverage_<slug>.json rollup, and publishes them to the leave-tracker/report branch. The SPA reads leave_manifest.json at runtime and falls back to the bundled snapshot in public/data/.

Because the branch name contains a slash, the raw URL needs the /refs/heads/ form.

The action needs no token and no network.

Run it locally

cd leave-dashboard
npm install
npm run dev

# Regenerate from the spreadsheet (stdlib-only, offline):
python leave/generate_leave_tracker.py \
  --xlsx-path "leave/source/DSE Leave Tracker - 26.4.xlsx" --out-dir reports