Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search for prevalence patients and some perf improvements #59

Merged
merged 1 commit into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/CompositionRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import { ModuleD2Repository } from "./data/repositories/ModuleD2Repository";
import { DataStoreClient } from "./data/DataStoreClient";
import { ModulesTestRepository } from "./data/repositories/testRepositories/ModuleTestRepository";
import { GetSurveyUseCase } from "./domain/usecases/GetSurveyUseCase";
import { GetFilteredPatientsUseCase } from "./domain/usecases/GetFilteredPatientsUseCase";
import { GetFilteredPPSPatientsUseCase } from "./domain/usecases/GetFilteredPPSPatientsUseCase";
import { SurveyRepository } from "./domain/repositories/SurveyRepository";
import { UserD2Repository } from "./data/repositories/UserD2Repository";
import { SurveyD2Repository } from "./data/repositories/SurveyFormD2Repository";
import { SurveyTestRepository } from "./data/repositories/testRepositories/SurveyFormTestRepository";
import { SaveFormDataUseCase } from "./domain/usecases/SaveFormDataUseCase";
import { GetPaginatedPatientSurveysUseCase } from "./domain/usecases/GetPaginatedPatientSurveysUseCase";
import { GetPaginatedSurveysUseCase } from "./domain/usecases/GetPaginatedSurveysUseCase";
import { GetPopulatedSurveyUseCase } from "./domain/usecases/GetPopulatedSurveyUseCase";
import { NonAdminUserTestRepository } from "./data/repositories/testRepositories/NonAdminUserTestRepository";
import { DeleteSurveyUseCase } from "./domain/usecases/DeleteSurveyUseCase";
Expand All @@ -38,6 +38,7 @@ import { GetASTGuidelinesUseCase } from "./domain/usecases/GetASTGuidelinesUseCa
import { ASTGuidelinesD2Repository } from "./data/repositories/ASTGuidelinesD2Repository";
import { ASTGuidelinesTestRepository } from "./data/repositories/testRepositories/ASTGuidelinesTestRepository";
import { RemoveRepeatableProgramStageUseCase } from "./domain/usecases/RemoveRepeatableProgramStageUseCase";
import { GetFilteredPrevalencePatientsUseCase } from "./domain/usecases/GetFilteredPrevalencePatientsUseCase";

export type CompositionRoot = ReturnType<typeof getCompositionRoot>;

Expand Down Expand Up @@ -74,11 +75,15 @@ function getCompositionRoot(repositories: Repositories) {
repositories.astGuidelinesRepository
),
getSurveys: new GetAllSurveysUseCase(repositories.surveyFormRepository),
getFilteredPatients: new GetFilteredPatientsUseCase(
getFilteredPPSPatients: new GetFilteredPPSPatientsUseCase(
repositories.paginatedSurveyRepository,
repositories.surveyFormRepository
),
getPaginatedSurveys: new GetPaginatedPatientSurveysUseCase(
getFilteredPrevalencePatients: new GetFilteredPrevalencePatientsUseCase(
repositories.paginatedSurveyRepository,
repositories.surveyFormRepository
),
getPaginatedSurveys: new GetPaginatedSurveysUseCase(
repositories.paginatedSurveyRepository,
repositories.surveyFormRepository
),
Expand Down
33 changes: 33 additions & 0 deletions src/data/repositories/PaginatedSurveyD2Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { PaginatedSurveyRepository } from "../../domain/repositories/PaginatedSu
import { PaginatedReponse } from "../../domain/entities/TablePagination";
import { getParentDataElementForProgram, isTrackerProgram } from "../utils/surveyProgramHelper";
import {
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_CRF,
AMR_SURVEYS_PREVALENCE_TEA_UNIQUE_PATIENT_ID,
PPS_PATIENT_REGISTER_ID,
PREVALENCE_CASE_REPORT_FORM_ID,
SURVEY_PATIENT_CODE_DATAELEMENT_ID,
SURVEY_PATIENT_ID_DATAELEMENT_ID,
WARD_ID_DATAELEMENT_ID,
Expand Down Expand Up @@ -188,6 +191,36 @@ export class PaginatedSurveyD2Repository implements PaginatedSurveyRepository {
});
}

getFilteredPrevalencePatientSurveysByPatientId(
keyword: string,
orgUnitId: Id,
parentId: Id
): FutureData<PaginatedReponse<Survey[]>> {
return apiToFuture(
this.api.tracker.trackedEntities.get({
fields: { attributes: true, enrollments: true, trackedEntity: true, orgUnit: true },
program: PREVALENCE_CASE_REPORT_FORM_ID,
orgUnit: orgUnitId,
pageSize: 10,
totalPages: true,
filter: ` ${AMR_SURVEYS_PREVALENCE_TEA_UNIQUE_PATIENT_ID}:like:${keyword}, ${AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_CRF}:eq:${parentId}`,
})
).flatMap(trackedEntities => {
const surveys = mapTrackedEntityToSurvey(trackedEntities, "PrevalenceCaseReportForm");

const paginatedSurveys: PaginatedReponse<Survey[]> = {
pager: {
page: trackedEntities.page,
pageSize: trackedEntities.pageSize,
total: trackedEntities.total,
},
objects: surveys,
};

return Future.success(paginatedSurveys);
});
}

getPaginatedSurveyChildCount(
parentProgram: Id,
orgUnitId: Id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import { FutureData } from "../../api-futures";
import { SurveyChildCountType } from "../../utils/surveyChildCountHelper";

export class PaginatedSurveyTestRepository implements PaginatedSurveyRepository {
getFilteredPrevalencePatientSurveysByPatientId(
keyword: string,
orgUnitId: string,
parentId: string
): FutureData<PaginatedReponse<Survey[]>> {
throw new Error("Method not implemented.");
}
getFilteredPPSPatientByPatientCodeSurveys(
keyword: string,
orgUnitId: string,
Expand Down
12 changes: 10 additions & 2 deletions src/domain/entities/Questionnaire/Questionnaire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,12 @@ export class Questionnaire {

const updatedQuestionnaire = allQsInQuestionnaire.reduce(
(questionnaireAcc, question) => {
return this.updateQuestionnaire(questionnaireAcc, question, question.stageId);
return this.updateQuestionnaire(
questionnaireAcc,
question,
question.stageId,
true
);
},
questionnaire
);
Expand Down Expand Up @@ -240,7 +245,8 @@ export class Questionnaire {
static updateQuestionnaire(
questionnaire: Questionnaire,
updatedQuestion: Question,
stageId?: string
stageId?: string,
initialLoad = false
): Questionnaire {
//For the updated question, get all rules that are applicable
const allQsInQuestionnaire = questionnaire.stages.flatMap((stage: QuestionnaireStage) => {
Expand All @@ -255,6 +261,8 @@ export class Questionnaire {
allQsInQuestionnaire
);

if (initialLoad && applicableRules.length === 0) return questionnaire;

const isEntityQuestionUpdated = questionnaire.entity?.questions.find(
question => question.id === updatedQuestion.id
);
Expand Down
2 changes: 0 additions & 2 deletions src/domain/entities/Questionnaire/QuestionnaireRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,8 @@ const handleRuleFunctions = (condition: string): boolean => {
if (match) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(subjective) for beeing more redeable:

    if (!match) return false;
    const innerString = match[1];
    return innerString?.trim() === "" ? false : true;

const innerString = match[1];
if (innerString?.trim() === "") {
console.debug('The string between "fn:hasValue(" and ")" is empty.');
return false;
} else {
console.debug(`The string between "fn:hasValue(" and ")" is: ${innerString}`);
return true;
}
} else return false;
Expand Down
5 changes: 5 additions & 0 deletions src/domain/repositories/PaginatedSurveyRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export interface PaginatedSurveyRepository {
orgUnitId: Id,
parentId: Id
): FutureData<PaginatedReponse<Survey[]>>;
getFilteredPrevalencePatientSurveysByPatientId(
keyword: string,
orgUnitId: Id,
parentId: Id
): FutureData<PaginatedReponse<Survey[]>>;
getPaginatedSurveyChildCount(
parentProgram: Id,
orgUnitId: Id,
Expand Down
2 changes: 1 addition & 1 deletion src/domain/usecases/GetAllSurveysUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class GetAllSurveysUseCase {
});
});

return Future.sequential(surveysWithName);
return Future.parallel(surveysWithName, { concurrency: 5 });
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { PaginatedSurveyRepository } from "../repositories/PaginatedSurveyReposi
import { SurveyRepository } from "../repositories/SurveyRepository";
import { getPaginatedSurveysWithParentName } from "../utils/surveyParentNameHelper";

export class GetFilteredPatientsUseCase {
export class GetFilteredPPSPatientsUseCase {
constructor(
private paginatedSurveyRepo: PaginatedSurveyRepository,
private surveyReporsitory: SurveyRepository
Expand Down
65 changes: 65 additions & 0 deletions src/domain/usecases/GetFilteredPrevalencePatientsUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Id } from "@eyeseetea/d2-api";
import { FutureData } from "../../data/api-futures";
import { Survey, SurveyBase } from "../entities/Survey";
import { PaginatedReponse } from "../entities/TablePagination";
import { PaginatedSurveyRepository } from "../repositories/PaginatedSurveyRepository";
import { SurveyRepository } from "../repositories/SurveyRepository";
import { Future } from "../entities/generic/Future";
import { getChildCount } from "../utils/getChildCountHelper";

export class GetFilteredPrevalencePatientsUseCase {
constructor(
private paginatedSurveyRepo: PaginatedSurveyRepository,
private surveyReporsitory: SurveyRepository
) {}

public execute(
keyword: string,
orgUnitId: Id,
parentId: Id
): FutureData<PaginatedReponse<Survey[]>> {
return this.paginatedSurveyRepo
.getFilteredPrevalencePatientSurveysByPatientId(keyword, orgUnitId, parentId)
.flatMap(filteredSurveys => {
const surveysWithName = filteredSurveys.objects.map(survey => {
return Future.join2(
this.surveyReporsitory.getSurveyNameAndASTGuidelineFromId(
survey.rootSurvey.id,
survey.surveyFormType
),
getChildCount({
surveyFormType: "PrevalenceCaseReportForm",
orgUnitId: survey.assignedOrgUnit.id,
parentSurveyId: survey.rootSurvey.id,
secondaryparentId: survey.id,
surveyReporsitory: this.paginatedSurveyRepo,
})
).map(([parentDetails, childCount]): Survey => {
const newRootSurvey: SurveyBase = {
surveyType: survey.rootSurvey.surveyType,
id: survey.rootSurvey.id,
name:
survey.rootSurvey.name === ""
? parentDetails.name
: survey.rootSurvey.name,
};

const updatedSurvey: Survey = {
...survey,
rootSurvey: newRootSurvey,
childCount: childCount,
};
return updatedSurvey;
});
});

return Future.parallel(surveysWithName, { concurrency: 5 }).map(updatedSurveys => {
const paginatedSurveys: PaginatedReponse<Survey[]> = {
pager: filteredSurveys.pager,
objects: updatedSurveys,
};
return paginatedSurveys;
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import _ from "../entities/generic/Collection";
import { getChildCount } from "../utils/getChildCountHelper";
import { Future } from "../entities/generic/Future";

//This use case fetched only patient surveys for both Prevalence and PPS modules
export class GetPaginatedPatientSurveysUseCase {
export class GetPaginatedSurveysUseCase {
constructor(
private paginatedSurveyRepo: PaginatedSurveyRepository,
private surveyReporsitory: SurveyRepository
Expand Down Expand Up @@ -68,7 +67,7 @@ export class GetPaginatedPatientSurveysUseCase {
});
});

return Future.sequential(surveysWithName).map(updatedSurveys => {
return Future.parallel(surveysWithName, { concurrency: 5 }).map(updatedSurveys => {
const paginatedSurveys: PaginatedReponse<Survey[]> = {
pager: surveys.pager,
objects: updatedSurveys,
Expand Down
13 changes: 13 additions & 0 deletions src/webapp/components/survey-list/SurveyList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ export const SurveyList: React.FC<SurveyListProps> = ({ surveyFormType }) => {
/>
</>
)}
{surveyFormType === "PrevalenceCaseReportForm" && (
<>
<TextField
label={i18n.t("Search Patient ID")}
helperText={i18n.t("Filter by patient id")}
value={patientIdSearchKeyword}
onChange={e =>
setPatientIdSearchKeyword(e.target.value)
}
onKeyDown={handlePatientIdSearch}
/>
</>
)}
<Button
variant="contained"
color="primary"
Expand Down
28 changes: 26 additions & 2 deletions src/webapp/components/survey-list/hook/usePatientSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ export const usePatientSearch = (
setTotal: Dispatch<SetStateAction<number | undefined>>
) => {
const { compositionRoot } = useAppContext();
const { currentHospitalForm, currentWardRegister } = useCurrentSurveys();
const {
currentHospitalForm,
currentWardRegister,
currentFacilityLevelForm,
currentPrevalenceSurveyForm,
} = useCurrentSurveys();
const snackbar = useSnackbar();

const [patientIdSearchKeyword, setPatientIdSearchKeyword] = useState("");
Expand Down Expand Up @@ -46,7 +51,7 @@ export const usePatientSearch = (
searchBy === "patientId" ? patientIdSearchKeyword : patientCodeSearchKeyword;
if (surveyFormType === "PPSPatientRegister" && searchKeyword) {
setIsLoading(true);
compositionRoot.surveys.getFilteredPatients
compositionRoot.surveys.getFilteredPPSPatients
.execute(
searchKeyword,
currentHospitalForm?.orgUnitId ?? "",
Expand All @@ -64,6 +69,25 @@ export const usePatientSearch = (
setIsLoading(false);
}
);
} else if (surveyFormType === "PrevalenceCaseReportForm" && searchKeyword) {
setIsLoading(true);
compositionRoot.surveys.getFilteredPrevalencePatients
.execute(
searchKeyword,
currentFacilityLevelForm?.orgUnitId ?? "",
currentPrevalenceSurveyForm?.id ?? ""
)
.run(
response => {
setSearchResultSurveys(response.objects);
setTotal(response.pager.total);
setIsLoading(false);
},
() => {
snackbar.error(i18n.t("Error fetching surveys"));
setIsLoading(false);
}
);
}
};

Expand Down
Loading