ROWS
Review of Weekly Statistics
Five systems disagreed about the same 119 communities every Monday. This turned that into one number set, one operating picture, and a written brief nobody had to fact-check.
Open the live demo- Role
- Sole designer and engineer
- Stack
- Python · SQLite · React · Vite
- Scale
- 119 communities · 37 KPIs · 54 weeks
- Cadence
- Weekly, every Monday
- Sources
- Five upstream systems
The problem
A regional operations team ran its Monday leadership meeting off a spreadsheet assembled by hand. The inputs came from five separate systems — the property-management platform, the leasing CRM, the reviews platform, the social scheduler, and a second PMS running three assets that had never been migrated. None of them agreed on how a community was named, and two of them disagreed on how many communities there were.
Assembly took most of a day. By the time the numbers were in front of anyone they were partly stale, and nobody could say with confidence which figures had been checked.
What it does
A week of raw exports goes into a dated folder. One command does the rest: parse every source against a declared header contract, resolve the naming differences, derive the roster from evidence rather than trusting any single system, compute 37 KPIs, run a verification gate, author the narrative, render the PDFs, and deploy.
The output is a dashboard with a page per community, a page per manager, a portfolio summary, and a 54-week trend behind every metric — plus a PDF for anyone who still wants paper in the room.
Deriving the roster
The hardest part was not the arithmetic, it was deciding which communities counted this week. Properties get acquired, disposed, renamed and re-coded mid-week, and each upstream system learns about it on its own schedule. Trusting any one of them produces a portfolio that silently gains or loses assets.
So the roster is derived: a community is in this week if it is active in the client's own list and present in all four core reports and shows at least one leased unit. Anything contradictory — a suspected code rename, a property in three sources but not the fourth — halts the run before a single number is written, and reports what it saw.
The part I would show first
Every page carries written analysis. It is drafted by a language model, and the model is never allowed to calculate anything.
Each author receives a fact sheet for exactly one entity, with every number pre-rendered in the units the interface displays. It narrates that sheet. Then a deterministic gate extracts every numeric token from every sentence and checks it against that entity's own sheet, in each rendering the app uses. A number that cannot be traced is not a warning — the batch is rejected and nothing ships.
Without the gate, a model will confidently write “up 4.2 points” beside a card reading 2.8, and nothing downstream would ever notice.
A second rule bounds scope: a community's paragraph may name only that community, and a manager's may name only their own assets. That one is enforced the same way, and it is what makes the per-person pages safe to hand out.
Roughly seventeen authors run in parallel, one per chunk, each seeing a few tens of kilobytes rather than the whole portfolio — which keeps them fast and keeps each one blind to data it has no business reading.
Verification
Nothing publishes without passing a nine-section gate: manifest and dates, roster changes, coverage across every community and KPI, cross-source consistency, week-over-week plausibility, spot re-reads taken straight from the raw cells while bypassing the parser, range sanity, store integrity, and a check that no personally identifying column has crept into scope.
The spot re-read matters more than it sounds. It is the only check that can catch the parser itself being wrong — everything else validates the parser's own output against the parser's own assumptions.
The pipeline is public
The Python that does all of this is published, running on an invented portfolio:the weekly pipeline, and the runbook that drives it,/weekly-update.
Publishing it needed the five upstream systems to exist, so there is a generator that writes spreadsheets matching the declared contracts cell for cell — including the header with a double internal space, the two different columns both called Pending, the property whose name is spelled four ways across five systems, and a review export carrying reviewer names and phone numbers in columns the pipeline never opens. Handing it clean fixtures instead would have left every defence in the parsing layer untested, which is most of what the parsing layer is.
What is in the demo
The linked demo is a faithful rebuild with a fully synthetic portfolio. The client, their communities, their staff and their results are not masked — they are absent.
The generated data is built to hold together rather than merely to look plausible: occupancy derives from unit counts, rent-ready units are a subset of vacant ones, the leasing funnel cannot invert, every portfolio rollup equals the sum of its members, and the last point of every trend line is the same number as the card beside it. A verifier asserts all of that on every build.