Skip to content

vxAnnotate ↔ vxData sync

ID mapping between vxAnnotate and vxData, and why mask RLE decoding belongs in vxa-sdk, not in consumer jobs.

Context: apps/vxdata-jobs/src/vxdata/jobs/f_20260702_vxannotate_sync (mono PR #161) syncs vxAnnotate submissions (PIRADS/PIQUAL scoring, annotations, comments, artifacts) into vxData.

ID mapping (the part that’s expensive to rediscover)

  • An annotation’s series_id = the vxAnnotate Series.id UUID.
  • Retrieve that series: Series.series_identifier = "{volume_uid}_{description}".
  • vxData Volume.identifier = "volume/{volume_uid}".
  • VxAnnotateCaseRecord.included_resources = the case’s Volume identifiers.
  • Artifact comments target the mask’s series Volume; annotator comments target the case’s ImagingStudy (parent_identifier).
  • reference_mri comes from mask.series_id — never derive it from a T2/transversal heuristic.
  • PIRADSLesionAssessment.derived_from links to the lesion’s VoxelMap.

Score payload shapes

  • PIRADS is per-lesion, camelCase keys (T2Score, DWIScore, laterality, zones, …); label_name = the lesion mask id. zones is a JSON-encoded string array, not a native array.
  • PIQUAL is case-level, snake_case.
  • vxAnnotate’s pirads extension is lesion-only — vxData’s global PIRADSAssessment (index_lesion_id, score, free_text) has no annotation source from this sync and would need a v2.1 derivation; it is intentionally left unproduced.
  • Turn silent fallbacks into loud raises for: missing annotator email, missing label_name, unmapped submission status, non-str zones.

Mask truth model — use the SDK, don’t hand-roll RLE

  • MaskAnnotation.rle (NiiVue PackBits, native-resolution, binary per mask; class lives in label_value) is the authoritative source.
  • The export’s session.mask_niftis (CBOR-baked) is best-effort only — it can come back empty or at the wrong resolution. Root cause found 2026-07-09: vxAnnotate frontend generateMaskNiftis (radiology/page.tsx) picked series.find(s => s.image_type != "SEG") — the first non-SEG series — as the reference volume for all masks, ignoring mask.seriesId; decodeRLE(rle, wrongVoxelCount) then truncated the native-res drawing into a background sub-region. Fixed in draft PR virdx/vxannotate.com#51 (per-series reference).
  • Use vxa-sdk >= 0.1.5’s client.exports.mask_niftis (reuses the stored NIfTI if it matches the RLE, else rebuilds from RLE + series.file) and the public client.series.file. Do not reconstruct RLE decoding in a consumer job — a job-local rle_helper.py workaround was built and then thrown away the moment SDK 0.1.5 shipped it properly. When the correct fix belongs in the SDK/frontend layer and is plausibly imminent, coordinate before building a workaround in your own job.

Known non-applicable path

essen01 (MRI project id c7cc1c0b-289e-4525-a2a1-6e9638f5b55a) has NIfTI-uploaded series (no DICOMSeries), so the DICOM-bridge integration path (f_20260305_vxannotate_integration) does not apply to it.

Hygiene note

Never git add <dir> on a job directory that contains untracked creds/PHI (run.sh, run_debug.sh, vxa.json) — it silently stages them, and a post-commit git reset does not undo an already-pushed commit (history rewrite via --amend --force-with-lease doesn’t un-expose a pushed secret either — rotate it). Keep such files gitignored per job.

Job layout and SDK-surface split (verified 2026-08-06)

  • f_20260702_vxannotate_sync has a handlers/ directory (not a handlers.py); the PROJECT_HANDLERS registry lives in main.py.
  • The job isolates its heavy imaging stack in a dedicated pixi feature ([tool.pixi.feature.vxannotate.dependencies], own solve-group) and depends on the published conda vxa-sdk >=0.1.5,<0.2 rather than a source build. Reuse that shape for any job pulling a large third-party stack.
  • Two SDK surfaces coexist in apps/vxdata-jobs: ~51 older jobs still build ResourceCreateRequest objects, which no longer exist in current vxdata-schemas, so those jobs cannot run as written. Copy f_20260612_essen01_histo instead — it is the current-surface template (typed Client namespaces, flat schemas). See vxdata-sdk-and-schema-model.md.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close