Appearance
PDP Intelligence Report — How It Works
UserStory1121. A monthly AI-written report that turns the previous month's Personal Development Plan (PDP) responses into management intelligence. It covers participation, safety, WIP / KPI pressure, fleet, operational issues, employee comments, themes, sentiment, health, risks, recommendations and a bottom line. It is read at the monthly management meeting (3rd Wednesday). It replaces a manual ChatGPT run over the exported Google Forms spreadsheet.
1. Pieces
| Piece | Where |
|---|---|
| Endpoint | POST /api/Forms/pdp/report/intelligence[?month=yyyy-MM] on WebAPI (FormsController), basic auth. Returns 202 and runs in the background |
| Logic | WebAPI/Services/PDPIntelligenceReportService.cs |
| PDF layout | WebAPI/Services/PDPIntelligenceReportPdf.cs, on CommonFormsPdf/PdfCanvas (Table, Bullet) |
| Data | dbo.ethFormPDPGetIntelligenceReportData (DbMigrations/Scripts/Programmable/Procedures/) via IFormsData.GetPDPIntelligenceReportData |
| Prompt | CommonAIModel/PromptTemplates/PDPIntelligenceReportPromptTemplate.md (embedded) |
| Storage | Container pdp-intelligence-reports in the forms storage account (Azure:FormsBlobStorage) |
| Viewer | AdminPortal /view/forms/pdp-intelligence-reports/..., Ethos sign-in required (container allowlisted in FormsAttachmentService) |
PDPIntelligenceReportEmail.html (PDP email chrome), queued with IEmailMessageData.InsertHtmlMessage | |
| Schedule | n8n flow PDP Intelligence Report - Run |
2. Schedule and period
- n8n fires every Saturday at 7am. An IF node passes only days 11–17: the Saturday before the 3rd Wednesday, which always falls on the 15th–21st.
- The report covers the previous calendar month in Melbourne time.
?month=yyyy-MMre-runs an earlier month. - A PDP counts for the month it was submitted (
SubmittedDate, UTC). Drafts are ignored. - The endpoint returns at once because the AI call takes about a minute. That call runs under a 15-minute per-request timeout (
AiRequest.Timeout). A synchronous call risked the ~230s App Service front-end timeout, and an n8n retry would then send the report twice. If the app restarts mid-run, that run is lost: re-trigger it with?month=.
3. Who counts
- Cohort = current employees, using the same filter as the PDP completion report (
ethFormPDPGetEmployeesWithoutPDP: active Prime user,@ethosbuilding.com.au, not (CE), not inprimeUsersExcluded, not the two report recipients). Plus former employees (Prime user no longer active) who had a PDP this financial year. They appear in the non-participation list as "(left business)" but are not in the participation-rate denominator. - Managers = anyone who is someone's manager in
primeUsersManager(AdminPortal > Settings > Manager Relationships). Managers count towards participation, but their PDPs are not sent to the AI. Every analytical section therefore reflects workforce feedback only. The report does not say so. - PDP Completion by Manager (UserStory1946) is the old PDP completion report email (
pdp/report/missing) folded in as a section after the non-participation list. It is the manager / employee / completed this month / last PDP table fromethFormPDPGetEmployeesWithoutPDPover the same month window. It lists current employees only, sorted by manager, with "No manager set" last. - Role for "participation by role" = the PDP type of the employee's latest submitted PDP. "Not yet classified" = has never had a PDP.
4. Counted vs interpreted
The service counts everything numeric, so the figures always reconcile: responses, unique participants, cohort, participation rate, by-role table, non-participant names, SWMS compliance (SWMSCompliance Yes of Yes/No) and schedule control (IsScheduleUpToDate Yes of Yes/No). Blank and N/A answers count as neither. Issue-theme frequencies are also counted from the employee names the AI lists.
The AI gets one object per non-manager PDP, holding employee, type, date, reviewing manager, left-business flag and the answered fields. Photos, identity fields and unasked checkboxes are dropped: every snapshot carries every checkbox as false, so only the PDP type that asks a checkbox sends it. The AI returns one JSON object with the narrative sections, which is deserialised into PDPReportAnalysis. Model = OpenAI:DefaultModel.
5. Output
{yyyy-MM}/{guid}_Ethos_PDP_Intelligence_Report_YYYY_MM.pdf, plus the full report as.jsonbeside it for future trend dashboards. The GUID keeps a re-run from overwriting the copy an earlier email links to.- The email goes to
PDPSummary:Recipient(pdp@ethosbuilding.com.au).PDPSummary:EmailOverride, when set, replaces it. Set the override everywhere but production, because the report names employees and their feedback. - About 10 pages for a month of ~25 PDPs. Empty sections keep their heading with "Nothing reported this month".
Not built: the action register (issue → owner → status carried month to month), and loading the JSON into a table for Metabase.

