Implementation
Future<List<TrackedEntityInstance>?> upload(
Function(RequestProgress, bool) callback,
{Dio? dioTestClient}) async {
callback(
RequestProgress(
resourceName: this.apiResourceName as String,
message:
'Retrieving ${this.apiResourceName?.toLowerCase()} from phone database....',
status: '',
percentage: 0),
false);
List<TrackedEntityInstance> trackedEntityInstances = await this
.withAttributes()
.withEnrollments()
.withRelationships()
.where(attribute: 'synced', value: false)
.where(attribute: 'dirty', value: true)
.get();
callback(
RequestProgress(
resourceName: this.apiResourceName as String,
message:
'${trackedEntityInstances.length} ${this.apiResourceName?.toLowerCase()} retrieved successfully',
status: '',
percentage: 50),
false);
callback(
RequestProgress(
resourceName: this.apiResourceName as String,
message:
'Uploading ${trackedEntityInstances.length} ${this.apiResourceName?.toLowerCase()} into the server...',
status: '',
percentage: 51),
false);
List<String> enrollmentIds = [];
final List<String> trackedEntityInstanceIds =
trackedEntityInstances.map((trackedEntityInstance) {
trackedEntityInstance.enrollments?.forEach((enrollment) {
if (enrollment.id != null) {
enrollmentIds.add(enrollment.id as String);
}
});
return trackedEntityInstance.id as String;
}).toList();
final List<Event> events = await EventQuery(database: database)
.whereIn(attribute: 'enrollment', values: enrollmentIds, merge: false)
.withDataValues()
.get();
List<dynamic> trackedEntityInstanceUploadPayload =
trackedEntityInstances.map((trackedEntityInstance) {
return TrackedEntityInstance.toUpload(trackedEntityInstance, events);
}).toList();
HttpResponse response = await HttpClient.post(
this.apiResourceName as String,
{'trackedEntityInstances': trackedEntityInstanceUploadPayload},
database: this.database,
dioTestClient: dioTestClient);
if (response.statusCode == 500) {
trackedEntityInstanceUploadPayload =
trackedEntityInstances.map((trackedEntityInstance) {
return TrackedEntityInstance.toUploadWithoutEnrollment(
trackedEntityInstance, events);
}).toList();
response = await HttpClient.post(this.apiResourceName as String,
{'trackedEntityInstances': trackedEntityInstanceUploadPayload},
database: this.database, dioTestClient: dioTestClient);
await HttpClient.post(
"enrollments",
{
'enrollments': trackedEntityInstances.map((trackedEntityInstance) {
return TrackedEntityInstance.toUploadEnrollment(
trackedEntityInstance, events);
}).toList()
},
database: this.database,
dioTestClient: dioTestClient);
}
final List<Future<HttpResponse>> transferApis = trackedEntityInstances
.where((tei) => tei.transfer == true)
.map((teiToTransfer) => HttpClient.put(
"tracker/ownership/transfer?trackedEntityInstance=${teiToTransfer.trackedEntityInstance}&program=${teiToTransfer.enrollments?[0].program as String}&ou=${teiToTransfer.orgUnit}",
null,
database: this.database,
dioTestClient: dioTestClient))
.toList();
await Future.wait(transferApis);
callback(
RequestProgress(
resourceName: this.apiResourceName as String,
message:
'Upload for ${trackedEntityInstances.length} ${this.apiResourceName?.toLowerCase()} is completed.',
status: '',
percentage: 75),
true);
callback(
RequestProgress(
resourceName: this.apiResourceName as String,
message: 'Saving import summaries into the phone database...',
status: '',
percentage: 76),
true);
final List<dynamic> importSummaries = response.body.runtimeType == String
? [
{
"responseType": "ImportSummary",
"status": "ERROR",
"reference": "",
"enrollments": {
"responseType": "ImportSummary",
"status": "ERROR",
"imported": 0,
"updated": 0,
"ignored": 1,
"deleted": 0,
"importSummaries:": [],
"total": 0
},
"importCount": {
"imported": 0,
"updated": 0,
"ignored": 1,
"deleted": 0
},
"total": 0,
"importSummaries:": [],
"conflicts": [
{
"object": "Server.ERROR",
"value": '${response.body.toString()}: ${response.statusCode}'
}
]
}
]
: (response.body?['response']?['importSummaries'] ?? []).toList();
final queue = Queue(parallel: 50);
num availableItemCount = 0;
trackedEntityInstances.forEach((trackedEntityInstance) {
bool syncFailed = true;
if (!((response.statusCode >= 200 && response.statusCode < 300) ||
response.statusCode == 409)) {
syncFailed = true;
trackedEntityInstance.lastSyncSummary =
TrackedEntityInstanceImportSummary.fromJson({
"responseType": "ImportSummary",
"status": "ERROR",
"reference": "",
"enrollments": {
"responseType": "ImportSummary",
"status": "ERROR",
"imported": 0,
"updated": 0,
"ignored": 1,
"deleted": 0,
"importSummaries:": [],
"total": 0
},
"importCount": {
"imported": 0,
"updated": 0,
"ignored": 1,
"deleted": 0
},
"total": 0,
"importSummaries:": [],
"conflicts": [
{
"object": "Server.ERROR",
"value": response.body.runtimeType == String
? response.body.toString()
: response.body['message'] ??
"Server Error code:" + response.statusCode.toString()
}
]
});
} else {
final importSummary = importSummaries.lastWhere(
(summary) => summary['reference'] == trackedEntityInstance.id,
orElse: (() => null));
if (importSummary != null) {
syncFailed = importSummary['status'] == 'ERROR';
trackedEntityInstance.lastSyncSummary =
TrackedEntityInstanceImportSummary.fromJson(importSummary);
} else {
syncFailed = true;
trackedEntityInstance.lastSyncSummary =
TrackedEntityInstanceImportSummary.fromJson({
"responseType": "ImportSummary",
"status": "ERROR",
"reference": "",
"enrollments": {
"responseType": "ImportSummary",
"status": "ERROR",
"imported": 0,
"updated": 0,
"ignored": 1,
"deleted": 0,
"importSummaries:": [],
"total": 0
},
"importCount": {
"imported": 0,
"updated": 0,
"ignored": 1,
"deleted": 0
},
"total": 0,
"importSummaries:": [],
"conflicts": [
{
"object": "ImportSummary.DOES_NOT_EXIST",
"value": response.body['message'] ?? "Invalid Import Summary"
}
]
});
}
}
availableItemCount++;
trackedEntityInstance.synced = !syncFailed;
trackedEntityInstance.dirty = true;
trackedEntityInstance.syncFailed = syncFailed;
trackedEntityInstance.lastSyncDate = DateTime.now().toIso8601String();
queue.add(() => TrackedEntityInstanceQuery(database: database)
.setData(trackedEntityInstance)
.save());
});
if (availableItemCount == 0) {
queue.cancel();
} else {
await queue.onComplete;
}
callback(
RequestProgress(
resourceName: this.apiResourceName as String,
message: 'Import summaries saved succussfully',
status: '',
percentage: 100),
true);
return await TrackedEntityInstanceQuery(database: database)
.byIds(trackedEntityInstanceIds)
.get();
}