From e5980d489917475e366305e113425d4c0f1f8770 Mon Sep 17 00:00:00 2001 From: 9sneha-n <9sneha.n@gmail.com> Date: Thu, 24 Oct 2024 19:46:06 +0530 Subject: [PATCH 1/8] fix: risk assessment history graphs --- src/webapp/components/chart/Chart.tsx | 5 +-- .../visualisation/Visualisation.tsx | 35 ++----------------- 2 files changed, 3 insertions(+), 37 deletions(-) diff --git a/src/webapp/components/chart/Chart.tsx b/src/webapp/components/chart/Chart.tsx index 7a8e9580..eb7b41bb 100644 --- a/src/webapp/components/chart/Chart.tsx +++ b/src/webapp/components/chart/Chart.tsx @@ -20,10 +20,7 @@ export const Chart: React.FC = React.memo(props => { const { id } = useChart(chartType, chartKey); - const chartUrl = - chartType === "risk-assessment-history" - ? `${api.baseUrl}/dhis-web-event-visualizer/?id=${id}` - : `${api.baseUrl}/dhis-web-data-visualizer/#/${id}`; + const chartUrl = `${api.baseUrl}/dhis-web-data-visualizer/#/${id}`; return ( diff --git a/src/webapp/components/visualisation/Visualisation.tsx b/src/webapp/components/visualisation/Visualisation.tsx index a7e2fa7f..dfb50e29 100644 --- a/src/webapp/components/visualisation/Visualisation.tsx +++ b/src/webapp/components/visualisation/Visualisation.tsx @@ -29,13 +29,8 @@ export const Visualisation: React.FC = React.memo(props => { await setMapStyling(iframe); setState(prevState => ({ ...prevState, type: "loaded" })); } else { - if (srcUrl.includes("dhis-web-data-visualizer")) { - await setChartStyling(iframe); - setState(prevState => ({ ...prevState, type: "loaded" })); - } else { - await setEventChartStyling(iframe); - setState(prevState => ({ ...prevState, type: "loaded" })); - } + await setChartStyling(iframe); + setState(prevState => ({ ...prevState, type: "loaded" })); } }); } @@ -146,29 +141,3 @@ async function setChartStyling(iframe: HTMLIFrameElement) { iframeDocument.querySelectorAll(".main-center-titlebar").forEach(el => el.remove()); iFrameRoot?.querySelectorAll(".main-center-titlebar").forEach(el => el.remove()); } - -async function setEventChartStyling(iframe: HTMLIFrameElement) { - if (!iframe.contentWindow) return; - const iframeDocument = iframe.contentWindow.document; - - await waitforDocumentToLoad(iframeDocument, "#viewport-1316-embedded-center"); - await waitforDocumentToLoad(iframeDocument, ".x-box-inner"); - - const iFrameRoot = iframeDocument.querySelector("#viewport-1316-embedded-center"); - console.debug(`iFrameRoot: ${iFrameRoot}`); - - console.debug(` toolbar-north : ${iframeDocument.querySelectorAll(".toolbar-north")}`); - iframeDocument.querySelectorAll(".toolbar-north").forEach(el => el.remove()); - iFrameRoot?.querySelectorAll(".toolbar-north").forEach(el => el.remove()); - - console.debug(`#panel-1305 : ${iframeDocument.querySelectorAll("#panel-1305")}`); - iframeDocument.querySelectorAll("#panel-1305").forEach(el => { - console.debug(`Removing element: ${el}`); - el.remove(); - }); - iFrameRoot?.querySelectorAll("#panel-1305").forEach(el => el.remove()); - - const eventChartContainer = iframeDocument.querySelector("#panel-1310"); - console.debug(`eventChartContainer: ${eventChartContainer}`); - if (eventChartContainer) eventChartContainer.style.inset = "0px"; -} From 90403052a90cec5db9cf7b2e4ca7ee737487a233 Mon Sep 17 00:00:00 2001 From: 9sneha-n <9sneha.n@gmail.com> Date: Sat, 26 Oct 2024 23:38:32 +0530 Subject: [PATCH 2/8] refactor: fetch option sets on app load --- src/CompositionRoot.ts | 25 +- .../AppConfigurationD2Repository.ts | 234 ++++++++++ src/data/repositories/OptionsD2Repository.ts | 261 ----------- src/data/repositories/OrgUnitD2Repository.ts | 9 +- .../PerformanceOverviewD2Repository.ts | 1 + .../test/AppConfigurationTestRepository.ts | 51 +++ .../test/OptionsTestRepository.ts | 139 ------ src/domain/entities/AppConfigurations.ts | 43 ++ src/domain/entities/Ref.ts | 2 + .../DiseaseOutbreakEvent.ts | 5 +- .../AppConfigurationRepository.ts | 6 + src/domain/repositories/OptionsRepository.ts | 57 --- .../GetAllAppConfigurationsUseCase.ts | 49 +++ .../usecases/GetDiseaseOutbreakByIdUseCase.ts | 54 ++- .../usecases/GetEntityWithOptionsUseCase.ts | 46 +- .../usecases/GetIncidentActionByIdUseCase.ts | 13 +- .../MapDiseaseOutbreakToAlertsUseCase.ts | 46 +- .../MapDiseaseOutbreakToAlertsUseCase.spec.ts | 34 +- .../GetDiseaseOutbreakWithOptions.ts | 107 ++--- .../incident-action/GetIncidentActionById.ts | 105 ++--- .../GetIncidentActionPlanWithOptions.ts | 86 ++-- .../risk-assessment/GetRiskAssessmentById.ts | 411 ++++++++---------- .../GetRiskAssessmentWithOptions.ts | 231 +++++----- src/scripts/mapDiseaseOutbreakToAlerts.ts | 11 +- src/utils/tests.tsx | 41 ++ src/webapp/contexts/app-context.ts | 2 + src/webapp/pages/app/App.tsx | 16 +- src/webapp/pages/dashboard/DashboardPage.tsx | 4 + .../pages/event-tracker/EventTrackerPage.tsx | 21 +- .../event-tracker/useDiseaseOutbreakEvent.ts | 6 +- src/webapp/pages/form-page/useForm.ts | 71 +-- .../useIncidentActionPlan.ts | 4 +- 32 files changed, 1062 insertions(+), 1129 deletions(-) create mode 100644 src/data/repositories/AppConfigurationD2Repository.ts delete mode 100644 src/data/repositories/OptionsD2Repository.ts create mode 100644 src/data/repositories/test/AppConfigurationTestRepository.ts delete mode 100644 src/data/repositories/test/OptionsTestRepository.ts create mode 100644 src/domain/entities/AppConfigurations.ts create mode 100644 src/domain/repositories/AppConfigurationRepository.ts delete mode 100644 src/domain/repositories/OptionsRepository.ts create mode 100644 src/domain/usecases/GetAllAppConfigurationsUseCase.ts diff --git a/src/CompositionRoot.ts b/src/CompositionRoot.ts index 21556037..8f2701dc 100644 --- a/src/CompositionRoot.ts +++ b/src/CompositionRoot.ts @@ -7,14 +7,13 @@ import { UserRepository } from "./domain/repositories/UserRepository"; import { GetCurrentUserUseCase } from "./domain/usecases/GetCurrentUserUseCase"; import { GetDiseaseOutbreakByIdUseCase } from "./domain/usecases/GetDiseaseOutbreakByIdUseCase"; import { D2Api } from "./types/d2-api"; -import { OptionsRepository } from "./domain/repositories/OptionsRepository"; + import { TeamMemberRepository } from "./domain/repositories/TeamMemberRepository"; import { OrgUnitRepository } from "./domain/repositories/OrgUnitRepository"; -import { OptionsD2Repository } from "./data/repositories/OptionsD2Repository"; + import { TeamMemberD2Repository } from "./data/repositories/TeamMemberD2Repository"; import { OrgUnitD2Repository } from "./data/repositories/OrgUnitD2Repository"; import { AlertD2Repository } from "./data/repositories/AlertD2Repository"; -import { OptionsTestRepository } from "./data/repositories/test/OptionsTestRepository"; import { TeamMemberTestRepository } from "./data/repositories/test/TeamMemberTestRepository"; import { OrgUnitTestRepository } from "./data/repositories/test/OrgUnitTestRepository"; import { GetAllDiseaseOutbreaksUseCase } from "./domain/usecases/GetAllDiseaseOutbreaksUseCase"; @@ -64,6 +63,10 @@ import { SystemRepository } from "./domain/repositories/SystemRepository"; import { SystemD2Repository } from "./data/repositories/SystemD2Repository"; import { SystemTestRepository } from "./data/repositories/test/SystemTestRepository"; import { GetOverviewCardsUseCase } from "./domain/usecases/GetOverviewCardsUseCase"; +import { GetAllAppConfigurationsUseCase } from "./domain/usecases/GetAllAppConfigurationsUseCase"; +import { AppConfigurationRepository } from "./domain/repositories/AppConfigurationRepository"; +import { AppConfigurationD2Repository } from "./data/repositories/AppConfigurationD2Repository"; +import { AppConfigurationTestRepository } from "./data/repositories/test/AppConfigurationTestRepository"; export type CompositionRoot = ReturnType; @@ -72,7 +75,7 @@ type Repositories = { diseaseOutbreakEventRepository: DiseaseOutbreakEventRepository; alertRepository: AlertRepository; alertSyncRepository: AlertSyncRepository; - optionsRepository: OptionsRepository; + teamMemberRepository: TeamMemberRepository; orgUnitRepository: OrgUnitRepository; riskAssessmentRepository: RiskAssessmentRepository; @@ -83,6 +86,7 @@ type Repositories = { incidentManagementTeamRepository: IncidentManagementTeamRepository; chartConfigRepository: ChartConfigRepository; systemRepository: SystemRepository; + eventTrackerConfigurationRespository: AppConfigurationRepository; }; function getCompositionRoot(repositories: Repositories) { @@ -97,8 +101,11 @@ function getCompositionRoot(repositories: Repositories) { getAll: new GetAllDiseaseOutbreaksUseCase(repositories.diseaseOutbreakEventRepository), mapDiseaseOutbreakEventToAlerts: new MapDiseaseOutbreakToAlertsUseCase( repositories.alertRepository, - repositories.alertSyncRepository, - repositories.optionsRepository + repositories.alertSyncRepository + ), + getConfigurations: new GetAllAppConfigurationsUseCase( + repositories.eventTrackerConfigurationRespository, + repositories.teamMemberRepository ), }, incidentActionPlan: { @@ -141,7 +148,7 @@ export function getWebappCompositionRoot(api: D2Api) { diseaseOutbreakEventRepository: new DiseaseOutbreakEventD2Repository(api), alertRepository: new AlertD2Repository(api), alertSyncRepository: new AlertSyncDataStoreRepository(api), - optionsRepository: new OptionsD2Repository(api), + teamMemberRepository: new TeamMemberD2Repository(api), orgUnitRepository: new OrgUnitD2Repository(api), riskAssessmentRepository: new RiskAssessmentD2Repository(api), @@ -152,6 +159,7 @@ export function getWebappCompositionRoot(api: D2Api) { incidentManagementTeamRepository: new IncidentManagementTeamD2Repository(api), chartConfigRepository: new ChartConfigD2Repository(dataStoreClient), systemRepository: new SystemD2Repository(api), + eventTrackerConfigurationRespository: new AppConfigurationD2Repository(api), }; return getCompositionRoot(repositories); @@ -163,7 +171,7 @@ export function getTestCompositionRoot() { diseaseOutbreakEventRepository: new DiseaseOutbreakEventTestRepository(), alertRepository: new AlertTestRepository(), alertSyncRepository: new AlertSyncDataStoreTestRepository(), - optionsRepository: new OptionsTestRepository(), + teamMemberRepository: new TeamMemberTestRepository(), orgUnitRepository: new OrgUnitTestRepository(), riskAssessmentRepository: new RiskAssessmentTestRepository(), @@ -174,6 +182,7 @@ export function getTestCompositionRoot() { incidentManagementTeamRepository: new IncidentManagementTeamTestRepository(), chartConfigRepository: new ChartConfigTestRepository(), systemRepository: new SystemTestRepository(), + eventTrackerConfigurationRespository: new AppConfigurationTestRepository(), }; return getCompositionRoot(repositories); diff --git a/src/data/repositories/AppConfigurationD2Repository.ts b/src/data/repositories/AppConfigurationD2Repository.ts new file mode 100644 index 00000000..b797c510 --- /dev/null +++ b/src/data/repositories/AppConfigurationD2Repository.ts @@ -0,0 +1,234 @@ +import { D2Api, MetadataPick } from "@eyeseetea/d2-api/2.36"; +import { AppConfigurationRepository } from "../../domain/repositories/AppConfigurationRepository"; +import { Option } from "../../domain/entities/Ref"; +import { apiToFuture, FutureData } from "../api-futures"; + +import _ from "../../domain/entities/generic/Collection"; +import { getHazardTypeByCode } from "./consts/DiseaseOutbreakConstants"; + +import { Future } from "../../domain/entities/generic/Future"; +import { AppConfigurations } from "../../domain/entities/AppConfigurations"; +import { RiskAssessmentGrading } from "../../domain/entities/risk-assessment/RiskAssessmentGrading"; +const MAIN_SYNDROME_OPTION_SET_CODE = "AGENTS"; +export const SUSPECTED_DISEASE_OPTION_SET_CODE = "RTSL_ZEB_OS_DISEASE"; +export const NOTIFICATION_SOURCE_OPTION_SET_CODE = "RTSL_ZEB_OS_SOURCE"; +const optionSetCode = { + dataSources: "RTSL_ZEB_OS_DATA_SOURCE", + hazardTypes: "RTSL_ZEB_OS_HAZARD_TYPE", + hazardTypesByCode: "RTSL_ZEB_OS_HAZARD_TYPE", + mainSyndromes: "AGENTS", + suspectedDiseases: SUSPECTED_DISEASE_OPTION_SET_CODE, + notificationSources: NOTIFICATION_SOURCE_OPTION_SET_CODE, + incidentStatus: "RTSL_ZEB_OS_INCIDENT_STATUS", + populationAtRisk: "RTSL_ZEB_OS_POPULATION_AT_RISK", + lowMediumHigh: "RTSL_ZEB_OS_LOW_MEDIUM_HIGH", + geographicalSpread: "RTSL_ZEB_OS_GEOGRAPHICAL_SPREAD", + capability: "RTSL_ZEB_OS_CAPABILITY", + capacity: "RTSL_ZEB_OS_CAPACITY", +}; +export const optionSetsFields = { + name: true, + code: true, + options: { id: true, name: true, code: true }, +} as const; + +export type D2OptionSet = MetadataPick<{ + optionSets: { fields: typeof optionSetsFields }; +}>["optionSets"][number]; + +export class AppConfigurationD2Repository implements AppConfigurationRepository { + constructor(private api: D2Api) {} + + getAppConfigurations(): FutureData { + return apiToFuture( + this.api.metadata.get({ + optionSets: { fields: optionSetsFields }, + }) + ).flatMap(response => { + const appConfig: AppConfigurations = { + eventTrackerConfigurations: { + dataSources: [], + hazardTypes: [], + mainSyndromes: [], + suspectedDiseases: [], + notificationSources: [], + incidentStatus: [], + incidentManagers: [], + }, + riskAssessmentGradingConfigurations: { + populationAtRisk: [], + lowMediumHigh: [], + geographicalSpread: [], + capability: [], + capacity: [], + }, + riskAssessmentSummaryConfigurations: { + overAllConfidencGlobal: [], + overAllConfidencNational: [], + overAllConfidencRegional: [], + overallRiskGlobal: [], + overallRiskNational: [], + overallRiskRegional: [], + riskAssessors: [], + }, + riskAssessmentQuestionnaireConfigurations: { + consequences: [], + likelihood: [], + risk: [], + }, + incidentActionPlanConfigurations: { + iapType: [], + phoecLevel: [], + }, + incidentResponseActionConfigurations: { + searchAssignRO: [], + status: [], + verification: [], + }, + }; + + Object.entries(optionSetCode).map(([key, value]) => { + if (key === "dataSources") { + const dataSources = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (dataSources) + appConfig.eventTrackerConfigurations.dataSources = + this.mapD2OptionSetToOptions(dataSources); + } else if (key === "hazardTypes") { + const hazardTypes = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (hazardTypes) { + const hazardOptions = this.mapD2OptionSetToOptions(hazardTypes); + // appConfig.eventTrackerConfigurations.hazardTypesByCode = + // this.mapD2OptionSetToOptions(hazardTypes); + appConfig.eventTrackerConfigurations.hazardTypes = + this.getHazardTypes(hazardOptions); + } + } else if (key === "mainSyndromes") { + const mainSyndromes = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (mainSyndromes) + appConfig.eventTrackerConfigurations.mainSyndromes = + this.mapD2OptionSetToOptions(mainSyndromes); + } else if (key === "suspectedDiseases") { + const suspectedDiseases = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (suspectedDiseases) + appConfig.eventTrackerConfigurations.suspectedDiseases = + this.mapD2OptionSetToOptions(suspectedDiseases); + } else if (key === "notificationSources") { + const notificationSources = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (notificationSources) + appConfig.eventTrackerConfigurations.notificationSources = + this.mapD2OptionSetToOptions(notificationSources); + } else if (key === "incidentStatus") { + const incidentStatus = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (incidentStatus) + appConfig.eventTrackerConfigurations.incidentStatus = + this.mapD2OptionSetToOptions(incidentStatus); + } else if (key === "populationAtRisk") { + const populationAtRisk = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (populationAtRisk) + appConfig.riskAssessmentGradingConfigurations.populationAtRisk = + populationAtRisk.options.map(populationAtRisk => { + return RiskAssessmentGrading.getOptionTypeByCodePopulation( + populationAtRisk.code + ); + }); + } else if (key === "lowMediumHigh") { + const lowMediumHighOptions = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (lowMediumHighOptions) { + appConfig.riskAssessmentGradingConfigurations.lowMediumHigh = + lowMediumHighOptions.options.map(lowMediumHigh => { + return RiskAssessmentGrading.getOptionTypeByCodeWeighted( + lowMediumHigh.code + ); + }); + appConfig.riskAssessmentSummaryConfigurations.overallRiskGlobal = + this.mapD2OptionSetToOptions(lowMediumHighOptions); + appConfig.riskAssessmentSummaryConfigurations.overallRiskNational = + this.mapD2OptionSetToOptions(lowMediumHighOptions); + appConfig.riskAssessmentSummaryConfigurations.overallRiskRegional = + this.mapD2OptionSetToOptions(lowMediumHighOptions); + appConfig.riskAssessmentSummaryConfigurations.overAllConfidencGlobal = + this.mapD2OptionSetToOptions(lowMediumHighOptions); + appConfig.riskAssessmentSummaryConfigurations.overAllConfidencNational = + this.mapD2OptionSetToOptions(lowMediumHighOptions); + appConfig.riskAssessmentSummaryConfigurations.overAllConfidencRegional = + this.mapD2OptionSetToOptions(lowMediumHighOptions); + } + } else if (key === "geographicalSpread") { + const geographicalSpreadOptions = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (geographicalSpreadOptions) + appConfig.riskAssessmentGradingConfigurations.geographicalSpread = + geographicalSpreadOptions.options.map(geographicalSpread => { + return RiskAssessmentGrading.getOptionTypeByCodeGeographicalSpread( + geographicalSpread.code + ); + }); + } else if (key === "capability") { + const capabilityOptions = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (capabilityOptions) + appConfig.riskAssessmentGradingConfigurations.capability = + capabilityOptions.options.map(capability => { + return RiskAssessmentGrading.getOptionTypeByCodeCapability( + capability.code + ); + }); + } else if (key === "capacity") { + const capacityOptions = response.optionSets.find( + optionSet => optionSet.code === value + ); + if (capacityOptions) + appConfig.riskAssessmentGradingConfigurations.capacity = + capacityOptions.options.map(capacity => { + return RiskAssessmentGrading.getOptionTypeByCodeCapacity( + capacity.code + ); + }); + } + }); + + return Future.success(appConfig); + }); + } + + mapD2OptionSetToOptions(optionSet: D2OptionSet): Option[] { + return optionSet.options.map( + (option): Option => ({ + id: option.code, + name: option.name, + }) + ); + } + + getHazardTypes(hazardTypesByCode: Option[]): Option[] { + return _(hazardTypesByCode) + .compactMap(hazardType => { + const hazardTypeId = getHazardTypeByCode(hazardType.id); + if (hazardTypeId) { + return { + id: hazardTypeId, + name: hazardType.name, + }; + } + }) + .toArray(); + } +} diff --git a/src/data/repositories/OptionsD2Repository.ts b/src/data/repositories/OptionsD2Repository.ts deleted file mode 100644 index a0209774..00000000 --- a/src/data/repositories/OptionsD2Repository.ts +++ /dev/null @@ -1,261 +0,0 @@ -import _ from "../../domain/entities/generic/Collection"; -import { D2Api, MetadataPick } from "../../types/d2-api"; -import { Code, Option } from "../../domain/entities/Ref"; -import { apiToFuture, FutureData } from "../api-futures"; -import { OptionsRepository } from "../../domain/repositories/OptionsRepository"; -import { assertOrError } from "./utils/AssertOrError"; -import { getHazardTypeByCode } from "./consts/DiseaseOutbreakConstants"; - -import { - Capability1, - Capability2, - HighCapacity, - HighGeographicalSpread, - HighPopulationAtRisk, - HighWeightedOption, - LowCapacity, - LowGeographicalSpread, - LowPopulationAtRisk, - LowWeightedOption, - MediumCapacity, - MediumGeographicalSpread, - MediumPopulationAtRisk, - MediumWeightedOption, - RiskAssessmentGrading, -} from "../../domain/entities/risk-assessment/RiskAssessmentGrading"; -import { Future } from "../../domain/entities/generic/Future"; - -const MAIN_SYNDROME_OPTION_SET_CODE = "AGENTS"; -const SUSPECTED_DISEASE_OPTION_SET_CODE = "RTSL_ZEB_OS_DISEASE"; -const NOTIFICATION_SOURCE_OPTION_SET_CODE = "RTSL_ZEB_OS_SOURCE"; - -export class OptionsD2Repository implements OptionsRepository { - constructor(private api: D2Api) {} - private likelihoodOptions: Map = new Map(); - private consequencesOptions: Map = new Map(); - private lowMediumHighOptions: Map = new Map(); - private statusOptions: Map = new Map(); - private verificationOptions: Map = new Map(); - - getMainSyndrome(optionCode: Code): FutureData