Announcement

Collapse
No announcement yet.

Mirth Tools: User defined functions

Collapse
This is a sticky topic.
X
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #31
    Using results from xmlToHL7 function

    The xmlToHL7 function is really helpful, but I had some problems using it with javascript string functions until I added a null string:

    var deSerializedLocation = xmlToHL7(msg.PV1['PV1.3'])+'';

    After which I could use it as a javastring variable.

    Comment


    • #32
      Am I right that user defined functions are not available for Deploy, Preprocessor, Postprocessor and Undeploy scripts in 3.1?

      Comment


      • #33
        Originally posted by Shamil View Post
        Am I right that user defined functions are not available for Deploy, Preprocessor, Postprocessor and Undeploy scripts in 3.1?
        They are. Make sure your context is set appropriately. Set it to Global if you're not sure.
        Step 1: JAVA CACHE...DID YOU CLEAR ...wait, ding dong the witch is dead?

        Nicholas Rupley
        Work: 949-237-6069
        Always include what Mirth Connect version you're working with. Also include (if applicable) the code you're using and full stacktraces for errors (use CODE tags). Posting your entire channel is helpful as well; make sure to scrub any PHI/passwords first.


        - How do I foo?
        - You just bar.

        Comment


        • #34
          Unescape \Xdd..\ HL7 Escape Sequences

          UPDATE: I've created a public GitHub repository to track these example channels, code templates, scripts, or whatever else!

          https://github.com/nextgenhealthcare/connect-examples

          To start with I only added the ones I wrote, because I didn't want to presume and add code from others without their explicit permission. Pull requests welcome!
          This has been asked before, and recently again, so I just created a template for it.

          unescapeXSequences: Recursively iterates through all descendant nodes of an E4X XML object and replaces any \Xdd..\ escape sequences with the corresponding character(s). The given charset will be used for string conversion (US-ASCII if not specified). Any encoding characters (e.g. "|^~\&") detected in the replacement will instead be replaced with the appropriate escape sequence (e.g. "\F\").

          Parameters:
          • node: The node to recursively iterate through.
          • charset: The character encoding set to use when converting bytes to Unicode code points. If not specified, US-ASCII will be used.
          • encodingCharacters: The HL7 encoding characters to exclude from the replacement. For example if the sequence \X7C\ is encountered, then it will be replaced with "\F\" instead of a literal "|" character. If not specified, the function will attempt to infer the characters from the MSH.1 and MSH.2 fields, or default to "|^~\&".


          Examples:
          • Replace all X escape sequences, using the default charset and inferring encoding characters:
            Code:
            unescapeXSequences(msg);
          • Replace all X escape sequences using a specific charset:
            Code:
            unescapeXSequences(msg, 'UTF-8');


          The code:

          Code:
          function unescapeXSequences(node, charset, encodingCharacters) {
          	if (!charset) {
          		charset = 'US-ASCII';
          	}
          
          	if (!encodingCharacters) {
          		encodingCharacters = node.MSH['MSH.1'].toString().split('').concat(node.MSH['MSH.2'].toString().split(''));
          		if (encodingCharacters.length == 0) {
          			encodingCharacters = ['|', '^', '~', '\\', '&'];
          		}
          	}
          
          	for each (child in node.children()) {
          		if (child.hasComplexContent()) {
          			unescapeXSequences(child, charset, encodingCharacters);
          		} else if (!/MSH\.[12]/.test(child.name())) {
          			var childString = child.toString();
          			var matched = false;
          
          			for each (match in childString.match(/\\X([\dA-F]{2})+\\/ig)) {
          				matched = true;
          				var hexString = match.substr(2, match.length-3);
          				var buffer = java.nio.ByteBuffer.allocate(hexString.length / 2);
          
          				while (hexString.length > 0) {
          					var intValue = new java.lang.Integer((java.lang.Character.digit(hexString.substr(0,1), 16) << 4) + java.lang.Character.digit(hexString.substr(1,1), 16));
          					buffer.put(intValue.byteValue());
          					hexString = hexString.substr(2);
          				}
          
          				childString = childString.replace(match, new java.lang.String(buffer.array(), charset));
          			}
          
          			if (matched) {
          				if (encodingCharacters.length > 3) {
          					childString = childString.replace(encodingCharacters[3], '\\E\\');
          				}
          				if (encodingCharacters.length > 0) {
          					childString = childString.replace(encodingCharacters[0], '\\F\\');
          				}
          				if (encodingCharacters.length > 1) {
          					childString = childString.replace(encodingCharacters[1], '\\S\\');
          				}
          				if (encodingCharacters.length > 2) {
          					childString = childString.replace(encodingCharacters[2], '\\R\\');
          				}
          				if (encodingCharacters.length > 4) {
          					childString = childString.replace(encodingCharacters[4], '\\T\\');
          				}
          
          				node.children()[child.childIndex()] = <{child.name()}>{childString}</{child.name()}>;
          			}
          		}
          	}
          }
          Attached Files
          Last edited by narupley; 06-08-2018, 10:36 AM.
          Step 1: JAVA CACHE...DID YOU CLEAR ...wait, ding dong the witch is dead?

          Nicholas Rupley
          Work: 949-237-6069
          Always include what Mirth Connect version you're working with. Also include (if applicable) the code you're using and full stacktraces for errors (use CODE tags). Posting your entire channel is helpful as well; make sure to scrub any PHI/passwords first.


          - How do I foo?
          - You just bar.

          Comment


          • #35
            Using your template began to give this error
            TypeError: Cannot read property "PID" from undefined

            Comment


            • #36
              Encrypt PDF files with password

              UPDATE: I've created a public GitHub repository to track these example channels, code templates, scripts, or whatever else!

              https://github.com/nextgenhealthcare/connect-examples

              To start with I only added the ones I wrote, because I didn't want to presume and add code from others without their explicit permission. Pull requests welcome!
              In response to a recent thread:

              encryptPDF: Encrypts a PDF with a password. Either a Base64 string or by array can be passed in, and the output can be written to a file or returned as a Base64 string.

              Parameters:
              • pdfBase64OrBytes: The PDF data to encrypt. May be a Base64 string, or a byte array.
                password: The password to use for encryption.
                outputFilename: (Optional) The filename to output the encrypted PDF to. If omitted, the data will be returned from the function as a Base64 string.


              Examples:
              • Take a Base64 encoded PDF in OBX.5.5, encrypt it, and put it back into the OBX:
                Code:
                msg['OBX']['OBX.5']['OBX.5.5'] = encryptPDF(msg['OBX']['OBX.5']['OBX.5.5'].toString(), 'password');


              The code:
              Code:
              function encryptPDF(pdfBase64OrBytes, password, outputFilename) {
              	var document;
              	var fos;
              
              	try {
              		var pdfBytes;
              		if (pdfBase64OrBytes instanceof Array) {
              			pdfBytes = pdfBase64OrBytes;
              		} else {
              			pdfBytes = FileUtil.decode(pdfBase64OrBytes);
              		}
              
              		var inputStream = new java.io.ByteArrayInputStream(pdfBytes);
              		document = org.apache.pdfbox.pdmodel.PDDocument.load(inputStream);
              
              		var accessPermission = new org.apache.pdfbox.pdmodel.encryption.AccessPermission();
              		accessPermission.setCanAssembleDocument(false);
              		accessPermission.setCanExtractContent(true);
              		accessPermission.setCanExtractForAccessibility(false);
              		accessPermission.setCanFillInForm(false);
              		accessPermission.setCanModify(false);
              		accessPermission.setCanModifyAnnotations(false);
              		accessPermission.setCanPrint(true);
              		accessPermission.setCanPrintDegraded(true);
              
              		var policy = new org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy(UUIDGenerator.getUUID(), password, accessPermission);
              		policy.setEncryptionKeyLength(128);
              		document.protect(policy);
              
              		if (outputFilename) {
              			fos = new java.io.FileOutputStream(new java.io.File(outputFilename));
              			document.save(fos);
              		} else {
              			var baos = new java.io.ByteArrayOutputStream();
              			document.save(baos);
              			return FileUtil.encode(baos.toByteArray());
              		}
              	} finally {
              		if (document) {
              			document.close();
              		}
              		if (fos) {
              			fos.close();
              		}
              	}
              }
              Attached Files
              Last edited by narupley; 06-08-2018, 10:36 AM.
              Step 1: JAVA CACHE...DID YOU CLEAR ...wait, ding dong the witch is dead?

              Nicholas Rupley
              Work: 949-237-6069
              Always include what Mirth Connect version you're working with. Also include (if applicable) the code you're using and full stacktraces for errors (use CODE tags). Posting your entire channel is helpful as well; make sure to scrub any PHI/passwords first.


              - How do I foo?
              - You just bar.

              Comment


              • #37
                Execute Runtime Command

                UPDATE: I've created a public GitHub repository to track these example channels, code templates, scripts, or whatever else!

                https://github.com/nextgenhealthcare/connect-examples

                To start with I only added the ones I wrote, because I didn't want to presume and add code from others without their explicit permission. Pull requests welcome!
                This is something that crops up from time to time. Some of you may know (or can find out via searching) that to execute an arbitrary command on your local OS shell, you can use java.lang.Runtime. However, a lot of people are confused about the correct way to use it. Hopefully this code template will help out.

                executeRuntimeCommand: Executes a command or an array of the command and arguments using a local OS shell. Returns an object containing the exit value, output, and errors.

                Parameters:
                • args: This can be either a string, or an array of strings. If you pass in a single string, it treats it as the entire command string, like "ls -l". If you pass in an array, it treats the first element as the command, and each subsequent element as parameters for the command. The array method is preferred, because you don't have to worry about quoting parameters that have spaces in them.
                • charset: (Optional) The charset encoding to use when reading the output from the command. If omitted, the JVM default charset will be used.


                Examples:
                • Execute a single command string (in this case, tailing system messages on Linux), and outputs results:
                  Code:
                  var result = executeRuntimeCommand('tail /var/log/messages');
                  
                  logger.info('Exit value: ' + result.exitValue);
                  logger.info('Output: ' + result.stdout);
                  if (result.stderr != '') {
                  	logger.error('Errors: ' + result.stderr);
                  }
                • Same thing, but this time using an array:
                  Code:
                  var result = executeRuntimeCommand(['tail', '/var/log/messages']);
                  
                  logger.info('Exit value: ' + result.exitValue);
                  logger.info('Output: ' + result.stdout);
                  if (result.stderr != '') {
                  	logger.error('Errors: ' + result.stderr);
                  }


                The code:

                Code:
                function executeRuntimeCommand(args, charset) {
                	var process = java.lang.Runtime.getRuntime().exec(args);
                	var stdoutConsumer = new StreamConsumer(process.getInputStream(), charset);
                	var stderrConsumer = new StreamConsumer(process.getErrorStream(), charset);
                	return {
                		exitValue : process.waitFor(),
                		stdout : stdoutConsumer.getOutput(),
                		stderr : stderrConsumer.getOutput()
                	};
                }
                
                function StreamConsumer(is, charset) {
                	var output = '';
                	
                	var thread = new java.lang.Thread({
                		run: function() {
                			if (typeof charset !== 'undefined') {
                				output = org.apache.commons.io.IOUtils.toString(is, charset);
                			} else {
                				output = org.apache.commons.io.IOUtils.toString(is);
                			}
                		}
                	});
                
                	this.interrupt = function() {
                		thread.interrupt();
                	}
                
                	this.getOutput = function() {
                		thread.join();
                		return output;
                	};
                
                	thread.start();
                }
                Attached Files
                Last edited by narupley; 06-08-2018, 10:36 AM.
                Step 1: JAVA CACHE...DID YOU CLEAR ...wait, ding dong the witch is dead?

                Nicholas Rupley
                Work: 949-237-6069
                Always include what Mirth Connect version you're working with. Also include (if applicable) the code you're using and full stacktraces for errors (use CODE tags). Posting your entire channel is helpful as well; make sure to scrub any PHI/passwords first.


                - How do I foo?
                - You just bar.

                Comment


                • #38
                  replaceValue

                  replaceValue: Replace an expected received value with a new value

                  This came in handy when having to replace hospital local codes with standard codes for the HIE. It just keeps the transformer a bit cleaner. It checks that the segment and field exist and if the value1 parameter exists, it will replace it with the value2 parameter.

                  Parameters:
                  segName - Segment to modify
                  field - Field in segment to modify
                  subComponent - Sub component within field to modify
                  value1 - receiving value
                  value2 - replacement value

                  Examples:
                  Code:
                  replaceValue('PID','PID.8','PID.8.1','Female','F');
                  replaceValue('PID','PID.8','PID.8.1','M','Male');
                  Code:
                  Code:
                  function replaceValue(segName,field,subComponent,value1,value2) {
                  	for each(seg in msg.children())
                  	{
                  		if(seg.name().toString() == segName)
                  		{
                  			var segField = seg[field][subComponent].toString();
                  			if(segField.length > -1)
                  			{
                  				if(seg[field][subComponent].toString() == value1)
                  				{
                  					seg[field][subComponent] = value2;
                  				}
                  			}
                  		}
                  	}
                  }

                  Comment


                  • #39
                    JavaScript Pooled Database Connections (and queries)

                    The code template "getPoolDbConn" creates a pool of 20 database connections using a very simple call: getPoolDbConn(DatabaseType, DatabaseName, ServerAddress, UserName, Password);
                    where
                    DatabaseType: is one of "mysql", "oracle", "postgres(ql)" or "sqlserver" (case insensitive)
                    DatabaseName: the name of the database you are connecting to, e.g. "mirthresults", "my_staging_db"
                    The rest is self-explanatory
                    The function returns an object of type java.sql.Connection

                    The code template "queryPoolDbConn" runs a database query using "getPoolDbConn()". It takes the same arguments as getPoolDbConn plus two: sql_string and args.
                    sql_string is any valid SQL select statement that may use ? in place of arguments.
                    args is an array of Java objects to be used with the query, most common ones are java.lang.String and java.lang.Long.
                    Example:
                    var sql = "select * from subject where subject_key = ?";
                    var args = [new java.lang.Long(12345)];
                    var db_res = queryPoolDbConn("postgres", "mirthresults", "localhost", "mirthuser", "secretpassword", sql, args);
                    If no arguments are to be specified pass a null instead of args.
                    The function returns a CachedResultSet that can be used directly (returned) by Database Reader or it can be iterated over in custom code.

                    The code template "updatePoolDbConn" executes an update or insert SQL statement using "getPoolDbConn()" and returns the number of affected rows.
                    Its syntax is identical to "queryPoolDbConn()":
                    var sql = "update staging_table set processed = 1 where key = ?;";
                    var args = [new java.lang.Long($('staging_key'))];
                    var row_count = updatePoolDbConn("mysql", "stagingdb", "localhost", "staginguser", "secretpassword", sql, args);
                    Attached Files

                    Comment


                    • #40
                      What about attributes

                      Hi, is there a simple method in e4x to check for the xexistence of attributes in a node. IE a countAttr() or something, without having to know what the attribute names are the XML schema I have to work with has child attributes in elements where there is no data. So using the strip function removed the element even though there is a populated attribute.

                      I know this goes against what I understood element/attribute to be about but the schema is not mine so I can't change it.

                      I haven't found a suitable method or property as yet in the docs. If anyone know such a thing off the top of their head, I'd be very grateful.

                      Thanks in advance

                      Stu.

                      Comment


                      • #41
                        Attributes?

                        Hi, is there a simple method in e4x to check for the xexistence of attributes in a node. IE a countAttr() or something, without having to know what the attribute names are the XML schema I have to work with has child attributes in elements where there is no data. So using the strip function removed the element even though there is a populated attribute.

                        I know this goes against what I understood element/attribute to be about but the schema is not mine so I can't change it.

                        I haven't found a suitable method or property as yet in the docs. If anyone know such a thing off the top of their head, I'd be very grateful.

                        Thanks in advance

                        Stu.

                        Comment


                        • #42
                          I think I have it

                          I modified the original strip function after finding method/props to count the number of attributes.

                          I have attached the updated code template.

                          Comments/Improvement/Suggestions invited - flame shields up
                          Attached Files

                          Comment


                          • #43
                            Submitting payload as multi-part form data

                            Here is my addition to this thread

                            Submitting payload as multi-part form data

                            I struggled for like 3 days straight to submit payload to a web-service as multi-part form data. Now, we did not had an HTTPS Sender with us, so I had to develop everything in JS, as a method inside code templates.

                            The concept behind multi-part form-data is that it has 2 or may be more different types of form-data, for eg. one can be JSON, and other XML or plain inside the same payload.


                            Code:
                                var url = new java.net.URL(URLstring);
                                var boundary = '------WebKitFormBoundary';
                                var dashdash = '--';
                                var crlf = '\r\n';
                                var prebody = {};  //part 1 JSON
                                //Define your JSON below
                                var addthisbody = JSON.stringify(prebody);
                            
                                //multipart/form-data builder. sensitive, and DO NOT MODIFY.
                                var content = boundary + crlf + 'Content-Type: application/json; charset=utf-8' + crlf + 'Content-Disposition: form-data' + crlf + crlf + addthisbody + crlf + crlf + crlf + boundary;
                                content = content + crlf + 'Content-Disposition: form-data; name="AutomationTest.xml"; filename="' + tag + '"' + crlf + 'Content-Type: text/xml' + crlf + crlf + ccdcontent + crlf + boundary + dashdash;
                                content = content + '\0';
                            
                               //Set Your individual headers per requirement.
                                conn.setDoOutput(true);
                                conn.setDoInput(true);
                                conn.setRequestMethod("POST");
                                conn.setRequestProperty("Authorization", "Bearer " + access_token); 
                                conn.setRequestProperty("Content-type", "multipart/form-data; boundary=----WebKitFormBoundary");    //this step is important
                                conn.setRequestProperty("Content-length", content.length);
                            
                                // Send request
                                var outStream = conn.getOutputStream();
                                var outWriter = new java.io.OutputStreamWriter(outStream);
                                outWriter.write(content);
                                outWriter.close();
                            
                            //Capture response appropriately
                            Everything will go as a string inside form-data!
                            Last edited by siddharth; 03-15-2016, 12:31 AM. Reason: forgot title
                            HL7v2.7 Certified Control Specialist!

                            Comment


                            • #44
                              This is my contribution

                              These scripts were developed on version 3.0.3.7171, but should carry forward with minimal adjustments.

                              Code:
                              /* 
                              Local Auto-Failover Channel Monitor 
                              Javascript reader source transformer
                              Polling type: Interval @ frequency: 5000 
                              **(Polling frequency should be no greater than 7000. 5000 seems to work best for normal circumstances.)
                              
                              Monitors the status of two local channels:
                              - "Fail_Test_Channel" (enabled, deployed and running) (Primary)
                              - "Fail_Test_Channel_failover" (enabled, undeployed) (Secondary)
                              When the script detects a channel state other than started or starting for the primary channel, it will auto deploy the secondary and fire off an email notifying the recipient.
                              Once a starting state is detected on the primary again, the secondary channel will be auto-undeployed.
                              */
                              
                              var channel = "Fail_Test_Channel";
                              var channel_failover = channel+"_failover";
                              
                              function monitor(channel){
                              	if(ChannelUtil.getChannelState(channel)=="Starting"){
                              		logger.info("Primary Channel: "+channel+" starting. Undeploying failover channel.");
                              		ChannelUtil.undeployChannel(channel_failover);
                              		}
                              	else if(ChannelUtil.getChannelState(channel)!="Started"){
                              		if(!ChannelUtil.isChannelDeployed(channel_failover)){
                              			var datetimestamp = DateUtil.getCurrentDate("[email protected]:mm:ss");
                              			logger.info("WARNING! CHANNEL FAILURE! Channel: "+channel+" Recorded @ "+datetimestamp);
                              			ChannelUtil.deployChannel(channel_failover);
                              			logger.info("Deploying Failover Channel");
                              			var smtpConn = SMTPConnectionFactory.createSMTPConnection();
                              			var emNotice = channel+" has stopped responding and "+channel_failover+" is being deployed.\[email protected]"+datetimestamp;
                              			var emSub = "Channel Failure: "+channel+" @"+datetimestamp;
                              			smtpConn.send('[email protected]', '', '[email protected]', emSub, emNotice, 'US-ASCII');
                              			}
                              		else{
                              			if(ChannelUtil.getChannelState(channel_failover)=="Started"){
                              				}
                              			else{
                              				ChannelUtil.startChannel(channel_failover);
                              				}
                              			}
                              		}
                              	return;
                              	}
                              
                              monitor(channel);
                              Code:
                              /*
                              This script is mostly a proof of concept for a remote version of the above script. 
                              Remote Auto-Failover Channel Monitor 
                              Javascript reader source transformer
                              Polling type: Interval @ frequency: 5000 
                              **(Polling frequency should be no greater than 7000. 5000 seems to work best for normal circumstances.)
                              
                              This script monitors two channels:
                              - "Remote_Failover_Test" (remote server, deployed, running) (Primary)
                              - "Remote_Failover_Test_failover" (local server, enabled, undeployed) (Secondary)
                              
                              When the script detects a channel state other than started or starting for the primary channel, it will auto deploy the secondary.
                              Once a starting state is detected on the primary again, the secondary channel will be auto-undeployed.
                              */
                              var info = {
                              	URL: 'https://localhost:8443',
                              	user: 'admin',
                              	pass: 'admin',
                              	version: '3.0.3.7171',
                              	timeout: 10000
                              	};
                              
                              function monitor(channel,state){
                              var channel_failover = channel+"_failover"
                              	if(state=="Starting"){
                              		logger.info("Primary Channel: "+channel+" starting. Undeploying failover channel.");
                              		ChannelUtil.undeployChannel(channel_failover);
                              		}
                              	else if(state!="Started"){
                              		if(!ChannelUtil.isChannelDeployed(channel_failover)){
                              			logger.info("WARNING! CHANNEL FAILURE! Channel: "+channel+" Recorded @ "+DateUtil.getCurrentDate("[email protected]:mm:ss"));
                              			ChannelUtil.deployChannel(channel_failover);
                              			logger.info("Deploying Failover Channel");
                              			}
                              		else{
                              			if(ChannelUtil.getChannelState(channel_failover)=="Started"){
                              				}
                              			else{
                              				ChannelUtil.startChannel(channel_failover);
                              				}
                              			}
                              		}
                              	return;
                              	}
                              
                              var monitor_list = [];
                              monitor_list[0] = "Remote_Failover_Test";
                              var monitorLog = {"Remote_Failover_Test" : "FAILED"};
                              var client = new com.mirth.connect.client.core.Client(info.URL,info.timeout);
                              var loginStatus = client.login(info.user,info.pass,info.version);
                              var status = client.getChannelStatusList().toString().split("channelId=");
                              var statLen = status.length-1;
                              var subStatus;
                              var chaName;
                              var chaState;
                              
                              
                              for(var i=1;i<=(statLen);i++){
                              	subStatus = status[i].split(",lifetimeStatistics=")[0];
                              	if(subStatus.indexOf("deployedDate=<null>")<0){
                              		chaName = subStatus.split("name=")[1].split(",state=")[0].toString();
                              		chaState = subStatus.split(",state=")[1].split(",")[0].toString();
                              		for(var k in monitor_list){
                              			if(monitor_list[k] == chaName){
                              				monitorLog[chaName]=chaState;
                              				}
                              			}
                              		}
                              	}
                              
                              for(var k in monitorLog){
                              	//logger.info(k+" "+monitorLog[k]);
                              	monitor(k,monitorLog[k]);
                              }
                              Matt

                              http://theunofficialmirthadmin.blogspot.com/

                              Comment


                              • #45
                                Thanks for all of these contributions. This is super helpful. I bookmarked this, to take a look again in the future.

                                Keep up the good work!

                                Comment

                                Working...
                                X