generated from EyeSeeTea/dhis2-app-skeleton
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: move script logic to a use case
- Loading branch information
Showing
25 changed files
with
700 additions
and
511 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import { D2Api } from "@eyeseetea/d2-api/2.36"; | ||
import { AlertData, OutbreakData } from "../../domain/entities/alert/AlertData"; | ||
import { AlertDataRepository } from "../../domain/repositories/AlertDataRepository"; | ||
import { | ||
D2TrackerTrackedEntity, | ||
TrackedEntitiesGetResponse, | ||
} from "@eyeseetea/d2-api/api/trackerTrackedEntities"; | ||
import { | ||
RTSL_ZEBRA_ALERTS_DISEASE_TEA_ID, | ||
RTSL_ZEBRA_ALERTS_EVENT_TYPE_TEA_ID, | ||
RTSL_ZEBRA_ALERTS_NATIONAL_DISEASE_OUTBREAK_EVENT_ID_TEA_ID, | ||
RTSL_ZEBRA_ALERTS_PROGRAM_ID, | ||
RTSL_ZEBRA_ORG_UNIT_ID, | ||
} from "./consts/DiseaseOutbreakConstants"; | ||
import { Id } from "../../domain/entities/Ref"; | ||
import { FutureData } from "../api-futures"; | ||
import { Future } from "../../domain/entities/generic/Future"; | ||
import _ from "../../domain/entities/generic/Collection"; | ||
import { getTEAttributeById } from "./utils/MetadataHelper"; | ||
import { DataSource } from "../../domain/entities/disease-outbreak-event/DiseaseOutbreakEvent"; | ||
import { mapTrackedEntityAttributesToNotificationOptions } from "./utils/AlertOutbreakMapper"; | ||
|
||
export class AlertDataD2Repository implements AlertDataRepository { | ||
constructor(private api: D2Api) {} | ||
|
||
get(): FutureData<AlertData[]> { | ||
return this.getAlertTrackedEntities().flatMap(alertTEIs => { | ||
const alertsWithNoEventId = this.getAlertData(alertTEIs); | ||
|
||
return alertsWithNoEventId; | ||
}); | ||
} | ||
|
||
private getAlertData(alertTrackedEntities: D2TrackerTrackedEntity[]): FutureData<AlertData[]> { | ||
const alertsWithNoEventId = _(alertTrackedEntities) | ||
.compactMap(trackedEntity => { | ||
const nationalEventId = getTEAttributeById( | ||
trackedEntity, | ||
RTSL_ZEBRA_ALERTS_NATIONAL_DISEASE_OUTBREAK_EVENT_ID_TEA_ID | ||
); | ||
const hazardType = getTEAttributeById( | ||
trackedEntity, | ||
RTSL_ZEBRA_ALERTS_EVENT_TYPE_TEA_ID | ||
); | ||
const diseaseType = getTEAttributeById( | ||
trackedEntity, | ||
RTSL_ZEBRA_ALERTS_DISEASE_TEA_ID | ||
); | ||
|
||
const notificationOptions = | ||
mapTrackedEntityAttributesToNotificationOptions(trackedEntity); | ||
|
||
const outbreakData = diseaseType | ||
? { id: diseaseType.attribute, value: diseaseType.value } | ||
: hazardType | ||
? { id: hazardType.value, value: hazardType.value } | ||
: undefined; | ||
|
||
if (!outbreakData) return undefined; | ||
if (!trackedEntity.trackedEntity || !trackedEntity.orgUnit) | ||
throw new Error("Tracked entity not found"); | ||
|
||
const alertData: AlertData = { | ||
alert: { | ||
id: trackedEntity.trackedEntity, | ||
district: trackedEntity.orgUnit, | ||
}, | ||
outbreakData: outbreakData, | ||
dataSource: diseaseType | ||
? DataSource.RTSL_ZEB_OS_DATA_SOURCE_IBS | ||
: DataSource.RTSL_ZEB_OS_DATA_SOURCE_EBS, | ||
notificationOptions: notificationOptions, | ||
}; | ||
|
||
return !nationalEventId && (hazardType || diseaseType) ? alertData : undefined; | ||
}) | ||
.value(); | ||
|
||
return Future.success(alertsWithNoEventId); | ||
} | ||
|
||
private async getTrackedEntitiesByTEACodeAsync(options: { | ||
program: Id; | ||
orgUnit: Id; | ||
ouMode: "SELECTED" | "DESCENDANTS"; | ||
filter?: OutbreakData; | ||
}): Promise<D2TrackerTrackedEntity[]> { | ||
const { program, orgUnit, ouMode, filter } = options; | ||
const d2TrackerTrackedEntities: D2TrackerTrackedEntity[] = []; | ||
|
||
const pageSize = 250; | ||
let page = 1; | ||
let result: TrackedEntitiesGetResponse; | ||
|
||
try { | ||
do { | ||
result = await this.api.tracker.trackedEntities | ||
.get({ | ||
program: program, | ||
orgUnit: orgUnit, | ||
ouMode: ouMode, | ||
totalPages: true, | ||
page: page, | ||
pageSize: pageSize, | ||
fields: { | ||
attributes: true, | ||
orgUnit: true, | ||
trackedEntity: true, | ||
trackedEntityType: true, | ||
enrollments: { | ||
events: { | ||
createdAt: true, | ||
dataValues: { | ||
dataElement: true, | ||
value: true, | ||
}, | ||
event: true, | ||
}, | ||
}, | ||
}, | ||
filter: filter ? `${filter.id}:eq:${filter.value}` : undefined, | ||
}) | ||
.getData(); | ||
|
||
d2TrackerTrackedEntities.push(...result.instances); | ||
|
||
page++; | ||
} while (result.page < Math.ceil((result.total as number) / pageSize)); | ||
return d2TrackerTrackedEntities; | ||
} catch { | ||
return []; | ||
} | ||
} | ||
|
||
private getTrackedEntitiesByTEACode(options: { | ||
program: Id; | ||
orgUnit: Id; | ||
ouMode: "SELECTED" | "DESCENDANTS"; | ||
filter?: OutbreakData; | ||
}): FutureData<D2TrackerTrackedEntity[]> { | ||
return Future.fromPromise(this.getTrackedEntitiesByTEACodeAsync(options)); | ||
} | ||
|
||
private getAlertTrackedEntities(): FutureData<D2TrackerTrackedEntity[]> { | ||
return this.getTrackedEntitiesByTEACode({ | ||
program: RTSL_ZEBRA_ALERTS_PROGRAM_ID, | ||
orgUnit: RTSL_ZEBRA_ORG_UNIT_ID, | ||
ouMode: "DESCENDANTS", | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,43 @@ | ||
import { D2Api } from "@eyeseetea/d2-api/2.36"; | ||
import { | ||
Notification, | ||
NotificationOptions, | ||
NotificationRepository, | ||
} from "../../domain/repositories/NotificationRepository"; | ||
import { apiToFuture, FutureData } from "../api-futures"; | ||
import { Future } from "../../domain/entities/generic/Future"; | ||
|
||
import { UserGroup } from "../../domain/entities/UserGroup"; | ||
import { AlertData } from "../../domain/entities/alert/AlertData"; | ||
export class NotificationD2Repository implements NotificationRepository { | ||
constructor(private api: D2Api) {} | ||
|
||
save(notification: Notification): FutureData<void> { | ||
return apiToFuture(this.api.messageConversations.post(notification)).flatMap(() => | ||
Future.success(undefined) | ||
); | ||
notifyNationalWatchStaff( | ||
alertData: AlertData, | ||
outbreakName: string, | ||
userGroups: UserGroup[] | ||
): FutureData<void> { | ||
const { notificationOptions } = alertData; | ||
|
||
return apiToFuture( | ||
this.api.messageConversations.post({ | ||
subject: `New Outbreak Alert: ${outbreakName} in zm Zambia Ministry of Health`, | ||
text: buildNotificationText(outbreakName, notificationOptions), | ||
userGroups: userGroups, | ||
}) | ||
).flatMap(() => Future.success(undefined)); | ||
} | ||
} | ||
|
||
function buildNotificationText(outbreakKey: string, notificationData: NotificationOptions): string { | ||
const { detectionDate, emergenceDate, incidentManager, notificationDate, verificationStatus } = | ||
notificationData; | ||
|
||
return `There has been a new Outbreak detected for ${outbreakKey} in zm Zambia Ministry of Health. | ||
Please see the details of the outbreak below: | ||
Emergence date: ${emergenceDate} | ||
Detection Date : ${detectionDate} | ||
Notification Date : ${notificationDate} | ||
Incident Manager : ${incidentManager} | ||
Verification Status : ${verificationStatus}`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { D2Api } from "@eyeseetea/d2-api/2.36"; | ||
import { UserGroupRepository } from "../../domain/repositories/UserGroupRepository"; | ||
import { apiToFuture, FutureData } from "../api-futures"; | ||
import { assertOrError } from "./utils/AssertOrError"; | ||
import { UserGroup } from "../../domain/entities/UserGroup"; | ||
|
||
export class UserGroupD2Repository implements UserGroupRepository { | ||
constructor(private api: D2Api) {} | ||
|
||
getUserGroupByCode(code: string): FutureData<UserGroup> { | ||
return apiToFuture( | ||
this.api.metadata.get({ | ||
userGroups: { | ||
fields: { | ||
id: true, | ||
}, | ||
filter: { | ||
code: { eq: code }, | ||
}, | ||
}, | ||
}) | ||
) | ||
.flatMap(response => assertOrError(response.userGroups[0], `User group ${code}`)) | ||
.map(userGroup => userGroup); | ||
} | ||
} |
Oops, something went wrong.