Skip to content

MIABIS diagnosis on Observation - #373

Open
a-tuerk wants to merge 1 commit into
samply:developfrom
a-tuerk:miabis-diagnosis-observation
Open

MIABIS diagnosis on Observation#373
a-tuerk wants to merge 1 commit into
samply:developfrom
a-tuerk:miabis-diagnosis-observation

Conversation

@a-tuerk

@a-tuerk a-tuerk commented Aug 10, 2026

Copy link
Copy Markdown

MIABIS diagnosis on Observation.

The MIABIS-on-FHIR IG puts sample-linked diagnoses on Observation
(miabis-observation, value[x].coding[icd-10-who]) and reserves Condition
for diagnoses of the patient the biobank holds no specimen for. Condition.code
is 0..1 and the reference implementation (BBMRI-cz/MoF-library, used by
BBMRI-cz/fhir-module) leaves it empty, so diagnosis, date_of_diagnosis and
diagnosis_age_donor returned zero against MIABIS data. Both placements are
valid, so the snippets now match either, the same way the bbmri flavour matches
its three ICD-10 code systems.

The Diagnosis define and the stratifier basis move to Observation, since a
stratifier takes one population basis and the sample-linked diagnosis is the one
worth breaking down.

Verified against a Blaze store written by fhir-module: diagnosis queries now
return the expected donors where they returned zero, a coded Condition is
still matched, an absent code still returns zero, and the stratifier reports the
ICD-10 codes present.

@DavidCroftDKFZ

Copy link
Copy Markdown
Member

@a-tuerk could you please upload a sample snippet of FHIR with Observation, Specimen etc. filled as expected by MIABIS? It isn't clear to me how the link from Specimen to Observation to Condition functions, is this via references? Or am I completely misunderstanding how this works?

@a-tuerk

a-tuerk commented Sep 10, 2026

Copy link
Copy Markdown
Author

Sorry for the slow reply, and thanks for looking at this.

There is no Specimen → Observation → Condition chain — that is the part that is confusing,
and it is the whole point of the PR. MIABIS uses two independent placements, and
Condition is not in the sample-linked path at all.

A diagnosis linked to a sample lives on Observation, which points at both the patient
and the specimen by reference, and carries the ICD-10 code in value[x]:

{
  "resourceType": "Observation",
  "meta": { "profile": ["https://fhir.bbmri-eric.eu/StructureDefinition/miabis-observation"] },
  "status": "final",
  "code":    { "coding": [{ "system": "http://loinc.org", "code": "52797-8" }] },
  "subject": { "reference": "Patient/DIEBBNMCNNYKG4DA" },
  "valueCodeableConcept": {
    "coding": [{ "system": "http://hl7.org/fhir/sid/icd-10", "code": "C50.9" }]
  },
  "specimen": { "reference": "Specimen/DIEBBNMERG6R2P5K" }
}

Condition is reserved for a patient-level diagnosis the biobank holds no sample for.
Here is a real one from the same store — note it has no code at all:

{
  "resourceType": "Condition",
  "meta": { "profile": ["https://fhir.bbmri-eric.eu/StructureDefinition/miabis-condition"] },
  "subject": { "reference": "Patient/DIEBBNMCNNYKG4DA" }
}

That is not a bug in the ETL. MiabisCondition.code is 0..1, and the IG's FSH source says
the profile is "used to represent only the diagnosis of the patient… not diagnosis linked
to the sample; for that use the observation resource". So MoF-library writes codeless
Conditions and puts every real diagnosis on Observation.

The Specimen for completeness — the link is Observation.specimen, there is no reference
from Specimen back to anything diagnostic:

{
  "resourceType": "Specimen",
  "id": "DIEBBNMERG6R2P5K",
  "meta": { "profile": ["https://fhir.bbmri-eric.eu/StructureDefinition/miabis-sample"] },
  "type": { "coding": [{
    "system": "https://fhir.bbmri-eric.eu/CodeSystem/miabis-detailed-samply-type-cs",
    "code": "Serum" }] },
  "subject": { "reference": "Patient/DIEBBNMCNNYKG4DA" }
}

Why the PR exists: the current MIABIS flavour queries Condition only. Against data
shaped like the above, every diagnosis criterion returns zero — verified end to end against
a real fhir-module → Blaze → focus stack, with each zero re-tested against a hand-inserted
control that flipped it to a match.

Why it matches both placements rather than moving to Observation: a coded Condition is
IG-valid — code is 0..1, not 0..0 — and both the synthetic corpus and the headlights
test-bundle.json use coded Conditions. An Observation-only query would silently break
those, so the snippets use the same parenthesised-or shape the bbmri flavour already uses
for its three ICD-10 systems. The stratifier does move to Observation alone, since a
stratifier takes a single population basis; the consequence worth knowing is that a site
recording only patient-level Conditions gets an empty diagnosis stratifier while its
searches still work.

Happy to add these as a fixture in the PR if that would help review.

(The Dockerize (focus) failure is unrelated to the change — a fork PR's token is not
allowed to write org packages. I will also rebase; the branch is behind develop now.)

@a-tuerk

a-tuerk commented Sep 10, 2026

Copy link
Copy Markdown
Author

One more thing that may help review: the MIABIS E2E bundle in samply/headlights carries diagnoses only as coded Conditions, so today's suite exercises just one of the two placements. I've opened samply/headlights#17 as a draft adding the other — a sample-linked Observation on patient-2, plus scenarios asserting C18 through the Condition path and C25 through the Observation path. The codes don't overlap with each other or with the BBMRI.de node, so a query matching only one placement fails visibly rather than passing by coincidence.

It's a draft because it asserts what this PR adds — it fails against today's focus:main. Happy to run it against a build of this branch if that would be useful evidence before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants