New features / improvements

Demographics v5+ : Title block (0)
This commit provides 2 changes:

  1. Main title frame will load patient and encounters information without need for loading of demographics frame. Currently the title frame calls demographics which then updates the patient data in title frame.

  2. Instead of loading encounter and document history, we automatically load the last encounter. For many practices, it is common to have patients with 50-100 encounters.

Not included in this commit are changes to demographics.php - that remove code that fills patient & encounter blocks.

Demographics v5+ : Deconstruction (0)
This commit is first step towards making patient demographics screen flexible.

Patient dashboard is most frequently used interface component. All users need ability to add/remove and rearrange components that they use. Unfortunately current structure does not lend itself to any flexibility.

For starters, this commit :

  1. Deconstructs existing code into fragments with a requirement that each fragment (for now) have its php, html and scripts in a single file.

  2. Introduces a class that is used for constructing fragment objects.

  3. Maps the page into an array making it possible to add/remove or rearrange the fragments.

In next step, these fragments would become providers of data for one of the views of underlying emr objects.

I looked briefly through the code and anything that breaks large massive files like the demographics.php into smaller logical chunks that can be better reasoned about is fine by me.

My understanding is that you are not wanting to make this into a pull request at this point? Is that just due to concerns about meeting PSR2 standards or what?

I actually like the approach of the fragments because it allows us to filter those array components using the event dispatcher if we want to extend / customize this via module extensions.

Just my 2 cents.

Stephen: These commits are based on our fork at 5.0. In the past we have tried to provide PRs of individual features that got obsolete - (e.g. in the code you will notice include for globals.php does not have hard coded path) . We also have switched to latest bootstrap 4 and will soon move to standardized view classes targeted for tablets.

As the forks diverge, creating each PR becomes prohibitively expensive. At the same time we feel the standard code could use these as hints/suggestions or just our 2 cents worth of contribution to this good project.

Been awhile since i’ve boned up on bootstrap. Can BS3 and BS4 coexist? I’m all for anything that breaks down demographics especially from a performance point of view. Also if BS4 is the goto for better responsiveness then am onboard as well. So how difficult would it be to stage BS4 into codebase?

Didn’t try but I suppose the script injector can be directed to select specific version. Good news here is BS isn’t widely used in the project as your efforts show.

Performance issues in current 2000 lines of demographics.php are architectural - it tries to do everything for everybody. As an example, if an user while reading messages wants to see documents for that patient, they go to demographics which loads issues, appointments (2-3 times), all encounters, all issues, prescriptions and birthday cake :wink:. As a bonus, the rules popup 2-3 alert boxes that need to be closed before they can click to see list of documents by category. If that patient has 50-60 encounters and 100+ documents, this check takes more than few minutes.
Then the user moves to the next message for another patient!

Now that basic rant is over, as far as demographics is concerned, BS version is not an issue - towards the end there are 4-5 lines that do overall remapping that can be removed in standard code. Bulk of the effort will be :

  1. Validating if fragments is the approach community wants to take
  2. Explode current (5.0.2) code to their respective fragments (php, js and html)
  3. Test full set of fragments and then possibly with few fragments removed from the array.

Effort for RTop is basically removing code from the updaters/callers without impacting frames users. We have removed left_nav from our code and have no idea if anything breaks there.

Bootstrap code can be very verbose. Recently came across old library that provides well structured classes to support forms. Just posted a basic wrapper class around that library to make it usable in emr setting.

If anyone is interested, they can follow these steps to see the basic form with some of the validation built-in :

  1. PFBC wrapper (0)
    Pull current dev version of the library avbdr/php-bootstrap-form from mdsupport repo.
  2. PFBC wrapper (1)
    Import current OeForm class.
  3. PFBC wrapper (2)
    A simple script that uses email and password fields. Check the pre-built validation classes that examine the email address entry. The test script uses New Assets Manager class that provides method for starting a standardized html document.

We plan to use this setup as basic engine for all forms on tablets / small devices.

@sjpadgett and others have gone through hoops to make dialog library work for frames and iframes interfaces. If frames interface is dropped, project can move towards delegating modal dialogs to native bootstrap. Here is a recipe for eliminating use of dlgopen by Popups menu -

  1. Add basic modal dialog template to main.php
<div id="mainModalContainer">
<div class="modal fade" id="mainModal" tabindex="-1" role="dialog" aria-labelledby="mainModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="mainModalLabel"><?php echo xlt('Error') ?></h5>
        <a role="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <div class="container-fluid modal-body-grid">
          <?php echo xlt('Error') ?>
        </div>
      </div>
      <div class="modal-footer">
        <?php echo xlt('Error') ?>
      </div>
    </div>
  </div>
</div>
  1. Make modal available globally - preferably in a .js file so it does not become inline. If that is not possible, add it to main.php.
function showMainModal(opts) {
    if (typeof(opts.title)==='undefined') {
        $('#mainModal .modal-header').addClass('d-none');
    } else {
        $('#mainModal .modal-header').removeClass('d-none');
        $('#mainModalLabel').html(opts.title);
    }
    if (typeof(opts.footer)==='undefined') {
        $('#mainModal .modal-footer').addClass('d-none');
    } else {
        $('#mainModal .modal-footer')
        .html(opts.footer)
        .removeClass('d-none');
        
    }
    $('#mainModal .modal-body').addClass('embed-responsive embed-responsive-16by9');
    $('#mainModal .modal-body').html('<iframe class="embed-responsive-item" src="'+opts.src+'"></iframe>');
    $('#mainModal').modal('show');
}
  1. Update tabs_view_model.js to bypass dlgopen -
function popMenuDialog(url, title) {
    let notlike = title.toLowerCase();
    // If mainmodal is available, display finder in modal
    if (typeof(showMainModal)==='function') {
        showMainModal({
            src: url,
            title: title
            }
        );
        return true;
    }
    dlgopen(url, 'menupopup', 'modal-mlg', 500, '', title, {
        sizeHeight: notlike.search('label') !== -1 ? 'full' : 'auto'
    });
}
  1. Other scripts can invoke the modal by using top.showMainModal.

Current code allows (or in many cases requires) the modal dialog to invoke functions in initiating script. As part of the migration, it may be better to put together a mechanism by which the dialog notifies top that it should be closed due to a given action. top.closeMainModal acting as controller, closes the dialog and takes follow-up actions.

Issues are functionally central to patient care. Unfortunately, this and most other systems focus on encounter and documentation features mostly needed for billing :heavy_dollar_sign:. As we look at bringing in issue management related enhancements, first step is to provide consistent display of the issues throughout the application. Following query currently forms the core of ptIssue class.

SELECT iss.* FROM lists iss
INNER JOIN issue_types it ON iss.type = it.type
LEFT JOIN list_options occ ON iss.occurrence = occ.option_id and occ.list_id = "occurrence"
WHERE iss.pid=? and it.active=1 and occ.activity=1
ORDER BY it.ordering, occ.seq DESC,
 (IFNULL(iss.enddate,0)=0) DESC, 
 IFNULL(iss.begdate,iss.date) DESC,
 IFNULL(iss.enddate,0) DESC, title ASC

Found one more reason to drop frames support.

demographics.php is the only way to navigate to all data related to this patient - may be selected from calendar. All links to are then visible but unresponsive until everything is loaded. In addition to large number of ajax calls for patient data, this process also includes display of encounters. If your practice has patients with several years of visits, it is now starting to take 30-45 seconds and beyond to get your clicks accepted by the browser.

In frames era that was tolerable since each frame responds when ready in its container. Encounters frame was immediately visible and needed to be updated. In case of tabs interface, the user ends up paying the penalty of waiting till encounter tab is populated in the background and in more than 90% of times does not use that list anyway.

Since we changed the navigation as part of Demographics v5+ : Title block (0) , the loading of enc tab is now discontinued. If required, users can now choose list or specific encounter from title block. This has cut the wait in half.

Initial attempt at being social - definitely worth exploring complete outsourcing of user credentials management !

Memcached integration with emr will offer an option for users to communicate across sessions at run time.

Has anyone already implemented this or any other caching functionality, could you please share your experience and code if possible?

Starting a PoC to cache calendar page(s) for small practices. Daily view is fetched by every user multiple times during normal hours when probably 99% of time there are no updates. So the code will cache a generated page that will expire at end of day. All appointment updates for cached dates will mark the cached entry as dirty. ‘Refresh’ action by any user will also invalidate current cached entry.

1 Like

Great idea. Though, i’d think this would be more beneficial to larger practices or practices with many users.

This commit implements caching for calendar.

Prerequisite: A cache server on localhost

Caute:hic sunt dracones

TLDR
Our public repository provides a database-centric approach to implement modifications to standard emr.

Like windows registry or linux package managers, dev_obj table stores various “things”. The dev_component table implements the relationship between defined objects. All “objects” are then supported by specific code that implements specific actions. As an example, DevFile object can be leveraged to control actions of standard scripts. Object versioning implements ability to support custom behavior for different emr versions or specific installations. guids implement integrity of objects globally.

Currently the code includes objects that works for MySQL or compatible engines. Interested developers can also look at the stored functions that use dbengine to generate guids as an alternative to emr code.

Changes needed to standard code:

  1. Review and install tables in database.
    Depending on your dbengine version, you may need to set uuid default to null and use the polyfill + trigger mechanism to manage uuid values.
  2. Modify composer.json and globals.php
  3. Review changes in mdpubapps branch to see how external content can be included in emr screens.

If any questions, please post in its own thread. Optionally refer to this post.

Added version upgrade tool that can be run in cli mode or setup as a cronjob to perform scripted upgrades.

Uses sql_upgrade approach with following improvements:

  1. Upgrade step is identified by sql x_x_x-to-x_y_z file as x_y_z version
  2. Database upgrade to x_y_z is performed
  3. Local sql upgrades supplement from vendor directory is performed
  4. Local post processing from vendor directory is performed using local php code.*
  5. Instead of single sql patch, using the x_y_z version multiple patches are applied using linked list.
  6. For each patch, local upgrades are checked.
  7. Allows post processing using php code that is version specific.

Sample tasks

php /var/www/html/emr6d/vendor/mdsupport/mdpub/oemods/upgrade/ver_upgrade.php from=5_0_0 site=default local=md
"update" flag missing. NO updates applied from
5_0_0->5_0_1
File - /var/www/html/emr6d/sql/5_0_0-to-5_0_1_upgrade.sql
5_0_1->5_0_2
File - /var/www/html/emr6d/sql/5_0_1-to-5_0_2_upgrade.sql
5_0_1->5_0_2(local)
File - /var/www/html/emr6d/vendor/mdsupport/mdpub/oemods/upgrade/md_5_0_1-to-5_0_2_upgrade.sql
5_0_2->6_0_0
File - /var/www/html/emr6d/sql/5_0_2-to-6_0_0_upgrade.sql
5_0_2->6_0_0(local)
File - /var/www/html/emr6d/vendor/mdsupport/mdpub/oemods/upgrade/md_5_0_2-to-6_0_0_upgrade.sql
Setup 6_0_0
File - /var/www/html/emr6d//vendor/mdsupport/mdpub/oemods/upgrade/ver_setup_6_0_0_upgrade.php
Setup 6_0_0(local)
File - /var/www/html/emr6d/vendor/mdsupport/mdpub/oemods/upgrade/md_ver_setup_6_0_0_upgrade.php
6_0_0->6_1_0
File - /var/www/html/emr6d/sql/6_0_0-to-6_1_0_upgrade.sql
6_1_0->xxx
File - /var/www/html/emr6d/sql/6_1_0-to-xxx_patch.sql
xxx->yyy
File - /var/www/html/emr6d/sql/xxx-to-yyy_patch.sql

Included sample bypasses lengthy process to populate document name using a sql UPDATE if admin is willing accept possible issues.

Please post questions in its own thread. Optionally refer to this post.

In case you are frustrated by infamous Site ID message, change main.php and reminder counters, another annoying background task that runs with hard-coded frequency.

Neither the underlying scripts or this fix belong in a good package. But here it goes.

1 Like

Added LOINC import tool that can be run in cli mode or setup as a cronjob to load LOINC codes in codes table.

@mdsupport Can you create a video on how to use this LOINC tool? And post it here.
It would help a whole lot.

Not much to display in video so here is basic recipe -

  1. Add mdsupport/mdpub repo as a dependency and let composer bring in command-line utilities from WordPress repo.
  2. Add a JSON column to codes table using codes_update.sql.
  3. Use your account to download latest codes from loinc.org.
  4. Add LOINC as code_type in EMR.
  5. Run LOINC import tool in console with --help to see expected parameters with defaults.

php /var/www/html/emr7d/vendor/mdsupport/mdpub/oemods/cli/import_loinc.php --help

Options
–zipIn, -z Set the staged Loinc package archive - Loinc_.zip [default: /home/emrsys/Loinc_2.75.zip]
–site, -s Set the EMR site [default: default]

  1. Import the downloaded codes by overriding the default filename used by -z option

php /var/www/html/emr7d/vendor/mdsupport/mdpub/oemods/cli/import_loinc.php -z /data/emr/Loinc_2.76.zip

This will bring in a zillion (well 101633 to be exact for version 2.76) LOINC codes used for various lab tests as well as other FHIR forms / panels.

3 Likes