Skip to content

Commit

Permalink
Merge pull request #205 from EyeSeeTea/development
Browse files Browse the repository at this point in the history
Release 1.4.0
  • Loading branch information
ifoche authored Nov 1, 2023
2 parents f043e8b + 3c0398a commit 4b1376c
Show file tree
Hide file tree
Showing 21 changed files with 275 additions and 191 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.3.5",
"version": "1.4.0",
"license": "GPL-3.0",
"author": "EyeSeeTea team",
"homepage": ".",
Expand Down
8 changes: 4 additions & 4 deletions src/CompositionRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ import { SavePasswordUseCase } from "./domain/usecases/SavePasswordUseCase";
import { SaveKeyDbLocaleUseCase } from "./domain/usecases/SaveKeyDbLocaleUseCase";
import { SaveKeyUiLocaleUseCase } from "./domain/usecases/SaveKeyUiLocaleUseCase";
import { ProgramRulesMetadataDefaultRepository } from "./data/repositories/program-rule/ProgramRulesMetadataDefaultRepository";
import { RISIndividualDataCSVDefaultRepository } from "./data/repositories/RISIndividualDataCSVDefaultRepository";
import { RISIndividualFunghiDataCSVDefaultRepository } from "./data/repositories/RISIndividualFunghiDataCSVDefaultRepository";
import { TrackerDefaultRepository } from "./data/repositories/TrackerDefaultRepository";
import { GetCaptureFormQuestionsUseCase } from "./domain/usecases/GetCaptureFormQuestionsUseCase";
import { CaptureFormDefaultRepository } from "./data/repositories/CaptureFormDefaultRepository";
Expand All @@ -90,7 +90,7 @@ export function getCompositionRoot(instance: Instance) {
const glassUploadsRepository = new GlassUploadsDefaultRepository(dataStoreClient);
const glassDocumentsRepository = new GlassDocumentsDefaultRepository(dataStoreClient, instance);
const risDataRepository = new RISDataCSVDefaultRepository();
const risIndividualRepository = new RISIndividualDataCSVDefaultRepository();
const risIndividualFunghiRepository = new RISIndividualFunghiDataCSVDefaultRepository();
const sampleDataRepository = new SampleDataCSVDeafultRepository();
const dataValuesRepository = new DataValuesDefaultRepository(instance);
const metadataRepository = new MetadataDefaultRepository(instance);
Expand Down Expand Up @@ -151,7 +151,7 @@ export function getCompositionRoot(instance: Instance) {
fileSubmission: getExecute({
primaryFile: new ImportPrimaryFileUseCase(
risDataRepository,
risIndividualRepository,
risIndividualFunghiRepository,
metadataRepository,
dataValuesRepository,
glassModuleRepository,
Expand All @@ -166,7 +166,7 @@ export function getCompositionRoot(instance: Instance) {
),
validatePrimaryFile: new ValidatePrimaryFileUseCase(
risDataRepository,
risIndividualRepository,
risIndividualFunghiRepository,
egaspDataRepository,
glassModuleRepository
),
Expand Down
34 changes: 17 additions & 17 deletions src/data/repositories/RISDataCSVDefaultRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,25 @@ export class RISDataCSVDefaultRepository implements RISDataRepository {
validate(file: File): FutureData<{ isValid: boolean; records: number; specimens: string[] }> {
return Future.fromPromise(new SpreadsheetXlsxDataSource().read(file)).map(spreadsheet => {
const sheet = spreadsheet.sheets[0]; //Only one sheet for AMR RIS
const firstRow = sheet?.rows[0];
const headerRow = sheet?.headers;

if (firstRow) {
if (headerRow) {
const allRISColsPresent =
doesColumnExist(firstRow, "COUNTRY") &&
doesColumnExist(firstRow, "YEAR") &&
doesColumnExist(firstRow, "SPECIMEN") &&
doesColumnExist(firstRow, "PATHOGEN") &&
doesColumnExist(firstRow, "GENDER") &&
doesColumnExist(firstRow, "ORIGIN") &&
doesColumnExist(firstRow, "AGEGROUP") &&
doesColumnExist(firstRow, "ANTIBIOTIC") &&
doesColumnExist(firstRow, "RESISTANT") &&
doesColumnExist(firstRow, "INTERMEDIATE") &&
doesColumnExist(firstRow, "NONSUSCEPTIBLE") &&
doesColumnExist(firstRow, "SUSCEPTIBLE") &&
doesColumnExist(firstRow, "UNKNOWN_NO_AST") &&
doesColumnExist(firstRow, "UNKNOWN_NO_BREAKPOINTS") &&
doesColumnExist(firstRow, "BATCHID");
doesColumnExist(headerRow, "COUNTRY") &&
doesColumnExist(headerRow, "YEAR") &&
doesColumnExist(headerRow, "SPECIMEN") &&
doesColumnExist(headerRow, "PATHOGEN") &&
doesColumnExist(headerRow, "GENDER") &&
doesColumnExist(headerRow, "ORIGIN") &&
doesColumnExist(headerRow, "AGEGROUP") &&
doesColumnExist(headerRow, "ANTIBIOTIC") &&
doesColumnExist(headerRow, "RESISTANT") &&
doesColumnExist(headerRow, "INTERMEDIATE") &&
doesColumnExist(headerRow, "NONSUSCEPTIBLE") &&
doesColumnExist(headerRow, "SUSCEPTIBLE") &&
doesColumnExist(headerRow, "UNKNOWN_NO_AST") &&
doesColumnExist(headerRow, "UNKNOWN_NO_BREAKPOINTS") &&
doesColumnExist(headerRow, "BATCHID");

const uniqSpecimens = _(sheet.rows)
.uniqBy("SPECIMEN")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Future, FutureData } from "../../domain/entities/Future";
import { RISIndividualData } from "../../domain/entities/data-entry/amr-i-external/RISIndividualData";
import { RISIndividualFunghiData } from "../../domain/entities/data-entry/amr-individual-funghi-external/RISIndividualFunghiData";
import { RISIndividualFunghiDataRepository } from "../../domain/repositories/data-entry/RISIndividualFunghiDataRepository";

import { RISIndividualDataRepository } from "../../domain/repositories/data-entry/RISIndividualDataRepository";
import { SpreadsheetXlsxDataSource } from "./SpreadsheetXlsxDefaultRepository";
import { doesColumnExist, getNumberValue, getTextValue } from "./utils/CSVUtils";

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

return (
sheet?.rows.map(row => {
Expand Down Expand Up @@ -42,46 +42,53 @@ export class RISIndividualDataCSVDefaultRepository implements RISIndividualDataR
RESULTMICSIGN: getTextValue(row, "RESULTMICSIGN"),
RESULTMICVALUE: getNumberValue(row, "RESULTMICVALUE"),
RESULTMICSIR: getTextValue(row, "RESULTMICSIR"),
...(moduleName === "AMR - Individual" && {
ABCLASS: getTextValue(row, "ABCLASS"),
}),
};
}) || []
);
});
}

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

if (firstRow) {
const allRISIndividualColsPresent =
doesColumnExist(firstRow, "COUNTRY") &&
doesColumnExist(firstRow, "YEAR") &&
doesColumnExist(firstRow, "HCF_ID") &&
doesColumnExist(firstRow, "HOSPITALUNITTYPE") &&
doesColumnExist(firstRow, "PATIENT_ID") &&
doesColumnExist(firstRow, "AGE") &&
doesColumnExist(firstRow, "GENDER") &&
doesColumnExist(firstRow, "PATIENTTYPE") &&
doesColumnExist(firstRow, "DATEOFHOSPITALISATION_VISIT") &&
doesColumnExist(firstRow, "LABORATORYCODE") &&
doesColumnExist(firstRow, "SAMPLE_DATE") &&
doesColumnExist(firstRow, "ISOLATE_ID") &&
doesColumnExist(firstRow, "SPECIMEN") &&
doesColumnExist(firstRow, "PATIENTCOUNTER") &&
doesColumnExist(firstRow, "PATHOGEN") &&
doesColumnExist(firstRow, "ANTIBIOTIC") &&
doesColumnExist(firstRow, "SIR") &&
doesColumnExist(firstRow, "REFERENCEGUIDELINESSIR") &&
doesColumnExist(firstRow, "DISKLOAD");
const headerRow = sheet?.headers;

if (headerRow) {
const allRISIndividualFunghiColsPresent =
doesColumnExist(headerRow, "COUNTRY") &&
doesColumnExist(headerRow, "YEAR") &&
doesColumnExist(headerRow, "HCF_ID") &&
doesColumnExist(headerRow, "HOSPITALUNITTYPE") &&
doesColumnExist(headerRow, "PATIENT_ID") &&
doesColumnExist(headerRow, "AGE") &&
doesColumnExist(headerRow, "GENDER") &&
doesColumnExist(headerRow, "PATIENTTYPE") &&
doesColumnExist(headerRow, "DATEOFHOSPITALISATION_VISIT") &&
doesColumnExist(headerRow, "LABORATORYCODE") &&
doesColumnExist(headerRow, "SAMPLE_DATE") &&
doesColumnExist(headerRow, "ISOLATE_ID") &&
doesColumnExist(headerRow, "SPECIMEN") &&
doesColumnExist(headerRow, "PATIENTCOUNTER") &&
doesColumnExist(headerRow, "PATHOGEN") &&
doesColumnExist(headerRow, "ANTIBIOTIC") &&
doesColumnExist(headerRow, "SIR") &&
doesColumnExist(headerRow, "REFERENCEGUIDELINESSIR") &&
doesColumnExist(headerRow, "DISKLOAD") &&
moduleName === "AMR - Individual"
? doesColumnExist(headerRow, "ABCLASS")
: !doesColumnExist(headerRow, "ABCLASS");

const uniqSpecimens = _(sheet.rows)
.uniqBy("SPECIMEN")
.value()
.map(row => (row["SPECIMEN"] ? row["SPECIMEN"] : ""));

return {
isValid: allRISIndividualColsPresent ? true : false,
isValid: allRISIndividualFunghiColsPresent ? true : false,
records: sheet.rows.length,
specimens: uniqSpecimens,
};
Expand Down
20 changes: 10 additions & 10 deletions src/data/repositories/SampleDataCSVDeafultRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ export class SampleDataCSVDeafultRepository implements SampleDataRepository {
validate(file: File): FutureData<{ isValid: boolean; records: number }> {
return Future.fromPromise(new SpreadsheetXlsxDataSource().read(file)).map(spreadsheet => {
const sheet = spreadsheet.sheets[0]; //Only one sheet for AMR RIS
const firstRow = sheet?.rows[0];
const headerRow = sheet?.headers;

if (firstRow) {
if (headerRow) {
const allSampleColsPresent =
doesColumnExist(firstRow, "COUNTRY") &&
doesColumnExist(firstRow, "YEAR") &&
doesColumnExist(firstRow, "SPECIMEN") &&
doesColumnExist(firstRow, "GENDER") &&
doesColumnExist(firstRow, "ORIGIN") &&
doesColumnExist(firstRow, "AGEGROUP") &&
doesColumnExist(headerRow, "COUNTRY") &&
doesColumnExist(headerRow, "YEAR") &&
doesColumnExist(headerRow, "SPECIMEN") &&
doesColumnExist(headerRow, "GENDER") &&
doesColumnExist(headerRow, "ORIGIN") &&
doesColumnExist(headerRow, "AGEGROUP") &&
// doesColumnExist(firstRow, "NUMINFECTED") &&
doesColumnExist(firstRow, "NUMSAMPLEDPATIENTS") &&
doesColumnExist(firstRow, "BATCHID");
doesColumnExist(headerRow, "NUMSAMPLEDPATIENTS") &&
doesColumnExist(headerRow, "BATCHID");

return { isValid: allSampleColsPresent ? true : false, records: sheet.rows.length };
} else
Expand Down
4 changes: 3 additions & 1 deletion src/data/repositories/SpreadsheetXlsxDefaultRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ export class SpreadsheetXlsxDataSource implements SpreadsheetDataSource {
const sheets = _(workbook.Sheets)
.toPairs()
.map(([sheetName, worksheet]): Sheet => {
const headers = XLSX.utils.sheet_to_json<string[]>(worksheet, { header: 1, defval: "" })[0] || [];
const rows = XLSX.utils.sheet_to_json<Row<string>>(worksheet, { raw: true, skipHidden: false });

return {
name: sheetName,
rows: rows.map(row => _.mapValues(row, cellValue => cellValue)),
headers,
rows,
};
})
.value();
Expand Down
4 changes: 2 additions & 2 deletions src/data/repositories/utils/CSVUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export function getNumberValue(row: Row<string>, column: string): number {
return +(row[column] || 0);
}

export function doesColumnExist(row: Row<string>, column: string): boolean {
return row[column] !== undefined;
export function doesColumnExist(header: string[], column: string): boolean {
return header.find(value => value === column) !== undefined;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface RISIndividualData {
export interface RISIndividualFunghiData {
COUNTRY: string;
YEAR: number;
HCF_ID: string;
Expand Down
1 change: 1 addition & 0 deletions src/domain/repositories/SpreadsheetXlsxRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export type Row<Header extends string> = Record<Header, string>;

export interface Sheet<Header extends string = string> {
name: string;
headers: string[];
rows: Row<Header>[];
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
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; records: number; specimens: string[] }>;
}
18 changes: 10 additions & 8 deletions src/domain/usecases/data-entry/ImportPrimaryFileUseCase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ import { ExcelRepository } from "../../repositories/ExcelRepository";
import { GlassDocumentsRepository } from "../../repositories/GlassDocumentsRepository";
import { GlassUploadsDefaultRepository } from "../../../data/repositories/GlassUploadsDefaultRepository";
import { ProgramRulesMetadataRepository } from "../../repositories/program-rules/ProgramRulesMetadataRepository";
import { ImportRISIndividualFile } from "./amr-i/ImportRISIndividualFile";
import { RISIndividualDataRepository } from "../../repositories/data-entry/RISIndividualDataRepository";
import { ImportRISIndividualFunghiFile } from "./amr-individual-funghi/ImportRISIndividualFunghiFile";
import { RISIndividualFunghiDataRepository } from "../../repositories/data-entry/RISIndividualFunghiDataRepository";
import { TrackerRepository } from "../../repositories/TrackerRepository";
import { GlassModuleDefaultRepository } from "../../../data/repositories/GlassModuleDefaultRepository";

export class ImportPrimaryFileUseCase implements UseCase {
constructor(
private risDataRepository: RISDataRepository,
private risIndividualRepository: RISIndividualDataRepository,
private risIndividualFunghiRepository: RISIndividualFunghiDataRepository,
private metadataRepository: MetadataRepository,
private dataValuesRepository: DataValuesRepository,
private moduleRepository: GlassModuleRepository,
Expand Down Expand Up @@ -73,22 +73,24 @@ export class ImportPrimaryFileUseCase implements UseCase {
return importEGASPFile.importEGASPFile(inputFile, action, eventListId, orgUnitId, orgUnitName, period);
}

case "AMR - Individual": {
const importRISIndividualFile = new ImportRISIndividualFile(
this.risIndividualRepository,
case "AMR - Individual":
case "AMR - Funghi": {
const importRISIndividualFunghiFile = new ImportRISIndividualFunghiFile(
this.risIndividualFunghiRepository,
this.trackerRepository,
this.glassDocumentsRepository,
this.glassUploadsRepository
);
return this.glassModuleDefaultRepository.getByName(moduleName).flatMap(module => {
return importRISIndividualFile.importRISIndividualFile(
return importRISIndividualFunghiFile.importRISIndividualFunghiFile(
inputFile,
action,
orgUnitId,
countryCode,
period,
eventListId,
module.programs !== undefined ? module.programs.at(0) : undefined
module.programs !== undefined ? module.programs.at(0) : undefined,
module.name
);
});
}
Expand Down
9 changes: 5 additions & 4 deletions src/domain/usecases/data-entry/ValidatePrimaryFileUseCase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { GlassModuleDefaultRepository } from "../../../data/repositories/GlassMo
import { FutureData, Future } from "../../entities/Future";
import { EGASPDataRepository } from "../../repositories/data-entry/EGASPDataRepository";
import { RISDataRepository } from "../../repositories/data-entry/RISDataRepository";
import { RISIndividualDataRepository } from "../../repositories/data-entry/RISIndividualDataRepository";
import { RISIndividualFunghiDataRepository } from "../../repositories/data-entry/RISIndividualFunghiDataRepository";

export class ValidatePrimaryFileUseCase implements UseCase {
constructor(
private risDataRepository: RISDataRepository,
private risIndividualRepository: RISIndividualDataRepository,
private risIndividualFunghiRepository: RISIndividualFunghiDataRepository,
private egaspDataRepository: EGASPDataRepository,
private glassModuleDefaultRepository: GlassModuleDefaultRepository
) {}
Expand All @@ -27,8 +27,9 @@ export class ValidatePrimaryFileUseCase implements UseCase {
return this.egaspDataRepository.validate(inputFile, module.dataColumns);
});
}
case "AMR - Individual": {
return this.risIndividualRepository.validate(inputFile);
case "AMR - Individual":
case "AMR - Funghi": {
return this.risIndividualFunghiRepository.validate(moduleName, inputFile);
}

default: {
Expand Down
Loading

0 comments on commit 4b1376c

Please sign in to comment.