diff --git a/api-android/src/main/java/org/sana/android/content/ModelContentProvider.java b/api-android/src/main/java/org/sana/android/content/ModelContentProvider.java index ff0c220..a590388 100644 --- a/api-android/src/main/java/org/sana/android/content/ModelContentProvider.java +++ b/api-android/src/main/java/org/sana/android/content/ModelContentProvider.java @@ -42,6 +42,7 @@ import org.sana.android.db.impl.NotificationsHelper; import org.sana.android.db.impl.ObservationsHelper; import org.sana.android.db.impl.ObserversHelper; +import org.sana.android.db.impl.ProcedureGroupsHelper; import org.sana.android.db.impl.ProceduresHelper; import org.sana.android.db.impl.SubjectsHelper; @@ -110,6 +111,8 @@ protected TableHelper getTableHelper(Uri uri) { return SubjectsHelper.getInstance(); case (Uris.ENCOUNTER_TASK): return EncounterTasksHelper.getInstance(); + case (Uris.PROCEDURE_GROUP): + return ProcedureGroupsHelper.getInstance(); default: throw new IllegalArgumentException("Invalid uri in " + "getTableHelper(): " + uri.toString()); diff --git a/api-android/src/main/java/org/sana/android/content/Uris.java b/api-android/src/main/java/org/sana/android/content/Uris.java index 2781c91..e09a41b 100644 --- a/api-android/src/main/java/org/sana/android/content/Uris.java +++ b/api-android/src/main/java/org/sana/android/content/Uris.java @@ -42,6 +42,7 @@ import org.sana.android.provider.ObservationTasks; import org.sana.android.provider.Observations; import org.sana.android.provider.Observers; +import org.sana.android.provider.ProcedureGroups; import org.sana.android.provider.Procedures; import org.sana.android.provider.Subjects; import org.sana.util.UUIDUtil; @@ -145,6 +146,7 @@ static interface Settings{ public static final int SUBJECT = 512 << CONTENT_SHIFT; public static final int ENCOUNTER_TASK = 1024 << CONTENT_SHIFT; public static final int OBSERVATION_TASK = 2048 << CONTENT_SHIFT; + public static final int PROCEDURE_GROUP = 4096 << CONTENT_SHIFT; // dir match codes OBJECT | ITEMS public static final int CONCEPT_DIR = CONCEPT | ITEMS; @@ -159,6 +161,7 @@ static interface Settings{ public static final int SUBJECT_DIR = SUBJECT | ITEMS; public static final int ENCOUNTER_TASK_DIR = ENCOUNTER_TASK | ITEMS; public static final int OBSERVATION_TASK_DIR = OBSERVATION_TASK | ITEMS; + public static final int PROCEDURE_GROUP_DIR = PROCEDURE_GROUP | ITEMS; // item match codes OBJECT | ITEM_ID public static final int CONCEPT_ITEM = CONCEPT | ITEM_ID; @@ -173,6 +176,7 @@ static interface Settings{ public static final int SUBJECT_ITEM = SUBJECT | ITEM_ID; public static final int ENCOUNTER_TASK_ITEM = ENCOUNTER_TASK | ITEM_ID; public static final int OBSERVATION_TASK_ITEM = OBSERVATION_TASK | ITEM_ID; + public static final int PROCEDURE_GROUP_ITEM = PROCEDURE_GROUP | ITEM_ID; // item match codes OBJECT | ITEM_UUID public static final int CONCEPT_UUID = CONCEPT | ITEM_UUID; @@ -187,6 +191,7 @@ static interface Settings{ public static final int SUBJECT_UUID = SUBJECT | ITEM_UUID; public static final int ENCOUNTER_TASK_UUID = ENCOUNTER_TASK | ITEM_UUID; public static final int OBSERVATION_TASK_UUID = OBSERVATION_TASK | ITEM_UUID; + public static final int PROCEDURE_GROUP_UUID = PROCEDURE_GROUP | ITEM_UUID; // Matcher for mapping the Uri to code mappings private static final UriMatcher mMatcher = new UriMatcher(UriMatcher.NO_MATCH); @@ -233,6 +238,9 @@ static interface Settings{ mMatcher.addURI(Models.AUTHORITY, "core/patient/", SUBJECT_DIR); mMatcher.addURI(Models.AUTHORITY, "core/patient/#", SUBJECT_ITEM); mMatcher.addURI(Models.AUTHORITY, "core/patient/*", SUBJECT_UUID); + mMatcher.addURI(Models.AUTHORITY, "core/proceduregroup/", PROCEDURE_GROUP_DIR); + mMatcher.addURI(Models.AUTHORITY, "core/proceduregroup/#", PROCEDURE_GROUP_ITEM); + mMatcher.addURI(Models.AUTHORITY, "core/proceduregroup/*", PROCEDURE_GROUP_UUID); mMatcher.addURI(Models.AUTHORITY, "tasks/encounter/", ENCOUNTER_TASK_DIR); mMatcher.addURI(Models.AUTHORITY, "tasks/encounter/#", ENCOUNTER_TASK_ITEM); @@ -417,6 +425,11 @@ public static String getType(Uri uri) { case OBSERVATION_TASK_UUID: case OBSERVATION_TASK_ITEM: return ObservationTasks.CONTENT_ITEM_TYPE; + case PROCEDURE_GROUP_DIR: + return ProcedureGroups.CONTENT_TYPE; + case PROCEDURE_GROUP_UUID: + case PROCEDURE_GROUP_ITEM: + return ProcedureGroups.CONTENT_ITEM_TYPE; case PACKAGE_DIR: return "application/vnd.android.package-archive"; default: diff --git a/api-android/src/main/java/org/sana/android/db/impl/ProcedureGroupsHelper.java b/api-android/src/main/java/org/sana/android/db/impl/ProcedureGroupsHelper.java new file mode 100644 index 0000000..b183a6d --- /dev/null +++ b/api-android/src/main/java/org/sana/android/db/impl/ProcedureGroupsHelper.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2013, Sana + * All rights reserved. + *

+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *

+ * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Sana nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Sana BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.sana.android.db.impl; + +import android.content.ContentValues; +import android.net.Uri; +import android.util.Log; + +import org.sana.android.db.TableHelper; +import org.sana.android.provider.ProcedureGroups.Contract; +import org.sana.core.ProcedureGroup; + +/** + * A database table helper for a table of procedure groups. + * + * @author Sana Development + * + */ +public class ProcedureGroupsHelper extends TableHelper { + public static final String TAG = ProcedureGroupsHelper.class.getSimpleName(); + + private static final ProcedureGroupsHelper HELPER = new ProcedureGroupsHelper(); + + /** + * Gets the singleton instance of this class. + * + * @return An instance of this class. + */ + public static ProcedureGroupsHelper getInstance() { + return HELPER; + } + + protected ProcedureGroupsHelper() { + super(ProcedureGroup.class); + } + + /* (non-Javadoc) + * @see org.sana.android.db.InsertHelper#onInsert(android.net.Uri, android.content.ContentValues) + */ + @Override + public ContentValues onInsert(ContentValues values) { + return super.onInsert(values); + } + + /* (non-Javadoc) + * @see org.sana.android.db.UpdateHelper#onUpdate(java.lang.String, android.content.ContentValues, java.lang.String, java.lang.String[]) + */ + @Override + public ContentValues onUpdate(Uri uri, ContentValues values) { + return super.onUpdate(uri, values); + } + + /* (non-Javadoc) + * @see org.sana.android.db.CreateHelper#onCreate(android.database.sqlite.SQLiteDatabase) + */ + @Override + public String onCreate() { + Log.i(TAG, "onCreate()"); + return "CREATE TABLE " + getTable() + " (" + + Contract.UUID + " TEXT PRIMARY KEY, " + + Contract.CREATED + " DATE, " + + Contract.MODIFIED + " DATE, " + + Contract.TITLE + " TEXT NOT NULL, " + + Contract.AUTHOR + " TEXT NOT NULL, " + + Contract.DESCRIPTION + " TEXT, " + + Contract.PROCEDURE_NAMES + " TEXT " + + ");"; + } + + /* (non-Javadoc) + * @see org.sana.android.db.UpgradeHelper#onUpgrade(int, int) + */ + @Override + public String onUpgrade(int oldVersion, int newVersion) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/api-android/src/main/java/org/sana/android/provider/ProcedureGroups.java b/api-android/src/main/java/org/sana/android/provider/ProcedureGroups.java new file mode 100644 index 0000000..8aa01cb --- /dev/null +++ b/api-android/src/main/java/org/sana/android/provider/ProcedureGroups.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2013, Sana + * All rights reserved. + *

+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *

+ * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Sana nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Sana BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.sana.android.provider; + +import android.net.Uri; + +import org.sana.core.ProcedureGroup; + +/** + * Metadata and contract for procedure groups in the database. + * + * @author Sana Development Team + */ +public class ProcedureGroups { + + /** The authority for procedure groups. */ + public static final String AUTHORITY = "org.sana.provider"; + + /** The content:// style URI for this content provider. */ + public static final Uri CONTENT_URI = Uri.parse("content://" + + AUTHORITY + "/core/proceduregroup"); + + /** The MIME type for a directory of procedure groups. */ + public static final String CONTENT_TYPE = + "vnd.android.cursor.dir/org.sana.proceduregroup"; + + /** The MIME type of single procedure group. */ + public static final String CONTENT_ITEM_TYPE = + "vnd.android.cursor.item/org.sana.proceduregroup"; + + /** The default sort order. */ + public static final String DEFAULT_SORT_ORDER = "modified DESC"; + + /** + * Contract for the Procedure Groups table in the database. + * + * @author Sana Development + * + */ + public interface Contract extends BaseContract { + /** The title of the procedure group. */ + String TITLE = "title"; + + /** The author of the procedure group. */ + String AUTHOR = "author"; + + /** The description of the procedure group. */ + String DESCRIPTION = "description"; + + /** A list of ids of the procedures. */ + String PROCEDURE_NAMES = "procedure_names"; + } +} diff --git a/api-android/src/main/java/org/sana/android/provider/Subjects.java b/api-android/src/main/java/org/sana/android/provider/Subjects.java index e2e3de3..f55ed34 100644 --- a/api-android/src/main/java/org/sana/android/provider/Subjects.java +++ b/api-android/src/main/java/org/sana/android/provider/Subjects.java @@ -41,7 +41,7 @@ public class Subjects { /** The content:// style URI for this content provider. */ public static final Uri CONTENT_URI = Uri.parse("content://" - + AUTHORITY + "/core/subject"); + + AUTHORITY + "core/subject"); /** The MIME type of CONTENT_URI providing a directory of subjects. */ public static final String CONTENT_TYPE = diff --git a/api/src/main/java/org/sana/api/IProcedureGroup.java b/api/src/main/java/org/sana/api/IProcedureGroup.java new file mode 100644 index 0000000..5576ab3 --- /dev/null +++ b/api/src/main/java/org/sana/api/IProcedureGroup.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2013, Sana + * All rights reserved. + *

+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *

+ * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Sana nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Sana BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.sana.api; + +import java.util.List; + +/** + * @author Sana Development + * + */ +public interface IProcedureGroup extends IModel { + + /** + * @return the title + */ + String getTitle(); + + /** + * @return the author + */ + String getAuthor(); + + /** + * @return the description + */ + String getDescription(); + + /** + * @return the list of associated procedure ids + */ + List getProcedureNames(); +} diff --git a/api/src/main/java/org/sana/core/ProcedureGroup.java b/api/src/main/java/org/sana/core/ProcedureGroup.java new file mode 100644 index 0000000..ab08a83 --- /dev/null +++ b/api/src/main/java/org/sana/core/ProcedureGroup.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2013, Sana + * All rights reserved. + *

+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *

+ * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Sana nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Sana BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.sana.core; + +import org.sana.api.IProcedureGroup; + +import java.util.List; + + +/** + * A group of Procedures + * + * @author Sana Development + */ +public class ProcedureGroup extends Model implements IProcedureGroup { + private String title; + private String author; + private String description; + private List procedures; + + @Override + public String getTitle() { + return this.title; + } + + @Override + public String getAuthor() { + return this.author; + } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public List getProcedureNames() { + return this.procedures; + } +} diff --git a/api/src/main/java/org/sana/net/http/handler/ProcedureGroupResponseHandler.java b/api/src/main/java/org/sana/net/http/handler/ProcedureGroupResponseHandler.java new file mode 100644 index 0000000..2036ed9 --- /dev/null +++ b/api/src/main/java/org/sana/net/http/handler/ProcedureGroupResponseHandler.java @@ -0,0 +1,18 @@ +package org.sana.net.http.handler; + +import com.google.gson.reflect.TypeToken; + +import org.sana.core.ProcedureGroup; +import org.sana.net.Response; + +import java.lang.reflect.Type; +import java.util.Collection; + +public class ProcedureGroupResponseHandler extends ApiResponseHandler>> { + @Override + public Type getType() { + Type type = new TypeToken>>() { + }.getType(); + return type; + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4364c77..d071164 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -318,6 +318,10 @@ + + diff --git a/app/src/main/java/org/sana/android/Constants.java b/app/src/main/java/org/sana/android/Constants.java index 0600c5e..23bc853 100644 --- a/app/src/main/java/org/sana/android/Constants.java +++ b/app/src/main/java/org/sana/android/Constants.java @@ -202,6 +202,16 @@ public class Constants { */ public static final String PREFERENCE_MDS_PORT = "s_mds_port"; + /** + * Key for ms timestamp of last procedure group sync. + */ + public static final String PREFERENCE_PROCEDURE_GROUP_LAST_SYNC = "procedure_group_last_sync"; + + /** + * Period in ms for procedure group sync. + */ + public static final int PROCEDURE_GROUP_SYNC_PERIOD = 86400000; + /** * Key for looking up mds server host. */ diff --git a/app/src/main/java/org/sana/android/activity/MainActivity.java b/app/src/main/java/org/sana/android/activity/MainActivity.java index 5bb54d8..98225d3 100644 --- a/app/src/main/java/org/sana/android/activity/MainActivity.java +++ b/app/src/main/java/org/sana/android/activity/MainActivity.java @@ -8,6 +8,7 @@ import java.util.Date; import org.sana.R; +import org.sana.android.task.ProcedureGroupSyncTask; import org.sana.api.IModel; import org.sana.net.Response; import org.sana.analytics.Runner; @@ -215,6 +216,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { public void hideViewsByRole() { mRoot = isAdmin(mObserver); if (!mRoot) { + findViewById(R.id.button_main_select_procedure_groups).setVisibility(View.GONE); LinearLayout main = (LinearLayout) findViewById(R.id.main_root); // TODO RBAC //main.removeView(findViewById(R.id.btn_main_select_patient)); @@ -266,6 +268,7 @@ protected void onResume() { // This prevents us from relaunching the login on every resume dump(); hideViewsByRole(); + syncProcedureGroups(); } @Override @@ -602,6 +605,10 @@ public void submit(View v) { .putExtra(Intents.EXTRA_OBSERVER, mObserver); startActivityForResult(intent, RUN_PROCEDURE); break; + case R.id.button_main_select_procedure_groups: + intent = new Intent(this.getApplicationContext(), SelectProcedureGroups.class); + startActivity(intent); + break; /* case R.id.btn_main_unregistered_subject: intent = new Intent(Intents.ACTION_RUN_PROCEDURE); @@ -820,4 +827,18 @@ protected void handleBroadcast(Intent data) { } } } + + private boolean syncProcedureGroups() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + long lastSync = prefs.getLong(Constants.PREFERENCE_PROCEDURE_GROUP_LAST_SYNC, 0); + long now = System.currentTimeMillis(); + + boolean synced = false; + if (now - lastSync > Constants.PROCEDURE_GROUP_SYNC_PERIOD) { + ProcedureGroupSyncTask procedureGroupSyncTask = new ProcedureGroupSyncTask(this); + procedureGroupSyncTask.execute(); + synced = true; + } + return synced; + } } diff --git a/app/src/main/java/org/sana/android/activity/PatientsList.java b/app/src/main/java/org/sana/android/activity/PatientsList.java index cea6a47..6f87a2e 100644 --- a/app/src/main/java/org/sana/android/activity/PatientsList.java +++ b/app/src/main/java/org/sana/android/activity/PatientsList.java @@ -99,7 +99,7 @@ public void onAttachFragment(Fragment fragment) { mFragmentPatientList = (PatientListFragment) fragment; mFragmentPatientList.setOnPatientSelectedListener(this); mFragmentPatientList.setOnScrollCompleteListener(this); - if (mFragmentPatientList.sync(this, Subjects.CONTENT_URI)) { + if (mFragmentPatientList.syncPatients(this, Subjects.CONTENT_URI)) { showProgressDialog(getString(R.string.general_synchronizing), getString(R.string.general_fetching_patients)); } @@ -149,7 +149,7 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; case R.id.menu_sync_patients: getContentResolver().delete(Subjects.CONTENT_URI, null, null); - mFragmentPatientList.syncForced(this, Subjects.CONTENT_URI); + mFragmentPatientList.syncPatientsForced(this, Subjects.CONTENT_URI); return true; case R.id.menu_delete_patients: getContentResolver().delete(Subjects.CONTENT_URI, null, null); @@ -318,7 +318,7 @@ public void submit(View view) { break; case R.id.sync: getContentResolver().delete(Subjects.CONTENT_URI, null, null); - mFragmentPatientList.syncForced(this, Subjects.CONTENT_URI); + mFragmentPatientList.syncPatientsForced(this, Subjects.CONTENT_URI); break; default: } diff --git a/app/src/main/java/org/sana/android/activity/SelectProcedureGroups.java b/app/src/main/java/org/sana/android/activity/SelectProcedureGroups.java new file mode 100644 index 0000000..0446aa6 --- /dev/null +++ b/app/src/main/java/org/sana/android/activity/SelectProcedureGroups.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2013, Sana + * All rights reserved. + *

+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *

+ * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Sana nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Sana BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.sana.android.activity; + + +import android.app.ListActivity; +import android.content.ContentValues; +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.TextView; + +import org.sana.R; +import org.sana.android.provider.ProcedureGroups; +import org.sana.android.task.FetchProcedureGroupsTask; +import org.sana.android.task.ProcedureGroupSyncTask; +import org.sana.core.ProcedureGroup; + +import java.util.List; + +/** + * A list activity that lets users initiate a sync for procedure groups. + * + * @author Sana Development + * + */ +public class SelectProcedureGroups extends ListActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.select_procedure_groups); + + new FetchProcedureGroupsTask(this, new FetchProcedureGroupsTask.FetchProcedureGroupsCallback() { + @Override + public void onProcedureGroupsLoaded(List procedureGroups) { + SelectProcedureGroupsAdapter adapter = new SelectProcedureGroupsAdapter(SelectProcedureGroups.this, procedureGroups); + setListAdapter(adapter); + } + }).execute(); + } + + private static class SelectProcedureGroupsAdapter extends ArrayAdapter { + private Context mContext; + private List mProcedureGroups; + + public SelectProcedureGroupsAdapter(Context context, List procedureGroups) { + super(context, 0, procedureGroups); + mContext = context; + mProcedureGroups = procedureGroups; + } + + @Override + public View getView(int position, View procedureItem, ViewGroup parent) { + if (procedureItem == null) { + procedureItem = LayoutInflater.from(mContext).inflate(R.layout.procedure_group_item, parent, false); + } + + final ProcedureGroup procedureGroup = mProcedureGroups.get(position); + + TextView textTitle = (TextView) procedureItem.findViewById(R.id.text_title); + textTitle.setText(procedureGroup.getTitle()); + + TextView textAuthor = (TextView) procedureItem.findViewById(R.id.text_author); + textAuthor.setText(procedureGroup.getAuthor()); + + TextView textNumProcedures = (TextView) procedureItem.findViewById(R.id.text_num_procedures); + int numProcedures = procedureGroup.getProcedureNames().size(); + textNumProcedures.setText(String.format(getContext().getString(R.string.procedure_group_num_procedures), numProcedures)); + + Button syncButton = (Button) procedureItem.findViewById(R.id.button_sync_procedure_group); + syncButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + insertProcedureGroup(procedureGroup); + + // Sync right away to get the new procedures. + ProcedureGroupSyncTask procedureGroupSyncTask = new ProcedureGroupSyncTask(getContext()); + procedureGroupSyncTask.execute(); + } + }); + + return procedureItem; + } + + private void insertProcedureGroup(ProcedureGroup procedureGroup) { + final ContentValues cv = new ContentValues(); + cv.put(ProcedureGroups.Contract.UUID, procedureGroup.getUuid()); + cv.put(ProcedureGroups.Contract.TITLE, procedureGroup.getTitle()); + cv.put(ProcedureGroups.Contract.AUTHOR, procedureGroup.getAuthor()); + cv.put(ProcedureGroups.Contract.DESCRIPTION, procedureGroup.getDescription()); + cv.put(ProcedureGroups.Contract.PROCEDURE_NAMES, TextUtils.join(",", procedureGroup.getProcedureNames())); + + int numUpdates = getContext().getContentResolver().update(ProcedureGroups.CONTENT_URI, cv, "uuid = ?", new String[] {procedureGroup.getUuid()}); + if (numUpdates == 0) { + getContext().getContentResolver().insert(ProcedureGroups.CONTENT_URI, cv); + } + } + } +} diff --git a/app/src/main/java/org/sana/android/db/DatabaseHelper.java b/app/src/main/java/org/sana/android/db/DatabaseHelper.java index d9e6ae5..286c7f6 100644 --- a/app/src/main/java/org/sana/android/db/DatabaseHelper.java +++ b/app/src/main/java/org/sana/android/db/DatabaseHelper.java @@ -28,6 +28,7 @@ package org.sana.android.db; import org.sana.R; +import org.sana.android.provider.ProcedureGroups; import android.content.Context; import android.database.sqlite.SQLiteDatabase; diff --git a/app/src/main/java/org/sana/android/db/impl/DatabaseOpenHelperImpl.java b/app/src/main/java/org/sana/android/db/impl/DatabaseOpenHelperImpl.java index 32a90ff..cff3c04 100644 --- a/app/src/main/java/org/sana/android/db/impl/DatabaseOpenHelperImpl.java +++ b/app/src/main/java/org/sana/android/db/impl/DatabaseOpenHelperImpl.java @@ -81,7 +81,8 @@ public void onCreate(SQLiteDatabase db) { ObservationsHelper.getInstance().onCreate(), ObserversHelper.getInstance().onCreate(), ProceduresHelper.getInstance().onCreate(), - SubjectsHelper.getInstance().onCreate() + SubjectsHelper.getInstance().onCreate(), + ProcedureGroupsHelper.getInstance().onCreate() }; //db.acquireReference(); for (String sql : create) { @@ -112,7 +113,9 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ObservationsHelper.getInstance(), ObserversHelper.getInstance(), ProceduresHelper.getInstance(), - SubjectsHelper.getInstance()}; + SubjectsHelper.getInstance(), + ProcedureGroupsHelper.getInstance() + }; for (TableHelper helper : helpers) { diff --git a/app/src/main/java/org/sana/android/fragment/PatientListFragment.java b/app/src/main/java/org/sana/android/fragment/PatientListFragment.java index ab556b9..d514bd0 100644 --- a/app/src/main/java/org/sana/android/fragment/PatientListFragment.java +++ b/app/src/main/java/org/sana/android/fragment/PatientListFragment.java @@ -72,7 +72,7 @@ public class PatientListFragment extends ListFragment implements LoaderCallbacks private OnPatientSelectedListener mListener; Handler mHandler; private boolean doSync = false; - private int delta = 1000 * 60; + private int patientsDelta = 1000 * 60; private ScrollCompleteListener mScrollListener = null; // @@ -88,7 +88,7 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setRetainInstance(true); Locales.updateLocale(this.getActivity(), getString(R.string.force_locale)); - delta = getResources().getInteger(R.integer.sync_delta_subjects); + patientsDelta = getResources().getInteger(R.integer.sync_delta_subjects); } @Override @@ -106,7 +106,7 @@ public void onActivityCreated(Bundle savedInstanceState) { setListAdapter(mAdapter); mAdapter.setOnScrollCompleteListener(this); // Do we sync with server - delta = getActivity().getResources().getInteger(R.integer.sync_delta_subjects); + patientsDelta = getActivity().getResources().getInteger(R.integer.sync_delta_subjects); //sync(getActivity(), Subjects.CONTENT_URI); LoaderManager.enableDebugLogging(true); getActivity().getSupportLoaderManager().initLoader(PATIENTS_LOADER, null, this); @@ -476,8 +476,8 @@ public void setOnScrollCompleteListener(ScrollCompleteListener listener) { } } - public final boolean sync(Context context, Uri uri) { - Log.d(TAG, "sync(Context,Uri)"); + public final boolean syncPatients(Context context, Uri uri) { + Log.d(TAG, "syncPatients(Context,Uri)"); boolean result = false; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); long lastSync = prefs.getLong("patient_sync", 0); @@ -485,7 +485,7 @@ public final boolean sync(Context context, Uri uri) { Log.d(TAG, "last: " + lastSync + ", now: " + now + ", delta: " + (now - lastSync) + ", doSync: " + ((now - lastSync) > 86400000)); // TODO // Once a day 86400000 - if ((now - lastSync) > delta) { + if ((now - lastSync) > patientsDelta) { Logf.W(TAG, "sync(): synchronizing patient list"); prefs.edit().putLong("patient_sync", now).commit(); Intent intent = new Intent(Intents.ACTION_READ, uri); @@ -495,8 +495,8 @@ public final boolean sync(Context context, Uri uri) { return result; } - public final boolean syncForced(Context context, Uri uri) { - Log.d(TAG, "syncForced(Context,Uri)"); + public final boolean syncPatientsForced(Context context, Uri uri) { + Log.d(TAG, "syncPatientsForced(Context,Uri)"); boolean result = false; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); long now = System.currentTimeMillis(); diff --git a/app/src/main/java/org/sana/android/net/MDSInterface2.java b/app/src/main/java/org/sana/android/net/MDSInterface2.java index 4604569..9d8febc 100644 --- a/app/src/main/java/org/sana/android/net/MDSInterface2.java +++ b/app/src/main/java/org/sana/android/net/MDSInterface2.java @@ -22,6 +22,7 @@ import javax.xml.parsers.ParserConfigurationException; +import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; @@ -38,6 +39,7 @@ 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.StringEntity; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.BasicCredentialsProvider; @@ -46,6 +48,7 @@ import org.apache.http.util.EntityUtils; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; @@ -66,10 +69,13 @@ import org.sana.android.provider.Subjects; import org.sana.android.service.QueueManager; import org.sana.android.util.Dates; +import org.sana.api.IProcedure; import org.sana.core.Patient; +import org.sana.core.ProcedureGroup; import org.sana.net.MDSResult; import org.sana.net.Response; import org.sana.net.http.HttpTaskFactory; +import org.sana.net.http.handler.ProcedureGroupResponseHandler; import org.sana.util.UUIDUtil; import org.xml.sax.SAXException; @@ -203,6 +209,36 @@ protected static MDSResult doPost(String url, HttpEntity entity) { return MDSInterface2.doExecute(post); } + /** + * Executes a POST method with Content-type being application/json. + * + * @param url the request url + * @param entity the JSON form data. + * @return + */ + protected static String doPostJson(String url, HttpEntity entity) { + HttpPost post = new HttpPost(url); + post.setEntity(entity); + post.setHeader("Content-type", "application/json"); + + HttpClient client = HttpTaskFactory.CLIENT_FACTORY.produce(); + HttpResponse httpResponse = null; + String responseString = null; + try { + httpResponse = client.execute(post); + Log.d(TAG, "doPostJson() got response code " + httpResponse.getStatusLine().getStatusCode()); + + responseString = EntityUtils.toString(httpResponse.getEntity()); + Log.d(TAG, "doPostJson() Received from MDS:" + responseString); + + return responseString; + } catch (IOException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + return null; + } + /** * Executes a client HttpMethod. * @@ -1353,4 +1389,64 @@ public static Response> updatePatient(Context context, } return response; } + + public static List getProcedureXMLsForGroup(Context context, String procedureGroupId, + List procedureTitles, + List procedureVersions) { + Log.i(TAG, "getProcedureXMLsForGroup called"); + List procedureXMLs = new ArrayList(); + try { + JSONObject procedureList = new JSONObject(); + for (int i = 0; i < procedureTitles.size(); i++) { + procedureList.put(procedureTitles.get(i), procedureVersions.get(i)); + } + + JSONObject postData = new JSONObject(); + postData.put("procedures", procedureList); + HttpEntity entity = new StringEntity(postData.toString()); + + String mdsURL = getMDSUrl(context); + String syncURL = mdsURL + "core/proceduregroup/" + procedureGroupId + "/sync/"; + String response = MDSInterface2.doPostJson(syncURL, entity); + + JSONObject responseJson = new JSONObject(response).getJSONObject("message"); + + JSONArray updatedProceduresJson = responseJson.getJSONArray("updated_procedures"); + List updatedProcedureXMLs = getXMLsFromSyncResponses(updatedProceduresJson); + procedureXMLs.addAll(updatedProcedureXMLs); + + JSONArray newProceduresJson = responseJson.getJSONArray("unknown_procedures"); + List newProcedureXMLs = getXMLsFromSyncResponses(newProceduresJson); + procedureXMLs.addAll(newProcedureXMLs); + } catch (Exception e) { + e.printStackTrace(); + } + return procedureXMLs; + } + + private static List getXMLsFromSyncResponses(JSONArray proceduresJson) { + List procedureXMLs = new ArrayList(); + for (int i = 0; i < proceduresJson.length(); i++) { + try { + JSONObject procedureJson = proceduresJson.getJSONObject(i); + String procedureXML = procedureJson.getString("source_file_content"); + procedureXMLs.add(procedureXML); + } catch(JSONException e) { + e.printStackTrace(); + } + } + return procedureXMLs; + } + + public static List getProcedureGroups(Context context) { + String mdsURL = getMDSUrl(context); + URI procedureGroupsURI = URI.create(mdsURL + "core/proceduregroup/"); + try { + Response> response = MDSInterface2.apiGet(procedureGroupsURI, new ProcedureGroupResponseHandler()); + return new ArrayList(response.getMessage()); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return new ArrayList(); + } } diff --git a/app/src/main/java/org/sana/android/task/FetchProcedureGroupsTask.java b/app/src/main/java/org/sana/android/task/FetchProcedureGroupsTask.java new file mode 100644 index 0000000..04b56f8 --- /dev/null +++ b/app/src/main/java/org/sana/android/task/FetchProcedureGroupsTask.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2013, Sana + * All rights reserved. + *

+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *

+ * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Sana nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Sana BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.sana.android.task; + +import android.content.Context; +import android.os.AsyncTask; + +import org.sana.android.net.MDSInterface2; +import org.sana.core.ProcedureGroup; + +import java.util.List; + +/** + * Pulls a user's procedure groups. + * + * @author Sana Development Team + */ +public class FetchProcedureGroupsTask extends AsyncTask> { + + public interface FetchProcedureGroupsCallback { + void onProcedureGroupsLoaded(List procedureGroups); + } + + private Context mContext; + private FetchProcedureGroupsCallback mCallback; + + public FetchProcedureGroupsTask(Context context, FetchProcedureGroupsCallback callback) { + mContext = context; + mCallback = callback; + } + + @Override + protected List doInBackground(Void... params) { + List procedureGroups = MDSInterface2.getProcedureGroups(mContext); + return procedureGroups; + } + + @Override + protected void onPostExecute(List procedureGroups) { + mCallback.onProcedureGroupsLoaded(procedureGroups); + } +} diff --git a/app/src/main/java/org/sana/android/task/ProcedureGroupSyncTask.java b/app/src/main/java/org/sana/android/task/ProcedureGroupSyncTask.java new file mode 100644 index 0000000..00938c5 --- /dev/null +++ b/app/src/main/java/org/sana/android/task/ProcedureGroupSyncTask.java @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2013, Sana + * All rights reserved. + *

+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *

+ * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Sana nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Sana BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.sana.android.task; + +import android.app.ProgressDialog; +import android.content.Context; +import android.database.Cursor; +import android.os.AsyncTask; +import android.util.Log; + +import org.sana.R; +import org.sana.android.net.MDSInterface2; +import org.sana.android.provider.ProcedureGroups; +import org.sana.android.provider.Procedures; +import org.sana.android.util.SanaUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * Pulls the procedures of procedure groups. + * + * @author Sana Development Team + */ +public class ProcedureGroupSyncTask extends AsyncTask { + private static final String TAG = ProcedureGroupSyncTask.class.getSimpleName(); + + private ProgressDialog mProgressDialog; + private Context mContext; + + public ProcedureGroupSyncTask(Context context) { + mContext = context; + } + + private static final String[] PROCEDURE_GROUP_PROJECTION = new String[] { + ProcedureGroups.Contract.UUID, + ProcedureGroups.Contract.PROCEDURE_NAMES + }; + + private static final String[] PROCEDURE_PROJECTION = new String[] { + Procedures.Contract.TITLE, + Procedures.Contract.VERSION, + }; + + @Override + protected void onPreExecute() { + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + mProgressDialog = null; + } + mProgressDialog = new ProgressDialog(mContext); + mProgressDialog.setMessage(mContext.getString(R.string.procedure_group_sync_loading_label)); + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + mProgressDialog.show(); + } + + @Override + protected Integer doInBackground(Void... params) { + Log.i(TAG, "Syncing procedure groups"); + + Cursor procedureGroupsCursor = mContext.getContentResolver().query( + ProcedureGroups.CONTENT_URI, + PROCEDURE_GROUP_PROJECTION, + null, + null, + null + ); + + while (procedureGroupsCursor.moveToNext()) { + String groupId = procedureGroupsCursor.getString(procedureGroupsCursor.getColumnIndex(ProcedureGroups.Contract.UUID)); + String[] procedureNames = procedureGroupsCursor.getString(procedureGroupsCursor.getColumnIndex(ProcedureGroups.Contract.PROCEDURE_NAMES)).split(","); + + List procedureTitles = new ArrayList(); + List procedureVersions = new ArrayList(); + for (String procedureName: procedureNames) { + Cursor proceduresCursor = mContext.getContentResolver().query( + Procedures.CONTENT_URI, + PROCEDURE_PROJECTION, + "title = ?", + new String[] {procedureName}, + null + ); + if (proceduresCursor.moveToFirst()) { + String title = proceduresCursor.getString(proceduresCursor.getColumnIndex(Procedures.Contract.TITLE)); + String version = proceduresCursor.getString(proceduresCursor.getColumnIndex(Procedures.Contract.VERSION)); + procedureTitles.add(title); + procedureVersions.add(version); + } + + List procedureXMLs = MDSInterface2.getProcedureXMLsForGroup(mContext, groupId, procedureTitles, procedureVersions); + for (String procedureXML: procedureXMLs) { + SanaUtil.insertProcedureFromXML(mContext, procedureXML); + } + } + } + + return 0; + } + + + @Override + protected void onPostExecute(Integer result) { + Log.i(TAG, "Completed sync of procedure groups"); + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + mProgressDialog = null; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/sana/android/util/SanaUtil.java b/app/src/main/java/org/sana/android/util/SanaUtil.java index dc1053f..814cf9c 100644 --- a/app/src/main/java/org/sana/android/util/SanaUtil.java +++ b/app/src/main/java/org/sana/android/util/SanaUtil.java @@ -230,55 +230,32 @@ public static void clearPatientData(Context ctx) { } /** - * Inserts a new procedure into the data store + * Inserts a procedure into the data store from a resource id * * @param ctx the Context where the data is stored * @param id the raw resource id */ - private static void insertProcedure(Context ctx, int id) { - - String title = SanaUtil.randomString("Procedure ", 10); - String author = ""; - String guid = ""; - String version = "1.0"; - String xmlFullProcedure; + private static void insertProcedureFromResourceId(Context ctx, int id) { try { InputStream rs = ctx.getResources().openRawResource(id); byte[] data = new byte[rs.available()]; rs.read(data); - xmlFullProcedure = new String(data); - - Procedure p = Procedure.fromXMLString(xmlFullProcedure); - title = p.getTitle(); - author = p.getAuthor(); - guid = p.getGuid(); - version = p.getVersion(); - - ContentValues cv = new ContentValues(); - cv.put(Procedures.Contract.TITLE, title); - cv.put(Procedures.Contract.AUTHOR, author); - cv.put(Procedures.Contract.UUID, guid); - cv.put(Procedures.Contract.VERSION, version); - cv.put(Procedures.Contract.PROCEDURE, xmlFullProcedure); - - if (searchDuplicateTitleAuthor(ctx, title, author)) { - Log.d(TAG, "Duplicate found!"); - ctx.getContentResolver().update(Procedures.CONTENT_URI, - cv, - "(title LIKE\"" + title + "\")", null); - } else - ctx.getContentResolver().insert(Procedures.CONTENT_URI, cv); + + String xmlFullProcedure = new String(data); + Procedure procedure = Procedure.fromXMLString(xmlFullProcedure); + insertProcedure(procedure, xmlFullProcedure, ctx); } catch (Exception e) { - Log.e(TAG, "Couldn't add procedure id=" + id + ", title = " + title - + ", to db. Exception : " + e.toString()); + Log.e(TAG, "Couldn't add procedure with resource id=" + id + + " to db. Exception : " + e.toString()); e.printStackTrace(); } } /** - * Code to insert procedure into database is a duplicate with - * insertProcedure this just takes the location from the sd card instead of - * an id from the resources. + * Inserts a procedure into the data store from an sd card + * + * @param ctx the Context where the data is stored + * @param location the location of the resource * * @throws IOException * @throws ProcedureParseException @@ -288,48 +265,61 @@ private static void insertProcedure(Context ctx, int id) { public static Integer insertProcedureFromSd(final Context ctx, String location) throws IOException, ParserConfigurationException, SAXException, ProcedureParseException { - String title = SanaUtil.randomString("Procedure ", 10); - String author = ""; - String guid = ""; - String version = "1.0"; - String xmlFullProcedure; Log.v(TAG, location); FileInputStream rs = new FileInputStream(location); byte[] data = new byte[rs.available()]; rs.read(data); - xmlFullProcedure = new String(data); + String xmlFullProcedure = new String(data); + Procedure procedure = Procedure.fromXMLString(xmlFullProcedure); + insertProcedure(procedure, xmlFullProcedure, ctx); + return 0; + } - Procedure p = Procedure.fromXMLString(xmlFullProcedure); - title = p.getTitle(); - author = p.getAuthor(); - guid = p.getGuid(); - version = p.getVersion(); + /** + * Inserts a procedure into the data store from its procedure XML + * + * @param ctx the Context where the data is stored + * @param procedureXML the xml of the procedure + */ + public static void insertProcedureFromXML(final Context ctx, String procedureXML) { + try { + Procedure procedure = Procedure.fromXMLString(procedureXML); + insertProcedure(procedure, procedureXML, ctx); + } catch (Exception e) { + Log.e(TAG, "Couldn't add procedure with xml=" + procedureXML + + " to db. Exception : " + e.toString()); + e.printStackTrace(); + } + } + + private static void insertProcedure(Procedure procedure, String procedureXML, Context ctx) { + String title = procedure.getTitle(); + String author = procedure.getAuthor(); + String guid = procedure.getGuid(); + String version = procedure.getVersion(); final ContentValues cv = new ContentValues(); cv.put(Procedures.Contract.TITLE, title); cv.put(Procedures.Contract.AUTHOR, author); cv.put(Procedures.Contract.UUID, guid); cv.put(Procedures.Contract.VERSION, version); - cv.put(Procedures.Contract.PROCEDURE, xmlFullProcedure); + cv.put(Procedures.Contract.PROCEDURE, procedureXML); if (searchDuplicateTitleAuthor(ctx, title, author)) { Log.i(TAG, "Duplicate found! Updating..."); // TODO Versioning - ctx.getContentResolver().update(p.getInstanceUri(), + ctx.getContentResolver().update(Procedures.CONTENT_URI, cv, "(title LIKE\"" + title + "\")", null); Log.i(TAG, "Updated"); - return 0; } else { Log.i(TAG, "Inserting record."); ctx.getContentResolver().insert( Procedures.CONTENT_URI, cv); } - Log.i(TAG, "Acquired procedure record from local cache."); - return 0; } private static boolean searchDuplicateTitleAuthor(Context ctx, String title, @@ -358,33 +348,33 @@ private static boolean searchDuplicateTitleAuthor(Context ctx, String title, */ public static void loadDefaultDatabase(Context ctx) { /* - * insertProcedure(ctx, R.raw.bronchitis); insertProcedure(ctx, - * R.raw.cervicalcancer); insertProcedure(ctx, R.raw.surgery_demo); - * insertProcedure(ctx, R.raw.tbcontact); insertProcedure(ctx, + * insertProcedureFromResourceId(ctx, R.raw.bronchitis); insertProcedureFromResourceId(ctx, + * R.raw.cervicalcancer); insertProcedureFromResourceId(ctx, R.raw.surgery_demo); + * insertProcedureFromResourceId(ctx, R.raw.tbcontact); insertProcedureFromResourceId(ctx, * R.raw.multiupload_test); - * insertProcedure(ctx, R.raw.upload_test); insertProcedure(ctx, - * R.raw.hiv); insertProcedure(ctx, R.raw.cervicalcancer); - * insertProcedure(ctx, R.raw.prenatal); insertProcedure(ctx, - * R.raw.surgery); insertProcedure(ctx, R.raw.derma); - * insertProcedure(ctx, R.raw.teleradiology); insertProcedure(ctx, - * R.raw.ophthalmology); insertProcedure(ctx, R.raw.tbcontact2); - * insertProcedure(ctx, R.raw.tbpatient); insertProcedure(ctx, + * insertProcedureFromResourceId(ctx, R.raw.upload_test); insertProcedureFromResourceId(ctx, + * R.raw.hiv); insertProcedureFromResourceId(ctx, R.raw.cervicalcancer); + * insertProcedureFromResourceId(ctx, R.raw.prenatal); insertProcedureFromResourceId(ctx, + * R.raw.surgery); insertProcedureFromResourceId(ctx, R.raw.derma); + * insertProcedureFromResourceId(ctx, R.raw.teleradiology); insertProcedureFromResourceId(ctx, + * R.raw.ophthalmology); insertProcedureFromResourceId(ctx, R.raw.tbcontact2); + * insertProcedureFromResourceId(ctx, R.raw.tbpatient); insertProcedureFromResourceId(ctx, * R.raw.oral_cancer); - insertProcedure(ctx, R.raw.cvd_protocol); - insertProcedure(ctx, R.raw.api_test); - insertProcedure(ctx, R.raw.ssi_two_site); - insertProcedure(ctx, R.raw.audio_upload_test); + insertProcedureFromResourceId(ctx, R.raw.cvd_protocol); + insertProcedureFromResourceId(ctx, R.raw.api_test); + insertProcedureFromResourceId(ctx, R.raw.ssi_two_site); + insertProcedureFromResourceId(ctx, R.raw.audio_upload_test); */ - insertProcedure(ctx, R.raw.demonstration); + //insertProcedureFromResourceId(ctx, R.raw.demonstration); /* - insertProcedure(ctx, R.raw.chain_test1); - insertProcedure(ctx, R.raw.chain_test2); - insertProcedure(ctx, R.raw.api_test_entry); - insertProcedure(ctx, R.raw.api_test_select); + insertProcedureFromResourceId(ctx, R.raw.chain_test1); + insertProcedureFromResourceId(ctx, R.raw.chain_test2); + insertProcedureFromResourceId(ctx, R.raw.api_test_entry); + insertProcedureFromResourceId(ctx, R.raw.api_test_select); */ /* Haiti procedures */ - //insertProcedure(ctx, R.raw.ssi); + //insertProcedureFromResourceId(ctx, R.raw.ssi); } /** diff --git a/app/src/main/res/layout-land/main.xml b/app/src/main/res/layout-land/main.xml index 5836c3c..02629cf 100644 --- a/app/src/main/res/layout-land/main.xml +++ b/app/src/main/res/layout-land/main.xml @@ -55,5 +55,12 @@ android:onClick="submit" android:text="@string/button_main_view_notifications" /> +