diff --git a/.gitignore b/.gitignore index b9736085..e30c06e7 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,11 @@ cypress/fixtures/ #vscode .vscode/* + +#script outputs +src/scripts/products_max_min_calculated_tons.json +src/scripts/substances_max_min_calculated_tons.json +src/scripts/error_response_report.json +src/scripts/d2_tracker_events_updated.json + + diff --git a/package.json b/package.json index b9066f2c..19d125a5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "glass", "description": "DHIS2 Glass App", - "version": "1.6.20", + "version": "1.6.21", "license": "GPL-3.0", "author": "EyeSeeTea team", "homepage": ".", @@ -120,7 +120,9 @@ "amr-agg-data-validation-ris": "npx ts-node -r dotenv/config src/scripts/amr_agg_data_validation.ts", "amr-agg-data-reset-ris": "npx ts-node -r dotenv/config src/scripts/amr_agg_data_reset.ts", "amr-agg-data-validation-sample": "npx ts-node -r dotenv/config src/scripts/amr_agg_data_validation_sample.ts", - "amr-agg-data-reset-sample": "npx ts-node -r dotenv/config src/scripts/amr_agg_data_reset_sample.ts" + "amr-agg-data-reset-sample": "npx ts-node -r dotenv/config src/scripts/amr_agg_data_reset_sample.ts", + "get-max-min-amc-calculated-tons": "npx ts-node -r dotenv/config src/scripts/get_max_min_amc_calculated_tons.ts", + "update-tons-to-kilograms": "npx ts-node -r dotenv/config src/scripts/update_autocalculated_and_manual_tons_to_kilograms.ts" }, "manifest.webapp": { "name": "glass", diff --git a/src/data/repositories/utils/importApiTracker.ts b/src/data/repositories/utils/importApiTracker.ts index 93ede609..815ad8a5 100644 --- a/src/data/repositories/utils/importApiTracker.ts +++ b/src/data/repositories/utils/importApiTracker.ts @@ -19,7 +19,7 @@ export function importApiTracker( request ) ).flatMap(response => { - console.debug(response.response.jobType); + console.debug(response.response); return apiToFuture(api.system.waitFor("TRACKER_IMPORT_JOB", response.response.id)).map(result => { if (result) return result; else { diff --git a/src/domain/entities/data-entry/amc/RawSubstanceConsumptionCalculated.ts b/src/domain/entities/data-entry/amc/RawSubstanceConsumptionCalculated.ts index cfe8103f..cb5dda9e 100644 --- a/src/domain/entities/data-entry/amc/RawSubstanceConsumptionCalculated.ts +++ b/src/domain/entities/data-entry/amc/RawSubstanceConsumptionCalculated.ts @@ -58,7 +58,7 @@ export type RawSubstanceConsumptionCalculated = { data_status_autocalculated: number; health_sector_autocalculated: string; health_level_autocalculated: string; - tons_autocalculated: number; + kilograms_autocalculated: number; packages_autocalculated: number; atc_version_autocalculated: ATCVersionKey; ddds_autocalculated: number; @@ -78,7 +78,7 @@ export const RAW_SUBSTANCE_CONSUMPTION_CALCULATED_KEYS = [ "route_admin_autocalculated", "salt_autocalculated", "packages_autocalculated", - "tons_autocalculated", + "kilograms_autocalculated", "atc_version_autocalculated", "data_status_autocalculated", "health_sector_autocalculated", diff --git a/src/domain/entities/data-entry/amc/SubstanceConsumptionCalculated.ts b/src/domain/entities/data-entry/amc/SubstanceConsumptionCalculated.ts index 76fbf3b4..7e66fcae 100644 --- a/src/domain/entities/data-entry/amc/SubstanceConsumptionCalculated.ts +++ b/src/domain/entities/data-entry/amc/SubstanceConsumptionCalculated.ts @@ -16,7 +16,7 @@ export type SubstanceConsumptionCalculated = { packages_autocalculated: number; ddds_autocalculated: number; atc_version_autocalculated: ATCVersionKey; - tons_autocalculated: number; + kilograms_autocalculated: number; data_status_autocalculated: number; health_sector_autocalculated: string; health_level_autocalculated: string; @@ -38,7 +38,7 @@ export const SUBSTANCE_CONSUMPTION_CALCULATED_KEYS = [ "packages_autocalculated", "ddds_autocalculated", "atc_version_autocalculated", - "tons_autocalculated", + "kilograms_autocalculated", "data_status_autocalculated", "health_sector_autocalculated", "health_level_autocalculated", diff --git a/src/domain/usecases/data-entry/ImportBLTemplateEventProgram.ts b/src/domain/usecases/data-entry/ImportBLTemplateEventProgram.ts index 18cb89a4..04e98098 100644 --- a/src/domain/usecases/data-entry/ImportBLTemplateEventProgram.ts +++ b/src/domain/usecases/data-entry/ImportBLTemplateEventProgram.ts @@ -21,9 +21,9 @@ import { Template } from "../../entities/Template"; import { ExcelReader } from "../../utils/ExcelReader"; import { InstanceRepository } from "../../repositories/InstanceRepository"; import { AMC_RAW_SUBSTANCE_CONSUMPTION_PROGRAM_ID } from "./amc/ImportAMCSubstanceLevelData"; -import moment from "moment"; import { GlassATCDefaultRepository } from "../../../data/repositories/GlassATCDefaultRepository"; import { ListGlassATCLastVersionKeysByYear } from "../../entities/GlassAtcVersionData"; +import moment from "moment"; const ATC_VERSION_DATA_ELEMENT_ID = "aCuWz3HZ5Ti"; @@ -206,14 +206,15 @@ export class ImportBLTemplateEventProgram { ): D2TrackerEvent[] { return dataPackage.dataEntries.map( ({ id, orgUnit, period, attribute, dataValues, dataForm, coordinate }, index) => { + console.debug({ id, orgUnit, period, attribute, dataValues, dataForm, coordinate }, index); + const occurredAt = dataForm === AMC_RAW_SUBSTANCE_CONSUMPTION_PROGRAM_ID - ? moment(new Date(`${period.split("-").at(0)}-01-01`)) - .toISOString() - .split("T") - .at(0) ?? period + ? moment(new Date(new Date(period).getFullYear(), 0, 1)).format("YYYY-MM-DD") ?? period : period; + console.debug(occurredAt); + return { event: id || (index + 6).toString(), program: dataForm, diff --git a/src/domain/usecases/data-entry/amc/utils/__tests__/calculationConsumptionProductLevelData.spec.ts b/src/domain/usecases/data-entry/amc/utils/__tests__/calculationConsumptionProductLevelData.spec.ts index bdf14f89..0c10705b 100644 --- a/src/domain/usecases/data-entry/amc/utils/__tests__/calculationConsumptionProductLevelData.spec.ts +++ b/src/domain/usecases/data-entry/amc/utils/__tests__/calculationConsumptionProductLevelData.spec.ts @@ -230,7 +230,7 @@ function verifyCalculationResult(result: RawSubstanceConsumptionCalculated[], ty expect(calculation.salt_autocalculated).toBe(expectedCalculation?.salt_autocalculated); expect(calculation.year).toBe(expectedCalculation?.year); expect(calculation.packages_autocalculated).toBe(expectedCalculation?.packages_autocalculated); - expect(calculation.tons_autocalculated).toBe(expectedCalculation?.tons_autocalculated); + expect(calculation.kilograms_autocalculated).toBe(expectedCalculation?.kilograms_autocalculated); expect(calculation.ddds_autocalculated).toBe(expectedCalculation?.ddds_autocalculated); expect(calculation.data_status_autocalculated).toBe(expectedCalculation?.data_status_autocalculated); expect(calculation.health_sector_autocalculated).toBe(expectedCalculation?.health_sector_autocalculated); diff --git a/src/domain/usecases/data-entry/amc/utils/__tests__/calculationConsumptionSubstanceLevelData.spec.ts b/src/domain/usecases/data-entry/amc/utils/__tests__/calculationConsumptionSubstanceLevelData.spec.ts index 671ac622..2e4e5ac9 100644 --- a/src/domain/usecases/data-entry/amc/utils/__tests__/calculationConsumptionSubstanceLevelData.spec.ts +++ b/src/domain/usecases/data-entry/amc/utils/__tests__/calculationConsumptionSubstanceLevelData.spec.ts @@ -112,7 +112,7 @@ function verifyCalculationResult(result: any[], type?: string) { expect(calculation.packages_autocalculated).toBe(expectedCalculation?.packages_autocalculated); expect(calculation.ddds_autocalculated).toBe(expectedCalculation?.ddds_autocalculated); expect(calculation.atc_version_autocalculated).toBe(expectedCalculation?.atc_version_autocalculated); - expect(calculation.tons_autocalculated).toBe(expectedCalculation?.tons_autocalculated); + expect(calculation.kilograms_autocalculated).toBe(expectedCalculation?.kilograms_autocalculated); expect(calculation.data_status_autocalculated).toBe(expectedCalculation?.data_status_autocalculated); expect(calculation.health_sector_autocalculated).toBe(expectedCalculation?.health_sector_autocalculated); expect(calculation.health_level_autocalculated).toBe(expectedCalculation?.health_level_autocalculated); diff --git a/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationConsumptionSubstanceLevelBasic.ts b/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationConsumptionSubstanceLevelBasic.ts index be197d85..cedc9b18 100644 --- a/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationConsumptionSubstanceLevelBasic.ts +++ b/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationConsumptionSubstanceLevelBasic.ts @@ -11,7 +11,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 151005, ddds_autocalculated: 75502.5, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.0755025, + kilograms_autocalculated: 75.5025, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -31,7 +31,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 152156, ddds_autocalculated: 760780, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.76078, + kilograms_autocalculated: 760.78, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -51,7 +51,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 152984, ddds_autocalculated: 50994.6666666667, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.0509946666666667, + kilograms_autocalculated: 50.9946666666667, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -71,7 +71,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 90169, ddds_autocalculated: 846493.333333333, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.846493333333333, + kilograms_autocalculated: 846.493333333333, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -91,7 +91,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 1545841, ddds_autocalculated: 21641774, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 21.641774, + kilograms_autocalculated: 21641.774, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -111,7 +111,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 1021561.548, ddds_autocalculated: 510780.774, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.510780774, + kilograms_autocalculated: 510.780774, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -131,7 +131,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 104843, ddds_autocalculated: 1467802, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 1.467802, + kilograms_autocalculated: 1467.8020000000001, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -151,7 +151,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 105151, ddds_autocalculated: 17525.1666666667, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.0175251666666667, + kilograms_autocalculated: 17.5251666666667, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -171,7 +171,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 15241, ddds_autocalculated: 88905.8333333333, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.0889058333333333, + kilograms_autocalculated: 88.90583333333329, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -191,7 +191,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 115145, ddds_autocalculated: 287862.5, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.2878625, + kilograms_autocalculated: 287.8625, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -211,7 +211,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 48, ddds_autocalculated: 280, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.00028, + kilograms_autocalculated: 0.27999999999999997, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -231,7 +231,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 1020, ddds_autocalculated: 5100, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.0051, + kilograms_autocalculated: 5.1000000000000005, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", @@ -251,7 +251,7 @@ export const calculationConsumptionSubstanceLevelBasic: SubstanceConsumptionCalc packages_autocalculated: 15151, ddds_autocalculated: 5050.33333333333, atc_version_autocalculated: "ATC-2023-v1", - tons_autocalculated: 0.00505033333333333, + kilograms_autocalculated: 5.05033333333333, data_status_autocalculated: 1, health_sector_autocalculated: "PUB", health_level_autocalculated: "C", diff --git a/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationMillionInternationalUnitDifferentDDDUnit.json b/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationMillionInternationalUnitDifferentDDDUnit.json index bfa74b6c..ac1e7253 100644 --- a/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationMillionInternationalUnitDifferentDDDUnit.json +++ b/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationMillionInternationalUnitDifferentDDDUnit.json @@ -6,7 +6,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 50, - "tons_autocalculated": 0.000030000000000000004, + "kilograms_autocalculated": 0.030000000000000006, "ddds_autocalculated": 8.333333333333334, "data_status_autocalculated": 1, "health_sector_autocalculated": "PUB", @@ -26,7 +26,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 108, - "tons_autocalculated": 0.00006480000000000002, + "kilograms_autocalculated": 0.06480000000000001, "ddds_autocalculated": 18, "data_status_autocalculated": 3, "health_sector_autocalculated": "PUB", @@ -46,7 +46,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 73, - "tons_autocalculated": 0.00004380000000000001, + "kilograms_autocalculated": 0.043800000000000006, "ddds_autocalculated": 12.166666666666668, "data_status_autocalculated": 2, "health_sector_autocalculated": "PUB", @@ -66,7 +66,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 45, - "tons_autocalculated": 0.000027000000000000002, + "kilograms_autocalculated": 0.027000000000000002, "ddds_autocalculated": 7.500000000000001, "data_status_autocalculated": 1, "health_sector_autocalculated": "PUB", @@ -86,7 +86,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 18, - "tons_autocalculated": 0.0000108, + "kilograms_autocalculated": 0.0108, "ddds_autocalculated": 3.0000000000000004, "data_status_autocalculated": 3, "health_sector_autocalculated": "PUB", diff --git a/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionBasic.json b/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionBasic.json index 154dc8af..f737dc5e 100644 --- a/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionBasic.json +++ b/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionBasic.json @@ -6,7 +6,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 50, - "tons_autocalculated": 0.00025, + "kilograms_autocalculated": 0.25, "ddds_autocalculated": 250, "data_status_autocalculated": 1, "health_sector_autocalculated": "PUB", @@ -26,7 +26,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 108, - "tons_autocalculated": 0.00054, + "kilograms_autocalculated": 0.54, "ddds_autocalculated": 540, "data_status_autocalculated": 3, "health_sector_autocalculated": "PUB", @@ -46,7 +46,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 73, - "tons_autocalculated": 0.000365, + "kilograms_autocalculated": 0.365, "ddds_autocalculated": 365, "data_status_autocalculated": 2, "health_sector_autocalculated": "PUB", @@ -66,7 +66,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 45, - "tons_autocalculated": 0.000225, + "kilograms_autocalculated": 0.225, "ddds_autocalculated": 112.5, "data_status_autocalculated": 1, "health_sector_autocalculated": "PUB", @@ -86,7 +86,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 18, - "tons_autocalculated": 0.00009, + "kilograms_autocalculated": 0.09000000000000001, "ddds_autocalculated": 45, "data_status_autocalculated": 3, "health_sector_autocalculated": "PUB", diff --git a/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionConcVolumeAndVolume.json b/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionConcVolumeAndVolume.json index 68018f4e..87621b01 100644 --- a/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionConcVolumeAndVolume.json +++ b/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionConcVolumeAndVolume.json @@ -6,7 +6,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 50, - "tons_autocalculated": 0.00025, + "kilograms_autocalculated": 0.25, "ddds_autocalculated": 166.66666666666669, "data_status_autocalculated": 1, "health_sector_autocalculated": "PUB", @@ -26,7 +26,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 108, - "tons_autocalculated": 0.00054, + "kilograms_autocalculated": 0.54, "ddds_autocalculated": 360, "data_status_autocalculated": 3, "health_sector_autocalculated": "PUB", @@ -46,7 +46,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 73, - "tons_autocalculated": 0.000365, + "kilograms_autocalculated": 0.365, "ddds_autocalculated": 243.33333333333334, "data_status_autocalculated": 2, "health_sector_autocalculated": "PUB", @@ -66,7 +66,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 45, - "tons_autocalculated": 0.000225, + "kilograms_autocalculated": 0.225, "ddds_autocalculated": 150, "data_status_autocalculated": 1, "health_sector_autocalculated": "PUB", @@ -86,7 +86,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 18, - "tons_autocalculated": 0.00009, + "kilograms_autocalculated": 0.09000000000000001, "ddds_autocalculated": 60, "data_status_autocalculated": 3, "health_sector_autocalculated": "PUB", diff --git a/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionUnitDoseCombCode.json b/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionUnitDoseCombCode.json index 05a5e195..884c37b1 100644 --- a/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionUnitDoseCombCode.json +++ b/src/domain/usecases/data-entry/amc/utils/__tests__/data/calculationSolutionUnitDoseCombCode.json @@ -6,7 +6,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 50, - "tons_autocalculated": 0.001, + "kilograms_autocalculated": 1, "ddds_autocalculated": 250, "data_status_autocalculated": 1, "health_sector_autocalculated": "PUB", @@ -26,7 +26,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 108, - "tons_autocalculated": 0.00216, + "kilograms_autocalculated": 2.16, "ddds_autocalculated": 540, "data_status_autocalculated": 3, "health_sector_autocalculated": "PUB", @@ -46,7 +46,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 73, - "tons_autocalculated": 0.00146, + "kilograms_autocalculated": 1.46, "ddds_autocalculated": 365, "data_status_autocalculated": 2, "health_sector_autocalculated": "PUB", @@ -66,7 +66,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 45, - "tons_autocalculated": 0.00045, + "kilograms_autocalculated": 0.45, "ddds_autocalculated": 112.5, "data_status_autocalculated": 1, "health_sector_autocalculated": "PUB", @@ -86,7 +86,7 @@ "salt_autocalculated": "XXXX", "year": "2020", "packages_autocalculated": 18, - "tons_autocalculated": 0.00018, + "kilograms_autocalculated": 0.18000000000000002, "ddds_autocalculated": 45, "data_status_autocalculated": 3, "health_sector_autocalculated": "PUB", diff --git a/src/domain/usecases/data-entry/amc/utils/calculationConsumptionProductLevelData.ts b/src/domain/usecases/data-entry/amc/utils/calculationConsumptionProductLevelData.ts index 124fb614..4c15e17c 100644 --- a/src/domain/usecases/data-entry/amc/utils/calculationConsumptionProductLevelData.ts +++ b/src/domain/usecases/data-entry/amc/utils/calculationConsumptionProductLevelData.ts @@ -683,7 +683,7 @@ function aggregateDataByAtcRouteAdminYearHealthSectorAndHealthLevel( const isAlreadyInTheAggregation = accWithThisId && - (accWithThisId?.tons_autocalculated || accWithThisId?.tons_autocalculated === 0) && + (accWithThisId?.kilograms_autocalculated || accWithThisId?.kilograms_autocalculated === 0) && (accWithThisId?.packages_autocalculated || accWithThisId?.packages_autocalculated === 0) && (accWithThisId?.ddds_autocalculated || accWithThisId?.ddds_autocalculated === 0); @@ -706,13 +706,13 @@ function aggregateDataByAtcRouteAdminYearHealthSectorAndHealthLevel( ]; } + const contentKilograms = contentTonnesOfProduct.result.contentTonnes * 1000; return { ...acc, [id]: isAlreadyInTheAggregation ? { ...accWithThisId, - tons_autocalculated: - accWithThisId.tons_autocalculated + contentTonnesOfProduct.result.contentTonnes, + kilograms_autocalculated: accWithThisId.kilograms_autocalculated + contentKilograms, packages_autocalculated: accWithThisId.packages_autocalculated + packages_manual, ddds_autocalculated: accWithThisId.ddds_autocalculated + @@ -725,7 +725,7 @@ function aggregateDataByAtcRouteAdminYearHealthSectorAndHealthLevel( salt_autocalculated: AMR_GLASS_AMC_TEA_SALT, year: period, packages_autocalculated: packages_manual, - tons_autocalculated: contentTonnesOfProduct.result.contentTonnes, + kilograms_autocalculated: contentKilograms, ddds_autocalculated: dddPerProductConsumptionPackages.result.dddConsumptionPackages, data_status_autocalculated: data_status_manual, health_sector_autocalculated: health_sector_manual, diff --git a/src/domain/usecases/data-entry/amc/utils/calculationConsumptionSubstanceLevelData.ts b/src/domain/usecases/data-entry/amc/utils/calculationConsumptionSubstanceLevelData.ts index 5a2d67c6..d8b17b56 100644 --- a/src/domain/usecases/data-entry/amc/utils/calculationConsumptionSubstanceLevelData.ts +++ b/src/domain/usecases/data-entry/amc/utils/calculationConsumptionSubstanceLevelData.ts @@ -150,6 +150,7 @@ export function calculateConsumptionSubstanceLevelData( const atcCodeByLevel = getAtcCodeByLevel(atcData, rawSubstanceConsumption.atc_manual); const aware = getAwareClass(awareClassData, rawSubstanceConsumption.atc_manual); + const rawSubstanceConsumptionKilograms = rawSubstanceConsumption.tons_manual * 1000; return { period, orgUnitId, @@ -160,7 +161,7 @@ export function calculateConsumptionSubstanceLevelData( packages_autocalculated: rawSubstanceConsumption.packages_manual, ddds_autocalculated: dddsAdjust.result, atc_version_autocalculated: currentAtcVersionKey, - tons_autocalculated: rawSubstanceConsumption.tons_manual, + kilograms_autocalculated: rawSubstanceConsumptionKilograms, data_status_autocalculated: rawSubstanceConsumption.data_status_manual, health_sector_autocalculated: rawSubstanceConsumption.health_sector_manual, health_level_autocalculated: rawSubstanceConsumption.health_level_manual, diff --git a/src/domain/usecases/data-entry/amc/utils/mapRawSubstanceCalculatedToSubstanceCalculated.ts b/src/domain/usecases/data-entry/amc/utils/mapRawSubstanceCalculatedToSubstanceCalculated.ts index ac0c2e40..7ebe0f9f 100644 --- a/src/domain/usecases/data-entry/amc/utils/mapRawSubstanceCalculatedToSubstanceCalculated.ts +++ b/src/domain/usecases/data-entry/amc/utils/mapRawSubstanceCalculatedToSubstanceCalculated.ts @@ -19,7 +19,7 @@ export function mapRawSubstanceCalculatedToSubstanceCalculated( packages_autocalculated: rawSubstanceConsumptionCalculated.packages_autocalculated, ddds_autocalculated: rawSubstanceConsumptionCalculated.ddds_autocalculated, atc_version_autocalculated: rawSubstanceConsumptionCalculated.atc_version_autocalculated, - tons_autocalculated: rawSubstanceConsumptionCalculated.tons_autocalculated, + kilograms_autocalculated: rawSubstanceConsumptionCalculated.kilograms_autocalculated, data_status_autocalculated: rawSubstanceConsumptionCalculated.data_status_autocalculated, health_sector_autocalculated: rawSubstanceConsumptionCalculated.health_sector_autocalculated, health_level_autocalculated: rawSubstanceConsumptionCalculated.health_level_autocalculated, diff --git a/src/domain/usecases/data-entry/amc/utils/updateRecalculatedConsumptionData.ts b/src/domain/usecases/data-entry/amc/utils/updateRecalculatedConsumptionData.ts index bd706c7a..99d5b93a 100644 --- a/src/domain/usecases/data-entry/amc/utils/updateRecalculatedConsumptionData.ts +++ b/src/domain/usecases/data-entry/amc/utils/updateRecalculatedConsumptionData.ts @@ -116,7 +116,7 @@ function linkEventIdToNewCalculatedConsumptionData( (currentCalculatedData.salt_autocalculated === newCalulatedData.salt_autocalculated || DEFAULT_SALT_CODE === newCalulatedData.salt_autocalculated) && currentCalculatedData.packages_autocalculated === newCalulatedData.packages_autocalculated && - currentCalculatedData.tons_autocalculated === newCalulatedData.tons_autocalculated && + currentCalculatedData.kilograms_autocalculated === newCalulatedData.kilograms_autocalculated && currentCalculatedData.health_sector_autocalculated === newCalulatedData.health_sector_autocalculated && currentCalculatedData.health_level_autocalculated === diff --git a/src/scripts/get_max_min_amc_calculated_tons.ts b/src/scripts/get_max_min_amc_calculated_tons.ts new file mode 100644 index 00000000..d0e7fe3c --- /dev/null +++ b/src/scripts/get_max_min_amc_calculated_tons.ts @@ -0,0 +1,232 @@ +import { boolean, command, flag, run } from "cmd-ts"; +import path from "path"; +import fs from "fs"; +import { D2TrackerEvent, TrackerEventsResponse } from "@eyeseetea/d2-api/api/trackerEvents"; + +import { getD2ApiFromArgs } from "./common"; +import { Future, FutureData } from "../domain/entities/Future"; +import { D2Api } from "../types/d2-api"; +import { Id } from "../domain/entities/Ref"; +import { apiToFuture } from "../utils/futures"; + +const KOSOVO_ORG_UNIT_ID = "I8AMbKhxlj9"; +const OLD_AMR_GLASS_AMC_DET_TONS_AUTOCALCULATED = "Ow8jz1uWB1V"; + +// PRODUCT LEVEL: +const AMC_PRODUCT_REGISTER_PROGRAM_ID = "G6ChA5zMW9n"; +const AMC_RAW_SUBSTANCE_CONSUMPTION_CALCULATED_STAGE_ID = "q8cl5qllyjd"; + +// SUBSTANCE LEVEL: +const AMC_CALCULATED_CONSUMPTION_DATA_PROGRAM_ID = "eUmWZeKZNrg"; + +function main() { + const cmd = command({ + name: path.basename(__filename), + description: "Get max and min value of autocalculated tons in AMC", + args: { + products: flag({ + type: boolean, + long: "products", + description: "Option to get max and min tons values from calculated consumption of products", + }), + substances: flag({ + type: boolean, + long: "substances", + description: "Option to get max and min tons values from calculated consumption of substances", + }), + }, + handler: async args => { + if (!process.env.REACT_APP_DHIS2_BASE_URL) + throw new Error("REACT_APP_DHIS2_BASE_URL must be set in the .env file"); + + if (!process.env.REACT_APP_DHIS2_AUTH) + throw new Error("REACT_APP_DHIS2_BASE_URL must be set in the .env file"); + + const username = process.env.REACT_APP_DHIS2_AUTH.split(":")[0] ?? ""; + const password = process.env.REACT_APP_DHIS2_AUTH.split(":")[1] ?? ""; + + if (username === "" || password === "") { + throw new Error("REACT_APP_DHIS2_AUTH must be in the format 'username:password'"); + } + + if (!args.products && !args.substances) throw new Error("products or substances flag is required"); + const programId = args.products + ? AMC_PRODUCT_REGISTER_PROGRAM_ID + : AMC_CALCULATED_CONSUMPTION_DATA_PROGRAM_ID; + const programStageId = args.products ? AMC_RAW_SUBSTANCE_CONSUMPTION_CALCULATED_STAGE_ID : undefined; + + const envVars = { + url: process.env.REACT_APP_DHIS2_BASE_URL, + auth: { + username: username, + password: password, + }, + }; + + const api = getD2ApiFromArgs(envVars); + + try { + console.debug(`Fetching all org units...`); + return getAllOrgUnitsIds(api).run( + orgUnitsIds => { + console.debug(`Total org units fetched: ${orgUnitsIds.length}`); + return getAutocalculatedTonsValues(api, orgUnitsIds, programId, programStageId).run( + maxMinValues => { + const content = `Mínimo: ${maxMinValues.min}\nMáximo: ${maxMinValues.max}`; + console.debug(content); + + const maxMin = JSON.stringify(maxMinValues, null, 2); + const filePath = path.join( + __dirname, + `${args.products ? "products" : "substances"}_max_min_calculated_tons.json` + ); + fs.writeFileSync(filePath, maxMin); + }, + error => console.error(`ERROR: ${error}.`) + ); + }, + error => console.error(`ERROR: ${error}.`) + ); + } catch (error) { + console.error(`Error thrown when getting max and min value of autocalculated tons: ${error}`); + } + }, + }); + + run(cmd, process.argv.slice(2)); +} + +main(); + +function getAllOrgUnitsIds(api: D2Api): FutureData { + return apiToFuture( + api.models.organisationUnits.get({ + fields: { + id: true, + }, + paging: false, + level: 3, + }) + ).map(response => { + return [...response.objects.map(orgUnit => orgUnit.id), KOSOVO_ORG_UNIT_ID]; + }); +} + +function getAutocalculatedTonsValues( + api: D2Api, + orgUnitsIds: Id[], + programId: Id, + programStageId?: Id +): FutureData<{ + min: number; + max: number; +}> { + console.debug(`Fetching autocaculated tons values...`); + return Future.sequential( + orgUnitsIds.map(orgUnitId => { + return Future.fromPromise(new Promise(resolve => setTimeout(resolve, 100))).flatMap(() => { + return getAllD2EventsFromProgramByOrgUnit(api, orgUnitId, programId, programStageId).map(d2Events => { + return d2Events.map(d2Event => { + const dataValue = d2Event.dataValues.find( + dataValue => dataValue.dataElement === OLD_AMR_GLASS_AMC_DET_TONS_AUTOCALCULATED + ); + return parseFloat(dataValue?.value ?? "0"); + }); + }); + }); + }) + ).map(values => { + console.debug(`Autocaculated tons values fetched`); + const flattenValues = values.flat(); + + const { min, max } = flattenValues + .filter(tons => tons !== 0) + .reduce( + (acc, val) => ({ + min: Math.min(acc.min, val), + max: Math.max(acc.max, val), + }), + { min: Infinity, max: -Infinity } + ); + + return { min: min, max: max }; + }); +} + +function getAllD2EventsFromProgramByOrgUnit( + api: D2Api, + orgUnitId: Id, + programId: Id, + programStageId?: Id +): FutureData { + return Future.fromPromise(getEventsFromProgramByOrgUnitAsync(api, orgUnitId, programId, programStageId)); +} + +async function getEventsFromProgramByOrgUnitAsync( + api: D2Api, + orgUnit: Id, + programId: Id, + programStageId?: Id +): Promise { + const d2TrackerEvents: D2TrackerEvent[] = []; + const totalPages = true; + const pageSize = 250; + let page = 1; + let result; + try { + do { + result = await getEventsFromProgramByOrgUnitOfPage( + api, + programStageId + ? { + orgUnit, + program: programId, + programStage: programStageId, + page, + pageSize, + totalPages, + } + : { + orgUnit, + program: programId, + page, + pageSize, + totalPages, + } + ); + if (!result.total) { + throw new Error( + `Error getting paginated events of program ${programId} and program stage ${programStageId} in organisation ${orgUnit}` + ); + } + d2TrackerEvents.push(...result.instances); + page++; + } while (result.page < Math.ceil((result.total as number) / pageSize)); + return d2TrackerEvents; + } catch (e) { + return []; + } +} + +function getEventsFromProgramByOrgUnitOfPage( + api: D2Api, + params: { + orgUnit: Id; + program: Id; + programStage?: Id; + occurredAfter?: string; + occurredBefore?: string; + page: number; + pageSize: number; + totalPages?: boolean; + } +): Promise { + return api.tracker.events + .get({ + fields: { + dataValues: true, + }, + ...params, + }) + .getData(); +} diff --git a/src/scripts/update_autocalculated_and_manual_tons_to_kilograms.ts b/src/scripts/update_autocalculated_and_manual_tons_to_kilograms.ts new file mode 100644 index 00000000..3155f45f --- /dev/null +++ b/src/scripts/update_autocalculated_and_manual_tons_to_kilograms.ts @@ -0,0 +1,243 @@ +import { boolean, command, flag, run } from "cmd-ts"; +import path from "path"; +import fs from "fs"; +import { D2TrackerEvent, TrackerEventsResponse } from "@eyeseetea/d2-api/api/trackerEvents"; +import _ from "lodash"; + +import { getD2ApiFromArgs } from "./common"; +import { Future, FutureData } from "../domain/entities/Future"; +import { D2Api } from "../types/d2-api"; +import { Id } from "../domain/entities/Ref"; +import { importApiTracker } from "../data/repositories/utils/importApiTracker"; +import { TrackerPostResponse } from "@eyeseetea/d2-api/api/tracker"; + +const OLD_AMR_GLASS_AMC_DET_TONS_AUTOCALCULATED = "Ow8jz1uWB1V"; + +// PRODUCT LEVEL: +const AMC_PRODUCT_REGISTER_PROGRAM_ID = "G6ChA5zMW9n"; +const AMC_RAW_SUBSTANCE_CONSUMPTION_CALCULATED_STAGE_ID = "q8cl5qllyjd"; + +// SUBSTANCE LEVEL: +const AMC_CALCULATED_CONSUMPTION_DATA_PROGRAM_ID = "eUmWZeKZNrg"; + +function main() { + const cmd = command({ + name: path.basename(__filename), + description: "Update in AMC tonnes autocalculated from tonnes to kilograms", + args: { + importUpdates: flag({ + type: boolean, + long: "import-updates", + description: "Option to import to DHIS2 the updated D2TrackerEvents from tonnes to kilograms", + }), + }, + handler: async ({ importUpdates }) => { + if (!process.env.REACT_APP_DHIS2_BASE_URL) + throw new Error("REACT_APP_DHIS2_BASE_URL must be set in the .env file"); + + if (!process.env.REACT_APP_DHIS2_AUTH) + throw new Error("REACT_APP_DHIS2_BASE_URL must be set in the .env file"); + + const username = process.env.REACT_APP_DHIS2_AUTH.split(":")[0] ?? ""; + const password = process.env.REACT_APP_DHIS2_AUTH.split(":")[1] ?? ""; + + if (username === "" || password === "") { + throw new Error("REACT_APP_DHIS2_AUTH must be in the format 'username:password'"); + } + console.debug( + `Update in AMC tonnes autocalculated from tonnes to kilograms in ${process.env.REACT_APP_DHIS2_BASE_URL}` + ); + + const envVars = { + url: process.env.REACT_APP_DHIS2_BASE_URL, + auth: { + username: username, + password: password, + }, + }; + + try { + const api = getD2ApiFromArgs(envVars); + console.debug(`Fetching all D2TrackerEvents...`); + return getD2TrackerEventsInPrograms(api).run( + d2TrackerEvents => { + const d2TrackerEventsWithKilogram = updateDataValueTonsToKilograms(d2TrackerEvents); + if (importUpdates && d2TrackerEvents.length > 0) { + console.debug( + `Updated ${d2TrackerEventsWithKilogram.length} D2TrackerEvents from tonnes to kilograms. Next import them in DHIS2.` + ); + return importUpdatedD2TrackerEvents(api, d2TrackerEventsWithKilogram).run( + response => { + if (response.status === "ERROR") { + console.error(`Error importing updated D2TrackerEvents.`); + const errorReport = JSON.stringify( + [ + ...response.validationReport.errorReports, + ...response.validationReport.warningReports, + ], + null, + 2 + ); + const filePath = path.join(__dirname, `error_response_report.json`); + fs.writeFileSync(filePath, errorReport); + throw new Error( + `Error importing updated D2TrackerEvents: ${response.stats.ignored}/${response.stats.total}` + ); + } else { + console.debug( + `Successfully updated in DHIS2 from tonnes to kilograms: ${response.stats.updated}/${response.stats.total} events updated.` + ); + } + }, + error => console.error(`ERROR: ${error}.`) + ); + } else { + const d2TrackerEventsJSON = JSON.stringify(d2TrackerEventsWithKilogram, null, 2); + const filePath = path.join(__dirname, `d2_tracker_events_updated.json`); + fs.writeFileSync(filePath, d2TrackerEventsJSON); + console.debug( + `Successfully updated ${d2TrackerEventsWithKilogram.length} D2TrackerEvents from tonnes to kilograms.` + ); + } + }, + error => console.error(`ERROR: ${error}.`) + ); + } catch (error) { + console.error(`Error thrown when updating from tonnes to kilograms: ${error}`); + } + }, + }); + + run(cmd, process.argv.slice(2)); +} + +main(); + +function updateDataValueTonsToKilograms(d2TrackerEvents: D2TrackerEvent[]): D2TrackerEvent[] { + return d2TrackerEvents + .filter( + d2TrackerEvent => + d2TrackerEvent.dataValues.length > 0 && + d2TrackerEvent.dataValues.some( + dataValue => dataValue.dataElement === OLD_AMR_GLASS_AMC_DET_TONS_AUTOCALCULATED + ) + ) + .map(d2TrackerEvent => { + const dataValues = d2TrackerEvent.dataValues.map(dataValue => { + if (dataValue.dataElement === OLD_AMR_GLASS_AMC_DET_TONS_AUTOCALCULATED) { + return { ...dataValue, value: (parseFloat(dataValue.value) * 1000).toString() }; + } else { + return dataValue; + } + }); + + return { ...d2TrackerEvent, dataValues }; + }); +} + +function importUpdatedD2TrackerEvents(api: D2Api, d2TrackerEvents: D2TrackerEvent[]): FutureData { + console.debug(`Importing updated D2TrackerEvents...`); + return importApiTracker(api, { events: d2TrackerEvents }, "UPDATE"); +} + +function getD2TrackerEventsInPrograms(api: D2Api): FutureData { + return Future.sequential( + [AMC_PRODUCT_REGISTER_PROGRAM_ID, AMC_CALCULATED_CONSUMPTION_DATA_PROGRAM_ID].map(programId => { + return Future.fromPromise(new Promise(resolve => setTimeout(resolve, 1000))).flatMap(() => { + console.debug(`Fetching D2TrackerEvents from program ${programId}...`); + const programStageId = + programId === AMC_PRODUCT_REGISTER_PROGRAM_ID + ? AMC_RAW_SUBSTANCE_CONSUMPTION_CALCULATED_STAGE_ID + : undefined; + return getAllD2TrackerEventsFromProgram(api, programId, programStageId); + }); + }) + ).map(values => { + return _.flatten(values); + }); +} + +function getAllD2TrackerEventsFromProgram( + api: D2Api, + programId: Id, + programStageId?: Id +): FutureData { + return Future.fromPromise(getD2TrackerEventsFromProgramAsync(api, programId, programStageId)); +} + +async function getD2TrackerEventsFromProgramAsync( + api: D2Api, + programId: Id, + programStageId?: Id +): Promise { + const d2TrackerEvents: D2TrackerEvent[] = []; + const totalPages = true; + const pageSize = 250; + let page = 1; + let result; + try { + do { + result = await getD2TrackerEventsFromProgramOfPage( + api, + programStageId + ? { + program: programId, + programStage: programStageId, + page, + pageSize, + totalPages, + } + : { + program: programId, + page, + pageSize, + totalPages, + } + ); + console.debug( + `Fetched page ${page}/${Math.ceil( + (result.total as number) / pageSize + )} of D2TrackerEvents from program ${programId}` + ); + if (!result.total) { + throw new Error( + `Error getting paginated events of program ${programId} and program stage ${programStageId}` + ); + } + d2TrackerEvents.push(...result.instances); + page++; + } while (result.page < Math.ceil((result.total as number) / pageSize)); + return d2TrackerEvents; + } catch (e) { + return []; + } +} + +function getD2TrackerEventsFromProgramOfPage( + api: D2Api, + params: { + program: Id; + programStage?: Id; + page: number; + pageSize: number; + totalPages?: boolean; + } +): Promise { + return api.tracker.events + .get({ + fields: eventFields, + ...params, + }) + .getData(); +} + +const eventFields = { + event: true, + occurredAt: true, + dataValues: true, + trackedEntity: true, + enrollment: true, + program: true, + programStage: true, + orgUnit: true, +} as const;