Often there is a need to have a custom complex form in Profile for collecting a large amount of information from checkboxes, drop downs, edit boxes, etc. and sometimes including a final edit box to summarize. Some of this information would be important to putting into the encounter notes so the next clinician can review a summary and not have to click through to parsing a complex form. Of course double entry is an option but frowned upon and rightfully so as we humans have horrible consistency to errorless duplication.
Intrahealth has made this task very simple to implement. When saving your form the forms event OnMGetTextForContact gets triggered. And in most cases that event macro can be a single line of code to get the expected behaviour.
Profile.Variable("TextForContact").Value = Controls_("memNotes").Text
This takes the text contents of a memo/text field and will instantly insert it into the Encounter the form was launched from.
No complex macro coding to hook the encounter editor or other complexities are needed.
It wouldn't be much more complex if you wanted multiple values from a form to be inserted into the notes. You simply set the Value to a composed string from multiple controls values, like composing a paragraph of sentences into a single string.
That's it. Sweet simple, and powerful.
We have seen in many places within Profile the ability to build custom actions and sometimes large solutions. However to achieve this is still within the confine of the Profile application using dialog boxes, report windows, macros, forms, etc.
What if we want to create a more modern looking full blown application that can still interact with Profile's rich objects.
Since Profile is mostly written in .Net it is easier than ever to include an assembly and call methods within those assemblies but you are only getting a very small fraction of functionality. Of course you can write an application to talk directly to the database, but most customers are cloud hosted so the database is not directly accessible, not to mention the immense effort to reverse engineer their database design, which I must add is done very well and is complex.
There are many cases in which you would want to create a rich interface that tightly integrates with Profile where you don't have access to the database directly and not have the time to build an API web service.
An example that perhaps most can visualize is an appointment sign-in kiosk. This could be sitting at the front desk signed into a clerical account and the kiosk app is launched as a full-screen Windows application (WPF/Winforms). A touch screen keyboard presents the user with the ability to search for their appointment by last name, or a swipe reader attached to swipe your health card. This action would show your appointment and you can sign in and maybe even get a number for in the lobby to be called. As a developer the application seems very straight forward but to have real-time appointment information becomes the big challenge.
In my example I didn't create a kiosk but a proof of concept to demonstrate some of the principles needed.
All the source code needed is available on my github at https://github.com/DigitalOxCanada/ProfileWpfApp1
So below shows the WPF App after being called from inside Profile. The yellow highlighted data was declared inside the WPF app code, the green highlighted data was declared inside the calling macro in Profile. When you click the Click Me button it calls a macro directly inside Profile which is responsible of throwing up that Hello message box.
This might not seem overly impressive but from a developer standpoint this is a very powerful approach. There is automatic databinding on the controls and you get the use of a full rich Visual Studio editing experience with colour syntax highlighting and auto-completion.
I hope this opens the doors for those that can think a bit outside of the box. I know when I discovered I could do this a bunch of ideas came to mind that would be so much nicer to develop this way.
To share some of my ideas:
- Sign-in Kiosk
- Appointment SMS reminders
- User Auditing
- Family hierarchy management (Genetics)
- Data Scraping
- etc. (can't give you all my great ideas now can I ?!)
Let me know if you found this useful. The in-depth technical details will be on the github repository.
Required fields are a big part of developing powerful and effective forms in Profile. At first glance of the richness of forms you would think it was easy to mark a field as a required field to save the form. This feature is missing completely and left up to writing the behaviour manually inside the save macro event. Although it's not overly difficult to write the code there is quite the detail to get it correct and some forms have some dependent or hierarchical conditions.
Required fields at its simplest is to have a mandatory field be checked or filled in with a value in order to save the form. To perform this action in macro is to check if the fields control has a value and if not cancel the save action with a warning message beside the control that has failed the mandatory check.
'not successful validation
Profile.Variable("CanSave").Value = False
Profile.Variable("ShowMessage").Value = True
'check of both radio buttons are not checked
if Controls_("rbAbuseInLast12Months").Checked = false and Controls_("rbAbuseInLast12Months2").Checked = false then
Profile.Variable("ControlName").Value = "lblAbuseInLast12Months"
Profile.Variable("ErrorMessage").Value = "Must be specified"
Profile.Variable("ShowMessage").Value = False
Profile.Variable("CanSave").Value = True
So imagine a form with 100 fields and the majority have mandatory conditions, this will be quite the bit of code to write and mostly tedious.
At the most simplest usage of a required field, Intrahealth could implement this feature fairly easily. But the dependent or hierarchical nature of forms would quickly make this much more difficult to solve without coding.
However, in the latest version of Profile (8.4.x) they have taken a step towards solving the more complicated scenarios at least in the GUI part of the form. There are 2 new properties to take notice of; ControlsToEnable, ControlsToEnableReverse
Here is an example inside a form. If the user checks N/A then all the other options should disable, if the user checks the option other then the other textbox becomes available.
Normally this would all happen in macro code on each of the checked events of the controls where code would make that happen.
So we set the ControlsToEnable property of the N/A checkbox to the 4 other checkboxes. We also set the property ControlsToEnableReverse to true because in this scenario we want the other 4 controls to disable when checked. The default is enable when checked so the logic for this first part is reversed.
And the Other checkbox ControlsToEnable property to edOther (the name of the edit control) we effectively create a 2 tier logic without a line of code. We leave the ControlsToEnableReverse as false because we want the default behaviour of enabling the textbox when the Other checkbox is checked.
|First time viewing form
||N/A checked disables all other controls
||Checking Other enables the editbox for editing.
As there are multiple scenarios of logic this 2 new properties can do quite a bit and without the need for many lines of code that is more prone to human error.
However this is all done in the GUI only and as of yet I see now way for this logic to affect the forms save event for required fields.
This is a very nice and welcome addition to form design and we hope to see more.
Building forms in Profile isn't necessarily a complex or complicated task but there are a bunch of steps that can make building forms tedious. The general steps are to define the termset codes, design a visual form, add the termset codes to the form, then individually associate every code to a control on the form.
A new and very welcome improvement is when designing a form from scratch some of the steps are combined saving a load of time and frustration.
In our example we created a RUCS Form for Sexual Health with approx 35 coded fields.
Starting first with defining the fields in termset maintenance.
Second create a new form tied to the termset of the RUCS Form and get prompted by the collections list of fields in which the type of field can be defined.
This saves time from individually or even using drag and drop having to search for all the terms to go on a form as well as defining the data type behind the coded terms.
The next step is to start visually designing the form by dropping controls onto the page. This is where another huge time-saver could be done but unfortunately it doesn't exist (yet). If only you could now drag the coded field onto the page and the appropriate control type is created and linked to the coded term. Maybe we will see this in the future.
After dropping a control onto the page and setting any properties needed the HRI must be defined by choosing the coded term to link the control to. When this is performed the name of the control automatically updates to a human readable string. This is a good practice to name all your controls both for clarity and in the case you want to add macro code it will be much easier to find the control by its name with proper naming.
So this might not seem like a huge improvement but rather it eliminates many many clicks which will add up quickly with forms containing a high amount of fields.
I hope to see more improvements like this in the near future.
If you have any suggestions on improvements let me know in the comments.
Does your medical facility use the medical record system "Profile" by Intrahealth? I have started a new working group specifically for developers of the Profile product. This product is far superior to the many other medical record systems I have seen out there and probably by far the most developer extensible system.
For those new or unknown to this product, it is a Win32 application built mostly on the .net platform and throughout the product are many, many hooks for developers to take advantage of. Writing your own custom code can be done directly in the product client application using .Net C#, VBScript, or JScript.
With many years of experience developing on this product, and as powerful and great this product is the difficulty is the sheer size of the features and customizations that can be utilized. My goal is to help those working on Profile increase the speed of knowledge and skills needed to maintain and support their environment. Some organizations have little to no I.T. so we offer our direct help with almost any aspect of Profile. However even with larger organizations there can be more dedicated people involved but also more turnover in staff. The goal of this working group is to have an ongoing, ever growing platform to discuss, and share knowledge, accomplishments, tactics, documents, forms, macros, etc.
I have chosen to use the Microsoft Teams product. If you are unfamiliar the Teams product is now a complete replacement for Skype for Business and a free version is available for anyone to start using Teams.
Teams is part of the Office 365 platform, thus is backed by the Azure cloud infrastructure with Onedrive storage and SharePoint sites, and utilizes the rest of the Office product suite. For those organizations concerned about the location of storage of our files (referring to PHIPA, etc.), our office tenant is in Canada on Canadian servers.
If you and your organization are interested to join this group please contact me with a valid organization email address account. I'm not accepting people using their personal Hotmail, gmail, yahoo, email accounts.
Kudos go out to Intrahealth for making a far superior product and we all look forward to see how this product continues to advance.