Skip to content

Commit

Permalink
Release 1.4.2
Browse files Browse the repository at this point in the history
New features:
- Read AMR-I and AMR-F columns from datastore

Bugfixing:
- Fix AMR-I and AMR-F format adapting it to a new metadata format

Merge remote-tracking branch 'upstream/master'
  • Loading branch information
ifoche committed Dec 26, 2023
2 parents 4365a1e + 7ac87ff commit 723c893
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 151 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "glass",
"description": "DHIS2 Glass App",
"version": "1.4.1",
"version": "1.4.2",
"license": "GPL-3.0",
"author": "EyeSeeTea team",
"homepage": ".",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,114 +1,51 @@
import { Future, FutureData } from "../../../domain/entities/Future";
import { RISIndividualFunghiData } from "../../../domain/entities/data-entry/amr-individual-funghi-external/RISIndividualFunghiData";
import {
CustomDataColumns,
CustomDataElementNumber,
CustomDataElementString,
} from "../../../domain/entities/data-entry/amr-individual-funghi-external/RISIndividualFunghiData";
import { SpreadsheetXlsxDataSource } from "../SpreadsheetXlsxDefaultRepository";
import { doesColumnExist, getNumberValue, getTextValue } from "../utils/CSVUtils";
import { RISIndividualFunghiDataRepository } from "../../../domain/repositories/data-entry/RISIndividualFunghiDataRepository";

export class RISIndividualFunghiDataCSVDefaultRepository implements RISIndividualFunghiDataRepository {
get(moduleName: string, file: File): FutureData<RISIndividualFunghiData[]> {
get(dataColumns: CustomDataColumns, file: File): FutureData<CustomDataColumns[]> {
return Future.fromPromise(new SpreadsheetXlsxDataSource().read(file)).map(spreadsheet => {
const sheet = spreadsheet.sheets[0]; //Only one sheet for AMR Individual & Funghi

return (
const rows: CustomDataColumns[] =
sheet?.rows.map(row => {
return {
COUNTRY: getTextValue(row, "COUNTRY"),
YEAR: getNumberValue(row, "YEAR"),
HEALTHCAREFACILITYTYPE: getTextValue(row, "HEALTHCAREFACILITYTYPE"),
HOSPITALUNITTYPE: getTextValue(row, "HOSPITALUNITTYPE"),
PATIENT_ID: getTextValue(row, "PATIENT_ID"),
AGE: getTextValue(row, "AGE"),
GENDER: getTextValue(row, "GENDER"),
PATIENTTYPE: getTextValue(row, "PATIENTTYPE"),
DATEOFADMISSION: getTextValue(row, "DATEOFADMISSION"),
DATEUSEDFORSTATISTICS: getTextValue(row, "DATEUSEDFORSTATISTICS"),
SPECIMEN: getTextValue(row, "SPECIMEN"),
PATIENTCOUNTER: getNumberValue(row, "PATIENTCOUNTER"),
ANTIBIOTIC: getTextValue(row, "ANTIBIOTIC"),
SIR: getTextValue(row, "SIR"),
REFERENCEGUIDELINESSIR: getTextValue(row, "REFERENCEGUIDELINESSIR"),
DISKLOAD: getTextValue(row, "DISKLOAD"),
RESULTETESTSIGN: getTextValue(row, "RESULTETESTSIGN"),
RESULTETESTVALUE: getNumberValue(row, "RESULTETESTVALUE"),
RESULTETESTSIR: getTextValue(row, "RESULTETESTSIR"),
RESULTZONESIGN: getTextValue(row, "RESULTZONESIGN"),
RESULTZONEVALUE: getNumberValue(row, "RESULTZONEVALUE"),
RESULTZONESIR: getTextValue(row, "RESULTZONESIR"),
RESULTMICSIGN: getTextValue(row, "RESULTMICSIGN"),
RESULTMICVALUE: getNumberValue(row, "RESULTMICVALUE"),
RESULTMICSIR: getTextValue(row, "RESULTMICSIR"),
AB_CLASS: moduleName === "AMR - Individual" ? getTextValue(row, "AB_CLASS") : "",
ISOLATEID: moduleName === "AMR - Funghi" ? getTextValue(row, "ISOLATEID") : "",
PATHOGEN_DET: moduleName === "AMR - Funghi" ? getTextValue(row, "PATHOGEN_DET") : "",
AST_HFC_ID: moduleName === "AMR - Funghi" ? getTextValue(row, "AST_HFC_ID") : "",
AMR_LABORATORY_CODE:
moduleName === "AMR - Funghi" ? getTextValue(row, "AMR_LABORATORY_CODE") : "",
AST_METHOD2: moduleName === "AMR - Funghi" ? getTextValue(row, "AST_METHOD2") : "",
IDENT_METHOD2: moduleName === "AMR - Funghi" ? getTextValue(row, "IDENT_METHOD2") : "",
PERFORMED_TEST2: moduleName === "AMR - Funghi" ? getTextValue(row, "PERFORMED_TEST2") : "",
};
}) || []
);
const data: CustomDataColumns = dataColumns.map(column => {
if (column.type === "string")
return {
key: column.key,
type: column.type,
value: getTextValue(row, column.key),
} as CustomDataElementString;
else
return {
key: column.key,
type: column.type,
value: getNumberValue(row, column.key),
} as CustomDataElementNumber;
});
return data;
}) || [];
return rows;
});
}

validate(moduleName: string, file: File): FutureData<{ isValid: boolean; specimens: string[]; rows: number }> {
validate(
dataColumns: CustomDataColumns,
file: File
): FutureData<{ isValid: boolean; specimens: string[]; rows: number }> {
return Future.fromPromise(new SpreadsheetXlsxDataSource().read(file)).map(spreadsheet => {
const sheet = spreadsheet.sheets[0]; //Only one sheet for AMR RIS

const headerRow = sheet?.headers;

if (headerRow) {
const allRISIndividualFunghiColsPresent =
doesColumnExist(headerRow, "COUNTRY") &&
doesColumnExist(headerRow, "YEAR") &&
doesColumnExist(headerRow, "HEALTHCAREFACILITYTYPE") &&
doesColumnExist(headerRow, "HOSPITALUNITTYPE") &&
doesColumnExist(headerRow, "PATIENT_ID") &&
doesColumnExist(headerRow, "AGE") &&
doesColumnExist(headerRow, "GENDER") &&
doesColumnExist(headerRow, "PATIENTTYPE") &&
doesColumnExist(headerRow, "DATEOFADMISSION") &&
doesColumnExist(headerRow, "DATEUSEDFORSTATISTICS") &&
doesColumnExist(headerRow, "SPECIMEN") &&
doesColumnExist(headerRow, "PATIENTCOUNTER") &&
doesColumnExist(headerRow, "ANTIBIOTIC") &&
doesColumnExist(headerRow, "SIR") &&
doesColumnExist(headerRow, "REFERENCEGUIDELINESSIR") &&
doesColumnExist(headerRow, "DISKLOAD") &&
doesColumnExist(headerRow, "RESULTETESTSIGN") &&
doesColumnExist(headerRow, "RESULTETESTVALUE") &&
doesColumnExist(headerRow, "RESULTETESTSIR") &&
doesColumnExist(headerRow, "RESULTZONESIGN") &&
doesColumnExist(headerRow, "RESULTZONEVALUE") &&
doesColumnExist(headerRow, "RESULTZONESIR") &&
doesColumnExist(headerRow, "RESULTMICSIGN") &&
doesColumnExist(headerRow, "RESULTMICVALUE") &&
doesColumnExist(headerRow, "RESULTMICSIR") &&
(moduleName === "AMR - Individual"
? doesColumnExist(headerRow, "AB_CLASS")
: !doesColumnExist(headerRow, "AB_CLASS")) &&
(moduleName === "AMR - Funghi"
? doesColumnExist(headerRow, "ISOLATEID")
: !doesColumnExist(headerRow, "ISOLATEID")) &&
(moduleName === "AMR - Funghi"
? doesColumnExist(headerRow, "PATHOGEN_DET")
: !doesColumnExist(headerRow, "PATHOGEN_DET")) &&
(moduleName === "AMR - Funghi"
? doesColumnExist(headerRow, "AST_HFC_ID")
: !doesColumnExist(headerRow, "AST_HFC_ID")) &&
(moduleName === "AMR - Funghi"
? doesColumnExist(headerRow, "AMR_LABORATORY_CODE")
: !doesColumnExist(headerRow, "AMR_LABORATORY_CODE")) &&
(moduleName === "AMR - Funghi"
? doesColumnExist(headerRow, "AST_METHOD2")
: !doesColumnExist(headerRow, "AST_METHOD2")) &&
(moduleName === "AMR - Funghi"
? doesColumnExist(headerRow, "IDENT_METHOD2")
: !doesColumnExist(headerRow, "IDENT_METHOD2")) &&
(moduleName === "AMR - Funghi"
? doesColumnExist(headerRow, "PERFORMED_TEST2")
: !doesColumnExist(headerRow, "PERFORMED_TEST2"));
const allRISIndividualFunghiColsPresent = dataColumns.every(col => doesColumnExist(headerRow, col.key));

const uniqSpecimens = _(sheet.rows)
.uniqBy("SPECIMEN")
Expand Down
2 changes: 2 additions & 0 deletions src/domain/entities/GlassModule.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Id } from "./Base";
import { CustomDataColumns } from "./data-entry/amr-individual-funghi-external/RISIndividualFunghiData";
import { QuestionnaireRule, QuestionnairesType } from "./Questionnaire";
import { UserGroup } from "./User";

Expand Down Expand Up @@ -35,6 +36,7 @@ export interface GlassModule {
programStageId: string;
}[];
populateCurrentYearInHistory?: boolean;
customDataColumns?: CustomDataColumns;
}

interface QuestionnaireConfig {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,12 @@
export interface RISIndividualFunghiData {
COUNTRY: string;
YEAR: number;
HEALTHCAREFACILITYTYPE: string;
HOSPITALUNITTYPE: string;
PATIENT_ID: string;
AGE: string;
GENDER: string;
PATIENTTYPE: string;
DATEOFADMISSION: string;
DATEUSEDFORSTATISTICS: string;
SPECIMEN: string;
PATIENTCOUNTER: number;
ANTIBIOTIC: string;
SIR: string;
REFERENCEGUIDELINESSIR: string;
DISKLOAD: string;
RESULTETESTSIGN: string;
RESULTETESTVALUE: number;
RESULTETESTSIR: string;
RESULTZONESIGN: string;
RESULTZONEVALUE: number;
RESULTZONESIR: string;
RESULTMICSIGN: string;
RESULTMICVALUE: number;
RESULTMICSIR: string;

//AMR - Individual
AB_CLASS: string;

//AMR - Funghi
ISOLATEID: string;
PATHOGEN_DET: string;
AST_HFC_ID: string;
AMR_LABORATORY_CODE: string;
AST_METHOD2: string;
IDENT_METHOD2: string;
PERFORMED_TEST2: string;
export interface CustomDataElementString {
key: string;
type: "string";
value?: string;
}
export interface CustomDataElementNumber {
key: string;
type: "number";
value?: number;
}

export type CustomDataColumns = (CustomDataElementString | CustomDataElementNumber)[];
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { CustomDataColumns } from "../../entities/data-entry/amr-individual-funghi-external/RISIndividualFunghiData";
import { FutureData } from "../../entities/Future";
import { RISIndividualFunghiData } from "../../entities/data-entry/amr-individual-funghi-external/RISIndividualFunghiData";

export interface RISIndividualFunghiDataRepository {
get(moduleName: string, file: File): FutureData<RISIndividualFunghiData[]>;
validate(moduleName: string, file: File): FutureData<{ isValid: boolean; specimens: string[]; rows: number }>;
get(dataColumns: CustomDataColumns, file: File): FutureData<CustomDataColumns[]>;
validate(
dataColumns: CustomDataColumns,
file: File
): FutureData<{ isValid: boolean; specimens: string[]; rows: number }>;
}
3 changes: 2 additions & 1 deletion src/domain/usecases/data-entry/ImportPrimaryFileUseCase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ export class ImportPrimaryFileUseCase {
period,
eventListId,
module.programs !== undefined ? module.programs.at(0) : undefined,
module.name
module.name,
module.customDataColumns ? module.customDataColumns : []
);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ export class ValidatePrimaryFileUseCase implements UseCase {
}
case "AMR - Individual":
case "AMR - Funghi": {
return this.risIndividualFunghiRepository.validate(moduleName, inputFile);
return this.glassModuleDefaultRepository.getByName(moduleName).flatMap(module => {
const customDataColumns = module.customDataColumns ? module.customDataColumns : [];
return this.risIndividualFunghiRepository.validate(customDataColumns, inputFile);
});
}
case "AMC": //TO DO : Implement validation for AMC
return this.glassModuleDefaultRepository.getByName(moduleName).flatMap(module => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import i18n from "@eyeseetea/d2-ui-components/locales";
import { Future, FutureData } from "../../../entities/Future";
import { ImportStrategy } from "../../../entities/data-entry/DataValuesSaveSummary";
import { ConsistencyError, ImportSummary } from "../../../entities/data-entry/ImportSummary";
import { RISIndividualFunghiData } from "../../../entities/data-entry/amr-individual-funghi-external/RISIndividualFunghiData";
import { GlassDocumentsRepository } from "../../../repositories/GlassDocumentsRepository";
import { GlassUploadsRepository } from "../../../repositories/GlassUploadsRepository";
import { TrackerRepository } from "../../../repositories/TrackerRepository";
Expand All @@ -13,6 +12,7 @@ import { D2TrackerEvent } from "@eyeseetea/d2-api/api/trackerEvents";
import { mapToImportSummary, uploadIdListFileAndSave } from "../ImportBLTemplateEventProgram";
import { MetadataRepository } from "../../../repositories/MetadataRepository";
import { downloadIdsAndDeleteTrackedEntities } from "../amc/ImportAMCProductLevelData";
import { CustomDataColumns } from "../../../entities/data-entry/amr-individual-funghi-external/RISIndividualFunghiData";

const AMRIProgramID = "mMAj6Gofe49";
const AMR_GLASS_AMR_TET_PATIENT = "CcgnfemKr5U";
Expand Down Expand Up @@ -44,11 +44,12 @@ export class ImportRISIndividualFunghiFile {
programStageId: string;
}
| undefined,
moduleName: string
moduleName: string,
dataColumns: CustomDataColumns
): FutureData<ImportSummary> {
if (action === "CREATE_AND_UPDATE") {
return this.risIndividualFunghiRepository
.get(moduleName, inputFile)
.get(dataColumns, inputFile)
.flatMap(risIndividualFunghiDataItems => {
return this.validateDataItems(risIndividualFunghiDataItems, countryCode, period).flatMap(
validationSummary => {
Expand Down Expand Up @@ -111,7 +112,7 @@ export class ImportRISIndividualFunghiFile {
}

private validateDataItems(
risIndividualFunghiDataItems: RISIndividualFunghiData[],
risIndividualFunghiDataItems: CustomDataColumns[],
orgUnit: string,
period: string
): FutureData<ImportSummary> {
Expand All @@ -126,13 +127,15 @@ export class ImportRISIndividualFunghiFile {
return Future.success(summary);
}

private checkCountry(risIndividualFunghiDataItems: RISIndividualFunghiData[], orgUnit: string): ConsistencyError[] {
private checkCountry(risIndividualFunghiDataItems: CustomDataColumns[], orgUnit: string): ConsistencyError[] {
const errors = _(
risIndividualFunghiDataItems.map((dataItem, index) => {
if (dataItem.COUNTRY !== orgUnit) {
if (dataItem.find(item => item.key === "COUNTRY")?.value !== orgUnit) {
return {
error: i18n.t(
`Country is different: Selected Data Submission Country : ${orgUnit}, Country in file: ${dataItem.COUNTRY}`
`Country is different: Selected Data Submission Country : ${orgUnit}, Country in file: ${
dataItem.find(item => item.key === "COUNTRY")?.value
}`
),
line: index,
};
Expand All @@ -150,13 +153,15 @@ export class ImportRISIndividualFunghiFile {
lines: errors[error] || [],
}));
}
private checkPeriod(risIndividualFunghiDataItems: RISIndividualFunghiData[], period: string): ConsistencyError[] {
private checkPeriod(risIndividualFunghiDataItems: CustomDataColumns[], period: string): ConsistencyError[] {
const errors = _(
risIndividualFunghiDataItems.map((dataItem, index) => {
if (dataItem.YEAR !== parseInt(period)) {
if (dataItem.find(item => item.key === "YEAR")?.value !== parseInt(period)) {
return {
error: i18n.t(
`Year is different: Selected Data Submission Country : ${period}, Country in file: ${dataItem.YEAR}`
`Year is different: Selected Data Submission Country : ${period}, Country in file: ${
dataItem.find(item => item.key === "YEAR")?.value
}`
),
line: index,
};
Expand All @@ -176,7 +181,7 @@ export class ImportRISIndividualFunghiFile {
}

private mapIndividualFunghiDataItemsToEntities(
individualFunghiDataItems: RISIndividualFunghiData[],
individualFunghiDataItems: CustomDataColumns[],
orgUnit: string,
AMRIProgramIDl: string,
AMRDataProgramStageIdl: string,
Expand All @@ -188,17 +193,15 @@ export class ImportRISIndividualFunghiFile {
(attr: { id: string; name: string; code: string }) => {
return {
attribute: attr.id,
// @ts-ignore
value: Object.keys(dataItem).includes(attr.code) ? dataItem[attr.code] : "",
value: dataItem.find(item => item.key === attr.code)?.value ?? "",
};
}
);
const AMRDataStage: { dataElement: string; value: string }[] = metadata.programStageDataElements.map(
(de: { id: string; name: string; code: string }) => {
return {
dataElement: de.id,
// @ts-ignore
value: Object.keys(dataItem).includes(de.code) ? dataItem[de.code] : "",
value: dataItem.find(item => item.key === de.code)?.value ?? "",
};
}
);
Expand Down

0 comments on commit 723c893

Please sign in to comment.