upload method

Future<List<TrackedEntityInstance>?> upload(
  1. dynamic callback(
    1. RequestProgress,
    2. bool
    ), {
  2. Dio? dioTestClient,
})

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();
}