Skip to content
New issue

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

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

Already on GitHub? # to your account

fix: changes to form save validations for duplicate facilities #93

Open
wants to merge 7 commits into
base: development
Choose a base branch
from
16 changes: 14 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,27 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2025-02-13T17:58:53.810Z\n"
"PO-Revision-Date: 2025-02-13T17:58:53.810Z\n"
"POT-Creation-Date: 2025-02-21T18:07:25.668Z\n"
"PO-Revision-Date: 2025-02-21T18:07:25.668Z\n"

msgid "There was a problem with \"{{name}}\" - {{prop}} is not set"
msgstr ""

msgid "There was an error processing the form"
msgstr ""

msgid "Survey ID expected but could not be resolved"
msgstr ""

msgid "Prevalence Facility already exists for this Survey."
msgstr ""

msgid "Hospital already exists for this Survey"
msgstr ""

msgid "Country already exist for this Survey"
msgstr ""

msgid "Facilities"
msgstr ""

Expand Down
29 changes: 23 additions & 6 deletions i18n/es.po
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
msgid ""
msgstr ""
"Project-Id-Version: i18next-conv\n"
"POT-Creation-Date: 2025-01-28T16:57:55.539Z\n"
"POT-Creation-Date: 2025-02-21T18:07:25.668Z\n"
"PO-Revision-Date: 2018-10-25T09:02:35.143Z\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"


msgid "There was a problem with \"{{name}}\" - {{prop}} is not set"
msgstr ""

msgid "There was an error processing the form"
msgstr ""

msgid "There was an error processing the form"
msgid "Survey ID expected but could not be resolved"
msgstr ""

msgid "Prevalence Facility already exists for this Survey."
msgstr ""

msgid "Hospital already exists for this Survey"
msgstr ""

msgid "Country already exist for this Survey"
msgstr ""

msgid "Facilities"
Expand Down Expand Up @@ -83,6 +91,12 @@ msgstr ""
msgid "New Survey"
msgstr ""

msgid "Prevalence Surveys"
msgstr ""

msgid "Case reports"
msgstr ""

msgid "Treatment/Indication Saved!"
msgstr ""

Expand All @@ -106,9 +120,6 @@ msgstr ""
msgid "Filter by patientcode"
msgstr ""

msgid "Country"
msgstr ""

msgid "Filter by Status"
msgstr ""

Expand Down Expand Up @@ -136,6 +147,12 @@ msgstr ""
msgid "HOSP"
msgstr ""

msgid "Country"
msgstr ""

msgid "Case Reports"
msgstr ""

msgid ""
"This survey has other surveys associated with it.\n"
" Please delete all associated surveys, before you can delete this one."
Expand Down
21 changes: 3 additions & 18 deletions src/data/utils/questionHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,15 @@ import {
AMR_SURVEYS_MORTALITY_TEA_PAT_ID_COH2,
AMR_SURVEYS_MORTALITY_TEA_PAT_ID_DF2,
AMR_SURVEYS_MORTALITY_TEA_PAT_ID_FUP2,
AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_COH,
AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_DF,
AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_FUP,
AMR_SURVEYS_PREVALENCE_DEA_SURVEY_ID,
AMR_SURVEYS_PREVALENCE_TEA_AMRPATIENT_IDPREVALENCE,
AMR_SURVEYS_PREVALENCE_TEA_PATIENT_ID,
AMR_SURVEYS_PREVALENCE_TEA_PATIENT_IDA19,
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_CRF,
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_CRL,
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_PIS,
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_SRL,
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_SSTF,
SURVEY_ID_DATAELEMENT_ID,
SURVEY_ID_FACILITY_LEVEL_DATAELEMENT_ID,
SURVEY_ID_PATIENT_TEA_ID,
WARD2_ID_DATAELEMENT_ID,
WARD_ID_TEA_ID,
parentPrevalenceSurveyIdList,
} from "../entities/D2Survey";
import _ from "../../domain/entities/generic/Collection";
import { D2TrackerEvent } from "@eyeseetea/d2-api/api/trackerEvents";
Expand Down Expand Up @@ -391,15 +383,8 @@ export const mapTrackedAttributesToQuestions = (
);
if (
currentQuestion &&
(currentQuestion.id === SURVEY_ID_FACILITY_LEVEL_DATAELEMENT_ID ||
currentQuestion?.id === AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_SSTF ||
currentQuestion.id === AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_CRL ||
currentQuestion.id === AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_PIS ||
currentQuestion.id === AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_SRL ||
currentQuestion.id === AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_CRF ||
currentQuestion.id === AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_FUP ||
currentQuestion.id === AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_DF ||
currentQuestion.id === AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_COH ||
(parentPrevalenceSurveyIdList.includes(currentQuestion.id) ||
// TODO: check if patientIdList can be used here (not all IDs overlap)
Copy link
Contributor

Choose a reason for hiding this comment

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

@MatiasArriola maybe we can check with Miquel if all the ids in patientIdList should be disabled. Maybe we have missed out a few and they should ideally overlap?

Copy link
Contributor

Choose a reason for hiding this comment

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

@9sneha-n what do you mean by disabled? That the field in a detail is not editable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I think It could be a good idea to handle this refactor in a separate task/PR.
It is related to the list of metadata IDs that disable a question (e.g. survey id, patient id). Recently parentPrevalenceSurveyIdList and patientIdList were introduced as a code improvement to group these Ids. Although unrelated to this PR, I noticed an opportunity to reuse these lists, but it wasn't straightforward for patientIdList. We would need to review the IDs first.
As Sneha mentioned, reviewing this list might reveal some inconsistent behavior.

Copy link
Contributor

Choose a reason for hiding this comment

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

Noted. We can add this backlog.

currentQuestion.id === AMR_SURVEYS_PREVALENCE_TEA_PATIENT_ID ||
currentQuestion.id === AMR_SURVEYS_PREVALENCE_TEA_AMRPATIENT_IDPREVALENCE ||
currentQuestion.id === AMR_SURVEYS_PREVALENCE_TEA_PATIENT_IDA19 ||
Expand Down
8 changes: 8 additions & 0 deletions src/domain/entities/Questionnaire/Questionnaire.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getParentDataElementForProgram } from "../../../data/utils/surveyProgramHelper";
import { generateUid } from "../../../utils/uid";
import { SurveyRule } from "../AMRSurveyModule";
import { Id, Ref } from "../Ref";
Expand Down Expand Up @@ -90,6 +91,13 @@ export class Questionnaire {
return [...stageQuestions, ...entityQuestions];
}

getParentSurveyId(): Id | undefined {
const dataElementId = getParentDataElementForProgram(this.id);
return this.getAllQuestions()
.find(question => question.id === dataElementId)
?.value?.toString();
}

public static create(data: QuestionnaireData): Questionnaire {
//TO DO : Add validations if any
return new Questionnaire({
Expand Down
25 changes: 5 additions & 20 deletions src/domain/usecases/GetSurveyUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,13 @@ import {
AMR_SURVEYS_MORTALITY_TEA_PAT_ID_COH2,
AMR_SURVEYS_MORTALITY_TEA_PAT_ID_DF2,
AMR_SURVEYS_MORTALITY_TEA_PAT_ID_FUP2,
AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_COH,
AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_DF,
AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_FUP,
AMR_SURVEYS_PREVALENCE_TEA_AMRPATIENT_IDPREVALENCE,
AMR_SURVEYS_PREVALENCE_TEA_PATIENT_ID,
AMR_SURVEYS_PREVALENCE_TEA_PATIENT_IDA19,
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_CRF,
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_CRL,
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_PIS,
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_SRL,
AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_SSTF,
SURVEY_ID_DATAELEMENT_ID,
SURVEY_ID_FACILITY_LEVEL_DATAELEMENT_ID,
SURVEY_ID_PATIENT_TEA_ID,
WARD_ID_TEA_ID,
parentPrevalenceSurveyIdList,
} from "../../data/entities/D2Survey";
import { isTrackerProgram } from "../../data/utils/surveyProgramHelper";
import { Future } from "../entities/generic/Future";
Expand Down Expand Up @@ -168,17 +160,10 @@ export class GetSurveyUseCase {
}
const updatedEntityQuestions: Question[] = questionnaire.entity.questions.map(
question => {
const isSurveyIdQuestion =
question.id === SURVEY_ID_FACILITY_LEVEL_DATAELEMENT_ID ||
question.id === AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_SSTF ||
question.id === AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_CRL ||
question.id === AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_PIS ||
question.id === AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_SRL ||
question.id === AMR_SURVEYS_PREVALENCE_TEA_SURVEY_ID_CRF ||
question.id === AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_FUP ||
question.id === AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_DF ||
question.id === AMR_SURVEYS_MORTALITY_TEA_SURVEY_ID_COH;

const isSurveyIdQuestion = parentPrevalenceSurveyIdList.includes(
question.id
);
// TODO: check if patientIdList can be used (it includes more IDs than these)
const isPatientIdQuestion =
question.id === AMR_SURVEYS_PREVALENCE_TEA_PATIENT_ID ||
question.id === AMR_SURVEYS_PREVALENCE_TEA_AMRPATIENT_IDPREVALENCE ||
Expand Down
67 changes: 45 additions & 22 deletions src/domain/usecases/SaveFormDataUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import {
import { ASTGUIDELINE_TYPES } from "../entities/ASTGuidelines";
import { SelectQuestion } from "../entities/Questionnaire/QuestionnaireQuestion";
import { ASTGuidelinesRepository } from "../repositories/ASTGuidelinesRepository";
import i18n from "../../utils/i18n";

export const GLOBAL_OU_ID = "H8RixfF8ugH";
export class SaveFormDataUseCase {
constructor(
private surveyReporsitory: SurveyRepository,
private surveyRepository: SurveyRepository,
private astGuidelineRepository: ASTGuidelinesRepository
) {}

Expand All @@ -33,35 +34,57 @@ export class SaveFormDataUseCase {
const ouId =
surveyFormType === "PPSSurveyForm" && orgUnitId === "" ? GLOBAL_OU_ID : orgUnitId;

//Do not allow creation of multiple Prevalence Facility Level Forms for the same facility.
if (!eventId && surveyFormType === "PrevalenceFacilityLevelForm") {
return this.surveyReporsitory
return this.validate(surveyFormType, questionnaire, ouId, programId, eventId).flatMap(() =>
this.saveFormData(surveyFormType, questionnaire, ouId, programId, eventId)
);
}

validate = (
surveyFormType: SURVEY_FORM_TYPES,
questionnaire: Questionnaire,
orgUnitId: Id,
programId: Id,
eventId: string | undefined = undefined
): FutureData<boolean> => {
const isNew = !eventId;
if (
isNew &&
(surveyFormType === "PrevalenceFacilityLevelForm" ||
surveyFormType === "PPSHospitalForm" ||
surveyFormType === "PPSCountryQuestionnaire")
) {
// avoid duplicate orgUnit in the same parent survey (Facility Level and Hospital)
const surveyId = questionnaire.getParentSurveyId();
if (!surveyId) {
return Future.error(
new Error(i18n.t("Survey ID expected but could not be resolved"))
);
}
return this.surveyRepository
.getSurveys({
surveyFormType: surveyFormType,
programId: programId,
orgUnitId: ouId,
orgUnitId: orgUnitId,
chunked: false,
parentId: surveyId,
})
.flatMap(surveys => {
if (surveys.length > 0) {
return Future.error(
new Error(
"Prevalence Facility Level Form already exists for this facility."
)
);
} else
return this.saveFormData(
surveyFormType,
questionnaire,
ouId,
programId,
eventId
);
const errorMessages = {
PrevalenceFacilityLevelForm: i18n.t(
"Prevalence Facility already exists for this Survey."
),
PPSHospitalForm: i18n.t("Hospital already exists for this Survey"),
PPSCountryQuestionnaire: i18n.t(
"Country already exist for this Survey"
),
};
return Future.error(new Error(errorMessages[surveyFormType]));
} else return Future.success(true);
});
}

return this.saveFormData(surveyFormType, questionnaire, ouId, programId, eventId);
}
return Future.success(true);
};

saveFormData = (
surveyFormType: SURVEY_FORM_TYPES,
Expand All @@ -70,7 +93,7 @@ export class SaveFormDataUseCase {
programId: string,
eventId: string | undefined = undefined
): FutureData<Id> => {
return this.surveyReporsitory
return this.surveyRepository
.saveFormData(questionnaire, "CREATE_AND_UPDATE", ouId, eventId, programId)
.flatMap(surveyId => {
return this.saveCustomASTGuidelineToDatastore(
Expand Down
12 changes: 5 additions & 7 deletions src/webapp/components/survey/SurveyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,15 @@ export const SurveyForm: React.FC<SurveyFormProps> = props => {
);

useEffect(() => {
if (saveCompleteState && saveCompleteState.status === "success") {
if (!saveCompleteState) return;
if (saveCompleteState.status === "success") {
snackbar.info(saveCompleteState.message);
if (props.hideForm) props.hideForm();
}

if (saveCompleteState && saveCompleteState.status === "error") {
} else if (saveCompleteState.status === "error") {
offlineError(saveCompleteState.message);
setLoading(false);
}

if (saveCompleteState && saveCompleteState.status === "intermediate-success") {
resetSaveActionOutcome();
} else if (saveCompleteState.status === "intermediate-success") {
snackbar.info(saveCompleteState.message);
setLoading(false);
resetSaveActionOutcome();
Expand Down
Loading