/**
*
*/
package org.sana.android.net;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.sana.R;
import org.sana.android.Constants;
import org.sana.android.content.Uris;
import org.sana.android.db.ModelWrapper;
import org.sana.android.db.SanaDB.BinarySQLFormat;
import org.sana.android.db.SanaDB.ImageSQLFormat;
import org.sana.android.db.SanaDB.SoundSQLFormat;
import org.sana.android.procedure.Procedure;
import org.sana.android.procedure.ProcedureParseException;
import org.sana.android.procedure.ProcedureElement.ElementType;
import org.sana.android.provider.Encounters;
import org.sana.android.provider.Observations;
import org.sana.android.provider.Patients;
import org.sana.android.provider.Procedures;
import org.sana.android.provider.Subjects;
import org.sana.android.service.QueueManager;
import org.sana.android.service.impl.DispatchService;
import org.sana.android.util.Dates;
import org.sana.core.Patient;
import org.sana.net.MDSResult;
import org.sana.net.Response;
import org.sana.net.http.HttpTaskFactory;
import org.sana.util.UUIDUtil;
import org.xml.sax.SAXException;
import android.annotation.TargetApi;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
/**
* @author Sana Development
*
*/
public class MDSInterface2 {
public static final String TAG = MDSInterface2.class.getSimpleName();
/**
* Gets the value in the MDS url setting and add the correct scheme, i.e.
* http or https, depending on the value of the use secure transmission
* setting.
* @param context The current context.
* @param path an additional path to append if not null
* @return The mds url with correct scheme as a String.
*/
public static String getMDSUrl(Context context, String path){
String host = context.getString(R.string.host_mds);
String root = context.getString(R.string.path_root);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
host = preferences.getString(Constants.PREFERENCE_MDS_URL, host);
// Realistacally should never use http
boolean useSecure = preferences.getBoolean(
Constants.PREFERENCE_SECURE_TRANSMISSION, true);
String scheme = (useSecure)? "https": "http";
String url = scheme + "://" + host +"/"+ root;
return (TextUtils.isEmpty(path)? url: url + path);
}
/**
* Gets the value in the MDS url setting and add the correct scheme, i.e.
* http or https, depending on the value of the use secure transmission
* setting.
* @param context The application context.
* @return The mds url with correct scheme.
*/
public static String getMDSUrl(Context context){
return getMDSUrl(context, null);
}
private static String constructProcedureSubmitURL(String mdsURL) {
return mdsURL + Constants.PROCEDURE_SUBMIT_PATTERN;
}
protected static MDSResult doPost(String scheme, String host, int port,
String path,
List<NameValuePair> postData) throws UnsupportedEncodingException{
URI uri = null;
try {
uri = URIUtils.createURI(scheme, host, port, path, null, null);
} catch (URISyntaxException e) {
e.printStackTrace();
throw new IllegalArgumentException(String.format("Can not post to mds: %s, %s, %d, %s", scheme,host,port,path),e);
}
Log.d(TAG, "doPost() uri: " + uri.toASCIIString());
HttpPost post = new HttpPost(uri);
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(postData, "UTF-8");
post.setEntity(entity);
return MDSInterface2.doExecute(post);
}
/**
* Executes a POST method. Provides a wrapper around doExecute by
* preparing the PostMethod.
*
* @param url the request url
* @param postData the form data.
* @return
* @throws UnsupportedEncodingException
*/
protected static MDSResult doPost(String url,
List<NameValuePair> postData) throws UnsupportedEncodingException
{
HttpPost post = new HttpPost(url);
Log.d(TAG, "doPost(): " + url + ", " + postData.size());
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(postData, "UTF-8");
post.setEntity(entity);
return MDSInterface2.doExecute(post);
}
protected static MDSResult doPost(String scheme,
String host,
int port,
String path,
HttpEntity entity)
{
URI uri = null;
try {
uri = URIUtils.createURI(scheme, host, port, path, null, null);
} catch (URISyntaxException e) {
e.printStackTrace();
throw new IllegalArgumentException(String.format("Can not post to mds: %s, %s, %d, %s", scheme,host,port,path),e);
}
Log.d(TAG, "doPost() uri: " + uri.toASCIIString());
HttpPost post = new HttpPost(uri);
post.setEntity(entity);
return MDSInterface2.doExecute(post);
}
/**
* Executes a POST method. Provides a wrapper around doExecute by
* preparing the PostMethod.
*
* @param url the request url
* @param entity the form data.
* @return
*/
protected static MDSResult doPost(String url, HttpEntity entity)
{
HttpPost post = new HttpPost(url);
post.setEntity(entity);
return MDSInterface2.doExecute(post);
}
/**
* Executes a client HttpMethod.
*
* @param method The Http
* @return
*/
protected static MDSResult doExecute(HttpUriRequest method){
HttpClient client = HttpTaskFactory.CLIENT_FACTORY.produce();
MDSResult response = null;
HttpResponse httpResponse = null;
String responseString = null;
try {
httpResponse = client.execute(method);
Log.d(TAG, "doExecute() got response code " + httpResponse.getStatusLine().getStatusCode());
char buf[] = new char[20560];
responseString = EntityUtils.toString(httpResponse.getEntity());
Log.d(TAG, "doExecute() Received from MDS:" + responseString);//.length()+" chars");
Gson gson = new Gson();
response = gson.fromJson(responseString, MDSResult.class);
} catch (IOException e1) {
Log.e(TAG, e1.toString());
e1.printStackTrace();
} catch (JsonParseException e) {
Log.e(TAG, "doExecute(): Error parsing MDS JSON response: "
+ e.getMessage());
}
return response;
}
private static boolean postResponses(Context c,
String savedProcedureGuid,
String procedureUUID,
String subjectUUID,
String jsonResponses,
String username,
String password) throws UnsupportedEncodingException
{
SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(c);
String mdsURL = getMDSUrl(c);
Log.d(TAG, "mds url: " + mdsURL);
//mdsURL = checkMDSUrl(mdsURL);
String mUrl = constructProcedureSubmitURL(mdsURL);
String phoneId = preferences.getString("s_phone_name",
Constants.PHONE_ID);
return MDSInterface2.postResponses(c, savedProcedureGuid,
procedureUUID, subjectUUID, jsonResponses, username, password, phoneId);
}
static boolean postResponses(Context context,
String savedProcedureGuid,
String procedureUUID,
String subjectUUID,
String jsonResponses,
String username,
String password,
String phoneId) throws UnsupportedEncodingException
{
SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(context);
String scheme = MDSInterface.getScheme(preferences);
String host = MDSInterface.getHost(preferences, context.getString(R.string.host_mds));
String path = context.getString(R.string.path_root) + context.getString(R.string.path_encounter);
int port = MDSInterface.getPort(context);
List<NameValuePair> postData = new ArrayList<NameValuePair>();
postData.add(new BasicNameValuePair("savedproc_guid", savedProcedureGuid));
postData.add(new BasicNameValuePair("procedure_guid", procedureUUID));
postData.add(new BasicNameValuePair("phone", phoneId));
postData.add(new BasicNameValuePair("username", username));
postData.add(new BasicNameValuePair("password", password));
postData.add(new BasicNameValuePair("responses", jsonResponses));
postData.add(new BasicNameValuePair("subject", subjectUUID));
String mdsURL = getMDSUrl(context);
Log.d(TAG, "mds url: " + mdsURL);
//mdsURL = checkMDSUrl(mdsURL);
String mUrl = constructProcedureSubmitURL(mdsURL);
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(postData, "UTF-8");
MDSResult postResponse = MDSInterface2.doPost(mUrl, entity);
if(postResponse != null){
Log.d(TAG, "postResponses(...): " +postResponse.toString());
int code = Integer.valueOf(postResponse.getCode());
return (code == 200)? postResponse.succeeded(): false;
} else {
Log.d(TAG, "postResponses(...): null response");
return false;
}
}
static class NetworkConfig{
String host;
int port;
int proxy;
String path;
}
/*
public static Uri getInfo(Context c, Uri uri){
SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(c);
String scheme = ((preferences.getBoolean(
Constants.PREFERENCE_SECURE_TRANSMISSION, true))? "https":"http");
// If there's a proxy enabled, use it.
String proxyHost = preferences.getString(Constants.PREFERENCE_PROXY_HOST, "");
String sProxyPort = null;
int proxyPort = 0;
try {
sProxyPort = preferences.getString(Constants.PREFERENCE_PROXY_PORT, "0");
if (!TextUtils.isEmpty(sProxyPort));
proxyPort = Integer.parseInt(sProxyPort);
} catch(NumberFormatException e) {
Log.w(TAG, "Invalid proxy port: " + sProxyPort);
}
URI uri = null;
try {
uri = URIUtils.createURI(scheme, host, port, path, null, null);
} catch (URISyntaxException e) {
e.printStackTrace();
throw new IllegalArgumentException(String.format("Can not post to mds: %s, %s, %d, %s", scheme,host,port,path),e);
}
*/
/**
*
* @author Sana development
*
*/
static class EncounterCompat{
String uuid = null;
String subject = null;
String observer = null;
String procedure = null;
String concept = "521b0825-14c9-49e5-a95e-462a01e2ae05";
String device = null;
boolean finished = false;
boolean uploaded = false;
String observations = null;
public static EncounterCompat readFromCursor(Context ctx, Uri uri){
EncounterCompat ec = new EncounterCompat();
Cursor c = null;
c = ctx.getContentResolver().query(
uri, savedProcedureProjection, null,
null, null);
ec.uuid = c.getString(c.getColumnIndex(Encounters.Contract.UUID));
ec.subject = c.getString(c.getColumnIndex(Encounters.Contract.SUBJECT));
ec.observer = c.getString(c.getColumnIndex(Encounters.Contract.OBSERVER));
ec.procedure = c.getString(c.getColumnIndex(Encounters.Contract.PROCEDURE));
ec.uploaded = c.getInt(c.getColumnIndex(Encounters.Contract.UPLOADED)) > 0;
return ec;
}
}
public static String[] savedProcedureProjection = new String[] {
Encounters.Contract._ID,
Encounters.Contract.PROCEDURE,
Encounters.Contract.STATE,
Encounters.Contract.FINISHED,
Encounters.Contract.UUID,
Encounters.Contract.UPLOADED,
Encounters.Contract.SUBJECT,
Encounters.Contract.OBSERVER};
public static boolean postProcedureToDjangoServer(Uri uri, Context context, String user, String password) throws UnsupportedEncodingException {
Log.i(TAG, "In Post procedure to Django server for background uploading service.");
Log.i(TAG, "Attempting to upload: " + uri);
QueueManager.setProcedureUploadStatus(context, uri, QueueManager.UPLOAD_STATUS_IN_PROGRESS);
SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(context);
String mdsURL = getMDSUrl(context);
Log.d(TAG, "mds url: " + mdsURL);
//mdsURL = checkMDSUrl(mdsURL);
String mUrl = constructProcedureSubmitURL(mdsURL);
// get the tel number - default to ten-digit all zero's if null
TelephonyManager tMgr =(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
String device = tMgr.getLine1Number();
device = (TextUtils.isEmpty(device))? "9999999999":device;
Cursor cursor = context.getContentResolver().query(
uri, savedProcedureProjection, null,
null, null);
// First get the saved procedure...
int savedProcedureId = -1;
String answersJson = null;
boolean finished = false;
String savedProcedureGUID = null;
boolean savedProcedureUploaded = false;
String observerUUID = null;
String subjectUUID = null;
String phoneId = null;
String procedureId = null;
//todo rEMOVE THIS AND REPLACE
try{
cursor.moveToFirst();
procedureId = cursor.getString(cursor.getColumnIndex(Encounters.Contract.PROCEDURE));
savedProcedureId = cursor.getInt(0);
answersJson = cursor.getString(2);
finished = cursor.getInt(3) != 0;
savedProcedureGUID = cursor.getString(cursor.getColumnIndex(Encounters.Contract.UUID));
savedProcedureUploaded = cursor.getInt(5) != 0;
observerUUID = cursor.getString(
cursor.getColumnIndex(Encounters.Contract.OBSERVER));
subjectUUID = cursor.getString(
cursor.getColumnIndex(Encounters.Contract.SUBJECT));
} finally {
if(cursor != null) cursor.close();
}
Log.i(TAG, "...encounter " + savedProcedureGUID);
Log.i(TAG, "...procedure" + procedureId );
Log.i(TAG, "...subject" + subjectUUID);
Log.i(TAG, "...device" + device);
Log.i(TAG, "...observer " + observerUUID);
//if(savedProcedureUploaded)
// return true;
// TODO Remove this entirely and replace
Uri procedureUri = null;
if(UUIDUtil.isValid(procedureId))
procedureUri = Uris.withAppendedUuid(Procedures.CONTENT_URI, procedureId);
else{
procedureUri = ContentUris.withAppendedId(
Procedures.CONTENT_URI, Long.parseLong(procedureId));
}
Log.i(TAG, "Getting procedure uuid "+ ModelWrapper.getUuid(procedureUri, context.getContentResolver()));
Log.i(TAG, "Getting procedure " + procedureUri.toString());
String procedureTitle = null;
String procedureXml = null;
String procedureUUID = null;
cursor = context.getContentResolver().query(procedureUri,
new String[] { Procedures.Contract.TITLE,
Procedures.Contract.PROCEDURE,
Procedures.Contract.UUID},
null, null, null);
try{
cursor.moveToFirst();
procedureTitle = cursor.getString(
cursor.getColumnIndex(Procedures.Contract.TITLE));
procedureXml = cursor.getString(
cursor.getColumnIndex(Procedures.Contract.PROCEDURE));
procedureUUID = cursor.getString(
cursor.getColumnIndex(Procedures.Contract.UUID));
} finally {
if(cursor != null) cursor.close();
}
Log.i(TAG, "...encounter " + savedProcedureGUID);
Log.i(TAG, "...procedure" + procedureUUID + " '" + procedureTitle +"'");
Log.i(TAG, "...subject" + subjectUUID);
Log.i(TAG, "...device" + device);
Log.i(TAG, "...observer " + observerUUID);
if(!finished) {
Log.i(TAG, "Not finished. Not uploading. (just kidding)"
+ uri.toString());
//return false;
}
// Map of all of the Procedure Elements; i.e. observation data
// binaries get parsed out later
Map<String, Map<String,String>> elementMap = null;
try {
Procedure p = Procedure.fromXMLString(procedureXml);
p.setInstanceUri(uri);
JSONTokener tokener = new JSONTokener(answersJson);
JSONObject answersDict = new JSONObject(tokener);
Map<String,String> answersMap = new HashMap<String,String>();
Iterator<?> it = answersDict.keys();
while(it.hasNext()) {
String key = (String)it.next();
answersMap.put(key, answersDict.getString(key));
}
Log.i(TAG, "restoreAnswers");
p.restoreAnswers(answersMap);
elementMap = p.toElementMap();
} catch (IOException e2) {
Log.e(TAG, e2.toString());
} catch (ParserConfigurationException e2) {
Log.e(TAG, e2.toString());
} catch (SAXException e2) {
Log.e(TAG, e2.toString());
} catch (ProcedureParseException e2) {
Log.e(TAG, e2.toString());
} catch (JSONException e) {
Log.e(TAG, e.toString());
}
// check that we don't have empty map
if(elementMap == null) {
Log.i(TAG, "Could not encounter text " + uri + ". Not uploading.");
return false;
}
Map<String, Map<String, String>> observations = getObservationsCompat(context,savedProcedureGUID, elementMap);
// Add in procedureTitle as a fake answer
/*
Map<String,String> titleMap = new HashMap<String,String>();
titleMap.put("answer", procedureTitle);
titleMap.put("id", "procedureTitle");
titleMap.put("type", "HIDDEN");
elementMap.put("procedureTitle", titleMap);
*/
class ElementAnswer {
public String id;
public String answer;
public String type;
public ElementAnswer(String id, String answer, String type) {
this.id = id;
this.answer = answer;
this.type = type;
}
}
// Convert saved procedure to JSON
JSONObject jsono = new JSONObject();
int totalBinaries = 0;
ArrayList<ElementAnswer> binaries = new ArrayList<ElementAnswer>();
for(Entry<String,Map<String,String>> e : observations.entrySet()) {
try {
jsono.put(e.getKey(), new JSONObject(e.getValue()));
} catch (JSONException e1) {
Log.e(TAG, "JSON conversion fail: " + e1.getMessage());
}
String id = e.getKey();
String type = e.getValue().get("type");
String answer = e.getValue().get("answer");
if (id == null || type == null || answer == null)
continue;
// Find elements that require binary uploads
if(type.equals(ElementType.PICTURE.toString()) ||
type.equals(ElementType.BINARYFILE.toString()) ||
type.equals(ElementType.SOUND.toString()) ||
type.equals(ElementType.PLUGIN.toString())){
binaries.add(new ElementAnswer(id, answer, type));
if(!"".equals(answer)) {
String[] ids = answer.split(",");
totalBinaries += ids.length;
}
}
}
Log.i(TAG, "About to post responses.");
// check if it is already uploaded
if(savedProcedureUploaded) {
Log.i(TAG, "Responses have already been sent to MDS, not posting.");
} else {
QueueManager.setProcedureUploadStatus(context, uri, QueueManager.UPLOAD_STATUS_IN_PROGRESS);
// upload the question and answer pairs text, without packetization
String json = jsono.toString();
Log.i(TAG, "json string: " + json.length());
// try repeating upload on fail to some preset number
final int MAX_TRIES = 3;
int tries = 0;
while(tries < MAX_TRIES) {
if (MDSInterface2.postResponses(context,
savedProcedureGUID, procedureTitle, subjectUUID,json,
user, password,device)) {
// Mark the procedure text as uploaded in the database
ContentValues cv = new ContentValues();
cv.put(Encounters.Contract.UPLOADED, true);
context.getContentResolver().update(uri, cv, null, null);
Log.i(TAG, "Responses were uploaded successfully.");
break;
}
tries++;
}
// if tries >= maximum we bail and try again later
if(tries == MAX_TRIES) {
Log.e(TAG, "Could not post responses, bailing.");
QueueManager.setProcedureUploadStatus(context, uri, QueueManager.UPLOAD_STATUS_FAILURE);
return false;
}
}
Log.i(TAG, "Posted responses, now sending " + totalBinaries
+ " binaries.");
// lookup starting packet size
int newPacketSize;
try {
newPacketSize = Integer.parseInt(
PreferenceManager.getDefaultSharedPreferences(context)
.getString("s_packet_init_size",
Integer.toString(Constants.DEFAULT_INIT_PACKET_SIZE)));
} catch (NumberFormatException e) {
newPacketSize = Constants.DEFAULT_INIT_PACKET_SIZE;
}
// adjust from KB to bytes
newPacketSize *= 1000;
int totalProgress = 1+totalBinaries;
int thisProgress = 2;
// upload each binary file where each binary should be represented by
// one value in a comma separated list of ints starting
final int MAXBINARY_POST_ATTEMPT = 5;
for(ElementAnswer e : binaries) {
if(TextUtils.isEmpty(e.answer)){
Log.w(TAG, "Got empty answer! Element: " + e.id);
continue;
}
// parse csv list
String[] ids = e.answer.split(",");
// loop over each value
for(String binaryId : ids) {
Uri binUri = null;
ElementType type = ElementType.INVALID;
try {
type = ElementType.valueOf(e.type);
} catch(IllegalArgumentException ex) {
Log.e(TAG, ex.getMessage());
}
if (type == ElementType.PICTURE) {
binUri = ContentUris.withAppendedId(
ImageSQLFormat.CONTENT_URI,
Long.parseLong(binaryId));
} else if (type == ElementType.SOUND) {
binUri = ContentUris.withAppendedId(
SoundSQLFormat.CONTENT_URI,
Long.parseLong(binaryId));
} else if (type == ElementType.PLUGIN) {
binUri = ContentUris.withAppendedId(
BinarySQLFormat.CONTENT_URI,
Long.parseLong(binaryId));
} else if (type == ElementType.BINARYFILE) {
binUri = Uri.fromFile(new File(e.answer));
// We can't tell if a BINARYFILE has been uploaded before.
// Maybe if we grab the mtime/filesize on the file and store
// it when we upload it.
}
int binaryPostCount = 0;
while(binaryPostCount < MAXBINARY_POST_ATTEMPT){
binaryPostCount++;
try {
Log.i(TAG, "Uploading " + binUri);
// reset the new packet size each time to the last
// successful transmission size
newPacketSize = MDSInterface.transmitBinary(context, savedProcedureGUID,
e.id, binaryId, type, binUri,
newPacketSize);
// Delete the file!
switch(type) {
case PICTURE:
case SOUND:
//This was deleting the pictures after upload - should
// not happen, leave commented out!
//context.getContentResolver().delete(binUri,null,null);
break;
default:
}
} catch (Exception x) {
Log.e(TAG, "Uploading " + binUri + " failed : "
+ x.toString());
x.printStackTrace();
QueueManager.setProcedureUploadStatus(context, uri, QueueManager.UPLOAD_STATUS_FAILURE);
return false;
}
thisProgress++;
}
}
}
// TODO Tag entire procedure in db as done transmitting
QueueManager.setProcedureUploadStatus(context, uri, QueueManager.UPLOAD_STATUS_SUCCESS);
return true;
}
public static boolean postProcedureToDjangoServer(Uri uri, Context context) throws UnsupportedEncodingException {
SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(context);
String username = preferences.getString(
Constants.PREFERENCE_EMR_USERNAME, Constants.DEFAULT_USERNAME);
String password = preferences.getString(
Constants.PREFERENCE_EMR_PASSWORD, Constants.DEFAULT_PASSWORD);
return MDSInterface2.postProcedureToDjangoServer(uri,context, username,password);
}
/*
String mdsUuid = null;
String uuid = null;
String subject = null;
String observer = null;
String procedure = null;
String encConcept = "521b0825-14c9-49e5-a95e-462a01e2ae05";
// get the tel number - default to ten-digit all zero's if null
TelephonyManager tMgr =(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
String device = tMgr.getLine1Number();
device = (TextUtils.isEmpty(device))? "0000000000":device;
// Fetch the encounter information
Cursor c = null;
c = context.getContentResolver().query(uri, null, null, null, null);
c.moveToFirst();
uuid = c.getString(c.getColumnIndex(Encounters.Contract.UUID));
subject = c.getString(c.getColumnIndex(Encounters.Contract.SUBJECT));
observer = c.getString(c.getColumnIndex(Encounters.Contract.OBSERVER));
procedure = c.getString(c.getColumnIndex(Encounters.Contract.PROCEDURE));
if( c!= null)
c.close();
// TODO Remove this at release
Log.i(TAG, "uuid: " + uuid);
Log.i(TAG, "subject: " + subject);
Log.i(TAG, "observer: " + observer);
Log.i(TAG, "procedure: " + procedure);
Log.i(TAG, "concept: " + encConcept);
Log.i(TAG, "device: " + device);
// build out the observations
String[] obsProjection = new String[]{
Observations.Contract.ID,
Observations.Contract.CONCEPT,
Observations.Contract.VALUE
};
Cursor cursor = context.getContentResolver().query(
Observations.CONTENT_URI,
obsProjection,
Observations.Contract.ENCOUNTER + " = ?",
new String[]{ uuid } ,
Observations.Contract.ID + " ASC");
List<Map<String,String>> observations = new ArrayList<Map<String,String>>(cursor.getCount());
while(cursor.moveToNext()){
Map<String,String> obs = new HashMap<String,String>();
String id = cursor.getString(cursor.getColumnIndex(Observations.Contract.ID));
String concept = cursor.getString(cursor.getColumnIndex(Observations.Contract.CONCEPT));
String value = cursor.getString(cursor.getColumnIndex(Observations.Contract.VALUE));
//TODO handle complex observations
obs.put(Observations.Contract.ID, id);
obs.put(Observations.Contract.CONCEPT, concept);
obs.put(Observations.Contract.VALUE, value);
observations.add(obs);
}
return mdsUuid;
}
*/
public static List<Map<String, String>> getObservations(Context context, String uuid){
final String[] obsProjection = new String[] {
Observations.Contract.UUID, Observations.Contract.ID,
Observations.Contract.CONCEPT, Observations.Contract.VALUE, };
List<Map<String, String>> observations = Collections.EMPTY_LIST;
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(
Observations.CONTENT_URI, obsProjection,
Observations.Contract.ENCOUNTER + " = ?",
new String[] { uuid }, Observations.Contract.ID + " ASC");
observations = new ArrayList<Map<String, String>>(cursor.getCount());
while (cursor.moveToNext()) {
Map<String, String> obs = new HashMap<String, String>(4);
// TODO handle complex observations
obs.put(Observations.Contract.UUID, cursor.getString(0));
obs.put(Observations.Contract.ID, cursor.getString(1));
obs.put(Observations.Contract.CONCEPT, cursor.getString(2));
obs.put(Observations.Contract.VALUE, cursor.getString(3));
observations.add(obs);
Log.d(TAG, DatabaseUtils.dumpCurrentRowToString(cursor));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
return observations;
}
/**
* Returns the observations for a single encounter in a format that can be passed to the mds 1.x
* responses post
* @param context The current context
* @param uuid The encounter uuid String.
* @param elementMap A 1.x style map of elements which includes the element type.
* @return
*/
public static Map<String, Map<String, String>> getObservationsCompat(Context context, String uuid, Map<String, Map<String, String>> elementMap){
final String[] obsProjection = new String[] {
Observations.Contract.UUID,
Observations.Contract.ID,
Observations.Contract.CONCEPT,
Observations.Contract.VALUE,
Observations.Contract.PARENT};
Map<String, Map<String, String>> observations = null;
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(
Observations.CONTENT_URI, obsProjection,
Observations.Contract.ENCOUNTER + " = ?",
new String[] { uuid }, Observations.Contract.ID + " ASC");
observations = new HashMap<String, Map<String, String>>(cursor.getCount());
while (cursor.moveToNext()) {
Map<String, String> obs = new HashMap<String, String>(4);
// TODO handle complex observations
String eID = cursor.getString(1);
Map<String,String> element = elementMap.get(eID);
if(element == null){
Log.w(TAG, "No element with id: " + eID);
String[] subID = eID.split("_");
// Complex node so we inherit from parent
Log.w(TAG, "Trying parent id: " + subID[0]);
Map<String,String> parent = elementMap.get(subID[0]);
element = new HashMap<String,String>();
element.put("type", parent.get("type"));
}
element.put(Observations.Contract.UUID, cursor.getString(0));
element.put("id", eID);
element.put(Observations.Contract.CONCEPT, cursor.getString(2));
element.put("answer", cursor.getString(3));
observations.put(cursor.getString(1), element);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
return observations;
}
public static List<String> postObservations(Context context, String uuid) throws UnsupportedEncodingException {
Cursor cursor = null;
List<Map<String, String>> observations = null;
cursor = context.getContentResolver().query(
Observations.CONTENT_URI,
new String[]{
Observations.Contract.UUID,
Observations.Contract.ENCOUNTER,
Observations.Contract.CONCEPT,
Observations.Contract.ID,
Observations.Contract.PARENT,
Observations.Contract.VALUE
},
Observations.Contract.ENCOUNTER + " = ?",
new String[]{ uuid } ,
Observations.Contract.ID + " ASC");
return null;
}
public static HttpPost createSessionRequest(Context context, String username,
String password) throws URISyntaxException
{
String url = getMDSUrl(context, context.getString(R.string.path_session));
Log.d(TAG, "createSessionRequest() "+url);
URI uri = getURI(context,context.getString(R.string.path_session));
List<NameValuePair> postData = new ArrayList<NameValuePair>();
postData.add(new BasicNameValuePair("username", username));
postData.add(new BasicNameValuePair("password", password));
return HttpRequestFactory.getPostRequest(uri, postData);
}
public static boolean getFile(Context context, Uri file) throws IOException {
String path = "media/" + file.getLastPathSegment();
return getFile(context,path, new File(file.getPath()));
}
public static boolean getFile(Context context, String path, File outfile) throws IOException{
String url = getMDSUrl(context, path);
Log.d(TAG, "getFile() "+url +", outfile:" + outfile);
URI uri = URI.create(url);
HttpGet get = new HttpGet(uri);
HttpClient client = HttpTaskFactory.CLIENT_FACTORY.produce();
HttpResponse response;
FileOutputStream fileOutput = null;
InputStream inputStream = null;
boolean result = false;
try {
response = client.execute(get);
fileOutput = new FileOutputStream(outfile);
inputStream = response.getEntity().getContent();
byte[] buffer = new byte[1024];
int bufferLength = 0;
while ( (bufferLength = inputStream.read(buffer)) > 0 ) {
fileOutput.write(buffer, 0, bufferLength);
Log.d(TAG, "... wrote bytes:" + bufferLength);
}
result = true;
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(fileOutput != null) fileOutput.close();
if(inputStream != null) inputStream.close();
}
return result;
}
/**
* Builds and returns an HttpClient with Basic Authorization headers initialized
* with a BasicCredentialsProvider
*
* @param username The username credential.
* @param password The password credential.
* @return a new HttpClient
*/
public static HttpClient buildBasicAuthClient(String username, String password){
HttpClient client = HttpTaskFactory.CLIENT_FACTORY.produce();
if(!(TextUtils.isEmpty(username) || TextUtils.isEmpty(password))){
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(AuthScope.ANY_HOST,AuthScope.ANY_PORT),
new UsernamePasswordCredentials(username,password));
((DefaultHttpClient)client).setCredentialsProvider(credsProvider);
}
return client;
}
public static Response<String> apiGet(URI uri) throws UnsupportedEncodingException
{
return apiGet(uri, null, null);
}
public static Response<String> apiGet(URI uri, String username, String password) throws UnsupportedEncodingException
{
HttpClient client = buildBasicAuthClient(username,password);
HttpGet request = new HttpGet(uri);
request.setHeader("Accept", "application/json");
HttpResponse httpResponse = null;
Response<String> response = Response.empty();
try {
httpResponse = client.execute(request);
response.message = EntityUtils.toString(httpResponse.getEntity());
response.code = httpResponse.getStatusLine().getStatusCode();
response.status = Response.SUCCESS;
} catch (ClientProtocolException e) {
response.setCode(500);
response.setStatus(Response.FAILURE);
e.printStackTrace();
} catch (IOException e) {
response.setCode(501);
response.setStatus(Response.FAILURE);
e.printStackTrace();
}
return response;
}
public static synchronized <T> Response<T> apiGet(URI uri, String username, String password,
ResponseHandler<Response<T>> handler) throws UnsupportedEncodingException
{
HttpClient client = buildBasicAuthClient(username,password);
HttpGet request = new HttpGet(uri);
request.setHeader("Accept", "application/json");
HttpResponse httpResponse = null;
Response<T> response = Response.empty();
try {
httpResponse = client.execute(request);
response = handler.handleResponse(httpResponse);
} catch (ClientProtocolException e) {
response.setCode(500);
response.setStatus(Response.FAILURE);
response.errors = new String[]{
"ClientProtocolException executing request",
e.toString()
};
e.printStackTrace();
} catch (IOException e) {
response.setCode(501);
response.setStatus(Response.FAILURE);
response.errors = new String[]{
"IOException executing request",
e.toString()
};
e.printStackTrace();
} catch (OutOfMemoryError e){
response.setCode(500);
response.setStatus(Response.FAILURE);
response.errors = new String[]{
"OutOfMemoryError reading server response",
e.toString()
};
e.printStackTrace();
} catch (Exception e) {
response.setCode(501);
response.setStatus(Response.FAILURE);
response.errors = new String[]{
"Exception",
e.toString()
};
e.printStackTrace();
}
return response;
}
public static <T> Response<T> apiGet(URI uri, ResponseHandler<Response<T>> handler) throws UnsupportedEncodingException
{
return apiGet(uri,"","",handler);
}
public static synchronized <T> Response<T> apiPost(URI uri, String username, String password,
Map<String, String> values,
ResponseHandler<Response<T>> handler)
{
Log.i(TAG, "apiPost()" + uri);
HttpClient client = buildBasicAuthClient(username,password);
HttpPost request = new HttpPost(uri);
HttpResponse httpResponse = null;
Response<T> response = Response.empty();
try {
HttpEntity entity = new UrlEncodedFormEntity(mapToPost(values), "UTF-8");
request.setEntity(entity);
Log.i(TAG, "apiPost(): executing" + request.getMethod());
httpResponse = client.execute(request);
response = handler.handleResponse(httpResponse);
} catch (ClientProtocolException e) {
response.setCode(500);
response.setStatus(Response.FAILURE);
e.printStackTrace();
} catch (IOException e) {
response.setCode(501);
response.setStatus(Response.FAILURE);
e.printStackTrace();
}
return response;
}
/**
*
* @param uri
* @param username
* @param password
* @param form
* @param handler
* @return
*/
public static synchronized <T> Response<T> apiPut(URI uri, String username, String password,
Map<String, String> form,
Map<String, String> files,
ResponseHandler<Response<T>> handler){
HttpClient client = buildBasicAuthClient(username,password);
HttpPut request = new HttpPut(uri);
HttpResponse httpResponse = null;
Response<T> response = Response.empty();
try{
Log.d(TAG,"apiPut() BUILDING ENTITY");
//UrlEncodedFormEntity
HttpEntity entity;
if(files == null){
entity = new UrlEncodedFormEntity(mapToPost(form), "UTF-8");
} else {
entity = new MultipartEntity();
Log.d(TAG,"apiPut() updating n = " + form.entrySet().size() + "values");
}
request.setEntity(entity);
Log.d(TAG,"apiPut() executing" + request.getMethod());
httpResponse = client.execute(request);
Log.d(TAG,"apiPut() reading response");
response = handler.handleResponse(httpResponse);
Log.d(TAG,"apiPut() --> " + response.status + ": " + response.code);
} catch (ClientProtocolException e) {
response.setCode(500);
response.setStatus(Response.FAILURE);
e.printStackTrace();
} catch (IOException e) {
response.setCode(501);
response.setStatus(Response.FAILURE);
e.printStackTrace();
}
return response;
}
public static synchronized <T> Response<T> apiDelete(URI uri, String username, String password,
Map<String, Object> values,
ResponseHandler<Response<T>> handler){
HttpClient client = buildBasicAuthClient(username,password);
HttpDelete request = new HttpDelete(uri);
HttpResponse httpResponse = null;
Response<T> response = Response.empty();
try {
httpResponse = client.execute(request);
response = handler.handleResponse(httpResponse);
} catch (ClientProtocolException e) {
response.setCode(500);
response.setStatus(Response.FAILURE);
e.printStackTrace();
} catch (IOException e) {
response.setCode(501);
response.setStatus(Response.FAILURE);
e.printStackTrace();
}
return response;
}
/**
* Converts Android "content" style resource identifiers to URIs to use with the
* new MDS REST API. Only works for those objects whose path components in the new
* REST API are consistent with MDS. Requires the <code>host_mds</code> and
* <code>path_root</code> strings be declared as resources, <code>cfg_mds_port</code>
* integer be declared in the resources as well as the PREFERENCE_MDS_URL setting
* value declared in the settings.xml.
*
* @param context
* @param uri
* @return
* @throws MalformedURLException
* @throws URISyntaxException
*/
public static URI iriToURI(Context context, Uri uri) throws
MalformedURLException, URISyntaxException
{
String host = context.getString(R.string.host_mds);
String root = context.getString(R.string.path_root);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
host = preferences.getString(Constants.PREFERENCE_MDS_URL, host);
boolean useSecure = preferences.getBoolean(
Constants.PREFERENCE_SECURE_TRANSMISSION, true);
String scheme = (useSecure)? "https": "http";
String path = "/mds" + uri.getPath();
if(!path.endsWith("/"))
path = path + "/";
int port = 443;
try{
port = Integer.valueOf(preferences.getString(Constants.PREFERENCE_MDS_PORT, "443"));
} catch (Exception e){
e.printStackTrace();
}
return Uris.iriToURI(uri, scheme, host, port, root);
}
static String getScheme(SharedPreferences preferences){
if(preferences.getBoolean(Constants.PREFERENCE_SECURE_TRANSMISSION, true))
return "https";
else
return "http";
}
static int getPort(Context c){
SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(c);
if(preferences.getBoolean(Constants.PREFERENCE_SECURE_TRANSMISSION, true))
return 443;
else
return c.getResources().getInteger(R.integer.port_mds);
}
public static URI getRoot(Context c) throws URISyntaxException{
SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(c);
String scheme = getScheme(preferences);
String host = preferences.getString(Constants.PREFERENCE_MDS_URL,
c.getString(R.string.host_mds));
int port = getPort(c);
String path = c.getString(R.string.path_root);
return new URI(scheme, null, host, port,path, null, null);
}
public static URI getURI(Context c, String path) throws URISyntaxException{
return getURI(c,path,null);
}
public static URI getURI(Context c, String path, String query) throws URISyntaxException{
SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(c);
String scheme = getScheme(preferences);
String host = preferences.getString(Constants.PREFERENCE_MDS_URL,
c.getString(R.string.host_mds));
int port = getPort(c);
String root = c.getString(R.string.path_root);
Log.i(TAG," path:"+ root + path);
URI uri = new URI(scheme, null, host, port,root + path, query, null);
Log.i(TAG, "uri: " + uri.toString() +", path:" + path);
return uri;
}
public static void logObservations(Context context, String uuid){
Cursor cursor = context.getContentResolver().query(
Observations.CONTENT_URI,
null,
Observations.Contract.ENCOUNTER + " = ?",
new String[]{ uuid } ,
Observations.Contract.ID + " ASC");
StringBuilder obs = new StringBuilder("{ 'encounter': "+ uuid + ", 'observations' : [");
if(cursor != null){
while(cursor.moveToNext()){
obs.append("{");
obs.append("'"+ Observations.Contract._ID+ "': " + cursor.getLong(cursor.getColumnIndex(Observations.Contract._ID)) +", ");
obs.append("'"+ Observations.Contract.ID+ "': " + cursor.getString(cursor.getColumnIndex(Observations.Contract.ID)) +", ");
obs.append("'"+ Observations.Contract.CONCEPT+ "': " + cursor.getString(cursor.getColumnIndex(Observations.Contract.CONCEPT)) +", ");
obs.append("'"+ Observations.Contract.VALUE+ "': " + cursor.getString(cursor.getColumnIndex(Observations.Contract.VALUE)) +", ");
obs.append("}");
}
}
obs.append("]}");
Log.i(TAG, obs.toString());
}
public synchronized static final <T> Response<T> syncUpdate(Context ctx, Uri uri,
String username, String password,
Bundle form,
Bundle files,
ResponseHandler<Response<T>> handler)
{
Log.i(TAG, "syncUpdate()");
Map<String, String> data = new HashMap<String, String>();
Cursor c = null;
Response<T> response = Response.empty();
// Should have at least one field that need to be updated
if(form != null){
Iterator<String> keys = form.keySet().iterator();
while(keys.hasNext()){
String key = keys.next();
data.put(key, form.getString(key));
Log.d(TAG, "key: " + key +" --> value: "+ form.getString(key));
}
}
try{
URI target = iriToURI(ctx,uri);
response = apiPost(target,username,password,data,handler);
} catch(Exception e){
e.printStackTrace();
}
return response;
}
public static List<NameValuePair> mapToPost(Map<String, ? extends Object> map){
List<NameValuePair> postData = new ArrayList<NameValuePair>();
map = (map != null)?map: new HashMap<String,Object>();
for(String key: map.keySet()){
Log.i(TAG, " " + key + " : " + map.get(key));
postData.add(new BasicNameValuePair(key, String.valueOf(map.get(key))));
}
return postData;
}
public static HttpEntity buildEntity(Map<String, ? extends Object> form, Map<String,File> files)
throws UnsupportedEncodingException
{
if(files == null){
return new UrlEncodedFormEntity(mapToPost(form),"UTF-8");
} else {
MultipartEntity entity = new MultipartEntity();
for(String key: form.keySet()){
entity.addPart(key,
new StringBody(String.valueOf(form.get(key))));
}
for(Entry<String, File> entry: files.entrySet()){
//entity.addPart(entry.getKey(),
// new ByteArrayBody(entry.getValue()));
}
return entity;
}
}
public static Response<Collection<Patient>> postPatient(Context context,
Patient patient, String username, String password,
ResponseHandler<Response<Collection<Patient>>> handler){
Log.i(TAG, "postPatient(Context,Patient,String,String, " +
"ResponseHandler<Response<Collection<Patient>>>)");
Response<Collection<Patient>> response = Response.empty();
try {
URI target = getURI(context, Subjects.CONTENT_URI.getPath() + "/");
Map<String,String> values = new HashMap<String,String>();
values.put(Patients.Contract.GIVEN_NAME, patient.getGiven_name());
values.put(Patients.Contract.FAMILY_NAME, patient.getFamily_name());
values.put(Patients.Contract.GENDER, patient.getGender());
if(patient.getLocation() != null &&
!TextUtils.isEmpty(patient.getLocation().getUuid())) {
values.put(Patients.Contract.LOCATION,
patient.getLocation().getUuid());
} else {
values.put(Patients.Contract.LOCATION,
context.getString(R.string.cfg_default_location));
}
values.put(Patients.Contract.UUID, patient.getUuid());
values.put(Patients.Contract.PATIENT_ID, patient.system_id);
values.put(Patients.Contract.DOB, Dates.toSQL(patient.getDob()));
response = MDSInterface2.apiPost(target, username, password,
values, handler);
} catch (URISyntaxException e) {
e.printStackTrace();
response.code = 500;
response.message = Collections.EMPTY_LIST;
} catch (Exception e) {
e.printStackTrace();
response.code = 500;
response.message = Collections.EMPTY_LIST;
}
return response;
}
public static Response<Collection<Patient>> updatePatient(Context context,
Patient patient, String username, String password,
ResponseHandler<Response<Collection<Patient>>> handler){
Log.i(TAG, "postPatient(Context,Patient,String,String, " +
"ResponseHandler<Response<Collection<Patient>>>)");
Response<Collection<Patient>> response = Response.empty();
try {
URI target = getURI(context, Subjects.CONTENT_URI.getPath() + "/"
+ patient.getUuid());
Map<String,String> values = new HashMap<String,String>();
values.put(Patients.Contract.GIVEN_NAME, patient.getGiven_name());
values.put(Patients.Contract.FAMILY_NAME, patient.getFamily_name());
values.put(Patients.Contract.GENDER, patient.getGender());
if(patient.getLocation() != null &&
!TextUtils.isEmpty(patient.getLocation().getUuid())) {
values.put(Patients.Contract.LOCATION + "__uuid",
patient.getLocation().getUuid());
} else {
values.put(Patients.Contract.LOCATION + "__uuid",
context.getString(R.string.cfg_default_location));
}
values.put(Patients.Contract.PATIENT_ID, patient.system_id);
values.put(Patients.Contract.DOB, Dates.toSQL(patient.getDob()));
response = MDSInterface2.apiPut(target, username, password,
values, null, handler);
} catch (URISyntaxException e) {
e.printStackTrace();
response.code = 500;
response.message = Collections.EMPTY_LIST;
} catch (Exception e) {
e.printStackTrace();
response.code = 500;
response.message = Collections.EMPTY_LIST;
}
return response;
}
}