Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ccda service for ccd document generation to support CCM and Onsite portal. #543

Merged
merged 19 commits into from Mar 31, 2017

Conversation

sjpadgett
Copy link
Sponsor Member

This is a background service for generating MU compliant ccda documents.
See readme.md for testing implementation.

@sjpadgett
Copy link
Sponsor Member Author

What to do! I have tested hook to calling ccda fetching from CCM for portal use using cURL but this requires modification to CCM public index where action is handled for authentication. Also the fall through for that create data function call is missing the component array in called createdata function and I can not see this as anything but an oversight, but because that is the same public call made by myportal external portal soap web api, I can't be sure I'm not missing some setup soap controller does or it is not currently being used.
I'm not too experienced on Zend but perhaps I may be able to import the classes directly and call the ccda fetching, looking into. I suppose my point here would be CCM will need to be touched, otherwise, I have to fall back to re wrapped classes I created for portal a few months back so I could modify fetching to get missing patient chart data not supplied by CCM. This means maintaining two libraries for same functionality.

@bradymiller
Copy link
Sponsor Member

@sjpadgett
The offsite portal also required a work around to create the ccdas. Is this helpful:
a700e47

@sjpadgett
Copy link
Sponsor Member Author

sjpadgett commented Mar 16, 2017 via email

@bradymiller
Copy link
Sponsor Member

Sounds good. If we do decide that we need to improve things in the CCDA creation(which sounds like it will be a good thing to do), then will likely make sense at that point to "duplicate" the code (so MU2 stuff isn't touched); but can do that after the mechanism is working well.

@bradymiller
Copy link
Sponsor Member

@sjpadgett , Just let me know if you want me to start testing this out. -brady

@sjpadgett
Copy link
Sponsor Member Author

sjpadgett commented Mar 18, 2017 via email

…n. I put up if anyone happens to be following.
@bradymiller
Copy link
Sponsor Member

Hi @sjpadgett ,
Just did some quick testing and was able to produce ccda's in both OpenEMR and portal. Used the Background service mechanism. Very cool! (in the portal I have clicked on the CCDA Testing->Return Home buttons numerous times in awe)
Note my testing was quick sanity check. Planning to do more testing.
Regarding the document service, I assume you mean the winservice node thing(that fact that I am calling it a thing means I am still not confident I know what it is and looking forward to learning more about it; for example, looks like there is a FHIR library for the thing also). Initial thought is to keep it app dependent rather than apache dependent.
-brady

…authentication for portal and main. Going to move to ccdaservice directory for use elsewhere soon.

* Service changes seem radical but merely reorg of function ordering.
*  Also did some work in template for empty encounter history array blowing up(exception in template populate). Not sure encounter history should come from Billing table but, where else?
@sjpadgett
Copy link
Sponsor Member Author

Don't know what I want to do next. Starting to get bored with.

@bradymiller
Copy link
Sponsor Member

@sjpadgett ,

Sounds like we are nearing point to bring this into the codebase. Recommend placing the sql backround service code in database.sql and the upgrade script and setting it inactive by default. And then making a setting in globals to turn it on/off (rec looking at the Direct service code to see how they did this and could just go along for the ride). Then sounds like ready to bring this in.

And if still bored, there is fhir :)
https://www.npmjs.com/browse/keyword/fhir
lots of options, maybe https://www.npmjs.com/package/fhir

@@ -0,0 +1,49 @@
Copyright 2017 sjpadgett@gmail.com
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mention gpl license

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or whatever gpl compatible license you prefer

@@ -0,0 +1,1519 @@
"use strict";
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a standard author/copyright/license header

@@ -0,0 +1,66 @@
<?php
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a standard author/copyright/license header

@@ -0,0 +1,66 @@
<?php
$ignoreAuth = true;
// require_once(dirname(__file__) . './../interface/globals.php');
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can remove above 2 lines

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

set_time_limit( 0 );
ob_implicit_flush();
session_write_close(); // Release session lock to prevent freezing of other scripts
ignore_user_abort( 1 );
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a more strategic place for these flags (in case wish to use this library without starting session_write_close() )?

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot about them. They are hold over from my php socket server when I had them integrated together. Code it once and forget about it, that's my motto.:) They can go.

--

INSERT INTO `background_services` (`name`, `title`, `active`, `running`, `next_run`, `execute_interval`, `function`, `require_once`, `sort_order`) VALUES
('handle-ccda-service', 'C-CDA Node Service', 1, 0, '2017-03-02 06:15:58', 1, 'runCheck', '/ccdaservice/ssmanager.php', 99);
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add to database.sql and upgrade sql script. 0 for active and blank for next_run. And see below comments on having it turned on in globals. Also noted that the running flag is not being set when this process is running.

@@ -0,0 +1,1097 @@
/* Template javascript objects for generating cda.
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a standard author/copyright/license header

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testing file i'll pull it.

@@ -0,0 +1,18 @@
var isWin = /^win/.test(process.platform);
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a standard author/copyright/license header

@@ -0,0 +1,18 @@
var isWin = /^win/.test(process.platform);
if( isWin ){
var Service = require('node-windows').Service;
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried sudo node unservice and wasn't able to remove the service (it was started via background service); I can kill it with kill -9 if needed, though. Is there a way to turn off the background service within OpenEMR if, for example, make it inactive in globals?

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The winservice/unservice run as expected in windows however in Linux, the current setup is winservice starts in root and is not seen by OpenEMR running above root so it runs as a process. Prefer method would be to run in root and I keep going back and forth as to best way to handle. Thus the two methods. I haven't worried too much on how to stop it, several ways to do. Long time since I've looked at background service code but there, I assume, is where to enable feature. Remembering that if CCM and/or Portal is enabled, so must ccda service. I'll figure this out.

@@ -0,0 +1,37 @@
var isWin = /^win/.test(process.platform);
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a standard author/copyright/license header


if( isWin ){
var Service = require('node-windows').Service;
//f = '\\xampp\\htdocs\\openemr\\services\\ccdaservice\\serveccda.njs';
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, out of curiosity. For this script and the one above, if I go this in a web browser, will they run?

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not as is but could be using browserify. May be some permission issues though.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wanted to make sure they couldn't be run by a user going to them in the web browser.

@@ -43,6 +43,9 @@
if(strtolower($controllerName) == 'soap' && strtolower($actionName) == 'index') {
$ignoreAuth_offsite_portal = true;
}
if($_REQUEST['recipient'] === 'patient' && $_REQUEST['site']){
$ignoreAuth = true; //@todo figure this out - carry forward session maybe I don't trust globals
}
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely need to secure this, which you already know :)

if (!$parameterArray ['view']){
header ( 'Content-Type: application/xml' );
}
else $h='<a href="./home.php" </a><button style="font-size:18px; color:red;" >Return Home</button><br>';
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

translate

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can wait on that of course if this is still in flux

@@ -43,6 +43,9 @@
if(strtolower($controllerName) == 'soap' && strtolower($actionName) == 'index') {
$ignoreAuth_offsite_portal = true;
}
elseif($_REQUEST['recipient'] === 'patient' && $_REQUEST['site'] && $controllerName){
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there downstream credentialing authorization done here?

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added authentication through a forward of current portal session login authorizations.

@@ -210,17 +216,14 @@ function saveProfile(){
<div class="panel panel-primary collapse" id="downloadpanel">
<header class="panel-heading"> <?php echo xlt('Download Documents'); ?> </header>
<div id="docsdownload" class="panel-body">
<?php if ( $GLOBALS['portal_onsite_document_download'] ) { ?>
<?php if ( $GLOBALS['portal_onsite_document_download'] ) { ?>// really should always be able to download!
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you suggesting that patients actually should own their own medical data?(sarcasm) :)

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes I prefer to be in the dark.


#IfNotRow background_services name ccdaservice
INSERT INTO `background_services` (`name`, `title`, `execute_interval`, `function`, `require_once`, `sort_order`) VALUES ('ccdaservice', 'C-CDA Node Service', 1, 'runCheck', '/ccdaservice/ssmanager.php', 95);
ALTER TABLE `background_services` CHANGE `running` `running` TINYINT(1) NOT NULL DEFAULT '-1' COMMENT 'True indicates managed service is busy. Skip this interval.';
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the placing of this comment. Should also place it this in the database.sql table.

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@sjpadgett
Copy link
Sponsor Member Author

sjpadgett commented Mar 26, 2017 via email

@bradymiller
Copy link
Sponsor Member

Are you using curl to go into it to collect the ccda from the care coordination module? Is it standard practice to place onsite portal credentials on that call? Wouldn't it already get encrypted if the user has server set up to use https? It does look like MU3 may require OATH2 (I need to confirm this in the testing scripts), so perhaps could also leverage that here if needed. Lots of question :) (obviously I need to do some research on this stuff)

$parameterArray [0] [6] = $_SESSION ['portal_username']; // set to an onsite portal user

if(!isset($_SESSION ['site_id'])) $_SESSION ['site_id'] = 'default'; // do believe globals does this but I go rogue at times.
$server_url = 'http://localhost'. $GLOBALS['webroot']; // I alias into openemr directory on my sights causing webroot to be empty.
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to figure out if the user on the portal(or from within openemr) is using http or https along with the server name, since would be good to use https if that is being used (especially if passing credentials) and would need to work from outside localhost also (unless I am missing something; for example, is this call coming from the server itself?).

Add comment to running flag in database.sql
@sjpadgett
Copy link
Sponsor Member Author

sjpadgett commented Mar 27, 2017 via email

@sjpadgett
Copy link
Sponsor Member Author

sjpadgett commented Mar 27, 2017 via email

@sjpadgett
Copy link
Sponsor Member Author

sjpadgett commented Mar 27, 2017 via email

…unt task to get it down to 10meg.

2. Updated project readme with new install instructions.
@sjpadgett
Copy link
Sponsor Member Author

New commit. Testing of recent changes working well on Ubuntu and Windows. Scaled back dependencies for service. Did cursory FHIR api testing for api web service ( no commits for this yet ). I think, if possible, all ccda service ports and calls should be done through localhost. The only problem I see using localhost would be if a server was configured with several virtual hosts and localhost was aliased on a different virtual host than OpenEMR. I see no reason to access ccda service or ccda fetching( for my purposes ) outside of localhost but, I may be missing something.

sql/database.sql Outdated
@@ -151,7 +151,7 @@ CREATE TABLE `background_services` (
INSERT INTO `background_services` (`name`, `title`, `execute_interval`, `function`, `require_once`, `sort_order`) VALUES
('ccdaservice', 'C-CDA Node Service', 1, 'runCheck', '/ccdaservice/ssmanager.php', 95),
('phimail', 'phiMail Direct Messaging Service', 5, 'phimail_check', '/library/direct_message_check.inc', 100);

ALTER TABLE `background_services` CHANGE `running` `running` TINYINT(1) NOT NULL DEFAULT '-1' COMMENT 'True indicates managed service is busy. Skip this interval.';
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, should of been more clear. we don't do alters in the database.sql. We just go straight to the source and change the actual table.

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel silly, of course! I'm getting sloppy.

@bradymiller
Copy link
Sponsor Member

Hi @sjpadgett ,
Sessions also cause yawning on this end although they can do some cool things sometimes (ie. session_write_close). Everything you said makes sense and agree with localhost and your authentication code. Plan to test this stuff out tomorrow. Do you think it's ready to put into the codebase?
thanks and just the mere mention of cursory FHIR testing gets a big 👍 🥇 👍
-brady

@sjpadgett
Copy link
Sponsor Member Author

I think getting into code base is possible. I test all the time and am satisfied but two items need addressing beforehand.

  1. Remove menu item Ccda Testing, add to existing patient documents report.
  2. Write a perl(?) script for install. I've never actually looked at OpenEMRs install so some guidance here. Hint, Hint... :).

As soon as I get a decent testing jig setup for new api's, I'll put up and start a new pull request. You know, just so we can be cool, once FHIR api is in place we just have to write a mobile app!

@bradymiller
Copy link
Sponsor Member

whoohooo on the mobile app :)
Regarding perl, best to avoid if possible (if needed, though, can help with that). What do you need perl script to do?
-brady

Fixed database.sql comment for running flag.
@sjpadgett
Copy link
Sponsor Member Author

I was being facetious concerning perl. Seems I've written something in every other script or language lately, why not perl! It landed in my head, just didn't translate well.
I decided to hold up on integrating ccd document handling in portal until I see how FHIR interface shakes out. Seems FHIR is going to be far more involved than I originally assumed and I'm not so sure I want to take it on.
Concerning ccda service, I made available in source some object/array helper routines in case needed in further template sections implementation but, all in all, service in ready to go.

@sjpadgett sjpadgett closed this Mar 29, 2017
@sjpadgett sjpadgett reopened this Mar 29, 2017
@sjpadgett
Copy link
Sponsor Member Author

Wish they would make Close button RED!

@bradymiller
Copy link
Sponsor Member

I guess I fell for the perl statement since it's my second favorite language (after bash of course) :)
One more quick question. Would it make sense to run the command to import the dependencies into the main openemr codebase, so the user wouldn't need to do that?(or is this craziness?or maybe there is some os dependent stuff going on?)
After I have clarity on that, then I think this is ready to come into the codebas (either with or without dependencies).

@sjpadgett
Copy link
Sponsor Member Author

sjpadgett commented Mar 30, 2017 via email

@bradymiller
Copy link
Sponsor Member

Sounds like we are at a good point to bring this into the codebase since packaging issues can be improved after it's in the codebase. Do you think it's ready?(if you say so, I'll bring it in :) )

@sjpadgett
Copy link
Sponsor Member Author

sjpadgett commented Mar 31, 2017 via email

@bradymiller bradymiller merged commit 253eebe into openemr:master Mar 31, 2017
@bradymiller
Copy link
Sponsor Member

Just brought it into the codebase. thanks!! -brady

@sjpadgett
Copy link
Sponsor Member Author

Once again @bradymiller thanks for all the help. You make this process so much easier.

@sjpadgett sjpadgett deleted the ccdaservice branch April 3, 2017 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants