Repository navigation
Admin ability to generate synthetic data for an exercise to easily support UR / Testing AB#307770 #565
Description
Blocked by: the Window / Exercise / Ingest refactor (#466, branch
466-exercise-refactor-2). Start this work only after that branch is inmain. This design uses its releases,PublishedDatasetsandCheckingExerciseBlobPaths.
Background
During UR sessions, some organisations do not agree to the use of their live data. At the moment we make fake data by hand and upload it. We want an admin to make this data in the admin app, for one or more LAESTABs, in a few clicks.
All checking data is keyed on LAESTAB, so a LAESTAB list is the input.
If a user already signed in and saw no data or real data, they must see the fake data on their next page load. (The original request said "invalidate the cache". The design below does this without a cache eviction. See Caches.)
Decisions
| Question | Decision |
|---|---|
| Environments | All, production included. So the guard rails below are requirements, not extras. |
| Unit of generation | The whole window. The admin starts it from a window or exercise page, but it makes data for every exercise in the window from one fake population per LAESTAB. Results, campus, aims, value added and summary then name the same students as pupil data (they join on CYPMD_ID). |
| Window types | All window types and all exercises. No branch on CheckingWindowType or on the exercise kind. Any exercise with a published schema gets fake data: for example, a KS4 June window with a results enquiry gets fake results. |
| Students per LAESTAB | 60 included, 20 not included by default. The admin can change both for each generation. |
| How values are made | Schema + field rules. Walk each published schema. Known fields get realistic values. Unknown fields get plausible filler from their JSON type. A new data share works with no code change. |
| Lifecycle | One-off. Generation makes a new release. The next real run of the exercise is built from the supplier CSVs again, so the real data comes back for those schools. |
| Change requests made on fake data | Mark and block. They are saved and the school sees them, but they never go to Zendesk and egress never pulls them. |
| The school's existing requests | Leave them. The confirm page shows how many requests and drafts each LAESTAB already has. |
| Permission | ManageWindow. Every generation writes an audit row. |
| Typo risk in production | The confirm page is enough. No school-visible marker and no school name lookup. |
Scope
1. Admin screens
- A Generate synthetic data link on the window summary page, gated by
[RequireAdminSection]onManageWindow(seeAdminRouteGateTests). - Form (
admin/windows/{id}/synthetic-data):- LAESTABs, one per line. Accept
nnn/nnnnandnnnnnnn(theDfeNumberrule). Normalise withLaestabNormaliser. Remove duplicates. Set an upper limit (suggest 50). - Number of included and of non-included students per LAESTAB. The admin can change them for each generation. Defaults: 60 included, 20 not included.
- LAESTABs, one per line. Accept
- Confirm page (GET confirm then POST, like
DeleteExerciseController). It shows:- each exercise that will get a new release, and each exercise that is skipped, with the reason (no published schema, or
UsesExerciseStorage = false); - per LAESTAB: does it have real data now, and how many change requests and drafts does it hold;
- a clear warning that schools at these LAESTABs see fake data until the next run of each exercise, or until an admin makes an earlier release live. In production the warning names the environment.
- each exercise that will get a new release, and each exercise that is skipped, with the reason (no published schema, or
- Progress page. Copying the other schools' files can take time (see section 3), so run it as a background job with a progress stream, like validate.
- Result. Each exercise's Data releases table shows the new release, marked as synthetic, with its LAESTABs.
2. Generating the data
- One population per LAESTAB, shared by every exercise in the window. Each fake student has a stable
CYPMD_ID, UPN (with a valid check letter), forename, surname, sex and DOB (age to suit the window's key stage), plus inclusion status. Every dataset's rows for that school come from this list. - Deterministic. Seed the generator from window id + LAESTAB. Then a second generation for the same school gives the same students, and a UR script stays correct.
- Rows are supplier-shaped CSV rows, made from the dataset's schema (
x-csvcolumn names,x-ingress.sourcecolumns andx-ingress.separator). Push them through the same row transform the ingress run uses, so stamping (INCLUDED,SOURCE), the merge of journey slots, andMergedFileIsDatasetFileare identical to a real run. Do not write a second description of the output shape. - Inclusion by file of origin. On 16-19, split the population across the included and non-included slots. On KS4, set
P_INCLto an included code (401, 403, 414, 421, 431) or another code. - Field rules. Use a registry of named generators, matched on property name. Same pattern as
IFormatValidator/IJourneyCondition: pure logic in Application, registered in the ApplicationDependencyManager. Start with: names,SEX,DOB,UPN,CYPMD_ID,LAESTAB,URN,P_INCL, results fields (QAN,SYLLABUS,SESSION,QUAL_NAME, grades), and point scores. Unknown property: by JSON type (string →"Sample {label} {n}", number → small integer, nullable → sometimes null). - Layouts.
Vertical(e.g. the 16-19 Summary share) gets one record per school.TableandInclusionTabsget one row per student (results: 1 to 4 rows per student). - Datasets. Use
CheckingExerciseDto.PublishedDatasets, neverDatasetsToIngest. Retired slots are not generated.
3. Releases and storage
The release rule is "never write over a release's output". So generation never edits the current release's blobs. For each exercise that it generates:
- Make a new release whose parent is the current release. It names the same slot CSVs as its parent (no new CSVs). It also records that it is synthetic and lists its LAESTABs.
- Server-side copy every file of the parent release to the new release, except the files of the target LAESTABs.
- Write the fake files for the target LAESTABs: one per dataset (
DatasetBlobName) and the merged file (journey slots only). - Make the new release current in one save, through the same code as
PublishReleaseAsync.
If the current release is already synthetic, its LAESTAB list carries over to the new release (it is the union).
An exercise with no current release (e.g. its first file has not arrived): its release holds only the fake schools.
To remove fake data before the next run, the admin makes the parent release live from the Data releases table. No new screen is needed.
4. Caches
No IMemoryCache eviction is needed, and an eviction would not be enough: production runs more than one web replica, and an eviction clears only one. The cache keys already contain the current release:
CheckYourPupilDataRepository:pupils:{windowId}:{laestab}:{current release ids}StudentResultsBlobClient: the blob path, which names the releaseCheckingDataReader(exercise tabs, downloads,HasSchoolDataAsync): no cache
So a new current release is read at once on every replica. Add a test that pins this for each cached reader, so that a later change to a key cannot break it.
A user who is in a journey when the data changes holds the old pupil in their session. They must start the journey again. The confirm page says to generate before the session starts.
5. Change requests on fake data
- Stamp
ChangeRequest.IsSyntheticwhen a request is saved or submitted and its school's LAESTAB is in the synthetic list of the release it is stamped with (CheckingExerciseReleaseId). The flag stays set: a draft resumed after a real run still names fake pupils. - Requests with the flag:
- show to the school as usual (Amendment Requests, the submitted view);
- are not decided by the RulesConsumer, not queued by the close sweep (
ICloseExerciseService) or by results-enquiry submit, and not claimed by the ZendeskConsumer; - are not pulled by data egress.
- The admin Requests list shows them as synthetic.
6. Audit
Write a deliberate audit row (SyntheticData/Generate) with the window, the exercises, the new release ids, the LAESTABs and the user, in addition to the generic capture of the release rows.
Not in scope
- A school-facing "this is fake data" marker. UR needs the page to look real.
- A school name lookup (GIAS) on the confirm page.
- A new screen to remove fake data. Use "Make live" on the parent release.
- Exercises with
UsesExerciseStorage = false. They have no releases, so they are skipped. - Fake data that survives a re-run.
Acceptance criteria
- A
ManageWindowadmin can enter one or more LAESTABs for a window, confirm, and see progress until the job is done. - Each exercise with a published schema gets a new current release, marked synthetic, listing the LAESTABs. Other schools' files in that release are the same as in the parent.
- A school user at a target LAESTAB who is already signed in sees the fake data on the next page load, on every replica, with no restart and no wait for cache expiry.
- The fake population is the same across every exercise of the window (results and data shares name students that are in pupil data).
- The same LAESTAB and window give the same students each time.
- The journeys (pupil data, results enquiry) work on the fake data for every window type, and a new data share works with no code change.
- The admin can set the number of included and non-included students for each generation (defaults 60 and 20).
- A change request on fake data is saved and shown to the school, but never reaches Zendesk or egress.
- "Make live" on the parent release brings back the real data. The next real run also brings it back.
- Exercises that cannot be generated are listed on the confirm page with the reason, and are not changed.
- Each generation writes an audit row.
- The controller has
[RequireAdminSection](pinned byAdminRouteGateTests), and the views meet the accessibility rules inCLAUDE.md.
Activity
[-]Admin ability to generate synthetic data for an exercise to easily support UR / Testing[/-][+]Admin ability to generate synthetic data for an exercise to easily support UR / Testing AB#307770[/+]