Repository navigation
Medical Records What Is Left
Status: Open work list, 2026-09-30. Nothing on this page is done yet. Audience: Contributors who pick this work up later. Context: Phase 1 and phase 2 features 2a, 2b, 2c and 2f are on main (pull request #339, 2026-09-30). The rest waits, because the material to test it was not at hand. Related: Proposal, Feature page, Permissions, Privacy.
This page lists what is left of the medical records work. Each entry says what is missing, what it takes, and where to start. Remove an entry when it is done.
- Built, not checked on real data
- Small fixes
- Release steps
- Not built: features 2d, 2e and 2g
- Open questions
- Rules for picking this up
These parts are on main and pass their tests. The tests use invented data, so each part still needs one check with the real thing.
| Part | Checked so far | Still to check | What it takes |
|---|---|---|---|
| Camera scanner (2a) | On the test phone: the scanner opens, the camera starts, and Cancel stops it. Unit tests read a code from a camera frame. | A scan of a card. A scan of a card split over several codes. | Any SMART Health Card QR code, on paper or on a screen. The specification publishes examples. example-00-g-qr-code-0.svg is a card in one code. example-02-g-qr-code-0.svg, -1.svg and -2.svg are one card in three codes. |
| A card from a real issuer (2a) | Invented cards, as a photo and as a PDF, on the test phone. | A card an issuer gave out. | A vaccination or lab card with a SMART Health Card QR code. The examples above are the next best thing. |
| Apple DSTU2 records (2f) | Invented records, in unit tests only. No converted record has reached Health Connect. | That Health Connect accepts each converted record. | First, an invented Apple export with DSTU2 records, imported on a phone. Then a real Apple Health export from a provider that still sends DSTU2: in export.xml its ClinicalRecord entries say fhirVersion="1.0.2". The import report gives Health Connect's reason for each record it refuses. |
Apple export_cda.xml (2b) |
Nothing. The Apple import reads the clinical records and leaves this file alone. | What the file holds, and whether any of it is a medical record and not Apple's own measurements. | A real Apple Health export. Print its structure with every value masked first, as was done for the Estonian export. CcdaMapper already gives no vital signs for a document written by a device or an app. |
| CDA from a real portal (2b) | One invented C-CDA on the test phone: 16 of 16 records accepted. | A document from a real portal, and an EU patient summary. | A "download my record" XML file. HL7 publishes sample C-CDA documents, which are a step closer than the invented one. |
| Other Estonian document types (2c) | The owner's export: 177 documents, 1,415 records, none refused. | Types that export did not hold, such as a hospital stay. | An export with those types. Sections are read by their own codes, whatever the document type. A section the mapper does not know stays in the PDF. Run the local check below before the phone. |
| Sync of medical records (1.5) | Unit tests with two sessions. One phone only. | A real transfer between two phones. | Two phones with Health Connect medical records: Android 14 with the module update, or newer. |
| Another app's records | Nothing on a phone. | The button on a record's source row that opens Health Connect, where the record can be deleted. | A second app that writes medical records to Health Connect. |
EstonianPortalRealExportCheck runs the importer over an unzipped export on the developer's computer. It prints counts only: no names, codes, values or dates. CI skips it.
env OPENVITALS_EE_EXPORT=<folder> ./gradlew :domain:testCiUnitTest --tests '*EstonianPortalRealExportCheck*'
It also prints an id fingerprint. Keep it before a change to the CDA code and compare it after. See the rules.
FhirCorpusCheck runs the importer over whole folders of FHIR JSON and NDJSON, one import per file, and prints what it did: records ready and held back, types skipped, repairs, categories, references, and records taken for DSTU2. It fails when a file throws, when a record keeps an id or a contained record the importer should have repaired, or when an R4 record is taken for DSTU2. CI skips it. Paths are split by :.
env OPENVITALS_FHIR_CORPUS=<folder>:<folder> ./gradlew :domain:testCiUnitTest --tests '*FhirCorpusCheck*' --rerun
Three synthetic datasets have been run through it, with none held back that the importer should have repaired, and none taken for DSTU2:
-
Synthea, 1,180 patients as R4 transaction bundles, kept in the repository's ignored
fhir/folder. 13 are over the 8 MB limit and are refused whole. - SMART bulk export samples, NDJSON, one file per type. Each file becomes its own source, and references between the files do not resolve.
- HL7 AU test data, one resource or one patient summary per file. Four Observations are held back because Health Connect cannot classify them: exam findings coded in SNOMED CT.
A slice of each is committed for CI, in domain/src/test/resources/fhir/datasets, and FhirDatasetSamplesTest reads it. What only Health Connect checks is not seen by either: the AU patient summaries carry no-break spaces in their narratives, and only an import on a phone tells whether Health Connect refuses them there as it does in a code's text.
-
Kept Estonian PDFs show the portal's file name. That name holds the national ID code, and Saved documents lists it. Name the kept file by the document's title and date instead. The name is set in
CdaImportSource, where it builds eachMedicalSourceDocument. -
The PDF viewer shows a UUID as the title.
MedicalDocumentActions.kthands the stored file to the viewer under its storage name. Hand it over under its display name. - The medical screenshot goldens are out of date. The import button now reads "Import records", the pick step has the scan button, and outlined buttons draw a border since pull request #340. Re-record them on the test phone.
Do these when the feature ships, not before.
- Google Play declaration. The thirteen medical permissions are now in the manifest on main. The nightly upload to Play stops until the Health Connect declaration covers them. The proposal holds the draft justification. Review the Data safety form in the same sitting, for the medical data types and the camera.
- Changelog and landing page. A changelog entry and the landing page feature cards.
- If Play refuses a category. Not decided. A store flavor without the refused permissions is the likely path.
Three features are left, and the pieces they share. None has code yet. Each gets its own proposal page before work starts. The proposal describes them in full; this section says what each one waits for.
| Piece | Where it goes | What it is |
|---|---|---|
| Review table | features/imports/medical/ |
One row per proposed record: the source snippet, fields the user can correct, and a keep switch. Every record read from a PDF or a scan is a proposal until the user keeps it. |
PdfTextExtractor, OcrEngine
|
core/ |
Interfaces, so the library behind each can change. |
| Analyte dictionary | app/src/main/assets/medical/analytes.json |
Lab test names in each language, with their LOINC code and unit. LOINC and UCUM need their licence notices in the app. |
| Fixture corpus | app/src/test/resources/medical/ |
Invented documents with the records expected from each. A parser that gets worse fails the build. |
Lab reports first, then vaccination lists. See 2d in the proposal.
- Waits for: real PDFs to build against. The layout of a lab report differs by portal and by laboratory, so a parser built without samples would be a guess.
-
Decide first: the text library. The proposal lists three: the platform's page text (Android 15 and newer), Jetpack
androidx.pdf, and PdfBox-Android. Check whetherandroidx.pdfgives page text with positions. - Material at hand: the owner's Estonian export has 177 PDFs, and all 177 have a text layer (counted on 2026-09-30). Each has an XML file with the same content, which the app already imports. So a text extractor and a lab-row parser can be scored against known answers, on the developer's computer, with counts only. It is one portal's layout, in Estonian, so it proves the pipeline and not the dictionary for other languages.
-
First steps: the 2d proposal page, the
PdfTextExtractorinterface with one implementation, then the review table. - To test: invented PDFs in the fixture corpus, the local scoring above, and lab reports from at least two laboratories on a phone.
OCR first, then the same parsing as 2d. See 2e in the proposal.
- Waits for: 2d, and scans or photos of printed reports.
- Decide first: how Tesseract's language data arrives. It is 5 to 15 MB per language: bundled, or imported as a file like a map pack. An ML Kit build for Play only is a policy decision, taken only if Tesseract is too weak on real reports.
- To test: the same reports as 2d, printed and photographed, in good and poor light. Count wrong numbers and units, not only missed rows.
An experiment, after 2d and 2e. See 2g in the proposal.
- Waits for: the review table, and a phone that runs the model: one with AICore for Gemini Nano, or a Gemma weights file of 0.8 to 1.5 GB imported by hand.
- The risk: a model invents numbers and units. Its output is a suggestion beside the source text, never written without a keep.
- To test: the 2d and 2e corpus, scored the same way.
These are not open. The proposal records why.
- Card signatures are not checked. That needs the issuer's keys from the internet, and the app has no internet permission. Each record from a card says so.
-
SMART Health Links (
shlink:/) fetch from the network. -
The EU Digital COVID Certificate (
HC1:) is another format.
-
Apple
export_cda.xml. What it holds. See the table above. - Play refusal. What ships if Play refuses a category.
- PDF text library. Settled in the 2d proposal.
- Tesseract language data. Bundled or imported. Settled in the 2e proposal.
- Real documents never enter the repository. Not as fixtures, not in a test, not in a commit message. Read a real file's structure with its values masked, and write an invented fixture in the same shape.
-
Estonian record ids must not change. People have already imported their export, and a new id would duplicate every record on the next import. Run the local check before and after any change to
domain/medical/cda/and compare the id fingerprint. - A parsed record is a proposal. Nothing read from a PDF, a scan or a model is written until the user has seen it beside its source and kept it.
- No new network use. The app has no internet permission, and medical records do not change that.
- One feature, one proposal page. Then the feature page, the privacy and permission pages, and the strings in every language.
Generated from docs/proposals/medical-records-left.md. Edit it there; changes made here are overwritten.
This wiki mirrors the Markdown in Hale-MTU/OpenVitals on every push to main.
Features
- Features
- Achievements
- Activity And Training Plans
- Activity Metrics
- Apple Health Import
- Beverage Logging And Caffeine
- Bluetooth LE Sensors
- Body Energy
- Body Metrics
- CoMaps Navigation Context
- CSV Import
- Cycle Tracking
- Daily Readiness
- Elevation Correction
- Feature Map
- FIT Files Import
- Food Logging
- GPX/KML/KMZ/TCX Route Import
- Health Connect Metrics Dashboard
- Health Report Export
- Heart And Vitals
- Heart Rate Recovery
- Home Screen Widgets
- Hydration
- Manual Entry Of Metrics
- Medical Records
- Metric Detail Customization
- Mindfulness
- Non Health Connect Metrics Dashboard
- Nutrition
- Offline Maps Support
- Onboarding And Permissions
- Preloaded Beverage Nutrition Reference
- Privacy, Support, And Diagnostics
- Recording Of Activity
- Reminders
- Settings And Preferences
- Sleep Score And Recovery
- Sleep Tracking
- Statistics
- Sync With Another Phone
How-to
App
- App Guide
- FAQ
- Getting Started
- Health Connect
- Install
- Local And Connected Editions
- Permissions
- Privacy
- Screenshots
- Support
Reference
- Design Reference
- Reference
- Design Guidelines for Offline & Sync
- OpenVitals UI Notes
- Reference: Data Capture
- Reference: Data Display
- Reference: Health Connect Matchmaking
- Reference: Health Connect Onboarding
- Reference: Health Connect Permissions
- Reference: Health Connect UX
- Reference: Promoting Health Connect
Engineering
- Code Analysis
- Engineering
- Flutter ↔ Kotlin test-parity matrix
- apple-health-import
- Architecture
- body-cycle-recovery
- Design system conformance
- Development
- Feature Playbook
- Flutter -> Kotlin test parity: domain/data/core/contract/state/navigation/di/bootstrap/l10n/widget_test
- Flutter → Kotlin test-parity matrix: features section
- Flutter → Kotlin test-parity matrix: ui / goldens / integration / fixtures
- Flutter-to-Kotlin test-parity matrix — dashboard + devicesync
- manual-entry
- Translations
Proposals
- Proposals
- Activity Start Flow Analysis
- Activity Start Flow Simplification Proposals
- Caffeine Detail And Future Sleep Insights
- Cycle Tracking: Design and Status
- Medical Records Through Health Connect
- Medical Records: What Is Left
Releases
- OpenVitals 2.16.0
- OpenVitals 2.15.2
- OpenVitals 2.15.1
- OpenVitals 2.15.0
- OpenVitals 2.14.5
- OpenVitals 2.14.4
- OpenVitals 2.14.3
- OpenVitals 2.14.2
- OpenVitals 2.14.1
- OpenVitals 2.14.0
- OpenVitals 2.13.0
- OpenVitals 2.12.0
- OpenVitals 2.11.2
- OpenVitals 2.11.1
- OpenVitals 2.11.0
- OpenVitals 2.10.1
- OpenVitals 2.10.0
- OpenVitals 2.9.1
- OpenVitals 2.9.0
- OpenVitals 2.8.0
- OpenVitals 2.7.3
- OpenVitals 2.7.2
- OpenVitals 2.7.1
- OpenVitals 2.7.0
- OpenVitals 2.6.6
- OpenVitals 2.6.5
- OpenVitals 2.6.4
- OpenVitals 2.6.3
- OpenVitals 2.6.2
- OpenVitals 2.6.1
- OpenVitals 2.6.0
- OpenVitals 2.5.0
- OpenVitals 2.4.1
- OpenVitals 2.4.0
- OpenVitals 2.3.2
- OpenVitals 2.3.1
- OpenVitals 2.3.0
- OpenVitals 2.2.8
- OpenVitals 2.2.7
- OpenVitals 2.2.6
- OpenVitals 2.2.5
- OpenVitals 2.2.4
- OpenVitals 2.2.3
- OpenVitals 2.2.2
- OpenVitals 2.2.1
- OpenVitals 2.2.0
- OpenVitals 2.1.0
- OpenVitals 2.0.4
- OpenVitals 2.0.3
- OpenVitals 2.0.2
- OpenVitals 2.0.1
- OpenVitals 1.9.0
- OpenVitals 1.8.0
- OpenVitals 1.7.7
- OpenVitals 1.7.6
- OpenVitals 1.7.5
- OpenVitals 1.7.4
- OpenVitals 1.7.3
- OpenVitals 1.7.2
- OpenVitals 1.7.1
- OpenVitals 1.7.0
- OpenVitals 1.6.3
- OpenVitals 1.6.2
- OpenVitals 1.6.1
- OpenVitals 1.6.0
- OpenVitals 1.5.1
- OpenVitals 1.5.0
Project