package org.sana.android.fragment; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.text.ParseException; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.sana.R; import org.sana.android.content.Intents; import org.sana.android.content.Uris; import org.sana.android.content.core.PatientParcel; import org.sana.android.content.core.PatientWrapper; import org.sana.android.db.ModelWrapper; import org.sana.android.db.PatientInfo; import org.sana.android.procedure.PatientIdElement; import org.sana.android.procedure.Procedure; import org.sana.android.procedure.ProcedureElement; import org.sana.android.procedure.ProcedurePage; import org.sana.android.provider.Events.EventType; import org.sana.android.provider.Patients; import org.sana.android.provider.Procedures; import org.sana.android.provider.Subjects; import org.sana.android.service.impl.InstrumentationService; import org.sana.android.util.Dates; import org.sana.core.Location; import org.sana.core.Patient; import org.sana.net.Response; import org.sana.util.DateUtil; import org.sana.util.UUIDUtil; import android.app.Activity; import android.app.AlertDialog; import android.content.ContentValues; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; /** Class for creating a new patient. * * @author Sana Development Team */ public class PatientRunnerFragment extends BaseRunnerFragment { public static final String TAG = PatientRunnerFragment.class.getSimpleName(); protected PatientParcel mPatient = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); showCompleteConfirmation = false; } @Override protected boolean handlePostProcessedElements() { Log.i(TAG, "handlePostProcessedElements()"); // Start the subject lookup; if (mProcedure.current().hasElementWithId(Patients.Contract.PATIENT_ID) && mProcedure.getPatientInfo() == null) { String patientId = mProcedure.current().getElementByType (Patients.Contract.PATIENT_ID).getAnswer(); Uri uri = Subjects.CONTENT_URI; Uri.Builder builder = uri.buildUpon(); builder.appendQueryParameter(Patients.Contract.PATIENT_ID, patientId); Intent intent = new Intent(Intents.ACTION_READ, builder.build()); String message = String.format( getString(R.string.dialog_look_up_patient), patientId); showProgressDialogFragment(message); getActivity().startService(intent); return false; } else { return true; } } /** {@inheritDoc} */ @Override protected void loadProcedure(Bundle instance) { // Load procedure if (mProcedure == null) { ProcedureLoadRequest request = new ProcedureLoadRequest(); request.instance = instance; request.intent = getActivity().getIntent(); logEvent(EventType.ENCOUNTER_LOAD_STARTED, ""); new CreatePatientTask().execute(request); } } /** {@inheritDoc} */ @Override public void logEvent(EventType type, String value) { // TODO: log patient specific events } /** * * * @param finished -- Whether to set the patient as finished creating. */ @Override public void storeCurrentProcedure(boolean finished) { Log.i(TAG, "storeCurrentProcedure(boolean)"); storeCurrentProcedure(finished, false); } public void storeCurrentProcedure(boolean finished, boolean skipHidden) { Log.i(TAG, "storeCurrentProcedure(boolean,boolean)"); Log.d(TAG, "...Checking for null patient"); if(mPatient == null){ Log.w(TAG, "...Subject is null"); return; } // Update the patient object from the elements ProcedurePage page = mProcedure.current(); Log.d(TAG, "...current page: " + mProcedure.getCurrentIndex()); Log.d(TAG, "...current page should display: " + page.shouldDisplay()); Log.d(TAG, "...Patient: " + String.valueOf(mPatient)); // Set any patient fields from current page elements for(String concept: page.getConcepts()){ String id = page.elementWithConcept(concept); String val = page.getElementValue(id); String field = concept.replace(" ","_"); Log.d(TAG,"\tsetting field'" + field + "' for concept '" + concept + "'"); if(field.compareToIgnoreCase(Patients.Contract.PATIENT_ID) == 0) { Log.d(TAG, "\tsetting '" + Patients.Contract.PATIENT_ID + "'=" + val); if(!TextUtils.isEmpty(val)) mPatient.setSystemId(val); } if(field.compareToIgnoreCase(Patients.Contract.GIVEN_NAME) == 0) { Log.d(TAG, "\tsetting '" + Patients.Contract.GIVEN_NAME + "'=" + val); if(!TextUtils.isEmpty(val)) mPatient.setGiven_name(val); } if(field.compareToIgnoreCase(Patients.Contract.FAMILY_NAME) == 0) { Log.d(TAG, "\tsetting '" + Patients.Contract.FAMILY_NAME + "'=" + val); if(!TextUtils.isEmpty(val)) mPatient.setFamily_name(val); } if(field.compareToIgnoreCase(Patients.Contract.DOB) == 0) { Log.d(TAG, "\tsetting '" + Patients.Contract.DOB + "'=" + val); try { mPatient.setDob(DateUtil.parseDate(val)); } catch (ParseException e) { Log.e(TAG, e.getMessage()); e.printStackTrace(); } } if(field.compareToIgnoreCase(Patients.Contract.GENDER) == 0) { Log.d(TAG, "\tsetting '" + Patients.Contract.GENDER +"'=" +val); mPatient.setGender(val); } if(field.compareToIgnoreCase(Patients.Contract.IMAGE) == 0) { Log.d(TAG, "\tsetting '" + Patients.Contract.IMAGE +"'=" +val); URI file = URI.create(val); mPatient.setImage(file); } if(field.compareToIgnoreCase(Patients.Contract.LOCATION) == 0) { Log.d(TAG, "\tsetting '" + Patients.Contract.LOCATION + "'=" + val); Location location = new Location(); if(UUIDUtil.isValid(val)) { location.setUuid(val); } else { location.setName(val); } mPatient.setLocation(location); } if(field.compareToIgnoreCase(Patients.Contract.CONFIRMED) == 0) { Log.d(TAG, "\tsetting '" + Patients.Contract.CONFIRMED + "'=" + val); mPatient.setConfirmed(Boolean.valueOf(val)); } if(field.compareToIgnoreCase(Patients.Contract.DOB_ESTIMATED) == 0) { Log.d(TAG, "\tsetting '" + Patients.Contract.DOB_ESTIMATED + "'=" + val); mPatient.setConfirmed(Boolean.valueOf(val)); } } Log.d(TAG, "...Updated Patient: " + String.valueOf(mPatient)); // Only save to database after we are finished Log.d(TAG,"...finished=" + finished); if (finished) { uSubject = PatientWrapper.getOrCreate(getActivity(), mPatient); } } /** * Don't actually upload the patient when this gets called. Simply mark * the patient state as to be uploaded. */ @Override public void uploadProcedureInBackground() { storeCurrentProcedure(true); } /** A task for loading the patient registration procedure. * * @author Sana Development Team */ class CreatePatientTask extends ProcedureLoaderTask { @Override protected ProcedureLoadResult doInBackground(ProcedureLoadRequest... params) { ProcedureLoadRequest load = params[0]; instance = load.instance; intent = load.intent; ProcedureLoadResult result = new ProcedureLoadResult(); Uri procedureUri = uProcedure; Procedure p = null; // Create a stub for the patient if (Uris.isEmpty(uSubject)) { mPatient = new PatientParcel(); mPatient.setUuid(UUID.randomUUID().toString()); mPatient.setSystemId(""); mPatient.setFamily_name(""); mPatient.setGiven_name(""); mPatient.setGender("M"); mPatient.setDob(new Date()); uSubject = writeObject(mPatient, 0); } else { // Exists we reload into the Parcelable as below mPatient = new PatientParcel(PatientWrapper.get(getActivity(), uSubject)); } Log.d(TAG, "...using subject:" + uSubject); Log.d(TAG, "... data: " + mPatient); // Patient is loaded now we loaded procedure script Uri procedure = uProcedure; String uuid = procedure.getLastPathSegment(); String procedureXml = null; if(!UUIDUtil.isValid(uuid)){ Log.d(TAG, "...long format for uuid? = " + uuid); uuid = ModelWrapper.getUuid(procedure,getActivity().getContentResolver()); procedure = Uris.withAppendedUuid(Procedures.CONTENT_URI, uuid); } try { // Check for the int procedureResource = intent.getIntExtra( Intents.EXTRA_PROCEDURE_ID, R.raw.findpatient); InputStream rs = getActivity().getResources() .openRawResource(procedureResource); byte[] data = new byte[rs.available()]; rs.read(data); procedureXml = new String(data); p = Procedure.fromXMLString(procedureXml); } catch (IOException e) { e.printStackTrace(); } catch (Exception e){ e.printStackTrace(); } if (p != null) { p.setInstanceUri(uSubject); } result.p = p; result.success = p != null; result.procedureUri = procedure; result.savedProcedureUri = uSubject; return result; } @Override protected void handleResult(ProcedureLoadResult result){ requested--; hideProgressDialogFragment(); if (result != null && result.success) { mProcedure = result.p; uEncounter = result.savedProcedureUri; logEvent(EventType.ENCOUNTER_LOAD_FINISHED, ""); if (mProcedure != null){ mProcedure.setInstanceUri(uEncounter); boolean useId = getActivity().getResources().getBoolean( R.bool.display_registration_input_element_id); Log.d(TAG, "...Setting page display id=" + useId); mProcedure.setShowQuestionIds(useId); createView(); } else logEvent(EventType.ENCOUNTER_LOAD_FAILED, "Null procedure"); } else { // Show error logEvent(EventType.ENCOUNTER_LOAD_FAILED, ""); getActivity().finish(); } } } @Override public void deleteCurrentProcedure() { Log.i(TAG, "deleteCurrentProcedure()"); try { if(objectFlag == FLAG_OBJECT_TEMPORARY){ Log.d(TAG, "...flushing temporary object: " + uSubject); int deleted = getActivity().getContentResolver().delete (uSubject,null,null); Log.d(TAG, "..." + deleted + " deleted"); } else { Log.w(TAG, "...Patient exists but not updating"); } } catch (Exception e) { e.printStackTrace(); } } /** * Convenience wrapper around the older lookup listener method. * @param systemId */ public void onPatientLookupSuccess(String systemId){ Log.i(TAG,"onPatientLookupSuccess(String)"); Patient patient = null; try { patient = PatientWrapper.getOneBySystemId(getActivity() .getContentResolver(), systemId ); } catch(Exception e){ e.printStackTrace(); } Log.d(TAG, "...patient=" + patient); uSubject = Uris.withAppendedUuid(Subjects.CONTENT_URI, patient.getUuid()); onPatientLookupSuccess(patient); } protected Uri getOrCreateStub(ContentValues values){ Uri stub = PatientWrapper.getOrCreate(getActivity(), values); return stub; } /** * Callback to handle when a patient look up succeeds. Will result in an * alert being displayed prompting the user to confirm that it is the * correct patient. */ public void onPatientLookupSuccess(final Patient patient) { Log.i(TAG,"onPatientLookupSuccess(Patient)"); logEvent(EventType.ENCOUNTER_LOOKUP_PATIENT_SUCCESS, patient.getSystemId()); hideProgressDialogFragment(); // TODO: should move error messages to BaseFragment StringBuilder message = new StringBuilder(); message.append("Found patient record for ID "); message.append(patient.getSystemId()); message.append("\n"); message.append("First Name: "); message.append(patient.getGiven_name()); message.append("\n"); message.append("Last Name: "); message.append(patient.getFamily_name()); message.append("\n"); message.append("Gender: "); message.append(patient.getGender()); message.append("\n"); message.append("Is this the correct patient?"); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage(message) .setCancelable(false) .setPositiveButton(getResources().getString(R.string.general_yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); //mProcedure.setPatientInfo(patientInfo); // Delete the stub getActivity().getContentResolver().delete (uSubject,null,null); // set the subject as the confirmed setObject(patient); setObjectFlag(FLAG_OBJECT_UPDATED); mProcedure.restoreAnswers(getModelMap(patient)); nextPage(); } }) .setNegativeButton(getResources().getString(R.string.general_no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); patient.setSystemId(""); setObject(patient); setObjectFlag(FLAG_OBJECT_TEMPORARY); if(mProcedure.current().hasElementWithId (Patients.Contract.PATIENT_ID)) { ((PatientIdElement) mProcedure.current() .getElementByType( Patients.Contract.PATIENT_ID)) .setAndRefreshAnswer(""); } } }); AlertDialog alert = builder.create(); if (!getActivity().isFinishing()) alert.show(); } protected final void setObject(Patient patient){ mPatient = new PatientParcel(patient); uSubject = PatientWrapper.getOrCreate(getActivity(),mPatient); PatientInfo pi = null; if(patient != null) { pi = new PatientInfo(); pi.setPatientBirthdate(patient.getDob()); pi.setPatientFirstName(patient.getGiven_name()); pi.setPatientIdentifier(patient.getSystemId()); pi.setPatientGender(patient.getGender()); pi.setPatientLastName(patient.getFamily_name()); } mProcedure.setPatientInfo(pi); } protected Map<String, String> getModelMap(Patient patient){ Map<String, String> map = new HashMap<String, String>(); map.put(Patients.Contract.PATIENT_ID, patient.getSystemId()); map.put(Patients.Contract.GIVEN_NAME, patient.getGiven_name()); map.put(Patients.Contract.FAMILY_NAME, patient.getFamily_name()); map.put(Patients.Contract.DOB, Dates.toSQL(patient.getDob())); map.put(Patients.Contract.GENDER, patient.getGender()); map.put(Patients.Contract.LOCATION,((patient.getLocation() != null) ? patient.getLocation().getUuid(): getString(R.string.cfg_default_location))); //map.put(Patients.Contract., patient.); return map; } protected Uri writeObject(Patient patient, int state){ // Create Stub for patient ContentValues values = new ContentValues(); values.put(Patients.Contract.PATIENT_ID, patient.getSystemId()); values.put(Patients.Contract.FAMILY_NAME, patient.getFamily_name()); values.put(Patients.Contract.GIVEN_NAME, patient.getGiven_name()); values.put(Patients.Contract.DOB, DateUtil.format(patient.getDob())); values.put(Patients.Contract.GENDER, patient.getGender()); values.put(Patients.Contract.STATE, state); //TODO update db and uncomment //values.put(Patients.Contract.CONFIRMED, patient.getConfirmed()); //values.put(Patients.Contract.DOB_ESTIMATED, patient.isDobEstimated()); values.put(Patients.Contract.LOCATION, ((patient.getLocation() != null) ? patient.getLocation().getUuid() : getString(R.string.cfg_default_location))); // Add the UUID if this is a temporary object-i.e we are creating if(state == 0) { values.put(Patients.Contract.UUID, patient.getUuid()); } objectFlag = state; return PatientWrapper.getOrCreate(getActivity(), patient); } @Override public Intent getResult(String action){ Log.d(TAG, "getResult(String)"); Intent result = new Intent(action, uSubject); onSaveAppState(result); return result; } public int getObjectFlag(){ return objectFlag; } public void setObjectFlag(int flag){ objectFlag = flag; } /** * Trigger an update or create in the network layer depending on the state * of the patient object. */ @Override public void uploadProcedureInBackground2() { Log.i(TAG, "uploadProcedureInBackground2()"); // Stops any instrumentation activity in the background service Intent instrumentation = new Intent(getActivity(), InstrumentationService.class); getActivity().stopService(instrumentation); // Intent data = null; if(mProcedureListener != null){ switch (getObjectFlag()) { case FLAG_OBJECT_UPDATED: data = getResult(Intents.ACTION_UPDATE); break; case FLAG_OBJECT_TEMPORARY: data = getResult(Intents.ACTION_CREATE); break; default: } data.putExtra(Intents.EXTRA_ON_COMPLETE, mProcedure.getOnComplete()); mProcedureListener.onProcedureComplete(data); } else{ data = getResult(); data.putExtra(Intents.EXTRA_ON_COMPLETE, mProcedure.getOnComplete()); getActivity().setResult(Activity.RESULT_OK, data); getActivity().finish(); } } public void onCreateSuccess(Intent intent){ Uri uri = Uri.parse(intent.getStringExtra(Response.MESSAGE)); Patient subject = PatientWrapper.get(getActivity(), uri); if(!subject.getUuid().equalsIgnoreCase(mPatient.getUuid())){ if(objectFlag == FLAG_OBJECT_TEMPORARY) getActivity().getContentResolver().delete(uSubject, null, null); mPatient = new PatientParcel(subject); uSubject = uri; } } /** * Flushes any temporary subject registration in the database and * then calls super method. */ protected void onExitNoSave(){ Log.i(TAG, "onExitNoSave()"); if(objectFlag == FLAG_OBJECT_TEMPORARY) { int deleted = getActivity().getContentResolver().delete(uSubject, null, null); Log.d(TAG, "\tdeleted n=" + deleted); } super.onExitNoSave(); } }