I’m Amrita, and I’ll be working on Patient Visit Summary Printing for OpenMRS O3 this summer as part of GSoC 2026. I’ll be using this thread to share weekly progress updates, ask questions, and have discussions throughout the project. Feel free to follow along, drop feedback, or just say hi
What this project is about
Right now, if a clinician at an OpenMRS-powered facility wants a printed summary of a patient’s visit — for a referral, discharge, insurance, or just the patient’s own records — there’s no built-in way to generate one from O3.
This project adds that capability to the existing openmrs-module-patientdocuments module. The goal is a pluggable, configurable PDF pipeline that lets any deployment customize what shows up in their visit summaries without changing Java code.
How it works (high level)
The module uses an XML → XSLT → Apache FOP → PDF pipeline (same infrastructure the module already uses for patient ID stickers). Each section of the visit summary — vitals, diagnoses, allergies, medications, labs, visit notes — is a pluggable component that implements the VisitSummarySection SPI.
This means:
Deployers can toggle sections on/off via config
Deployers can customize which concepts appear (e.g. which vitals to include) without touching code
External modules (like a billing module) can plug in their own sections just by implementing the interface and registering a Spring bean
The XSLT stylesheet controls layout, so facilities can adjust page sizes (A4/A5/A6), add their logo, and customize formatting
Key goals
A working, production-ready visit summary PDF accessible from O3’s patient chart
Pluggable section architecture using Spring SPI (VisitSummarySection → TypedSection<T> → concrete sections)
Configurable via Initializer (concept references, section toggles, ordering)
Clinical safety: clear distinction between “no data recorded” and “data fetch failed” in the PDF output
Frontend print button integrated into O3’s visit detail view
The community bonding period wraps up today and coding starts tomorrow, so here’s a quick look back at what these past few weeks looked like.
Dev environment setup
Got the module building and running locally on Windows + WSL2 + Docker with the O3 distro. Now I can build, deploy, and test PDF generation end-to-end!
About the very first PR of this project…
The architecture took shape through review. I raised my proof-of-concept as a draft PR early in the bonding period. It’s been through several rounds of review and the architecture evolved a lot through those discussions — the renderer went from hardcoding all section calls by name to a properly pluggable SPI where each section owns its own XML rendering. Along the way I also learned about using concept maps over hardcoded UUIDs, making clinical data lists extensible so different deployments can customize what shows up without touching code, and following existing module conventions for config keys. The PR is now out of draft and ready for review.
My biggest takeaway…?
Honestly, the most valuable learning came from the review discussions. One thing that really stuck with me was around error handling, I initially thought silently skipping a section when data fetching fails was graceful handling. But from a clinical perspective, there’s a huge difference between “this patient has no allergies” and “we couldn’t fetch the allergy data.” A clinician reading the PDF might assume it’s safe to prescribe something the patient is actually allergic to. That changed how I think about edge cases in healthcare software — every design decision carries real responsibility.
A note to my mentors
A huge thank you to @wikumc and @nethmi for being incredibly patient and supportive throughout the bonding period. The reviews and discussions pushed me to think deeper, not just about making code work, but about why design decisions matter when the software is used in clinical settings. I’ve learned more in these few weeks than I expected, and I’m going to do my best to make this project something the community can genuinely use.
Now what’s next…?
Week 1 (May 25 – Jun 1): Addressing the remaining review feedback on the PR and working towards getting the first minimal working PDF merged — patient info + vitals with the full pipeline working end-to-end.
The visit summary PDF pipeline PR went through code review this week. The main feedback was about making the module work across different OpenMRS deployments, not just the standard CIEL setup. Here’s what changed:
ConfigUtil over raw DB calls — switched to ConfigUtil.getProperty() for cached config lookups
Concept maps over UUIDs — replaced hardcoded UUIDs with concept map lookups (CIEL:5085 format via getConceptByMapping) so the module works at sites with non-standard concept dictionaries
Extensible vitals list — vitals shown on the PDF are now configurable via a single GP (report.visitSummary.vitals.concepts) instead of 7 hardcoded lines; added respiratory rate which was missing
Configurable section ordering — removed hardcoded @Order annotations; sections read their position from report.visitSummary.section.<key>.order
GP key naming — renamed keys to follow the module’s existing report.* convention
Units from ConceptNumeric — units come from the concept itself instead of being hardcoded
The PR went through another review cycle this week. The architecture from last week got approved, so the focus shifted to correctness and clinical safety details:
Diagnosis filtering fix — replaced date-based filtering (which leaked diagnoses from other visits) with getDiagnosesByVisit(), which filters at the database level through encounter → visit. Added DiagnosesSectionTest to prove visit-scoping works.
Section-error rendering — the TypedSection error handler was emitting <section-error> XML correctly, but the XSLT never invoked the matching template (xsl:call-template vs xsl:apply-templates mismatch). Wired it so errors render in-context, right where the failed section would have appeared.
Empty facility header handling — wrapped facilityName/address/phone in xsl:if guards so empty values don’t leave blank lines in the PDF.
Also started researching the O3 frontend architecture for the print button — went through the existing sticker print code, extension slot registration, modal API, and i18n patterns. Discussing with mentors where the frontend code should live.
ConditionsSection for Visit Summary PDF — What should clinicians see?
Hi everyone!
I’m starting work on the ConditionsSection (O3-5670) for the visit summary PDF pipeline in openmrs-module-patientdocuments.
Context: The visit summary PDF already has sections for vitals, diagnoses, and allergies (PR #16, in final review). Conditions is the next section to implement.
The design question: In OpenMRS, conditions are patient-level data — they belong to the patient, not to a specific visit (unlike vitals or diagnoses which are tied to encounters within a visit). This is the same as how we handle allergies. So the section will show the patient’s conditions as part of their clinical picture at the time the summary is printed.
I have two questions about what a clinician would expect to see:
1. Status filter
Should the section show only Active conditions, or should it also include Inactive and History-of conditions?
My default assumption: only Active conditions, since the visit summary is a snapshot of the patient’s current clinical state. But I can see an argument for including History-of if clinicians find that useful for context (e.g., a resolved TB diagnosis that’s still clinically relevant).
2. Chronicity
The original mockup labeled this section “Chronic Conditions.” Should we filter to only chronic conditions, or include all active conditions regardless of whether they’re acute or chronic?
My default assumption: show all active conditions. A clinician printing a visit summary likely wants the full picture — an acute condition like pneumonia is just as relevant as a chronic one like hypertension. The section heading could simply be “Conditions” rather than “Chronic Conditions.”
Would love to hear about what makes the most clinical sense.
The Visit Summary is most useful when it gives a clinician a quick, accurate picture of what’s currently relevant to the patient’s care, which argues for Active conditions.
That said, it’s worth recognising that some History-of conditions still influence current decisions (e.g., “History of MI” matters when prescribing; “History of TB” matters when interpreting a chest X-ray). In my view, though, including inactive conditions adds noise.
For the MVP, I’d settle with Active only, and expand if implementers ask for something else.
Thanks, @veronica! Sticking to Active conditions for the MVP makes total sense to keep the summary noise-free. I’ll go ahead and build it that way for now…we can always expand it later if other implementers request it
Week 3 Update — First Merge and Community-Driven Design
The first PR got merged this week! After a few final review fixes (config failure handling, dead code cleanup, AllergiesSectionTest), the core visit summary pipeline is now in main. We have a working PDF with patient info, vitals, diagnoses, and allergies.
With that done, I moved on to the ConditionsSection (O3-5670). Based on the discussion earlier in this thread and feedback from the O3 squad call:
Active conditions only for the MVP, since inactive conditions are really past medical history and would be a separate section entirely
Section heading: “Conditions” not “Chronic Conditions,” since conditions can be acute too (e.g., pneumonia)
Include by default, make togglable — it’s easier to include something and let deployers turn it off than to leave it out and wait for someone to ask