we would like to introduce a new generic way of O3 forms printing (a’ka exporting to PDFs) by extending the existing patientdocuments module. Current version of this module only supports exporting patient id sticker to pdf file using default or MSF-specific FOP stylesheet.
Our idea is extending this functionality to support printing O3 forms based on JSON schema with one universal XSLT stylesheet that would be applied for every form in the system. Instead of designing manual templates for every form, the system will programmatically read the structure of any clinical form and automatically generate a matching, professionally formatted PDF.
UI idea:
User goes to the patient dashboard to the Encounters tab
User selects one or multiple encounters that he wants to export
User clicks “Print selected” button
Some indicator (e.g. spinner) should be visible to see that something happens in the background. It shouldn’t hang anything and user would be able to use the system as usual
Once the backend actions are finished, the UI replaces the spinner with e.g. “Download PDF” button or automatically triggers the browser download
Backend idea:
endpoint that accepts the list of encounter uuids
based on encounter we fetch
form json structure - we have to add o3forms dependency to the patientdocument module to access the services that fetch O3 form structure
patient data (obs list)
backend parses JSON form schema and merges it with retrieved patient data (obs)
it generates highly structured XML
FOP engine takes XML and our universal XSLT to generate the final PDF file
due to potential latency of PDF generation we want to allow controller to accept the request → spawns a background task leveraging OpenMRS Reporting module (jobs in the background) → poll the status to determine if request is completed
I’m also attaching an extensive High Level Design document with more detailed specification.
I’m sort of categorically opposed to any UI that is based on the Encounters tab, as I want that to go away. Instead, we should work out a UI for displaying “completed forms” and add printing to that.
I’m moderately concerned about how future-proof the proposed solution will be, i.e., currently the form engine processes forms that only fill in encounters, but that’s not guaranteed to remain the case, especially as we’ve had occasional requests to redo things like the registration form in the form engine. This sort of thing is signaled to the form engine via the forms “processor” type (which is currently defaulted to “EncounterProcessor” or something along those lines).
We might also have reasons to change or add new rendering types on the form. Mostly, though, I think a high-level plan would be best if it had some kind of accounting for how we’d be able to support evolutions in the form engine as well as just what’s available today.
I’m sort of categorically opposed to any UI that is based on the Encounters tab, as I want that to go away.
Does it mean that this tab will be permanently deleted? I’m asking because MSF would like to introduce exporting/printing forms in their distribution but it has to be aligned with OpenMRS community expectations of course. Idea of MSF flow was to add checkboxes for every encounter and Print button (to download pdf file for selected encounter forms). I also see that this “Encounters” tab is configurable and can be hidden/displayed via configuration so would it be a problem if we slightly modify this page? If someone does not want to use it, they simply turn it off.
Instead, we should work out a UI for displaying “completed forms” and add printing to that.
Is anyone already working on such UI or is it just a plan for the future? If the latter I guess it can be long process to deliver it so I’m thinking is there any chance to implement temporary solution (with adjustments on encounters page mentioned above).
Regarding future-proof solution we initially thought about the printing encounters (so data are retrieved from encounters/obses) - I think it is the most common case to print the encounters history but we can think about it how to make it generic. I think it will not be 100% generic and some work would have to be done if you want to have e.g. printing for encounters, printing for allergies? conditions? etc. I can imagine that we have e.g. one generic endpoint for export. We call this endpoint with appropriate parameters and based on that it delegates work to the correct workers e.g. if you print Encounters then EncounterXMLRenderer is run and produces XML, output XML is combined with Encounters XSL stylesheet (we can have some defaults stylesheet defined, but if someone wants to use its own then creates own and use it) and PDF file is returned. If you want to print Allergies then AllergiesXMLRenderer is run and produces XML with allergies data and it is merged with Allergies XSL stylesheet and PDF file is returned etc. File format can also be configurable.
I’m just thinking about something that we can start with, because we need printing encounters for MSF distribution so this could be the first step and then gradually develop the topic of printing. What do you think?
Yes. The “Encounters” tab was a developer hack to enable developers to see the submitted data. There’s some designs that would completely change it. That’s also why it’s hidden. I’d point out, though, that printing arbitrary encounters is a much bigger project than just printing encounters with a form, since there are a lot of non-form data that could be attached to encounters.
Is anyone already working on such UI or is it just a plan for the future?
No. We haven’t really had a feature yet where is necessarily makes sense to show all forms, but printing would kind of be that feature.
I think it is the most common case to print the encounters history but we can think about it how to make it generic.
I’m less concerned about the solution being generic so much as ensuring that the solution is developed in a way that makes it extensible, if that makes sense. I realize that it’s impossible to cover absolutely everything.
The “Encounters” tab was a developer hack to enable developers to see the submitted data.
Developer hack but seems that is usable even for real cases (as MSF organization would like to use it). Ok, so just to confirm - there is no chance to adjust anything in Encounters tab that would be merged to community, right? I was hoping there would be a chance as we can just hide this page (for those who don’t want to use it)
I’m less concerned about the solution being genericso much as ensuring that the solution is developed in a way that makes itextensible, if that makes sense. I realize that it’s impossible to cover absolutely everything.
I see your point but I think we just have to start somewhere. For example encounters - there are mainly metadata like patient name, visit date, visit place etc. and main data - observations. I think would be a good point to start with. I know that you mentioned earlier that there can be other data than observations as well but it can be added gradually, right?
It builds XML based on Form JSON schema and data from encounters. If e.g. in the future forms could include other elements e.g. embedded registration form then this printing feature would require adjustments to cover registration form printing. We rely on the O3 Form JSON schema so we know how this JSON structure does look like now, but we don’t know if something will change if other elements than encounter/obses will be added to the forms
@ibacher so as I understood correctly any adjustments made on “All Encounters” tab will not be never merged so how would you see this new page for “completed forms”? What would be the difference comparing to existing “All Encounters” tab? I can imagine that we have new option on the left side panel e.g. “Completed Forms” and going there we just fetch encounters for this patient and display them in tabular view. Each encounter row would be collapsed and expanded. When expanded we display detailed info (obs). We would have some basic filters by date/encounter type/visit type etc.? Each row would be selectable to printing feature. Would it be something as simple as I described here or would it require a lot of discussion, brainstorming, mockups, analysis etc.?
@ibacher Thanks for your comments and feedback. This is a very meaningful discussion.
I fully understand your point - you don’t want to introduce new elements to the UI (the Encounters tab) that may eventually be deprecated and removed from the system. However, my concern is that, at this stage, there is nothing concrete regarding the replacement of this tab. There are no designs, no roadmap - nothing beyond general ideas.
At the same time, we do have a specific need reported by end users to add printing functionality to the Encounter tab. Our proposal is to enable printing of forms created via the O3 Form Engine for Encounters. This is a concrete requirement that we would like to address, and I believe it would bring value to the community.
Even if the Encounters tab is eventually removed, the backend we develop could be reused once a new UI concept is designed and implemented. Therefore, the potential “waste” would be limited to the UI layer only. Since there are no concrete plans regarding the replacement of the Encounters tab, I would advocate for adding this functionality there for now.
I don’t know that there’s a lot of UI discussion needed here per se. We can build a table very similar to the existing encounters table but limited only to rows with forms. That solves what I think of as the biggest issue in the proposal which is that this print functionality really can only work for encounters that were created with forms, but the encounters table in the UI shows all encounters. I don’t think there’s any real design work needed there; it can function much as the current encounters table.
To try to be clear about this: there are currently encounters that will show up in the existing Encounters table that have, e.g., only Orders attached to them or only Obs created through a non-form-engine form (e.g., the Vitals form and the like). It seems a substantially harder effort to develop printing functionality for basically arbitrary encounters, since we wouldn’t have a form to figure out how to lay them out or “translate” obs correctly. But if we just create a simple new dashboard with a table that’s filtered to just forms, that’s a minimal amount of extra effort, but it builds a UI where the user’s will be able to select and print everything displayed, which seems like a better user experience.
I’m not saying something that uses the Encounters table will never be merged; I don’t have and shouldn’t have that kind of veto power. But I definitely think that using the existing Encounters table is the wrong place to build the functionality you’re looking for. I agree that once this backend exists we can use it in other places, and that’s great, but it doesn’t really resolve the fact that what you’re pushing for seems to me to be sub-optimal UX.
Thanks @ibacher . I initially thought that this would need to be designed and discussed in more depth. However, your explanation is very reasonable and should actually address this need even better.
Let us align with the client team and we will propose something. The UI for Completed Forms would likely replicate the Encounters view, but the data displayed in the table would be limited to Encounters created via the O3 Forms Engine only. @druchniewicz What’s your thought?
Since bulk printing of encounters could take longer to design and implement, especially with the UI questions around the Encounters tab vs a future “completed forms” view, what do you think about starting with a simpler entry point: adding a print button directly within the form engine itself?
The idea would be that when a user opens or edits an encounter through a form, they can print that specific form right from there. This would give a quicker win and let clinicians print a form immediately after filling it out or when reviewing it.
This wouldn’t replace the bulk encounter export feature that’s clearly valuable for MSF’s use case, but it could serve as a practical first step that delivers printing encounters sooner while UI discussions are being finalized. It also aligns well with the future direction of the form engine, as more form types like registration may eventually be handled through it.
adding a print button directly within the form engine itself?
Not to the form engine, but to the form wrapper, maybe. In general, I don’t think the form engine should be responsible for rendering that button, but maybe for rendering an extension slot where it could be rendered. That said, a print button on a form is potentially dangerous; what does it print? The form as it currently is or the form as it’s stored?)
However, I think even without the “bulk” feature, there’s still utility in having a view-only UI where you can print one form at a time. That seems better than requiring the user open the form in edit mode just to be able to print.
@ibacher I’m back with feedback from MSF about their needs and I’d like to ask about your opinion and thoughts.
First option is that you mentioned before that we can create a very similar table to the existing “All Encounters” table but limited only to rows with forms where there will be option to bulk printing. Quick example:
so you can print single encounter(form) from particular visit. No selectable rows.
What do you think about it? I saw in the source code that this table is the same component used in both places so seems it will not be much effort. We can add one additional props to determine whether rows in the table should be selectable and still we have generic table. On the backend we can have an endpoint that takes list of encounters (to be printed) and in the first option we can send multiple encounters, in the second option we can send a list with one encounter.
For MSF both proposal would fulfil their needs that’s why I’m presenting both of them.
Reminder… Please include a requirement for user & timestamp footer. Any patient data printed from OpenMRS should include a footer with user, system ID, and timestamp (“Printed by Jane Doe (123-0) at YYYY-DD-MM HH:MM:SS”). We don’t want to have any anonymous patient data printouts for security & accountability of handling patient data.