RingCentral Fax Gateway
Preface
This is a basic installation of an encrypted secure delivery to the RingCentral fax server for use with OSCAR.
Document Version History
- v1.0 – initial public release to worldemr.org with python – Dec 20, 2023
Copyright © 2023 by Peter Hutten-Czapski MD under the Creative Commons Attribution-Share Alike 3.0 Unported License.
Portions © 2023 by Tom Lee and other sources as indicated
Errors corrected and updated by Adrian Starzynski 2023 and 2024
Preamble
OSCAR drops files to a directory identified in oscar.properties as fax_file_location= . Failing that it uses the system property for java io tmp directory which usually resolves to the tmp directory to enable faxing. The three types are a text file with the fax number(s), the corresponding pdf and an optional signature.jpg. The .txt filename is one of the following patterns
- Prescription:prescription_<prescription_provider_no><serial>.txt
- eForm:EForm-<eform_data.fdid>.<serial>.txt
- Consult request form:CRF-<consultationRequests.requestId>.0.<serial>.txt
An Internet Fax Gateway is a commercial subscription service that allows for conversion of email to fax and vice versa, fax to email. The main advantage of this service over using a method to fax locally is that the phone line and the modem are provided. The costs are the need for internet connectivity and the gateway subscription cost.
Obtain a RingCentral account with the healthcare provider OSCAR EMR discount by contacting worldemr here.
The Bash Script
Use your favorite text editor and load the following into /usr/share/oscar-emr/RCFax_gateway.sh
#!/bin/bash
#
# Fax Gateway cron
# Picks up the files dropped by OSCAR for faxing
# and uploads them to Ring Central using the RCfax.py script
#
ARCH=/root/allfax_backup/
FAX_TEMP_FOLDER=/usr/share/oscar-emr/OscarDocument/fax
# Uncommented the FAX_TEMP_FOLDER and change it to correct path if neccessary
if [ -f /usr/share/tomcat9/bin/version.sh ] ; then
TOMCAT=tomcat9
#FAX_TEMP_FOLDER=
fi
if [ -f /usr/share/tomcat8/bin/version.sh ] ; then
TOMCAT=tomcat8
#FAX_TEMP_FOLDER=
fi
if [ -f $FAX_TEMP_FOLDER/faxlock.pid ] ; then
echo "Fax in progress --- EXIT"
exit;
fi
if [ ! -f $FAX_TEMP_FOLDER/prescription_*.pdf ] && [ ! -f $FAX_TEMP_FOLDER/CRF-*.pdf ] ; then
echo `date` " : NOTHING to FAX OUT"
exit;
fi
if test -n "$(find $FAX_TEMP_FOLDER -maxdepth 1 -name '*.txt' -print -quit)"; then
touch $FAX_TEMP_FOLDER/faxlock.pid
for f in `ls $FAX_TEMP_FOLDER/*.txt`; do
echo `date` " : START SENDING FAX"
t=`echo $f | sed -e s"/[._][0-9]*.txt//" -e s"/prescription_/Rx-/"`
#t=`echo $f | sed -e s"/\/tmp\/${TOMCAT}-${TOMCAT}-tmp\///" -e s"/prescription_/Rx-/" -e s"/[0-9]\{13\}\.txt//"`
/usr/local/bin/RCfax.py -s "Oscar Fax $t" -n `sed s"/ *//g" $f|tr -d "\n"` -f `echo $f | sed s"/txt/pdf/"` -b "$ARCH" < /dev/null
echo `date` " : END SENDING FAX"
echo ""
done
fi
if [ -f $FAX_TEMP_FOLDER/faxlock.pid ] ; then
rm $FAX_TEMP_FOLDER/faxlock.pid
fi
if test -n "$(find $FAX_TEMP_FOLDER -maxdepth 1 -name 'form*' -print -quit)"; then
rm $FAX_TEMP_FOLDER/form*
fi
if test -n "$(find $FAX_TEMP_FOLDER -maxdepth 1 -name 'sign*' -print -quit)"; then
rm $FAX_TEMP_FOLDER/sign*
fi
if test -n "$(find $FAX_TEMP_FOLDER -maxdepth 1 -name 'lab*' -print -quit)"; then
rm $FAX_TEMP_FOLDER/lab*
fi
if test -n "$(find $FAX_TEMP_FOLDER -maxdepth 1 -name '*.tmp' -print -quit)"; then
rm $FAX_TEMP_FOLDER/*.tmp
fi
if test -n "$(find $FAX_TEMP_FOLDER -maxdepth 1 -name 'pdfser*' -print -quit)"; then
rm $FAX_TEMP_FOLDER/pdfser*
fi
Of course you will need to provide an appropriate entry for $FAX_TEMP_FOLDER.
Note: 1) You may need to add a ‘;’ before the ‘then’, depending on your version of bash. 2) The script deletes the .txt and .pdf files in the /tmp folder, but the signature.jpg will remain. These get deleted when the server is restarted.
Now make it executable
chmod 710 RCFax_gateway.sh
Now a cron job
And set it up as a cron job (you will need to run it as root or the tomcat user to open the files that are dropped by Oscar into the tmp directory.) The following example has the root user sending the files
sudo crontab -e
And add an entry like the following that executes every 2 minutes
*/2 * * * * /usr/share/oscar-emr/RCFax_gateway.sh> /dev/null
Now install the Python Script
Install the dependencies
You should first install Python 3.11 then the pip package manager, and RingCentral SDK
sudo apt install python3-pip
pip3 install ringcentral
A python script should be installed in /usr/local/bin/RCfax.py. You need to have Python 3 to run it and to provide credentials for your RingCentral account for SERVER_URL = CALLER = CLIENT_ID CLIENT_SECRET and JWT_TOKEN
#!/usr/bin/env python3
# https://developers.ringcentral.com/guide/messaging/fax/sending-faxes#python
# extended by Tom Le
import re
import json
import os.path
import base64
import logging
import shutil, os
import sys, getopt
import time
from ringcentral import SDK
#****************************
# Tom Le added the following to handle fax info., eg. fax #, filename to be fax,
#****************************
# get command line args
myopts, args = getopt.getopt(sys.argv[1:],"n:f:s:b:")
#*******
# o == option
# a == argument passed to the o
#*******
# process the options
for o, a in myopts:
if o == '-n':
fax_no=a
elif o == '-f':
fax_file=a
elif o == '-s':
cover_subject=a
elif o == '-b':
archDir=a
else:
print("Usage: %s -n faxnumber -f file -s subject -b archDir" % sys.argv[0])
sys.exit
print (" ")
print("* Backup Dir. is : %s " % (archDir))
print("* Fax file is : %s " % (fax_file))
print ("* Fax number is : %s" % fax_no)
# Tom Le added this to get the fax number and pdf files to ready for archive
temp_filename = str(os.path.splitext(fax_file)[0])
print ("* Fax number filename is : %s" % temp_filename+".txt")
print ("* Fax PDF filename is : %s" % temp_filename+".pdf")
print (" ")
# should be the number in the filename in temp fax directory with .txt extension
RECIPIENT = fax_no
# should be the PDF filename that in temp fax directory with .pdf extension
FILE_TO_FAX = fax_file
#****************************
#****************************
# 1) uncomment the correct fax SERVER_URL
# 2) need to provide data for all variables in CAPITAL
#****************************
# For TESTING
#SERVER_URL = "https://platform.devtest.ringcentral.com"
#
# For PRODUCTION
#SERVER_URL = "https://platform.ringcentral.com"
#
CALLER = ""
CLIENT_ID = ""
CLIENT_SECRET = ""
JWT_TOKEN = ""
#****************************
# Send a high resolution fax message to a recipient number
def send_fax():
try:
builder = rcsdk.create_multipart_builder()
builder.set_body({
'to': [{ 'phoneNumber': RECIPIENT }],
# To send fax to multiple recipients, add more 'phoneNumber' object. E.g.
#
# to: [
# { 'phoneNumber': "Recipient1-Phone-Number" },
# { 'phoneNumber': "Recipient2-Phone-Number" }
# ],
'faxResolution': "High",
'coverPageText': ""
})
#with open('test.jpg', "rb") as f: #ORIGINAL CODE
with open(FILE_TO_FAX, "rb") as f:
content = f.read()
#attachment = ('test.jpg', content)
attachment = (FILE_TO_FAX, content)
builder.add(attachment)
request = builder.request('/restapi/v1.0/account/~/extension/~/fax')
resp = platform.send_request(request)
jsonObj = resp.json()
print ("Fax sent. Message id: " + str(jsonObj.id))
check_fax_message_status(jsonObj.id)
except Exception as e:
print (e)
# Check the sending message status until it's no longer in the queued status
def check_fax_message_status(messageId):
try:
endpoint = "/restapi/v1.0/account/~/extension/~/message-store/" + str(messageId)
resp = platform.get(endpoint)
jsonObj = resp.json()
print ("Message status: " + jsonObj.messageStatus)
if jsonObj.messageStatus == "Queued":
time.sleep(30)
check_fax_message_status(jsonObj.id)
#****************************
# Tom Le added this to move the fax to archive if fax successfully submitted,
# eg. got a messageStatus as Sent from RC
#****************************
print (" ")
if jsonObj.messageStatus == "Sent":
print (" ")
dst_txt_filename = archDir + os.path.basename(temp_filename)+".txt"
dst_pdf_filename = archDir + os.path.basename(temp_filename)+".pdf"
shutil.move(temp_filename+".txt", dst_txt_filename)
outputStr1 = "- Moved fax_no File : " + temp_filename + ".txt TO " + dst_txt_filename
print (outputStr1)
shutil.move(temp_filename+".pdf", dst_pdf_filename)
outputStr2 = "- Moved FAXED file : " + temp_filename + ".pdf TO " + dst_pdf_filename
print (outputStr2)
#****************************
except Exception as e:
print (e)
# Authenticate a user using a personal JWT token
def login():
try:
#platform.login( jwt=os.environ.get('RC_JWT') ) #ORIGINAL CODE
platform.login( jwt= JWT_TOKEN )
send_fax()
except Exception as e:
print ("Unable to authenticate to platform. Check credentials." + str(e))
##########
# Instantiate the SDK and get the platform instance
##########
rcsdk = SDK(CLIENT_ID, CLIENT_SECRET, SERVER_URL)
platform = rcsdk.platform()
login()
##########