Skip to content

Seed PDF Fixtures for the Ready for Annotation Project

The seeded Ready for Annotation project owns two systematic searches, and they exist for two different jobs:

Search ID Studies PDFs uploaded? What it is for
Included Studies …-000000000202 20 (17 with a PDF URL) Packaged with the environment's web build Testing the PDF viewer, region marking, graph assignment
Bulk PDF Upload Test Set …-000000000205 12 (all with a PDF path) No — deliberately Testing the bulk PDF upload feature end to end

Both are created by DatabaseSeeder in a fresh, explicitly seeded non-production database. Seeding requires matching non-production RuntimeEnvironment and ActiveEnvironment values, plus the existing seed-mode/enabled flag. Production, missing and conflicting environment signals are refused before database access. Existing databases are not overwritten or reset: they retain their studies, uploads and annotations. The paths live in SeedDataConstants (ReadyForAnnotationPdfPaths and ReadyForAnnotationBulkPdfPaths) and are index-aligned with their study ID arrays.

Ready-linked viewer fixtures

The 17 viewer PDFs are small synthetic documents committed under src/services/web/src/assets/seed-pdf-fixtures/seed/. They contain selectable text and a bar chart for region marking, and explicitly say they are not published research. They contain no real participant data and require no external-host upload or third-party licence.

{AppSettingsConfig:UiUrl}/assets/seed-pdf-fixtures/{fixtureRelativePath}

The seeder stores these absolute URLs in PdfRelativePath; the existing PdfLinkBuilder.Build absolute-URL behavior returns them unchanged. No product URL-building behavior changes. The preview seed job already supplies its own web URL. The supported local process-compose stack supplies http://localhost:${PORT_ANGULAR}. Missing or invalid web configuration fails before creating records. Deploy the matching web build before accepting these links as working.

After deployment, verify the actual URL (substitute the chosen preview or staging origin):

curl --fail --head "https://staging.syrf.org.uk/assets/seed-pdf-fixtures/seed/01-mcao-transient-neuroprotection.pdf"

Expect 200, Content-Type: application/pdf, and a rendered page in the signed-in annotation workflow. HTTP availability alone is not signed-in acceptance. These same-origin fixtures exercise the viewer; managed-upstream proxy acceptance remains a separate check in #3283/#3284.

The earlier CC BY bundle attribution file is retained for historical bundles; it does not describe these generated synthetic fixtures. No fixture has been published to the shared legacy ecrf1 host by this change.

Constraint: seed PDF paths must be portable ASCII

PdfLinkBuilder.BuildLegacy concatenates the relative path into the URL without percent-encoding it. That is deliberate — byte-identical output is what keeps existing production links working — but it means a seed path containing a space or a non-ASCII character would depend on the browser to repair the URL.

So every fixture-relative path is lowercase ASCII, hyphen-separated, and free of spaces. Tests and the bundle generator enforce portability. The seeder validates the exact expected set: absolute web-asset URLs for the viewer search, relative paths for the upload search.

Generate or download the bundles

Once this web build is deployed, reviewers can download:

  • <web-origin>/assets/seed-pdf-fixtures/syrf-seed-pdfs-bulk-test.zip — the 12 upload files.
  • <web-origin>/assets/seed-pdf-fixtures/syrf-seed-pdfs-hosted.zip — the 17 viewer files.
  • <web-origin>/assets/seed-pdf-fixtures/SHA256SUMS — deterministic bundle checksums.

Or regenerate locally from the worktree root, with Python 3 and no external dependencies:

python3 scripts/generate-seed-pdf-fixtures.py
python3 scripts/test-seed-pdf-fixtures.py

Output is .local/seed-pdf-fixtures/. Generation does not contact a server, seed a database, or publish files. Repeated generation preserves identical bytes; it refuses to overwrite different existing output. Choose a new --output directory for intentionally changed fixtures. The generator reads the C# path arrays directly, so the ZIP entries cannot silently drift.

To regenerate the packaged web assets, use --output src/services/web/src/assets/seed-pdf-fixtures and review the resulting diff.

Included Studies paths

The 17 studies that pass screening, in study order. The three studies excluded at screening deliberately carry no path, so the "study has no PDF" state stays representable.

# Asset path below /assets/seed-pdf-fixtures/
1 seed/01-mcao-transient-neuroprotection.pdf
2 seed/02-mcao-permanent-infarct-volume.pdf
3 seed/03-mcao-hypothermia-outcome.pdf
4 seed/04-intracerebral-haemorrhage-oedema.pdf
5 seed/05-subarachnoid-haemorrhage-vasospasm.pdf
6 seed/06-spinal-cord-injury-locomotor-recovery.pdf
7 seed/07-spinal-cord-injury-graft-survival.pdf
8 seed/08-traumatic-brain-injury-cognition.pdf
9 seed/09-traumatic-brain-injury-biomarkers.pdf
10 seed/10-autoimmune-encephalomyelitis-clinical-score.pdf
11 seed/11-autoimmune-encephalomyelitis-remyelination.pdf
12 seed/12-parkinsonian-lesion-rotational-behaviour.pdf
13 seed/13-alzheimer-model-spatial-memory.pdf
14 seed/14-neuropathic-pain-mechanical-allodynia.pdf
15 seed/15-global-ischaemia-hippocampal-cell-loss.pdf
16 seed/16-stroke-thrombolysis-adjunct-therapy.pdf
17 seed/17-stroke-stem-cell-therapy.pdf

Testing bulk PDF upload

The Bulk PDF Upload Test Set exists so the feature has real seeded studies to match against. No individual PDF is hosted for it, and ValidateSeedData asserts that none of its studies has a delivered path — a delivered file would quietly retire the fixture.

"Undelivered" means BulkPdfDeliveredPath is null, not that linkToPdf is necessarily null: legacy relative-path behavior can still produce a URL to a missing file. The fixture deliberately preserves that existing behavior; confirm the real delivery state and successful rendering after upload.

Bulk matching keys on the whole relative path within the folder you pick, not on the file name. The fixture therefore spans three depths on purpose: four files at the root, four one level down, and four two levels down. Lay out a local folder exactly like this and pick that folder:

<your-folder>/
├── bulk-01-mcao-dose-response.pdf
├── bulk-02-mcao-sex-differences.pdf
├── bulk-03-spinal-cord-injury-rehabilitation.pdf
├── bulk-04-traumatic-brain-injury-blast.pdf
├── batch-a/
│   ├── bulk-05-autoimmune-encephalomyelitis-diet.pdf
│   ├── bulk-06-parkinson-neuroinflammation.pdf
│   ├── bulk-07-alzheimer-tau-burden.pdf
│   └── bulk-08-neuropathic-pain-microglia.pdf
└── batch-b/
    └── nested/
        ├── bulk-09-ischaemic-preconditioning.pdf
        ├── bulk-10-haemorrhage-iron-chelation.pdf
        ├── bulk-11-neonatal-hypoxia-model.pdf
        └── bulk-12-retinal-ischaemia-outcome.pdf

Extract syrf-seed-pdfs-bulk-test.zip into a fresh folder and select that folder, not its parent. The archive contains exactly 12 synthetic PDFs in this layout, with no extra enclosing directory.

With that folder the match preview should report 12 matched, 0 unmatched, 0 missing. Useful variations to prove the preview is telling the truth:

  • Missing studies: delete batch-a/bulk-07-alzheimer-tau-burden.pdf → 11 matched, 1 missing.
  • Unmatched files: add any other PDF → it appears as an unmatched file, and nothing is linked to it.
  • Path, not filename: move bulk-01-mcao-dose-response.pdf into batch-a/ → it stops matching, because its key changes.

All 12 studies are dual-screened as included, so after a successful upload you can open one in the annotation stage and confirm its PDF now renders — the same viewer path the Included Studies search exercises, but reached through the upload pipeline.

Bulk PDF upload is behind the bulkPdfUpload feature flag, which also requires deletionLifecycle to be on; see Manage Feature Flags.

Changing the fixtures

  • Adding a study with a PDF to the Included Studies search: append its ID to ReadyForAnnotationStudyIds, append the path to ReadyForAnnotationPdfPaths at the same index, regenerate the assets and update the explicit count contract/tests. The arrays are index-aligned and the seeder throws if there are more paths than studies.
  • Renaming a viewer file: preserve already-seeded URLs; coordinate migration rather than silently removing an asset still referenced by an existing database.
  • Never upload files for the Bulk PDF Upload Test Set to the static host. That fixture is only useful while it is undelivered.

Reset and repeat safely

Regenerating or re-extracting the ZIP resets local files only. Rerunning the seeder against an existing database preserves completed uploads and annotations; it does not reset the fixture. For a clean end-to-end run, use a fresh disposable mock-data PR preview, or the established /reseed-db procedure only after confirming that preview is disposable and obtaining reset authorization. Never reseed shared staging merely to repeat this test. Existing staging seed records are not migrated by this change. A legacy seed dataset therefore needs a separately authorized reset or targeted migration before it can use the new fixture URLs.