Skip to content

Commit

Permalink
#13181 - Enhance National Health ID Search Functionality Across Modul…
Browse files Browse the repository at this point in the history
…es and Address Duplication Issues - v3
  • Loading branch information
SergiuPacurariu committed Dec 3, 2024
1 parent 14977ab commit cb8c136
Show file tree
Hide file tree
Showing 19 changed files with 299 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ public interface Strings {
String headingShowExternalMessage = "headingShowExternalMessage";
String headingSignsAndSymptoms = "headingSignsAndSymptoms";
String headingSimilarImmunization = "headingSimilarImmunization";
String headingSimilarPerson = "headingSimilarPerson";
String headingSomeCasesAlreadyInEvent = "headingSomeCasesAlreadyInEvent";
String headingSomeCasesNotRestored = "headingSomeCasesNotRestored";
String headingSomeContactsAlreadyInEvent = "headingSomeContactsAlreadyInEvent";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class PersonSimilarityCriteria extends BaseCriteria implements Cloneable
private String passportNumber;
private String nationalHealthId;
private String nameUuidExternalIdExternalTokenLike;
private boolean checkOnlyForNationalHealthId;
/**
* If true, compare the name of the person only to the first and last name fields of the database; if false, compare the
* name of the person to other fields like UUID and external ID as well.
Expand Down Expand Up @@ -106,6 +107,14 @@ public PersonSimilarityCriteria nationalHealthId(String nationalHealthId) {
return this;
}

public boolean isCheckOnlyForNationalHealthId() {
return checkOnlyForNationalHealthId;
}

public void setCheckOnlyForNationalHealthId(boolean checkOnlyForNationalHealthId) {
this.checkOnlyForNationalHealthId = checkOnlyForNationalHealthId;
}

public String getNameUuidExternalIdExternalTokenLike() {
return nameUuidExternalIdExternalTokenLike;
}
Expand Down Expand Up @@ -140,22 +149,27 @@ public void setBirthdateDD(Integer birthdateDD) {
}

public static PersonSimilarityCriteria forPerson(PersonDto person) {
return forPerson(person, false);
return forPerson(person, false, false);
}

public static PersonSimilarityCriteria forPerson(PersonDto person, boolean checkOnlyForNationalHealthId){
return forPerson(person, false, checkOnlyForNationalHealthId);
}

/**
* @param strictNameComparison
* If true, compares the name of the person only to the first and last name fields of the database; if false, compares the
* name of the person to other fields like UUID and external ID as well.
*/
public static PersonSimilarityCriteria forPerson(PersonDto person, boolean strictNameComparison) {
public static PersonSimilarityCriteria forPerson(PersonDto person, boolean strictNameComparison, boolean checkOnlyForNationalHealthId) {

PersonSimilarityCriteria personSimilarityCriteria = new PersonSimilarityCriteria().sex(person.getSex())
.birthdateDD(person.getBirthdateDD())
.birthdateMM(person.getBirthdateMM())
.birthdateYYYY(person.getBirthdateYYYY())
.passportNumber(person.getPassportNumber())
.nationalHealthId(person.getNationalHealthId());
personSimilarityCriteria.setCheckOnlyForNationalHealthId(checkOnlyForNationalHealthId);
if (strictNameComparison) {
personSimilarityCriteria.firstName(person.getFirstName()).lastName(person.getLastName()).strictNameComparison(Boolean.TRUE);
} else {
Expand Down
1 change: 1 addition & 0 deletions sormas-api/src/main/resources/strings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ headingShowExternalMessage = Message
headingSelfReportSideComponent = Self reports
headingSignsAndSymptoms = Clinical Signs and Symptoms
headingSimilarImmunization = Similar immunizaton
headingSimilarPerson = Similar persons found based on person introduced data
headingSyncUsers = Sync Users
headingTasksDeleted = Tasks deleted
headingTasksNotDeleted = None of the tasks were deleted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import de.symeda.sormas.backend.person.Person;
import org.apache.commons.lang3.StringUtils;

import de.symeda.sormas.api.ReferenceDto;
Expand Down Expand Up @@ -123,6 +124,7 @@ public Predicate buildCriteriaFilter(CriteriaBuilder cb, Root<ExternalMessage> l
CriteriaBuilderHelper.ilike(cb, labMessage.get(ExternalMessage.UUID), textFilter),
CriteriaBuilderHelper.unaccentedIlike(cb, labMessage.get(ExternalMessage.PERSON_FIRST_NAME), textFilter),
CriteriaBuilderHelper.unaccentedIlike(cb, labMessage.get(ExternalMessage.PERSON_LAST_NAME), textFilter),
CriteriaBuilderHelper.ilike(cb, labMessage.get(ExternalMessage.PERSON_NATIONAL_HEALTH_ID), textFilter),
CriteriaBuilderHelper.ilike(cb, labMessage.get(ExternalMessage.PERSON_POSTAL_CODE), textFilter),
CriteriaBuilderHelper.unaccentedIlike(cb, labMessage.get(ExternalMessage.REPORTER_NAME), textFilter),
CriteriaBuilderHelper.ilike(cb, labMessage.get(ExternalMessage.REPORTER_POSTAL_CODE), textFilter));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ protected void handlePickOrCreatePerson(PersonDto person, HandlerCallback<Entity
callback.cancel();
}
} else {
PersonSimilarityCriteria similarityCriteria = PersonSimilarityCriteria.forPerson(person, true);
PersonSimilarityCriteria similarityCriteria = PersonSimilarityCriteria.forPerson(person, true, false);
if (personFacade.checkMatchingNameInDatabase(user.toReference(), similarityCriteria)) {
logger.debug("[MESSAGE PROCESSING] Similar persons found in the database. Canceling processing.");
callback.cancel();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,16 +353,16 @@ private Predicate createAssociationPredicate(@NotNull PersonQueryContext queryCo
eventParticipantService.createDefaultFilter(cb, joins.getEventParticipant()));
break;
case IMMUNIZATION:
associationPredicate = CriteriaBuilderHelper.and(
cb,
immunizationService.createUserFilter(new ImmunizationQueryContext(cb, cq, joins.getImmunizationJoins())),
immunizationService.createDefaultFilter(cb, joins.getImmunization()));
associationPredicate = CriteriaBuilderHelper.and(
cb,
immunizationService.createUserFilter(new ImmunizationQueryContext(cb, cq, joins.getImmunizationJoins())),
immunizationService.createDefaultFilter(cb, joins.getImmunization()));
break;
case TRAVEL_ENTRY:
associationPredicate = CriteriaBuilderHelper.and(
cb,
travelEntryService.createUserFilter(new TravelEntryQueryContext(cb, cq, joins.getTravelEntryJoins())),
travelEntryService.createDefaultFilter(cb, joins.getTravelEntry()));
associationPredicate = CriteriaBuilderHelper.and(
cb,
travelEntryService.createUserFilter(new TravelEntryQueryContext(cb, cq, joins.getTravelEntryJoins())),
travelEntryService.createDefaultFilter(cb, joins.getTravelEntry()));
break;
case ALL:
default:
Expand Down Expand Up @@ -408,7 +408,8 @@ public Predicate buildCriteriaFilter(PersonCriteria personCriteria, PersonQueryC
CriteriaBuilderHelper.ilike(cb, location.get(Location.POSTAL_CODE), textFilter),
CriteriaBuilderHelper.ilike(cb, personFrom.get(Person.INTERNAL_TOKEN), textFilter),
CriteriaBuilderHelper.ilike(cb, personFrom.get(Person.EXTERNAL_ID), textFilter),
CriteriaBuilderHelper.ilike(cb, personFrom.get(Person.EXTERNAL_TOKEN), textFilter));
CriteriaBuilderHelper.ilike(cb, personFrom.get(Person.EXTERNAL_TOKEN), textFilter),
CriteriaBuilderHelper.ilike(cb, personFrom.get(Person.NATIONAL_HEALTH_ID), textFilter));
filter = CriteriaBuilderHelper.and(cb, filter, likeFilters);
}
}
Expand Down Expand Up @@ -924,7 +925,8 @@ public Predicate buildSimilarityCriteriaFilter(PersonSimilarityCriteria criteria
}
}

if (configFacade.isDuplicateChecksNationalHealthIdOverridesCriteria() && StringUtils.isNotBlank(criteria.getNationalHealthId())) {
if (StringUtils.isNotBlank(criteria.getNationalHealthId())
&& (configFacade.isDuplicateChecksNationalHealthIdOverridesCriteria() || criteria.isCheckOnlyForNationalHealthId())) {
filter = or(cb, filter, cb.equal(personFrom.get(Person.NATIONAL_HEALTH_ID), criteria.getNationalHealthId()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,7 @@ public Predicate buildCriteriaFilter(SampleCriteria criteria, SampleQueryContext
CriteriaBuilderHelper.ilike(cb, joins.getCaze().get(Case.UUID), textFilter),
CriteriaBuilderHelper.unaccentedIlike(cb, joins.getCasePerson().get(Person.FIRST_NAME), textFilter),
CriteriaBuilderHelper.unaccentedIlike(cb, joins.getCasePerson().get(Person.LAST_NAME), textFilter),
CriteriaBuilderHelper.ilike(cb, joins.getCasePerson().get(Person.NATIONAL_HEALTH_ID), textFilter),
CriteriaBuilderHelper.ilike(cb, joins.getCaze().get(Case.EPID_NUMBER), textFilter),
//contact
CriteriaBuilderHelper.ilike(cb, joins.getContact().get(Contact.UUID), textFilter),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ public void testGetSimilarPersonDtos() {
tmpPerson.setSex(Sex.UNKNOWN);
criteria = PersonSimilarityCriteria.forPerson(tmpPerson);
assertEquals(1, getPersonFacade().getSimilarPersonDtos(criteria).size());
criteria = PersonSimilarityCriteria.forPerson(tmpPerson, true);
criteria = PersonSimilarityCriteria.forPerson(tmpPerson, true, false);
assertEquals(0, getPersonFacade().getSimilarPersonDtos(criteria).size());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import com.vaadin.navigator.Navigator;
import com.vaadin.server.ExternalResource;
Expand Down Expand Up @@ -130,6 +131,8 @@
import de.symeda.sormas.ui.externalsurveillanceservice.ExternalSurveillanceServiceGateway;
import de.symeda.sormas.ui.hospitalization.HospitalizationForm;
import de.symeda.sormas.ui.hospitalization.HospitalizationView;
import de.symeda.sormas.ui.person.PersonFormHelper;
import de.symeda.sormas.ui.person.PersonSelectionGrid;
import de.symeda.sormas.ui.symptoms.SymptomsForm;
import de.symeda.sormas.ui.therapy.TherapyView;
import de.symeda.sormas.ui.utils.AbstractView;
Expand All @@ -151,6 +154,8 @@

public class CaseController {

CommitDiscardWrapperComponent<CaseCreateForm> caseCreateComponent;

public CaseController() {

}
Expand Down Expand Up @@ -187,7 +192,7 @@ public void registerViews(Navigator navigator) {
}

public void create() {
CommitDiscardWrapperComponent<CaseCreateForm> caseCreateComponent = getCaseCreateComponent(null, null, null, null, null, false);
caseCreateComponent = getCaseCreateComponent(null, null, null, null, null, false);
VaadinUiUtil.showModalPopupWindow(caseCreateComponent, I18nProperties.getString(Strings.headingCreateNewCase));
}

Expand Down Expand Up @@ -740,6 +745,12 @@ public CommitDiscardWrapperComponent<CaseCreateForm> getCaseCreateComponent(
dto.getSymptoms().setOnsetDate(createForm.getOnsetDate());
dto.setWasInQuarantineBeforeIsolation(YesNoUnknown.YES);

// if (StringUtils.isNotEmpty(person.getNationalHealthId())) {
// PersonFormHelper.warningSimilarPersons(person.getNationalHealthId(), null, () -> {
// transferDataToPerson(createForm, person);
// FacadeProvider.getPersonFacade().save(person);
// });
// }
transferDataToPerson(createForm, person);
FacadeProvider.getPersonFacade().save(person);

Expand Down Expand Up @@ -835,18 +846,60 @@ public CommitDiscardWrapperComponent<CaseCreateForm> getCaseCreateComponent(
} else {
// look for potential duplicate
final PersonDto duplicatePerson = PersonDto.build();
transferDataToPerson(createForm, duplicatePerson);
ControllerProvider.getPersonController()
.selectOrCreatePerson(
duplicatePerson,
I18nProperties.getString(Strings.infoSelectOrCreatePersonForCase),
selectedPerson -> {
if (selectedPerson != null) {
dto.setPerson(selectedPerson);
selectOrCreateCase(createForm, dto, selectedPerson);
}
},
true);

if (createForm.getWarningSimilarPersons() != null) {
CommitDiscardWrapperComponent<PersonSelectionGrid> warningPopUpDuplicatePerson =
(CommitDiscardWrapperComponent<PersonSelectionGrid>) editView.getWrappedComponent()
.getWarningSimilarPersons()
.getContent();

warningPopUpDuplicatePerson.getDiscardButton().setVisible(true);
warningPopUpDuplicatePerson.getCommitButton().setCaption("Continue to save");
warningPopUpDuplicatePerson.addDoneListener(()-> {
VaadinUiUtil.showModalPopupWindow(caseCreateComponent, I18nProperties.getString(Strings.headingCreateNewCase));
});
warningPopUpDuplicatePerson.addCommitListener(() -> {

transferDataToPerson(createForm, duplicatePerson);
ControllerProvider.getPersonController()
.selectOrCreatePerson(
duplicatePerson,
I18nProperties.getString(Strings.infoSelectOrCreatePersonForCase),
selectedPerson -> {
if (selectedPerson != null) {
dto.setPerson(selectedPerson);
selectOrCreateCase(createForm, dto, selectedPerson);
}
},
true);
});
} else {
transferDataToPerson(createForm, duplicatePerson);
ControllerProvider.getPersonController()
.selectOrCreatePerson(
duplicatePerson,
I18nProperties.getString(Strings.infoSelectOrCreatePersonForCase),
selectedPerson -> {
if (selectedPerson != null) {
dto.setPerson(selectedPerson);
selectOrCreateCase(createForm, dto, selectedPerson);
}
},
true);

}
// transferDataToPerson(createForm, duplicatePerson);
// ControllerProvider.getPersonController()
// .selectOrCreatePerson(
// duplicatePerson,
// I18nProperties.getString(Strings.infoSelectOrCreatePersonForCase),
// selectedPerson -> {
// if (selectedPerson != null) {
// dto.setPerson(selectedPerson);
// selectOrCreateCase(createForm, dto, selectedPerson);
// }
// },
// true);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import java.util.Date;
import java.util.List;

import com.vaadin.ui.Window;
import de.symeda.sormas.ui.person.PersonFormHelper;
import org.apache.commons.collections4.CollectionUtils;

import com.google.common.collect.Sets;
Expand Down Expand Up @@ -111,6 +113,7 @@ public class CaseCreateForm extends AbstractEditForm<CaseDataDto> {
private NullableOptionGroup ogCaseOrigin;

private PersonCreateForm personCreateForm;
private Window warningSimilarPersons;

private final boolean showHomeAddressForm;
private final boolean showPersonSearchButton;
Expand Down Expand Up @@ -209,6 +212,11 @@ protected void addFields() {
personCreateForm.setWidth(100, Unit.PERCENTAGE);
getContent().addComponent(personCreateForm, CaseDataDto.PERSON);

personCreateForm.getNationalHealthIdField().addTextFieldValueChangeListener(e -> {
warningSimilarPersons = PersonFormHelper
.warningSimilarPersons(personCreateForm.getNationalHealthIdField().getValue(), null, () -> warningSimilarPersons = null);
});

// Jurisdiction fields
Label jurisdictionHeadingLabel = new Label(I18nProperties.getString(Strings.headingCaseResponsibleJurisidction));
jurisdictionHeadingLabel.addStyleName(H3);
Expand Down Expand Up @@ -730,4 +738,8 @@ public void setValue(CaseDataDto caseDataDto) throws com.vaadin.v7.data.Property
public void setSearchedPerson(PersonDto searchedPerson) {
personCreateForm.setSearchedPerson(searchedPerson);
}

public Window getWarningSimilarPersons() {
return warningSimilarPersons;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,14 @@ public CommitDiscardWrapperComponent<PersonEditForm> getPersonEditComponent(
editView.addCommitListener(() -> {
if (!editForm.getFieldGroup().isModified()) {
PersonDto dto = editForm.getValue();
savePerson(dto);
if (editForm.getWarningSimilarPersons() != null) {
CommitDiscardWrapperComponent<PersonSelectionGrid> content =
(CommitDiscardWrapperComponent<PersonSelectionGrid>) editView.getWrappedComponent().getWarningSimilarPersons().getContent();
content.getDiscardButton().setVisible(true);
content.addCommitListener(() -> savePerson(dto));
} else {
savePerson(dto);
}
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import java.util.List;
import java.util.stream.Collectors;

import com.vaadin.ui.Window;
import de.symeda.sormas.ui.utils.components.TextFieldCustom;
import org.apache.commons.lang3.StringUtils;

import com.vaadin.icons.VaadinIcons;
Expand Down Expand Up @@ -86,6 +88,8 @@ public class PersonCreateForm extends AbstractEditForm<PersonDto> {
private final boolean showPresentCondition;
private final boolean showSymptomsOnsetDate;
private final boolean showPersonSearchButton;
private TextFieldCustom nationalHealthIdField;
private Window warningSimilarPersons;

private static final String HTML_LAYOUT =
"%s" + fluidRow(fluidRowLocs(PersonDto.BIRTH_DATE_YYYY, PersonDto.BIRTH_DATE_MM, PersonDto.BIRTH_DATE_DD), fluidRowLocs(PersonDto.SEX))
Expand Down Expand Up @@ -186,7 +190,12 @@ protected void addFields() {
ComboBox sex = addField(PersonDto.SEX, ComboBox.class);

addField(PersonDto.PASSPORT_NUMBER, TextField.class);
addField(PersonDto.NATIONAL_HEALTH_ID, TextField.class);

nationalHealthIdField = addField(PersonDto.NATIONAL_HEALTH_ID, TextFieldCustom.class);
// nationalHealthIdField.addTextFieldValueChangeListener(e -> {
// warningSimilarPersons =
// PersonFormHelper.warningSimilarPersons(nationalHealthIdField.getValue(), null, () -> getContent());
// });

ComboBox presentCondition = addField(PersonDto.PRESENT_CONDITION, ComboBox.class);
presentCondition.setVisible(showPresentCondition);
Expand Down Expand Up @@ -498,4 +507,12 @@ public PersonDto getSearchedPerson() {
public void setSearchedPerson(PersonDto searchedPerson) {
this.person = searchedPerson;
}

public Window getWarningSimilarPersons() {
return warningSimilarPersons;
}

public TextFieldCustom getNationalHealthIdField() {
return nationalHealthIdField;
}
}
Loading

0 comments on commit cb8c136

Please sign in to comment.