Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 36 additions & 9 deletions src/data/Dhis2RelationshipTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,30 @@ import { promiseMap } from "../utils/promises";
import { getUid } from "./dhis2-uid";
import { getTrackedEntities, TrackedEntityGetRequest } from "./Dhis2TrackedEntityInstances";
import { TrackerRelationship, RelationshipItem, TrackedEntitiesApiRequest } from "../domain/entities/TrackedEntity";
import { buildOrgUnitsParameter } from "../domain/entities/OrgUnit";
import { EventsAPIResponse } from "../domain/entities/DhisDataPackage";
import { getVersion, getMajorVersion } from "../utils/d2-api";

type RelationshipTypesById = Record<Id, Pick<D2RelationshipType, "id" | "toConstraint" | "fromConstraint">>;

export type RelationshipOrgUnitFilter = TrackedEntityOURequestApi["ouMode"];

export function buildOrgUnitMode(ouMode: RelationshipOrgUnitFilter, orgUnits?: Ref[]) {
function buildOrgUnitsParameter(apiVersion: number, orgUnitsIds: Ref[]): string {
const separator = apiVersion >= 42 ? "," : ";";
return orgUnitsIds.map(({ id }) => id).join(separator);
}

export function buildOrgUnitMode(apiVersion: string, ouMode: RelationshipOrgUnitFilter, orgUnits?: Ref[]) {
const majorVersion = getMajorVersion(apiVersion);
const isOuReq = ouMode === "SELECTED" || ouMode === "CHILDREN" || ouMode === "DESCENDANTS";
//issue: v41 - orgUnitMode/ouMode; v38-40 ouMode; ouMode to be deprecated
//issue: v42 - orgUnitMode and orgUnits; v41 - orgUnitMode/ouMode and orgUnit; v38-40 ouMode; ouMode to be deprecated
//can't use both orgUnitMode and ouMode in v41
if (!isOuReq) {
return { ouMode };
if (majorVersion >= 42) return { orgUnitMode: ouMode };
else return { ouMode };
} else if (orgUnits && orgUnits.length > 0) {
return { ouMode, orgUnit: buildOrgUnitsParameter(orgUnits) };
const orgUnitsParam = buildOrgUnitsParameter(majorVersion, orgUnits);
if (majorVersion >= 42) return { orgUnitMode: ouMode, orgUnits: orgUnitsParam };
else return { ouMode, orgUnit: orgUnitsParam };
} else {
throw new Error(`No orgUnits selected for ouMode ${ouMode}`);
}
Expand Down Expand Up @@ -169,6 +178,8 @@ export async function getRelationshipMetadata(
api: D2Api,
filters?: ProgramFilters
): Promise<RelationshipMetadata> {
const apiVersion = await getVersion(api);

const {
trackedEntityTypes,
relationshipTypes: allRelationshipTypes,
Expand Down Expand Up @@ -201,8 +212,22 @@ export async function getRelationshipMetadata(

if (!isProgramAssociatedWithSomeConstraint) return;

const fromConstraint = await getConstraint(api, trackedEntityTypes, programs, type.fromConstraint, filters);
const toConstraint = await getConstraint(api, trackedEntityTypes, programs, type.toConstraint, filters);
const fromConstraint = await getConstraint(
api,
apiVersion,
trackedEntityTypes,
programs,
type.fromConstraint,
filters
);
const toConstraint = await getConstraint(
api,
apiVersion,
trackedEntityTypes,
programs,
type.toConstraint,
filters
);

return fromConstraint && toConstraint
? { id: relType.id, name: relType.name, constraints: { from: fromConstraint, to: toConstraint } }
Expand All @@ -217,6 +242,7 @@ type ProgramInfo = NamedRef & { programStages: NamedRef[] };
const getConstraint = memoizeAsync(
async (
api: D2Api,
apiVersion: string,
trackedEntityTypes: NamedRef[],
programs: ProgramInfo[],
constraint: D2RelationshipConstraint,
Expand All @@ -235,7 +261,7 @@ const getConstraint = memoizeAsync(

switch (constraint.relationshipEntity) {
case "TRACKED_ENTITY_INSTANCE":
return getConstraintForTypeTei(api, filters, trackedEntityTypes, constraint);
return getConstraintForTypeTei(api, apiVersion, filters, trackedEntityTypes, constraint);
case "PROGRAM_STAGE_INSTANCE": {
if (constraint.program) {
const program = programsById[constraint.program.id];
Expand All @@ -251,14 +277,15 @@ const getConstraint = memoizeAsync(

async function getConstraintForTypeTei(
api: D2Api,
apiVersion: string,
filters: ProgramFilters | undefined,
trackedEntityTypes: NamedRef[],
constraint: D2RelationshipConstraint
): Promise<RelationshipConstraint> {
const { ouMode = "CAPTURE", organisationUnits = [] } = filters || {};
const trackedEntityTypesById = _.keyBy(trackedEntityTypes, obj => obj.id);

const ouModeQuery = buildOrgUnitMode(ouMode, organisationUnits);
const ouModeQuery = buildOrgUnitMode(apiVersion, ouMode, organisationUnits);

const query = {
...ouModeQuery,
Expand Down
19 changes: 14 additions & 5 deletions src/data/Dhis2TrackedEntityInstances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@ import { TrackedEntitiesApiRequest, TrackedEntitiesResponse, TrackedEntity } fro
import { Params } from "@eyeseetea/d2-api/api/common";
import { ImportDataPackageOptions } from "../domain/repositories/InstanceRepository";
import { MULTI_TEXT_OPTION_DELIMITER } from "../domain/helpers/ExcelBuilder";
import { getVersion } from "../utils/d2-api";

export interface GetOptions {
type GetOptions = {
api: D2Api;
program: Ref;
orgUnits: Ref[];
pageSize?: number;
enrollmentStartDate?: Moment;
enrollmentEndDate?: Moment;
relationshipsOuFilter?: RelationshipOrgUnitFilter;
}
};

type TrackerParams = Params & Omit<TeiGetRequest, "ou" | "ouMode">;

Expand All @@ -60,6 +61,8 @@ export async function getTrackedEntityInstances(options: GetOptions): Promise<Tr
} = options;
if (_.isEmpty(orgUnits)) return [];

const apiVersion = await getVersion(api);

const program = await getProgram(api, options.program.id);
if (!program) return [];

Expand All @@ -79,6 +82,7 @@ export async function getTrackedEntityInstances(options: GetOptions): Promise<Tr
for (let page = 1; ; page++) {
const { pageCount, instances } = await getTeisFromApi({
api,
apiVersion,
program,
orgUnits,
page,
Expand Down Expand Up @@ -462,8 +466,11 @@ function getMultiTextValue(options: {
}

async function getExistingTeis(api: D2Api): Promise<Ref[]> {
const apiVersion = await getVersion(api);
const ouModeParam = buildOrgUnitMode(apiVersion, "CAPTURE");

const query = {
ouMode: "CAPTURE",
...ouModeParam,
pageSize: 1000,
totalPages: true,
fields: "trackedEntity",
Expand Down Expand Up @@ -504,6 +511,7 @@ type TeiKey = KeysOfUnion<TrackedEntitiesApiRequest>;

async function getTeisFromApi(options: {
api: D2Api;
apiVersion: string;
program: Program;
orgUnits: Ref[];
page: number;
Expand All @@ -512,7 +520,8 @@ async function getTeisFromApi(options: {
enrollmentEndDate?: Moment;
ouMode: RelationshipOrgUnitFilter;
}): Promise<TrackedEntitiesResponse> {
const { api, program, orgUnits, page, pageSize, enrollmentStartDate, enrollmentEndDate, ouMode } = options;
const { api, apiVersion, program, orgUnits, page, pageSize, enrollmentStartDate, enrollmentEndDate, ouMode } =
options;

const fields: TeiKey[] = [
"trackedEntity",
Expand All @@ -524,7 +533,7 @@ async function getTeisFromApi(options: {
"geometry",
];

const ouModeQuery = buildOrgUnitMode(ouMode, orgUnits);
const ouModeQuery = buildOrgUnitMode(apiVersion, ouMode, orgUnits);

const filters: TrackedEntityGetRequest = {
...ouModeQuery,
Expand Down
6 changes: 1 addition & 5 deletions src/domain/entities/OrgUnit.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { Id, Ref } from "./ReferenceObject";
import { Id } from "./ReferenceObject";

export interface OrgUnit {
id: Id;
path: string;
name: string;
level: number;
}

export function buildOrgUnitsParameter(orgUnitsIds: Ref[]): string {
return orgUnitsIds.map(({ id }) => id).join(";");
}
6 changes: 6 additions & 0 deletions src/utils/d2-api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import _ from "lodash";
import { DhisInstance } from "../domain/entities/DhisInstance";
import { D2Api } from "../types/d2-api";
import { memoizeAsync } from "./cache";

export function getMajorVersion(version: string): number {
const apiVersion = _.get(version.split("."), 1);
Expand All @@ -15,3 +16,8 @@ export function getD2APiFromInstance(instance: DhisInstance) {
backend: "fetch",
});
}

export const getVersion = memoizeAsync(async (api: D2Api): Promise<string> => {
const { version } = await api.system.info.getData();
return version;
});
Loading