package org.sana.android.fragment;
import java.net.URISyntaxException;
import java.util.Locale;
import java.util.UUID;
import org.json.JSONObject;
import org.sana.android.activity.ProcedureRunner;
import org.sana.android.content.Uris;
import org.sana.android.content.core.ObservationWrapper;
import org.sana.android.db.EncounterDAO;
import org.sana.android.db.EventDAO;
import org.sana.android.db.ModelWrapper;
import org.sana.android.db.SanaDB;
import org.sana.android.procedure.ProcedureElement;
import org.sana.android.provider.BaseContract;
import org.sana.android.provider.Encounters;
import org.sana.android.provider.Events.EventType;
import org.sana.android.provider.Observations;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.text.TextUtils;
import android.util.Log;
/**
* Class for running a new encounter.
*
* @author Sana Development Team
*/
public class ProcedureRunnerFragment extends BaseRunnerFragment {
public static final String TAG = ProcedureRunnerFragment.class.getSimpleName();
/** {@inheritDoc} */
@Override
protected void loadProcedure(Bundle instance) {
Log.d(TAG, "loadProcedure(Bundle): " + ((instance == null)? null: true) );
if(getActivity().getIntent() != null)
onUpdateAppState(getActivity().getIntent());
// Load procedure
Log.d(TAG, "loadProcedure(Bundle): mProcedure: " + ((mProcedure == null)? null: "<Procedure Object>")
+ ", requested: " + requested);
if (mProcedure == null && requested < 1) {
ProcedureLoadRequest request = new ProcedureLoadRequest();
request.instance = instance;
request.intent = getActivity().getIntent();
logEvent(EventType.ENCOUNTER_LOAD_STARTED, "");
new ProcedureLoaderTask().execute(request);
} else if(requested == 0){
ProcedureLoadRequest request = new ProcedureLoadRequest();
request.instance = instance;
//Intent intent = new Intent();
//intent.fillIn(getActivity().getIntent(), Intent.FILL_IN_ACTION | Intent.FILL_IN_DATA);
//intent.putExtras(getActivity().getIntent());
//saveAppState
request.intent = getActivity().getIntent();
onSaveAppState(request.intent);
logEvent(EventType.ENCOUNTER_LOAD_STARTED, "");
new ProcedureLoaderTask().execute(request);
}
}
/** {@inheritDoc} */
@Override
public void logEvent(EventType type, String value) {
String savedProcedureGuid = "";
String patientId = (Uris.isEmpty(uSubject))? "NOT SET": uSubject.getLastPathSegment();
String userId = (Uris.isEmpty(uObserver))? "NOT SET": uObserver.getLastPathSegment();
if (uEncounter != null) {
savedProcedureGuid = EncounterDAO.getEncounterGuid(getActivity(),
uEncounter);
}
EventDAO.registerEvent(getActivity(), type, value, savedProcedureGuid,
patientId, userId);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void onActivtyCreated(Bundle instance){
super.onActivityCreated(instance);
setRetainInstance(true);
}
@Override
public void deleteCurrentProcedure() {
Log.i(TAG, "deleteCurrentProcedure()");
try {
// Remove related first
// Flush out any observations
String uuid = ModelWrapper.getUuid(uEncounter, getActivity()
.getContentResolver());
int deleted = getActivity().getContentResolver().delete(Observations
.CONTENT_URI,
Observations.Contract.ENCOUNTER + " = '"+uuid + "'",
null);
//new String[]{ uuid });
Log.d(TAG, "...deleted n=" + deleted + " observation(s)");
// flush out any images
long id = ModelWrapper.getRowId(uEncounter, getActivity()
.getContentResolver());
deleted = getActivity().getContentResolver().delete(SanaDB
.ImageSQLFormat.CONTENT_URI,
SanaDB.ImageSQLFormat.ENCOUNTER_ID + " = ?",
new String[]{ String.valueOf(id)});
Log.d(TAG, "...deleted n=" + deleted + " image(s)");
// Finally flush the encounter
deleted = getActivity().getContentResolver().delete(uEncounter,
null, null);
Log.d(TAG, "...deleted n=" + deleted + " encounter(s)");
} catch (Exception e){
e.printStackTrace();
}
}
@Override
public void storeCurrentProcedure(boolean finished) {
this.storeCurrentProcedure(finished, true);
}
/**
* Serializes the current procedure to the database. Takes the answers map
* from the procedure, serializes it to JSON, and stores it. If finished is
* set, then it will set the procedure's row to finished. This will signal
* to the upload service that it is ready for upload.
*
* @param finished -- Whether to set the procedure as ready for upload.
*/
@Override
public void storeCurrentProcedure(boolean finished, boolean skipHidden) {
dump();
java.util.Map<String, ProcedureElement> map = mProcedure.current()
.getElementMap();
String encounter = ModelWrapper.getUuid(uEncounter, getActivity()
.getContentResolver());
String subject = ModelWrapper.getUuid(uSubject, getActivity()
.getContentResolver());
for (ProcedureElement el : map.values()) {
ProcedureElement.ElementType type = el.getType();
// skip TEXT types
if (type.equals(ProcedureElement.ElementType.TEXT))
continue;
else {
// for non TEXT types we want to be certain we have an entry in
// the
// Observation table
mData = ObservationWrapper.getReferenceByEncounterAndId(
getActivity().getContentResolver(), encounter,
el.getId());
// Map values to an Observation
ContentValues vals = new ContentValues();
vals.put(Observations.Contract.ENCOUNTER, encounter);
vals.put(Observations.Contract.SUBJECT, subject);
vals.put(Observations.Contract.CONCEPT, el.getConcept());
vals.put(Observations.Contract.ID, el.getId());
// if(!TextUtils.isEmpty(el.getAnswer())){
Log.d(TAG, "...observation uri="+ mData);
if (mData == Observations.CONTENT_URI) {
vals.put(Observations.Contract.UUID, UUID.randomUUID()
.toString());
mData = getActivity().getContentResolver().insert(
Observations.CONTENT_URI, vals);
}
Log.d(TAG, "uri ::" + mData);
boolean updated = false;
switch (type) {
case TEXT:
break;
case HIDDEN:
vals.put(Observations.Contract.VALUE, el.getAnswer());
if (!skipHidden && !TextUtils.isEmpty(el.getAction())) {
Intent intent;
try {
Intent reply = new Intent();
reply.setClass(getActivity(),
ProcedureRunner.class);
reply.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
reply.putExtra(ProcedureRunner.INTENT_KEY_STRING,
ProcedureRunner.OBSERVATION_RESULT_CODE);
reply.putExtra("id", el.getId());
// the getCurrentIndex() returns a 1 based index
reply.putExtra("page",
this.mProcedure.getCurrentIndex() - 1);
PendingIntent replyTo = PendingIntent.getActivity(
getActivity(),
ProcedureRunner.OBSERVATION_RESULT_CODE,
reply, 0);
intent = Intent.parseUri(el.getAction(),
Intent.URI_INTENT_SCHEME);
intent.setData(mData);
intent.putExtra(Intent.EXTRA_INTENT, replyTo);
intent.putExtra("extra_data", reply.getExtras());
getActivity().startService(intent);
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
break;
case PICTURE:
saveOrUpdateListAnswers(encounter, subject, el);
break;
default:
vals.put(Observations.Contract.VALUE, el.getAnswer());
getActivity().getContentResolver().update(mData, vals,
null, null);
break;
}
Log.d(TAG, "updated: " + updated + "::" + mData);
Log.d(TAG, String.format(
"{ 'id': %s, 'concept': %s, 'value': %s", el.getId(),
el.getConcept(), el.getAnswer()));
}
}
saveEncounterStateJSON(finished);
}
@Deprecated
public void saveEncounterStateJSON(boolean finished){
// This handles the old API we are still using for display
// TODO get rid of this.
if (mProcedure != null && uEncounter != null) {
JSONObject answersMap = new JSONObject(mProcedure.toAnswers());
String json = answersMap.toString();
ContentValues cv = new ContentValues();
cv.put(Encounters.Contract.STATE, json);
if (finished)
cv.put(Encounters.Contract.FINISHED, finished);
int updatedObjects = getActivity().getContentResolver().update(
uEncounter, cv, null, null);
Log.i(TAG, "storeCurrentProcedure updated " + updatedObjects
+ " objects. (SHOULD ONLY BE 1)");
}
}
//TODO Fix this so we flush any unselected answers when navigating back and forth
public int deleteRemovedAnswers(String answer){
return 0;
}
public int saveOrUpdateListAnswers(String encounter, String subject, ProcedureElement el){
// This is a multi-response element as a comma separated list
String[] answers = (!TextUtils.isEmpty(el.getAnswer())) ? el
.getAnswer().split(",") : new String[] {};
// iterate over the answers
for (String answer : answers) {
String subId = String.format(Locale.US,"%s_%s", el.getId(),
answer);
Log.d(TAG,"Saving: parent: " + el.getId() + ", id: " + subId);
boolean exists = false;
Cursor c = null;
Uri mObs = Uri.EMPTY;
long id = -1;
try {
c = getActivity().getContentResolver().query(
Observations.CONTENT_URI,
new String[] { Observations.Contract._ID },
Observations.Contract.ID + "= ? AND "
+ Observations.Contract.ENCOUNTER
+ "= ? ",
new String[] { subId, encounter }, null);
if (c != null && c.moveToFirst()) {
exists = true;
id = c.getLong(0);
mObs = ContentUris.withAppendedId(
Observations.CONTENT_URI, id);
}
} catch (Exception e) {
} finally {
if (c != null)
c.close();
}
// What we want to update or insert
Log.i(TAG, "Does the obs exist already: " + exists);
ContentValues vals = new ContentValues();
vals.put(Observations.Contract.ENCOUNTER, encounter);
vals.put(Observations.Contract.SUBJECT, subject);
vals.put(Observations.Contract.CONCEPT, el.getConcept());
vals.put(Observations.Contract.PARENT, el.getId());
vals.put(Observations.Contract.VALUE, answer);
vals.put(Observations.Contract.ID, subId);
//TODO remove this debug block and move to a unit test
Log.d(TAG,Observations.Contract.ENCOUNTER+": " +encounter);
Log.d(TAG,Observations.Contract.SUBJECT+": " +subject);
Log.d(TAG,Observations.Contract.CONCEPT+": " +el.getConcept());
Log.d(TAG,Observations.Contract.PARENT+": " +el.getId());
Log.d(TAG,Observations.Contract.VALUE+": " +answer);
Log.d(TAG,Observations.Contract.ID+": " +subId);
if (!exists){
// !exists so we insert
String uuid = UUID.randomUUID().toString();
vals.put(Observations.Contract.UUID, uuid);
Log.d(TAG,Observations.Contract.UUID+": " +uuid);
Log.i(TAG, "Inserting: " + uuid);
getActivity().getContentResolver().insert(
Observations.CONTENT_URI, vals);
} else {
// exists so we update
Log.i(TAG, "Updating: " + mObs);
getActivity().getContentResolver().update(mObs,
vals, null, null);
}
}
return 0;
}
public int saveParentNode(String encounter, String subject, ProcedureElement el){
String answer = el.getAnswer();
String subId = String.format(Locale.US,"%s_%s", el.getId(),
answer);
Log.d(TAG,"Saving: parent: " + el.getId() + ", id: " + subId);
boolean exists = false;
Cursor c = null;
Uri mObs = Uri.EMPTY;
long id = -1;
try {
c = getActivity().getContentResolver().query(
Observations.CONTENT_URI,
new String[] { Observations.Contract._ID },
Observations.Contract.ID + "= ? AND "
+ Observations.Contract.ENCOUNTER
+ "= ? ",
new String[] { subId, encounter }, null);
if (c != null && c.moveToFirst()) {
exists = true;
id = c.getLong(0);
mObs = ContentUris.withAppendedId(
Observations.CONTENT_URI, id);
}
} catch (Exception e) {
} finally {
if (c != null)
c.close();
}
// What we want to update or insert
Log.i(TAG, "Does the obs exist already: " + exists);
ContentValues vals = new ContentValues();
vals.put(Observations.Contract.ENCOUNTER, encounter);
vals.put(Observations.Contract.SUBJECT, subject);
vals.put(Observations.Contract.CONCEPT, el.getConcept());
vals.put(Observations.Contract.PARENT, el.getId());
vals.put(Observations.Contract.VALUE, answer);
if (!exists){
// !exists so we insert
String uuid = UUID.randomUUID().toString();
vals.put(Observations.Contract.UUID, uuid);
Log.d(TAG,Observations.Contract.UUID+": " +uuid);
Log.i(TAG, "Inserting: " + uuid);
getActivity().getContentResolver().insert(
Observations.CONTENT_URI, vals);
} else {
// exists so we update
Log.i(TAG, "Updating: " + mObs);
getActivity().getContentResolver().update(mObs,
vals, null, null);
}
return 0;
}
}