Definition
“In the context of a patient, the list of findings that have been described as present/absent in exams of the patient.”1 An Imaging Problem List is the third level of the hierarchy and the project’s signature structure. It takes every Exam Finding List for one patient and re-keys the observations inside them by finding.
That single change of key is the whole point. The deck states the core query the structure exists to answer: “was finding X present on the most recent study?”2 Against a stack of reports organized by date that question is a search. Against a problem list it is a lookup.
Fields
| Field | Type | Notes |
|---|---|---|
$schema | URL string | points at http://example.com/schemas/imaging_problem_list.json; nothing is published there |
patient.id | string | medical record number |
patient.name | string | display name |
patient.dob | date string | birth date |
findings[] | array | one entry per grouped finding |
findings[].id | string | identity within this list, for example ipl-finding-116. This, not the finding code, is the stable key.3 |
findings[].finding_type_code | OIFM_[A-Z]{3,4}_[0-9]{6} | the OIFM identifier |
findings[].finding_type_display | string | the finding model’s display name |
findings[].anatomicLocation | {locationId, locationDisplay} | development branch only; the second half of the grouping key |
findings[].observations[] | array | every time this finding was documented, oldest first |
observations[].report_id | UUID string | the diagnosticReportId of the source Exam Finding List |
observations[].observation_id | string | the observationId within that list |
observations[].exam_date | date string | from the source exam’s studyDateTime |
observations[].exam_type_code | LOINC code | from the source exam header |
observations[].exam_type_display | string | exam display name |
observations[].presence | string | present, absent, or indeterminate in the sample data, flattened from the source attribute code and value code pair |
observations[].anatomicLocation | {locationId, locationDisplay} | development branch only; repeats the finding’s location |
observations[].reportText | string | the verbatim span, carried through so the timeline reads without fetching the source exams |
Note the field naming: the problem list uses snake_case for its own fields while carrying camelCase forward on the fields copied from the Exam Finding List, so finding_type_code and anatomicLocation sit in the same object. Two worked entries are in the Imaging Problem List example.
The grouping key
On the stable branch, observations group by finding code alone. On the development branch the key is the pair of finding code and anatomic location identifier, with the rationale stated in the repository’s own domain notes:
Groups observations by finding type and anatomic location (
locationId) across exams, preserving references to each source report, so one finding code at distinct sites (e.g. ascending vs. abdominal aortic aneurysm) yields separate entries; consumers key on the IPL findingid, notfinding_type_code.3
The generator implements it directly, keying on the tuple of finding code and location identifier, with observations that have no location grouping under a null location.4
The effect on real data is large. The same ten exams for one synthetic patient produce 98 problem list entries when grouped by finding code alone and 123 when grouped by finding code and location.5 The observation count is unchanged; the grouping is finer.
This is why the documented rule is that consumers key on id. A finding code is no longer unique within a list.
Temporal status
Status is computed at display time, not stored. No status field exists in the file. The viewer derives it per finding from the observation list.6
- No observations gives Unknown.
- Sort the observations by exam date, most recent first.
- If the most recent is
present, every observation ispresent, and there is more than one, the status is Always. - If the most recent is
presentotherwise, the status is Current. - If the most recent is not
presentbut some earlier observation was, the status is Resolved. - If no observation was ever
present, the status is Never.
The viewer sections a patient’s list under those four headings and offers them as a filter.
Two things follow. Never is not the same as absent from the list. A finding with only absent observations is a finding that was looked for on named exams and not found, and the list records that it was looked for. The scheme has four states, not three. The stable branch’s prose still describes an older three-state model of Present, Resolved, and Not Present or Ruled Out.7 That wording is stale relative to the code on both branches, which computes four. The discrepancy is recorded in the Imaging Problem List glossary entry.
What the structure is for
The deck lists four properties beyond the core query.2
- Organized by finding, meaning pathology, rather than by chronology. The reorganization is the feature.
- Precision filtering by way of anatomy-embedded definitions. This is what the grouping-key change on the development branch buys, and what the anatomy-aware viewer renders.
- Dynamic tracking of appearance, disappearance, and change. The computed status is the first cut at this; measurement trends over time are not modelled.
- A public demonstration at
imaging-problem-list.pages.dev.
The uses the deck names across the imaging life cycle, from protocoling to passive screening, are covered under Imaging Persona, because they draw on context beyond imaging results.
The pipeline diagram
The README carries imaging-problem-list-process.png, a hand-drawn pipeline in three stages.8 On the left, Report 1 through Report n feed an Extractor, drawn as a neural network and fed from below by a database labelled “Findings (CDE ‘stub’ defs)”. The Extractor emits one Findings List per report. Those feed an Assembler, also drawn as a neural network, which emits the Imaging Problem List on the right.
The output panel is the argument. Four finding cards, Pulmonary Nodule, Pulmonary Apical Scarring, Subsegmental Atelectasis, and Pleural Effusion, each list their observations by dated exam. Pulmonary Nodule shows “Chest CT 2020-11-23 (x8)”, the multiplicity collapsed into one dated row. Pleural Effusion shows five rows of which four are marked “(absent)”, which is the negative-as-data claim drawn out: a finding whose entire history is absence still earns a card.
The Assembler as drawn is a learned component. The implementation is not: generate_ipl_from_efls.py is deterministic aggregation.4
FHIR
The documented encoding is “Report containing a list of Condition objects (labeled with the finding identifier), where each Condition object also contains a list of Observation objects which document which exams (DiagnosticReports) the finding type has been documented on, including the exam date and exam type (LOINC type).”1 One FHIR Condition per problem list entry, with the observation trail underneath it.
The mapping is documented and implemented nowhere. No Condition resource is produced by any code in the repository. Its container is also called a “Report,” which is not a FHIR resource name. See FHIR mapping.
Known limitation
Grouping by exact location identifier is too strict when the same problem is described at different granularity on different exams. The assignment rules state the gap:
Parent/child or generic-vs-specific pairs for the same finding code across exams (e.g. “lung” vs “lower lobe of right lung”; “kidney” vs “left kidney”) still produce separate IPL groups. Deciding whether such observations are the same problem or distinct is the anatomic-compatibility reconciliation step.9
That step is named in the anatomic location plan and has not been started. It is the most concrete open problem in the structure. The full precedence, laterality, and specificity rules that produce the locations are in anatomic location assignment rules.
How it is produced
generate_ipl_from_efls.py reads a directory of *_efl.json files, sorted by filename so the chronology follows, takes the patient block from the first one, groups every finding by the key above, and writes one ipl.json.4 It is ordinary Python with no model calls. The rendering side is the Imaging Problem List viewer; the sample data it runs on is described in sample data.
Footnotes
-
“Open Imaging Data Model 2026 Status Update”, January 2026 ↩ ↩2
-
MRN0000001_ipl.json, dev branch ↩
-
viewer/app.js, the temporal status derivation, dev branch ↩
-
imaging-problem-list domain model notes, main branch ↩
-
imaging-problem-list-process.png, the pipeline diagram ↩
-
Anatomic location assignment rules, dev branch ↩