DFE-Digital / DFE-Digital/check-performance-data
Track when users search for a pupil by CYPMD ID or UPN instead of by name
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 0
- Forks
- 1
- Avg merge
- 19h 21m
- Merged PRs (30d)
- 54
Description
Why
Julia asked whether we can tell when a user searches for a pupil by CYPMD ID or UPN instead of by name, in the change journeys. Two requirements:
- detect numeric-only and UPN-shaped input and record it as a possible ID/UPN search attempt
- record it once per search interaction, not once per character typed into the autocomplete
Feasibility has been checked. Both parts are possible and most of the plumbing already exists. Estimate ~0.5 day, no architectural change.
What we already have
- A client-side beacon built for exactly this.
wwwroot/js/analytics-events.jsposts to/client-events, andClientEventsControllerbuilds the typed event server-side from an allowlist of event names, so the client cannot inject arbitrary fields. Three events already use it (help_details_expanded,external_link_clicked,evidence_file_selected). The script loads on every page from_Layout.cshtml:275and the antiforgery token is already exposed there. - Identifier search already works.
PupilSuggestionFormat.Matchesdoes aStartsWithagainst UPN/ULN and against the CYPMD ID, plus date of birth on 16-19. A user who types a CYPMD ID does get a hit today. This is a measure of how people search, not a broken path. - The suggestions endpoint fires on every keystroke. The autocomplete calls
/pupils/suggestionson each character from two upwards, with no debounce (Views/Journey/PupilSearch.cshtml:129). Counting server-side gives one row per character, which is the noise the second requirement is about. De-duplication has to happen in the browser.
Detecting the input shape
Classify in the browser and send a token only, never the typed text:
| Token | Rule | Catches |
|---|---|---|
upn |
^[A-Za-z]\d{11}[0-9A-Za-z]$ |
13-character UPN: check letter, serial, optional trailing letter |
numeric |
^\d{4,}$ |
CYPMD ID and ULN, without depending on either exact length |
date |
^\d{1,2}[/\-.]\d{1,2} |
Date-of-birth attempts, kept apart from ID attempts |
name |
everything else | The baseline to measure the others against |
Sending the token alone keeps the rule already written into docs/bigquery-analytics.md: no free text and no pupil identifiers as plain fields. Sending the raw query would mean marking it Hidden, and putting a UPN into hidden_data is a DPO decision, not a delivery-team one. Recommend the token only.
Once per search interaction
In the page script:
- debounce ~800 ms after typing stops, then classify the current input value
- hold a set of classifications already sent for this page view, so each fires at most once. Typing a CYPMD ID, deleting it and typing a name produces two events, not forty
- also fire on
onConfirmand on form submit, so an abandoned search is still counted
Tasks
- Add a
PupilSearchInputTypeEventrecord inApplication/Analytics/, carryinginput_type,page_pathand the checking window type - Add one arm to the
ClientEventsControllerallowlist, re-validating the token against the four permitted values so a forged post cannot widen the field - Add the classifier and debounce to
analytics-events.js, gated on#pupil-searchso_Autocomplete.cshtmland_PupilSearch.cshtmlpick it up too, rather than inline in one view - Unit tests beside
ClientEventsControllerTestsandClientEventsTests - Add a row to the event catalogue in
docs/bigquery-analytics.md
To settle before the work starts
- The metric on its own is not actionable. Because identifier search already works,
input_typeanswers "how often do users reach for an ID?" and not "where does it fail?". To make it useful, pair the token with whether the search found anything, so ID searches that returned nothing are visible. Doing that server-side returns us to one row per keystroke; the clean compromise is to attach the result count of the last suggestions response to the same client beacon. - The real CYPMD ID format is unconfirmed. The dev seed uses six digits (
SeedPupilData.cs:108,178), which is a local convenience and not the supplier's contract. Thenumericcatch-all avoids depending on it, but we should not claim the metric separates a CYPMD ID from a ULN until the supplier's shape is confirmed.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with wwwroot/js/analytics-events.js and the ClientEventsController allowlist, then inspect the existing ClientEventsControllerTests and ClientEventsTests. Check the pupil search wiring in Views/Journey/PupilSearch.cshtml and the shared autocomplete views before deciding how the page-view de-duplication works. Done means the permitted input tokens are recorded once per interaction, tests cover the classifier and endpoint, and docs/bigquery-analytics.md lists the event.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, javascript
- Domain
- analytics, full-stack
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- Half a day
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100