Skip to content

Commit

Permalink
Merge pull request #144 from dhis2/download_teis
Browse files Browse the repository at this point in the history
Download TrackedEntityInstances per Org.Unit
  • Loading branch information
dhis2-android authored Feb 16, 2018
2 parents 723a314 + cc22dfe commit 1cb4890
Show file tree
Hide file tree
Showing 11 changed files with 497 additions and 15 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ build
# Secret keys
secrets.properties

core/src/main/java/org/hisp/dhis/android/core/trackedentity/TeisEndPointCall.java
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ ext {
buildToolsVersion: "25.0.2",
minSdkVersion : 15,
targetSdkVersion : 25,
versionCode : 42_3,
versionName : "0.4.2.3-SNAPSHOT"
versionCode : 43_1,
versionName : "0.4.3.1-SNAPSHOT"
]

libraries = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ public void delete_tracked_entity_attribute_value_by_instance_and_attribute_uids
trackedEntityAttributeValues.get(TRACKED_ENTITY_INSTANCE_2).size(), is(2));
}

@Test(expected = SQLiteConstraintException.class)
//@Test(expected = SQLiteConstraintException.class)
//TODO Solve the foreign keys for missing attributes
public void
throw_sqlite_constraint_exception_when_insert_tracked_entity_attribute_value_with_invalid_tracked_entity_attribute() {
store.insert(VALUE, date, date, "wrong", TRACKED_ENTITY_INSTANCE);
Expand All @@ -230,7 +231,8 @@ public void delete_tracked_entity_attribute_value_by_instance_and_attribute_uids
store.insert(VALUE, date, date, TRACKED_ENTITY_ATTRIBUTE, "wrong");
}

@Test
//@Test
//TODO Solve the Foreign keys for missing attributes
public void delete_tracked_entity_attribute_value_in_data_base_when_delete_tracked_entity_attribute() {
insert_nullable_tracked_entity_attribute_value_in_data_base_when_insert_nullable_tracked_entity_attribute_value();

Expand Down
11 changes: 10 additions & 1 deletion core/src/main/java/org/hisp/dhis/android/core/D2.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.hisp.dhis.android.core.calls.SingleDataCall;
import org.hisp.dhis.android.core.calls.TrackedEntityInstancePostCall;
import org.hisp.dhis.android.core.calls.TrackerDataCall;
import org.hisp.dhis.android.core.calls.TrackerEntitiesDataCall;
import org.hisp.dhis.android.core.category.CategoryCategoryComboLinkStore;
import org.hisp.dhis.android.core.category.CategoryCategoryComboLinkStoreImpl;
import org.hisp.dhis.android.core.category.CategoryCategoryOptionLinkStore;
Expand Down Expand Up @@ -526,12 +527,20 @@ public Call<Response> syncTrackerData() {
}

@NonNull
public Call<Response<Payload<TrackedEntityInstance>>> syncTEI(String trackedEntityInstanceUid) {
public Call<Response<Payload<TrackedEntityInstance>>>
downloadTrackedEntityInstance(String trackedEntityInstanceUid) {
return new TrackedEntityInstanceEndPointCall(
trackedEntityInstanceService, databaseAdapter, trackedEntityInstanceHandler,
resourceHandler, new Date(), trackedEntityInstanceUid);
}

@NonNull
public Call<Response> downloadTrackedEntityInstances(int teiLimitByOrgUnit) {
return new TrackerEntitiesDataCall(organisationUnitStore, trackedEntityInstanceService, databaseAdapter,
trackedEntityInstanceHandler, resourceHandler, resourceStore, systemInfoService,
systemInfoStore, teiLimitByOrgUnit);
}

@NonNull
public Call<Response<WebResponse>> syncTrackedEntityInstances() {
return new TrackedEntityInstancePostCall(trackedEntityInstanceService,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package org.hisp.dhis.android.core.calls;


import android.support.annotation.NonNull;
import android.util.Log;

import org.hisp.dhis.android.core.common.Payload;
import org.hisp.dhis.android.core.data.api.Fields;
import org.hisp.dhis.android.core.data.database.DatabaseAdapter;
import org.hisp.dhis.android.core.data.database.Transaction;
import org.hisp.dhis.android.core.organisationunit.OrganisationUnit;
import org.hisp.dhis.android.core.organisationunit.OrganisationUnitStore;
import org.hisp.dhis.android.core.resource.ResourceHandler;
import org.hisp.dhis.android.core.resource.ResourceStore;
import org.hisp.dhis.android.core.systeminfo.SystemInfo;
import org.hisp.dhis.android.core.systeminfo.SystemInfoCall;
import org.hisp.dhis.android.core.systeminfo.SystemInfoService;
import org.hisp.dhis.android.core.systeminfo.SystemInfoStore;
import org.hisp.dhis.android.core.trackedentity.TeiQuery;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceEndPointCall;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceHandler;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceService;

import java.util.Date;
import java.util.List;

import retrofit2.Response;

@SuppressWarnings("PMD")
public class TrackerEntitiesDataCall implements Call<Response> {

private boolean isExecuted;
private final int teiLimitByOrgUnit;
private final OrganisationUnitStore organisationUnitStore;
private final TrackedEntityInstanceService trackedEntityInstanceService;
private final DatabaseAdapter databaseAdapter;
private final TrackedEntityInstanceHandler trackedEntityInstanceHandler;
private final ResourceHandler resourceHandler;
private final ResourceStore resourceStore;
private final SystemInfoService systemInfoService;
private final SystemInfoStore systemInfoStore;

public TrackerEntitiesDataCall(@NonNull OrganisationUnitStore organisationUnitStore,
@NonNull TrackedEntityInstanceService trackedEntityInstanceService,
@NonNull DatabaseAdapter databaseAdapter,
@NonNull TrackedEntityInstanceHandler trackedEntityInstanceHandler,
@NonNull ResourceHandler resourceHandler,
@NonNull ResourceStore resourceStore,
@NonNull SystemInfoService systemInfoService,
@NonNull SystemInfoStore systemInfoStore,
int teiLimitByOrgUnit) {

this.teiLimitByOrgUnit = teiLimitByOrgUnit;
this.organisationUnitStore = organisationUnitStore;
this.trackedEntityInstanceService = trackedEntityInstanceService;
this.databaseAdapter = databaseAdapter;
this.trackedEntityInstanceHandler = trackedEntityInstanceHandler;
this.resourceHandler = resourceHandler;
this.resourceStore = resourceStore;
this.systemInfoService = systemInfoService;
this.systemInfoStore = systemInfoStore;
}

@Override
public boolean isExecuted() {
synchronized (this) {
return isExecuted;
}
}

@Override
public Response call() throws Exception {
synchronized (this) {
if (isExecuted) {
throw new IllegalStateException("Already executed");
}
isExecuted = true;
}

Response response = null;

Transaction transaction = databaseAdapter.beginNewTransaction();

try {

response = new SystemInfoCall(
databaseAdapter, systemInfoStore,
systemInfoService, resourceStore
).call();

if (!response.isSuccessful()) {
return response;
}

SystemInfo systemInfo = (SystemInfo) response.body();
Date serverDate = systemInfo.serverDate();

response = trackerCall(serverDate);

if (response == null || !response.isSuccessful()) {
return response;
}

transaction.setSuccessful();

return response;
} finally {
transaction.end();
}
}

//TODO We may need to refactor the code here. Right now it is not very optimize.
// We need a better sync mechanism, based on? lastupdated?
private Response trackerCall(Date serverDate) throws Exception {
Response<Payload<TrackedEntityInstance>> response = null;

List<OrganisationUnit> organisationUnits = organisationUnitStore.queryOrganisationUnits();

int pageSize = TeiQuery.Builder.create().build().getPageSize();

int numPages = (int) Math.ceil((double) teiLimitByOrgUnit / pageSize);

int teisDownloaded = 0;

int pageLimit = 0;

for (OrganisationUnit orgUnit : organisationUnits) {

for (int page = 1; page <= numPages; page++) {

if (page == numPages && teiLimitByOrgUnit > 0) {
pageLimit = teiLimitByOrgUnit - teisDownloaded;
}

TeiQuery teiQuery = TeiQuery.
Builder.create()
.withOrgUnit(orgUnit.uid())
.withPage(page)
.withPageLimit(pageLimit)
.build();

response = trackedEntityInstanceService.getTEIs(teiQuery.getOrgUnit(), fields(),
Boolean.TRUE, teiQuery.getPage(), teiQuery.getPageLimit()).execute();

if (response.isSuccessful() && response.body().items() != null) {
List<TrackedEntityInstance> trackedEntityInstances = response.body().items();
int size = trackedEntityInstances.size();
Response<TrackedEntityInstance> apiResponse = null;

if (teiQuery.getPageLimit() > 0) {
size = teiQuery.getPageLimit();
}

for (int i = 0; i < size; i++) {
apiResponse = new TrackedEntityInstanceEndPointCall(trackedEntityInstanceService,
databaseAdapter, trackedEntityInstanceHandler, resourceHandler, serverDate,
trackedEntityInstances.get(i).uid()).call();

if (apiResponse == null || !apiResponse.isSuccessful()) {
Log.d(this.getClass().getSimpleName(), trackedEntityInstances.get(i).uid() + " conflict");
}
}

}

teisDownloaded = teisDownloaded + teiQuery.getPageSize();
}

}

return response;
}

private Fields<TrackedEntityInstance> fields() {
return Fields.<TrackedEntityInstance>builder().fields(
TrackedEntityInstance.uid, TrackedEntityInstance.created,
TrackedEntityInstance.lastUpdated,
TrackedEntityInstance.organisationUnit,
TrackedEntityInstance.trackedEntity,
TrackedEntityInstance.deleted
).build();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -729,11 +729,11 @@ public class DbOpenHelper extends CustomSQLBriteOpenHelper {
TrackedEntityAttributeValueModel.Columns.VALUE + " TEXT," +
TrackedEntityAttributeValueModel.Columns.TRACKED_ENTITY_ATTRIBUTE + " TEXT NOT NULL," +
TrackedEntityAttributeValueModel.Columns.TRACKED_ENTITY_INSTANCE + " TEXT NOT NULL," +
" FOREIGN KEY (" + TrackedEntityAttributeValueModel.Columns.TRACKED_ENTITY_ATTRIBUTE
/*" FOREIGN KEY (" + TrackedEntityAttributeValueModel.Columns.TRACKED_ENTITY_ATTRIBUTE
+ ")" +
" REFERENCES " + TrackedEntityAttributeModel.TABLE +
" (" + TrackedEntityAttributeModel.Columns.UID + ")" +
" ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, " +
" ON DELETE CASCADE, " +*/
" FOREIGN KEY (" + TrackedEntityAttributeValueModel.Columns.TRACKED_ENTITY_INSTANCE
+ ") " +
" REFERENCES " + TrackedEntityInstanceModel.TABLE +
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package org.hisp.dhis.android.core.trackedentity;

import android.support.annotation.Nullable;

import java.util.HashSet;
import java.util.Set;

public class TeiQuery {

private final int page;
private final int pageSize;
private final boolean paging;
private final String orgUnit;
private final int pageLimit;

@Nullable
private final Set<String> uIds;


public TeiQuery(boolean paging, int page, int pageSize,
String orgUnit, int pageLimit) {
this.paging = paging;
this.page = page;
this.pageSize = pageSize;
this.orgUnit = orgUnit;
this.pageLimit = pageLimit;
uIds = null;
}

public TeiQuery(boolean paging, int page, int pageSize,
String orgUnit, @Nullable Set<String> uIds, int pageLimit) {
this.paging = paging;
this.page = page;
this.pageSize = pageSize;
this.orgUnit = orgUnit;
this.uIds = uIds;
this.pageLimit = pageLimit;
}

@Nullable
public Set<String> getUIds() {
return uIds;
}

public int getPage() {
return page;
}

public int getPageSize() {
return pageSize;
}

public boolean isPaging() {
return paging;
}

public String getOrgUnit() {
return orgUnit;
}

public int getPageLimit() {
return pageLimit;
}

public static class Builder {
private int page = 1;
private int pageSize = 50;
private boolean paging;
private String orgUnit;
int pageLimit;

private Set<String> uIds = new HashSet<>();

private Builder() {
}

public static TeiQuery.Builder create() {
return new TeiQuery.Builder();
}

public TeiQuery.Builder withPaging(boolean paging) {
this.paging = paging;
return this;
}

public TeiQuery.Builder withPage(int page) {
this.page = page;
return this;
}

public TeiQuery.Builder withPageSize(int pageSize) {
this.pageSize = pageSize;
return this;
}

public TeiQuery.Builder withOrgUnit(String orgUnit) {
this.orgUnit = orgUnit;
return this;
}

public TeiQuery.Builder withUIds(Set<String> uIds) {
this.uIds = uIds;
return this;
}

public TeiQuery.Builder withPageLimit(int pageLimit) {
this.pageLimit = pageLimit;
return this;
}

public TeiQuery build() {
if (pageLimit > pageSize) {
throw new IllegalArgumentException(
"pageLimit can not be more greater than pageSize");
}

return new TeiQuery(paging, page, pageSize,
orgUnit, uIds, pageLimit);
}
}
}
Loading

0 comments on commit 1cb4890

Please sign in to comment.