Skip to content

Commit

Permalink
Merge pull request #13201 from SORMAS-Foundation/change-13181_enhance…
Browse files Browse the repository at this point in the history
…_national_ID_search_functionality

#13181 - Enhance National Health ID Search Functionality Across Modules and Address Duplication Issues
  • Loading branch information
leventegal-she authored Dec 10, 2024
2 parents bac6746 + 6c9f438 commit b83a604
Show file tree
Hide file tree
Showing 25 changed files with 426 additions and 72 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 = There are other persons with similar national health Id
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 @@ -315,7 +315,7 @@ && permitted(FeatureType.ADVERSE_EVENTS_FOLLOWING_IMMUNIZATION_MANAGEMENT, UserR
}

if (permitted(FeatureType.TRAVEL_ENTRIES, UserRight.TRAVEL_ENTRY_MANAGEMENT_ACCESS)
&& FacadeProvider.getConfigFacade().isConfiguredCountry(CountryHelper.COUNTRY_CODE_LUXEMBOURG)) {
&& FacadeProvider.getConfigFacade().isConfiguredCountry(CountryHelper.COUNTRY_CODE_GERMANY)) {
ControllerProvider.getTravelEntryController().registerViews(navigator);
menu.addView(
TravelEntriesView.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
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.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 +152,9 @@

public class CaseController {

private CommitDiscardWrapperComponent<CaseCreateForm> caseCreateComponent;
private boolean caseSaveTriggered;

public CaseController() {

}
Expand Down Expand Up @@ -187,7 +191,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 @@ -718,6 +722,7 @@ public CommitDiscardWrapperComponent<CaseCreateForm> getCaseCreateComponent(
editView.addFieldGroups(createForm.getHomeAddressForm().getFieldGroup());
}

caseSaveTriggered = false;
editView.addCommitListener(() -> {
if (!createForm.getFieldGroup().isModified()) {
final CaseDataDto dto = createForm.getValue();
Expand Down Expand Up @@ -835,25 +840,55 @@ 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(I18nProperties.getCaption(Captions.actionContinue));
warningPopUpDuplicatePerson.addDoneListener(() -> {
if (!caseSaveTriggered) {
VaadinUiUtil.showModalPopupWindow(caseCreateComponent, I18nProperties.getString(Strings.headingCreateNewCase));
}
});
warningPopUpDuplicatePerson.addCommitListener(() -> {
caseSaveTriggered = true;
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);
}
}
}
}
});

return editView;

}

private void selectOrCreateCase(CaseCreateForm createForm, CaseDataDto dto, PersonReferenceDto selectedPerson) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

import com.google.common.collect.Sets;
import com.vaadin.ui.Label;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.ValoTheme;
import com.vaadin.v7.data.util.converter.Converter;
import com.vaadin.v7.ui.AbstractSelect.ItemCaptionMode;
Expand Down Expand Up @@ -72,6 +73,7 @@
import de.symeda.sormas.ui.UiUtil;
import de.symeda.sormas.ui.location.LocationEditForm;
import de.symeda.sormas.ui.person.PersonCreateForm;
import de.symeda.sormas.ui.person.PersonFormHelper;
import de.symeda.sormas.ui.utils.AbstractEditForm;
import de.symeda.sormas.ui.utils.ComboBoxHelper;
import de.symeda.sormas.ui.utils.CssStyles;
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;
}
}
Loading

0 comments on commit b83a604

Please sign in to comment.