emnutt
Version:
part of the iHRIS family of health workforce data solutions
303 lines (291 loc) • 13.5 kB
JavaScript
/*
* config.json options:
* "rapidpro": {
* "flow_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", // flow UUID to send the message through
* "host" : "host",
* "port" : "80",
* "token" : "Token XXXXXXXXXXXXXX", // authorization token
* "testing" : "+1XXXXXXXXXX" // Phone number to use for testing if nothing else is found in the resource.
* },
*/
var http = require('http');
var convert = require('./convert');
exports.process = function( type, nconf, db, mongo, resource, callback ) {
if ( type == 'create' || type == 'update' || type == 'failed' ) {
if ( !resource.sent ) {
sendMessage( nconf, db, resource, callback );
} else {
console.log("Not sending because sent on " +resource.sent);
callback();
}
} else if ( type == 'sent' ) {
if ( resource.event ) {
if ( resource.event == 'mt_sent' || resource.event == 'mt_dlvd' ) {
findResource( resource, db, function ( communication, recipient ) {
if ( communication ) {
eventDate = new Date();
if ( resource.event == 'mt_sent' && !communication.sent ) {
communication.sent = eventDate;
}
if ( resource.event == 'mt_dlvd' && !communication.received ) {
communication.received = eventDate;
}
if ( communication.status != 'completed' ) {
communication.status = 'in-progress';
}
if ( !communication.extension ) {
communication.extension = [];
}
communication.extension.push( { url : "Communication.dissemination", extension : [
{ url : "Communication.dissemination.status", valueCodeableConcept : { coding : { code: "in-progress", system : "2.16.840.1.113883.4.642.1.79" } } },
{ url : "Communication.dissemination.timestamp", valueInstant : eventDate },
{ url : "Communication.dissemination.recipient", valueReference : recipient },
] } );
/*
if ( !communication.dissemination ) {
communication.dissemination = [];
}
communication.dissemination.push( {status : 'in-progress', timestamp : new Date(), recipient: recipient } );
*/
callback( communication );
}
});
}
}
} else if ( type == 'response' ) {
console.log("got response code");
findResourceByRun( resource.run, resource, db, function( communication, recipient ) {
if ( communication ) {
eventDate = new Date();
communication.status = 'completed';
if ( !communication.received ) {
communication.received = eventDate;
}
if ( !communication.extension ) {
communication.extension = [];
}
communication.extension.push( { url : "Communication.dissemination", extension : [
{ url : "Communication.dissemination.status", valueCodeableConcept : { coding : { code: "completed", system : "2.16.840.1.113883.4.642.1.79" } } },
{ url : "Communication.dissemination.timestamp", valueInstant : eventDate },
{ url : "Communication.dissemination.response", valueString : resource.text },
{ url : "Communication.dissemination.recipient", valueReference : recipient },
] } );
/*
if ( !communication.dissemination ) {
communication.dissemination = [];
}
communication.dissemination.push( {status : 'completed', timestamp : new Date(), response : resource.text, recipient: recipient } );
*/
callback( communication );
}
});
}
};
function sendMessage( nconf, db, resource, callback ) {
var msg;
for( i in resource.payload ) {
if ( resource.payload[i].contentString ) {
msg = resource.payload[i].contentString;
}
}
if ( !msg ) {
for( i in resource.payload ) {
if ( resource.payload[i].contentAttachment && resource.payload[i].contentAttachment.title ) {
msg = resource.payload[i].contentAttachment.title;
}
}
}
console.log("Payload is "+msg);
if ( !msg ) {
console.log("Unable to decipher message from "+resource.id);
} else {
for( i in resource.recipient ) {
//if ( resource.recipient[i].contained ) {
//var ref = resource.recipient[i].contained;
//runRapidPro(ref, msg, nconf, db, resource, callback);
//} else
if ( resource.recipient[i].reference ) {
console.log("looking for "+resource.recipient[i].reference);
if ( resource.recipient[i].reference.startsWith('#') ) {
if ( resource.contained ) {
for( j in resource.contained ) {
if ( resource.contained[j].id == resource.recipient[i].reference.substring(1) ) {
runRapidPro( resource.contained[j], msg, nconf, db, resource, callback );
break;
}
}
} else {
console.log("Recipient refers to a #, but no contained element.");
}
} else if ( /^[A-Za-z]+\/\w+/.test( resource.recipient[i].reference ) ) {
// No local lookup options, so just send through for testing...
runRapidPro({}, msg, nconf, db, resource, callback);
} else {
var req = http.get(resource.recipient[i].reference, function (res) {
var contentType = res.headers['content-type'].split(';')[0];
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
if ( contentType == 'application/json+fhir' || contentType == 'application/json' ) {
var ref = JSON.parse(body);
runRapidPro(ref, msg, nconf, db, resource, callback);
} else if ( contentType == 'application/xml+fhir' || contentType == 'application/xml' ) {
convert.format( 'xml', req.body, function( err, converted ) {
if ( err ) {
console.log("Failed to convert remote resource to JSON: "+resource.recipient[i].reference);
console.log(err);
} else {
updateCommunication( req.params.fhir_id, JSON.parse(converted), res, req );
runRapidPro(JSON.parse(converted), msg, nconf, db, resource, callback);
}
});
} else {
console.log("Invalid content type for "+resource.recipient[i].reference);
}
});
res.on('error', function(e) {
console.log("Error trying to access "+resource.recipient[i].reference);
console.log(e);
});
});
req.on('error', function(req_err) {
console.log("Got error on request to rapidpro");
console.log(req_err);
});
}
}
}
}
}
function runRapidPro( recipient, msg, nconf, db, resource, callback ) {
var rp_map = db.collection("contact_communication");
var phone;
if ( recipient.telecom && Array.isArray( recipient.telecom ) && recipient.telecom.length > 0 ) {
for ( j in recipient.telecom ) {
if ( recipient.telecom[j].use && recipient.telecom[j].use == 'mobile' ) {
phone = recipient.telecom[j].value;
break;
}
}
if ( !phone ) {
// Default to first number if no mobile for now.
phone = recipient.telecom[0].value;
}
}
if ( !phone ) {
// Check for testing number if nothing is found.
phone = nconf.get("rapidpro:testing");
}
if ( !phone ) {
return;
}
var postdata = JSON.stringify({
"flow_uuid": nconf.get("rapidpro:flow_uuid"),
"extra" : {
"msg" : msg,
"id" : resource.id
},
"phone" : [ phone ]
});
var req = http.request( {
hostname : nconf.get("rapidpro:host"),
port : nconf.get("rapidpro:port"),
path : "/api/v1/runs.json",
headers : {
'Content-Type': "application/json",
'Authorization' : nconf.get("rapidpro:token"),
'Content-Length' : postdata.length
},
method : 'POST' }, function( res ) {
console.log("RapidPro Status: " +res.statusCode );
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
console.log("RapidPro response: "+body);
try {
var details = JSON.parse( body );
for( i in details ) {
var detail = details[i];
rp_map.insertOne( { "phone": phone, recipient : resource.recipient[i], contact: detail.contact, text: msg, time: detail.created_on, run: detail.run, id: resource.id }, function( err, r ) {
if ( err ) {
console.log("Failed to insert contact_communication");
console.log(err);
resource.status = 'failed';
} else {
resource.status = 'in-progress';
}
});
}
} catch ( err ) {
console.log("Failed to parse rapid pro response.");
console.log(err);
resource.status = 'failed';
}
callback( resource );
});
res.on('error', function(e) {
console.log("RapidPro error: " +e.message);
resource.status = 'failed';
callback( resource );
});
});
req.on('error', function( req_err ) {
console.log("Got error on request to rapidpro");
console.log(req_err);
resource.status = 'failed';
callback( resource );
});
req.write(postdata);
req.end();
}
function findResource( rp_event, db, callback ) {
var findArgs = { phone : rp_event.phone, text : rp_event.text };
var rp_map = db.collection("contact_communication");
rp_map.findOne( findArgs, { "id" : 1, "recipient" : 1 }, { sort : [['time','desc']] }, function( err, doc ) {
if ( err ) {
console.log("Failed to find resource for ");
console.log(findArgs);
} else {
var comm = db.collection("Communication");
comm.findOne( {id:doc.id}, function( err, communication ) {
if ( err ) {
console.log("Failed to get resource for " +doc.id);
console.log(err);
} else {
callback( communication, doc.recipient );
}
});
}
});
}
function findResourceByRun( run, rp_event, db, callback ) {
var findArgs = { phone : rp_event.phone, run : parseInt(run) };
var rp_map = db.collection("contact_communication");
console.log("looking for ");
console.log(findArgs);
rp_map.findOne( findArgs, { "id" : 1, "recipient" : 1 }, { sort : [['time','desc']] }, function( err, doc ) {
if ( err ) {
console.log("Failed to find resource for ");
console.log(findArgs);
} else {
if ( !doc ) {
console.log("Failed to find contact_communication for:");
console.log(findArgs);
} else {
var comm = db.collection("Communication");
comm.findOne( {id:doc.id}, function( err, communication ) {
if ( err ) {
console.log("Failed to get resource for " +doc.id);
console.log(err);
} else {
callback( communication, doc.recipient );
}
});
}
}
});
}