-
Notifications
You must be signed in to change notification settings - Fork 25
Update observations from Xform submission response. #256
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,10 +25,11 @@ | |
| import com.android.volley.VolleyError; | ||
| import com.google.common.base.Charsets; | ||
| import com.google.common.base.Joiner; | ||
| import com.google.gson.Gson; | ||
| import com.google.gson.GsonBuilder; | ||
|
|
||
| import org.javarosa.core.model.data.IAnswerData; | ||
| import org.javarosa.core.model.instance.TreeElement; | ||
| import org.javarosa.xform.parse.XFormParser; | ||
| import org.joda.time.DateTime; | ||
| import org.joda.time.format.ISODateTimeFormat; | ||
| import org.json.JSONObject; | ||
|
|
@@ -37,14 +38,16 @@ | |
| import org.odk.collect.android.model.Preset; | ||
| import org.odk.collect.android.provider.FormsProviderAPI; | ||
| import org.odk.collect.android.tasks.DeleteInstancesTask; | ||
| import org.odk.collect.android.utilities.FileUtils; | ||
| import org.projectbuendia.client.App; | ||
| import org.projectbuendia.client.AppSettings; | ||
| import org.projectbuendia.client.events.FetchXformFailedEvent; | ||
| import org.projectbuendia.client.events.SubmitXformFailedEvent; | ||
| import org.projectbuendia.client.events.SubmitXformSucceededEvent; | ||
| import org.projectbuendia.client.exception.ValidationException; | ||
| import org.projectbuendia.client.json.JsonEncounter; | ||
| import org.projectbuendia.client.json.JsonUser; | ||
| import org.projectbuendia.client.json.Serializers; | ||
| import org.projectbuendia.client.models.Encounter; | ||
| import org.projectbuendia.client.net.OdkDatabase; | ||
| import org.projectbuendia.client.net.OdkXformSyncTask; | ||
| import org.projectbuendia.client.net.OpenMrsXformIndexEntry; | ||
|
|
@@ -87,17 +90,19 @@ public class OdkActivityLauncher { | |
| */ | ||
| public static void fetchAndCacheAllXforms() { | ||
| new OpenMrsXformsConnection(App.getConnectionDetails()).listXforms( | ||
| new Response.Listener<List<OpenMrsXformIndexEntry>>() { | ||
| @Override public void onResponse(final List<OpenMrsXformIndexEntry> response) { | ||
| for (OpenMrsXformIndexEntry formEntry : response) { | ||
| fetchAndCacheXForm(formEntry); | ||
| new Response.Listener<List<OpenMrsXformIndexEntry>>() { | ||
| @Override | ||
| public void onResponse(final List<OpenMrsXformIndexEntry> response) { | ||
| for (OpenMrsXformIndexEntry formEntry : response) { | ||
| fetchAndCacheXForm(formEntry); | ||
| } | ||
| } | ||
| } | ||
| }, new Response.ErrorListener() { | ||
| @Override public void onErrorResponse(VolleyError error) { | ||
| handleFetchError(error); | ||
| } | ||
| }); | ||
| }, new Response.ErrorListener() { | ||
| @Override | ||
| public void onErrorResponse(VolleyError error) { | ||
| handleFetchError(error); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -107,7 +112,7 @@ public static void fetchAndCacheAllXforms() { | |
| */ | ||
| public static void fetchAndCacheXForm(OpenMrsXformIndexEntry formEntry) { | ||
| new OdkXformSyncTask(null).fetchAndAddXFormToDb(formEntry.uuid, | ||
| formEntry.makeFileForForm()); | ||
| formEntry.makeFileForForm()); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -135,23 +140,25 @@ public static void fetchAndShowXform( | |
| } | ||
|
|
||
| new OpenMrsXformsConnection(App.getConnectionDetails()).listXforms( | ||
| new Response.Listener<List<OpenMrsXformIndexEntry>>() { | ||
| @Override public void onResponse(final List<OpenMrsXformIndexEntry> response) { | ||
| if (response.isEmpty()) { | ||
| LOG.i("No forms found"); | ||
| EventBus.getDefault().post(new FetchXformFailedEvent( | ||
| FetchXformFailedEvent.Reason.NO_FORMS_FOUND)); | ||
| return; | ||
| new Response.Listener<List<OpenMrsXformIndexEntry>>() { | ||
| @Override | ||
| public void onResponse(final List<OpenMrsXformIndexEntry> response) { | ||
| if (response.isEmpty()) { | ||
| LOG.i("No forms found"); | ||
| EventBus.getDefault().post(new FetchXformFailedEvent( | ||
| FetchXformFailedEvent.Reason.NO_FORMS_FOUND)); | ||
| return; | ||
| } | ||
| showForm(callingActivity, requestCode, patient, fields, findUuid(response, | ||
| uuidToShow)); | ||
| } | ||
| showForm(callingActivity, requestCode, patient, fields, findUuid(response, | ||
| uuidToShow)); | ||
| } | ||
| }, new Response.ErrorListener() { | ||
| @Override public void onErrorResponse(VolleyError error) { | ||
| LOG.e(error, "Fetching xform list from server failed. "); | ||
| handleFetchError(error); | ||
| } | ||
| }); | ||
| }, new Response.ErrorListener() { | ||
| @Override | ||
| public void onErrorResponse(VolleyError error) { | ||
| LOG.e(error, "Fetching xform list from server failed. "); | ||
| handleFetchError(error); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -310,12 +317,6 @@ public static void sendOdkResultToServer( | |
| throw new ValidationException("No id to delete for after upload: " + uri); | ||
| } | ||
|
|
||
| // Temporary code for messing about with xform instance, reading values. | ||
| byte[] fileBytes = FileUtils.getFileAsBytes(new File(filePath)); | ||
|
|
||
| // get the root of the saved and template instances | ||
| final TreeElement savedRoot = XFormParser.restoreDataModel(fileBytes, null).getRoot(); | ||
|
|
||
| final String xml = readFromPath(filePath); | ||
| if(!validateXml(xml)) { | ||
| throw new ValidationException("Xml form is not valid for uri: " + uri); | ||
|
|
@@ -327,7 +328,9 @@ public static void sendOdkResultToServer( | |
| LOG.i("Created new encounter successfully on server" + response.toString()); | ||
| // Only locally cache new observations, not new patients. | ||
| if (patientUuid != null) { | ||
| updateObservationCache(patientUuid, savedRoot, context.getContentResolver()); | ||
| updateObservationCache( | ||
| response, | ||
| context.getContentResolver()); | ||
| } | ||
| if (!settings.getKeepFormInstancesLocally()) { | ||
| deleteLocalFormInstances(formIdToDelete); | ||
|
|
@@ -447,7 +450,7 @@ private static Cursor getCursorAtRightPosition(final Context context, final Uri | |
| if (instanceCursor.getCount() != 1) { | ||
| LOG.e("The form that we tried to load did not exist: " + uri); | ||
| EventBus.getDefault().post( | ||
| new SubmitXformFailedEvent(SubmitXformFailedEvent.Reason.CLIENT_ERROR)); | ||
| new SubmitXformFailedEvent(SubmitXformFailedEvent.Reason.CLIENT_ERROR)); | ||
| return null; | ||
| } | ||
| instanceCursor.moveToFirst(); | ||
|
|
@@ -546,16 +549,33 @@ private static String readFromPath(String path) { | |
| } | ||
| return sb.toString(); | ||
| } catch (IOException e) { | ||
| LOG.e(e, format("Failed to read xml form into a String. FilePath= ", path)); | ||
| LOG.e(e, format("Failed to read xml form into a String. FilePath= %s", path)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had a lint warning :) |
||
| return null; | ||
| } | ||
| } | ||
|
|
||
| /** Updates observations locally from a JSON response. */ | ||
| private static void updateObservationCache(JSONObject response, ContentResolver resolver) { | ||
|
|
||
| // TODO: don't parse this here or do a roundtrip text --> JSON --> text --> GSON conversion | ||
| GsonBuilder gsonBuilder = new GsonBuilder(); | ||
| Serializers.registerTo(gsonBuilder); | ||
| Gson gson = gsonBuilder.create(); | ||
| JsonEncounter jsonEncounter = gson.fromJson(response.toString(), JsonEncounter.class); | ||
| Encounter encounter = Encounter.fromJson(jsonEncounter.patient_uuid, jsonEncounter); | ||
| ContentValues[] values = encounter.toContentValuesArray(); | ||
| if (values.length > 0) { | ||
| resolver.bulkInsert(Contracts.Observations.CONTENT_URI, values); | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Caches the observation changes locally for a given patient. | ||
| * Updates observations locally from an Xforms XML document. Use this when observations need to | ||
| * be updated locally, and haven't been sent to a server yet. | ||
| */ | ||
| private static void updateObservationCache(String patientUuid, TreeElement savedRoot, | ||
| ContentResolver resolver) { | ||
| private static void updateObservationCacheFromXformData( | ||
| String patientUuid, TreeElement savedRoot, ContentResolver resolver) { | ||
| ContentValues common = new ContentValues(); | ||
| // It's critical that UUID is {@code null} for temporary observations, so we make it | ||
| // explicit here. See {@link Contracts.Observations.UUID} for details. | ||
|
|
@@ -581,7 +601,7 @@ private static void updateObservationCache(String patientUuid, TreeElement saved | |
| } | ||
|
|
||
| resolver.bulkInsert(Contracts.Observations.CONTENT_URI, | ||
| toInsert.toArray(new ContentValues[toInsert.size()])); | ||
| toInsert.toArray(new ContentValues[toInsert.size()])); | ||
| } | ||
|
|
||
| /** Get a map from XForm ids to UUIDs from our local concept database. */ | ||
|
|
@@ -591,14 +611,14 @@ private static Map<String, String> mapFormConceptIdToUuid(Set<Integer> xformConc | |
|
|
||
| HashMap<String, String> xformIdToUuid = new HashMap<>(); | ||
| Cursor cursor = resolver.query(Contracts.Concepts.CONTENT_URI, | ||
| new String[] {Contracts.Concepts.UUID, Contracts.Concepts.XFORM_ID}, | ||
| Contracts.Concepts.XFORM_ID + " IN (" + inClause + ")", | ||
| null, null); | ||
| new String[] {Contracts.Concepts.UUID, Contracts.Concepts.XFORM_ID}, | ||
| Contracts.Concepts.XFORM_ID + " IN (" + inClause + ")", | ||
| null, null); | ||
|
|
||
| try { | ||
| while (cursor.moveToNext()) { | ||
| xformIdToUuid.put(Utils.getString(cursor, Contracts.Concepts.XFORM_ID), | ||
| Utils.getString(cursor, Contracts.Concepts.UUID)); | ||
| Utils.getString(cursor, Contracts.Concepts.UUID)); | ||
| } | ||
| } finally { | ||
| cursor.close(); | ||
|
|
@@ -616,8 +636,8 @@ private static Map<String, String> mapFormConceptIdToUuid(Set<Integer> xformConc | |
| * @param xformConceptIdsAccumulator the set to store the form concept ids found | ||
| */ | ||
| private static List<ContentValues> getAnsweredObservations(ContentValues common, | ||
| TreeElement savedRoot, | ||
| Set<Integer> xformConceptIdsAccumulator) { | ||
| TreeElement savedRoot, | ||
| Set<Integer> xformConceptIdsAccumulator) { | ||
| List<ContentValues> answeredObservations = new ArrayList<>(); | ||
| for (int i = 0; i < savedRoot.getNumChildren(); i++) { | ||
| TreeElement group = savedRoot.getChildAt(i); | ||
|
|
@@ -669,15 +689,15 @@ private static DateTime getEncounterAnswerDateTime(TreeElement root) { | |
| } | ||
|
|
||
| TreeElement encounterDatetime = | ||
| encounter.getChild("encounter.encounter_datetime", 0); | ||
| encounter.getChild("encounter.encounter_datetime", 0); | ||
| if (encounterDatetime == null) { | ||
| LOG.e("No encounter date time found in instance"); | ||
| return null; | ||
| } | ||
|
|
||
| IAnswerData dateTimeValue = encounterDatetime.getValue(); | ||
| try { | ||
| return ISODateTimeFormat.dateTime().parseDateTime((String) dateTimeValue.getValue()); | ||
| return ISODateTimeFormat.dateTime().parseDateTime((String) dateTimeValue.getValue()); | ||
| } catch (IllegalArgumentException e) { | ||
| LOG.e("Could not parse datetime" + dateTimeValue.getValue()); | ||
| return null; | ||
|
|
@@ -693,7 +713,7 @@ private static Integer getConceptId(Set<Integer> accumulator, String encodedConc | |
| } | ||
|
|
||
| private static boolean mapIdToUuid( | ||
| Map<String, String> idToUuid, ContentValues values, String key) { | ||
| Map<String, String> idToUuid, ContentValues values, String key) { | ||
| String id = (String) values.get(key); | ||
| String uuid = idToUuid.get(id); | ||
| if (uuid == null) { | ||
|
|
@@ -716,4 +736,6 @@ private static Integer getConceptId(String encodedConcept) { | |
| return null; | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@capnfabs It's difficult to use the Generic error listener (the one with the toasts)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, don't need to do it right now though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it would be difficult, it was just like this when I got here :)