Skip to content
← Back to projects

Case study · Government analytics

EPSB AnalyticsSAPOL · Professional standards reporting and tooling

I turn professional standards data into quarterly reports that can be reproduced and checked, and I build the tools that make that reporting repeatable.

Senior Data Analyst (ASO7), South Australia Police, since March 2026

Try the concept demo ↓

This page describes the kind of work only. The reports, data and systems belong to SAPOL and stay internal, so nothing on this page comes from them. The demo further down is a concept illustration built on synthetic data. It is not the real system, and it uses none of its figures or endpoint names.

The problem

The Ethical and Professional Standards Branch looks after complaints, integrity and professional standards for South Australia Police. Its numbers go to executives and oversight bodies, so they need to be right, comparable from one quarter to the next, and honest about how much they can say.

Counts like these are small enough to move around by chance. A quarter can look worse than the one before without anything having changed, and a real shift can hide inside ordinary variation. A report that treats every movement as news, or one that misses a genuine change, helps nobody.

The records also sit behind a large complaint-management system. Checking them one screen at a time is slow, and it is hard to show afterwards how a figure was reached.

What the work covers

  1. 1

    Quarterly statistical reports

    I produce the quarterly Use of Force and Vehicle Pursuit statistical reports for SAPOL executives. Each one has a written method, so every quarter can be reproduced and checked.

  2. 2

    Workflow review

    I led an end-to-end review of the branch's complaint administration workflow, from receipt to file closure. It was built from team interviews and the team's own procedure notes, and it gave branch leadership findings and recommendations.

  3. 3

    Expiation notice analysis

    I analysed a full financial year of hand-issued expiation notices for the Expiation Notice Branch. The pipeline in Python and Power BI is reproducible, and the analysis reports only what the data can support.

  4. 4

    Complaints API client

    I built a Python client and a web console for the complaint-management system's REST APIs. They cover more than 1,100 endpoints, so records can be queried and checked by script rather than by hand.

Impact

Executive reports with a written method behind every figure
Quarterly
Complaints API endpoints reachable through one Python client
1,100+
Of hand-issued expiation notices analysed in a reproducible pipeline
1 FY
Complaint administration reviewed from receipt to file closure
End to end

The common thread is that a number should be traceable. When a figure in an executive report can be rebuilt from its method and its data, a question about it becomes a quick check rather than a long debate. When records can be pulled by script, the same check can run again next quarter without anyone redoing it by hand.

The expiation notice analysis follows the same habit. It reports only what the data can support, so the people deciding what to do next know how much weight each finding can carry. The workflow review starts from the same place for process. It was built from the team's own interviews and procedure notes, so its recommendations begin with how the work is actually done.

How it is built

Reporting
Python, SQL and Power BI
Tooling
Python client, FastAPI and Vue console
Method
Statistical modelling, written down
Team
Intelligence & Probity Unit
  • Python
  • Power BI
  • SQL Server
  • FastAPI
  • Vue
  • Statistical modelling

The reporting runs as code rather than as a series of manual steps. Python does the cleaning and the statistics, and Power BI carries the visuals that executives read. A written method sits next to each report, so a quarter can be rerun and the result checked rather than taken on trust.

The API client started from a simple point. Writing a separate function for each of more than 1,100 endpoints would never keep up, so the client gives them one consistent interface. The web console, built with FastAPI and Vue, puts the same reach in front of colleagues who do not write Python.

My role

I am a Senior Data Analyst (ASO7) in the Intelligence & Probity Unit of the Ethical and Professional Standards Branch, and I started in March 2026. I lead data analysis for the branch. I produce the quarterly reports, I led the workflow review, and I built the API client and its web console.

I also look after the core data that management information, strategic planning and parliamentary reporting draw on, so that everyone works from the same source.

Concept demo

Concept illustration only. Every number below is synthetic, generated in your browser from a seed. Nothing comes from SAPOL, its reports or its systems, and the API catalogue uses made-up endpoint names. Nothing is sent anywhere.

The demo has two parts. The first builds a small quarterly report from synthetic counts: rates per 1,000 attendances with exact Poisson intervals, a u-chart that flags unusual quarters, and plain findings that claim only what the numbers support. The second is a toy API client. It reads a synthetic OpenAPI catalogue and turns every operation into a method, to show how one client can cover hundreds of endpoints.

Synthetic data · generated in your browser

Concept illustration, not the real system

Synthetic data set

Set 6
Size of the planted change
Interval level
u-chart: incidents per 1,000 attendances
u-chart: incidents per 1,000 attendancesTwelve synthetic quarters from Y1 Q1 to Y3 Q4. The centre line is 1.92 per 1,000. Across all twelve quarters, Y3 Q2 sat outside the control limits. The table below lists every value.01234Q1Q2Q3Q4Q1Q2Q3Q4Q1Q2Q3Q4Y1Y2Y3
  • Centre line
  • Control limits (3 SE)
  • Outside limits
  • Beyond 2 SE, worth watching
  • Reporting quarter

Synthetic data · generated in your browser

Quarterly statistical report (demo)

Y3 Q4 · synthetic incidents per 1,000 attendances

Incidents
93
Attendances
44,761
Rate per 1,000
2.08
95% interval
1.68–2.55

Findings

  • In Y3 Q4 there were 93 incidents across 44,761 attendances, a rate of 2.08 per 1,000 (95% interval 1.68 to 2.55).
  • The rate sits inside the control limits, so on the chart this is an ordinary quarter.
  • Against the previous quarter (Y3 Q3, 1.98 per 1,000), the rate ratio is 1.05 (95% interval 0.78 to 1.41). The interval includes 1, so the data do not support calling this a change.
  • Against the same quarter a year earlier (Y2 Q4, 1.83 per 1,000), the rate ratio is 1.14 (95% interval 0.83 to 1.56). The interval includes 1, so the data do not support calling this a change.
  • Across all twelve quarters, Y3 Q2 sat outside the control limits.

You planted a +50% change in Y3 Q2. The chart caught it.

Method

Rates are incidents per 1,000 attendances. Intervals are exact Poisson (Garwood) intervals. The u-chart centre line is the pooled rate across all twelve quarters, and each quarter's limits sit three standard errors either side of it, where a standard error is √(ū / n) for n thousand attendances. Rate ratios use the exact conditional binomial interval. All data are synthetic.

All twelve synthetic quarters
QuarterAttendancesIncidentsRate /1,00095% intervalChart
Y1 Q141,329791.911.51–2.38Inside limits
Y1 Q242,713721.691.32–2.12Inside limits
Y1 Q344,899761.691.33–2.12Inside limits
Y1 Q442,056741.761.38–2.21Inside limits
Y2 Q141,433751.811.42–2.27Inside limits
Y2 Q242,992791.841.45–2.29Inside limits
Y2 Q346,137841.821.45–2.25Inside limits
Y2 Q441,619761.831.44–2.29Inside limits
Y3 Q141,015711.731.35–2.18Inside limits
Y3 Q2planted44,9981262.802.33–3.33Above limit
Y3 Q347,443941.981.60–2.42Inside limits
Y3 Q444,761932.081.68–2.55Inside limits

Report for Y3 Q4: rate 2.08 per 1,000, Inside limits.

If you work with small counts that have to stand up in front of executives, or with an API too large to wrap by hand, I am always happy to compare notes.