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" />
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml
index d55e597..162c3a6 100644
--- a/app/src/main/res/layout/main.xml
+++ b/app/src/main/res/layout/main.xml
@@ -71,6 +71,14 @@
android:drawableLeft="@drawable/ic_menu_notifications"
android:text="@string/button_main_view_notifications"
style="@style/Widget.Button.Large.Dark" />
+
+
diff --git a/app/src/main/res/layout/procedure_group_item.xml b/app/src/main/res/layout/procedure_group_item.xml
new file mode 100644
index 0000000..4ef5d0e
--- /dev/null
+++ b/app/src/main/res/layout/procedure_group_item.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/select_procedure_groups.xml b/app/src/main/res/layout/select_procedure_groups.xml
new file mode 100644
index 0000000..f919ce4
--- /dev/null
+++ b/app/src/main/res/layout/select_procedure_groups.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5acfc29..6d16d46 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -26,6 +26,7 @@
EncountersView Prior EncountersNotifications
+ Select Procedure GroupsPatientsAssigned tasksRegister Patient
@@ -247,4 +248,11 @@
Notify uploadUse default value
+
+ Syncing procedure group
+ Procedure Groups
+ Sync Procedure Groups
+ Num procedures: %d
+ Sync
+