M365 Reports
AI-narrated PDF reports from Microsoft 365 data
- My clients. Client work, so they aren't named.
- 3 May 2026 as one engine. The reports themselves are older.
- A client's Microsoft 365 audit log and SharePoint lists
- A branded PDF. I read every page before it goes to the client.
One engine behind the branded PDF reports my clients get. It pulls data out of their Microsoft 365 tenant over Graph, works out the numbers, has Claude write the findings up, and renders a PDF in the client’s own brand.
- Departure audits. When someone resigns, 90 days of their SharePoint, OneDrive and Exchange activity, checked against a list of red flags and written up as a risk assessment. What happened when one said HIGH.
- Monthly enrolment reports for a childcare group: enquiries, tours and enrolments per centre, stale leads, and how fast each centre first responds, read out of SharePoint’s version history. How that works, and what it misses.
The same data also answered bigger questions: 22 months of enquiries turned up a finding that halved once it was split by centre.
Nothing goes to a client until I’ve read every page. These are internal tools, so there’s no public link, and the clients aren’t named.
Stack
- Microsoft Graph
- Pulls the audit log and the SharePoint lists out of the client's tenant, including each list item's version history.
- Python
- Works out every number and checks the red flags before the model writes a word.
- Claude
- Writes the summary and the findings from those numbers.
- ReportLab
- Lays out the PDF in the client's own brand.
- FastAPI
- A small service that starts a report on request and hands back the PDF.
Build log
-
Two departure audits. One scored HIGH. Twenty minutes in the raw rows made it MEDIUM, and turned up six sentences the template printed whatever the data said. All six fixed. Read →
-
Found every font weight had been rendering as the same one, so no report had real bold. Fixed.
-
Exported the whole enquiry list, 22 months of it, to ask what a monthly report can't. My favourite finding about halved once it was split by centre. Read →
-
The May enrolment report led with a group median first response of 0.1 days. The busiest centre's was four days. Not fixed yet. Read →
-
Rebuilt as one engine. The separate report scripts moved into one repo with a shared PDF core.
Writing
- The report said HIGH. The raw rows said MEDIUM. → An AI-written report on a departing employee counted six red flags and concluded HIGH risk. Twenty minutes in the raw audit log dissolved half of them.
- The centre, not the clock → Twenty-two months of a childcare group's enquiries said book the tour inside three days. Split by centre, about half of that gap was which centre the family had called.
- Nobody wrote down when they called back → A childcare group's enquiry list has no field for how fast anyone responds, but SharePoint's version history does. Then the May report showed what that number leaves out.