Skip to content
This repository was archived by the owner on May 5, 2021. It is now read-only.

Commit be62cc3

Browse files
lgallgal
authored andcommitted
SORMAS-Foundation#2539 code review fixes
1 parent 56329ac commit be62cc3

5 files changed

Lines changed: 81 additions & 151 deletions

File tree

sormas-api/src/main/java/de/symeda/sormas/api/person/PersonHelper.java

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,9 @@
1717
*******************************************************************************/
1818
package de.symeda.sormas.api.person;
1919

20-
import java.text.Normalizer;
2120
import java.util.Date;
22-
import java.util.regex.Pattern;
2321

2422
import org.apache.commons.lang3.StringUtils;
25-
import org.simmetrics.metrics.StringMetrics;
2623

2724
import de.symeda.sormas.api.Language;
2825
import de.symeda.sormas.api.caze.BurialInfoDto;
@@ -36,46 +33,6 @@ private PersonHelper() {
3633
// Hide Utility Class Constructor
3734
}
3835

39-
public static final double DEFAULT_NAME_SIMILARITY_THRESHOLD = 0.65D;
40-
41-
/**
42-
* Calculates the trigram distance between both names and returns true
43-
* if the similarity is high enough to consider them a possible match.
44-
* Uses a default of {@link PersonHelper#DEFAULT_NAME_SIMILARITY_THRESHOLD} for the threshold.
45-
*/
46-
protected static boolean areFullNamesSimilar(final String firstName, final String secondName, Double similarityThreshold) {
47-
final String name = normalizeString(firstName);
48-
final String otherName = normalizeString(secondName);
49-
return StringMetrics.qGramsDistance().compare(name, otherName)
50-
>= (similarityThreshold != null ? similarityThreshold : DEFAULT_NAME_SIMILARITY_THRESHOLD);
51-
}
52-
53-
/**
54-
* Calculates the trigram distance between firstName/lastname (also viceversa lastname/firstname) and otherFirstName/otherLastName,
55-
* returns true if the similarity is high enough to consider them a possible match.
56-
*/
57-
public static boolean areNamesSimilar(
58-
final String firstName,
59-
final String lastName,
60-
final String otherFirstName,
61-
final String otherLastName,
62-
Double similarityThreshold) {
63-
final String name = createFullName(firstName, lastName);
64-
final String nameInverted = createFullName(lastName, firstName);
65-
final String otherName = createFullName(otherFirstName, otherLastName);
66-
return areFullNamesSimilar(name, otherName, similarityThreshold) || areFullNamesSimilar(nameInverted, otherName, similarityThreshold);
67-
}
68-
69-
private static String createFullName(String firstName, String lastName) {
70-
return firstName + StringUtils.SPACE + lastName;
71-
}
72-
73-
public static String normalizeString(String str) {
74-
String nfdNormalizedString = Normalizer.normalize(str, Normalizer.Form.NFD).toLowerCase();
75-
Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
76-
return pattern.matcher(nfdNormalizedString).replaceAll("");
77-
}
78-
7936
public static String formatBirthdate(Integer birthdateDD, Integer birthdateMM, Integer birthdateYYYY, Language language) {
8037

8138
if (birthdateDD == null && birthdateMM == null && birthdateYYYY == null) {

sormas-api/src/test/java/de/symeda/sormas/api/person/PersonHelperTest.java

Lines changed: 3 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -17,87 +17,13 @@
1717
*******************************************************************************/
1818
package de.symeda.sormas.api.person;
1919

20-
import de.symeda.sormas.api.Language;
21-
import org.junit.Test;
22-
2320
import static org.junit.Assert.assertEquals;
24-
import static org.junit.Assert.assertFalse;
25-
import static org.junit.Assert.assertTrue;
26-
27-
public class PersonHelperTest {
28-
29-
@Test
30-
public void testNameSimilarityExceedsThreshold() {
31-
32-
String firstName = "Thomas Miller";
33-
String secondName = "Tomas Miller";
34-
35-
assertTrue(PersonHelper.areFullNamesSimilar(firstName, secondName, null));
36-
37-
firstName = "Thomas Jake Miller";
38-
secondName = "Thomas Miller";
39-
40-
assertTrue(PersonHelper.areFullNamesSimilar(firstName, secondName, null));
41-
42-
firstName = "Thomas Jake Miller";
43-
secondName = "Thomas Jacob Miller";
44-
45-
assertTrue(PersonHelper.areFullNamesSimilar(firstName, secondName, null));
46-
47-
firstName = "Dan Brown";
48-
secondName = "Dan Browning";
49-
50-
assertTrue(PersonHelper.areFullNamesSimilar(firstName, secondName, null));
51-
52-
firstName = "Dan Van";
53-
secondName = "Gan Van";
54-
55-
assertTrue(PersonHelper.areFullNamesSimilar(firstName, secondName, null));
5621

57-
firstName = "DAN BROWN";
58-
secondName = "Dan brown";
59-
60-
assertTrue(PersonHelper.areFullNamesSimilar(firstName, secondName, null));
61-
62-
firstName = "DAN";
63-
String lastName = "BROWN";
64-
String otherFirstName = "brown";
65-
String otherLastName = "dan";
66-
67-
assertTrue(PersonHelper.areNamesSimilar(firstName, lastName, otherFirstName, otherLastName, null));
68-
69-
firstName = "DÁN";
70-
lastName = "BRÓWN";
71-
otherFirstName = "bröwn";
72-
otherLastName = "dæn";
73-
74-
assertTrue(PersonHelper.areNamesSimilar(firstName, lastName, otherFirstName, otherLastName, null));
75-
76-
firstName = "DÉÁN";
77-
lastName = "BRÓÕWN";
78-
otherFirstName = "broown";
79-
otherLastName = "dean";
80-
81-
assertTrue(PersonHelper.areNamesSimilar(firstName, lastName, otherFirstName, otherLastName, null));
82-
}
83-
84-
@Test
85-
public void testNameSimilarityDeceedsThreshold() {
86-
String firstName = "Thomas Miller";
87-
String secondName = "Tomislav Millerton";
88-
89-
assertFalse(PersonHelper.areFullNamesSimilar(firstName, secondName, null));
90-
91-
firstName = "Jonathan Lee Sterling";
92-
secondName = "John Lee Langston";
93-
94-
assertFalse(PersonHelper.areFullNamesSimilar(firstName, secondName, null));
22+
import org.junit.Test;
9523

96-
firstName = "Gan Zan";
97-
secondName = "Don Van";
24+
import de.symeda.sormas.api.Language;
9825

99-
assertFalse(PersonHelper.areFullNamesSimilar(firstName, secondName, null));
100-
}
26+
public class PersonHelperTest {
10127

10228
@Test
10329
public void testFormatBirthdateEN() throws Exception {

sormas-backend/src/main/java/de/symeda/sormas/backend/common/ConfigFacadeEjb.java

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,25 @@
1717
*******************************************************************************/
1818
package de.symeda.sormas.backend.common;
1919

20+
import java.util.Locale;
21+
import java.util.Properties;
22+
23+
import javax.annotation.Resource;
24+
import javax.ejb.LocalBean;
25+
import javax.ejb.Stateless;
26+
27+
import org.apache.commons.lang3.StringUtils;
28+
import org.apache.commons.validator.routines.UrlValidator;
29+
import org.slf4j.Logger;
30+
import org.slf4j.LoggerFactory;
31+
2032
import de.symeda.sormas.api.ConfigFacade;
2133
import de.symeda.sormas.api.Language;
22-
import de.symeda.sormas.api.person.PersonHelper;
2334
import de.symeda.sormas.api.region.GeoLatLon;
2435
import de.symeda.sormas.api.utils.CompatibilityCheckResponse;
2536
import de.symeda.sormas.api.utils.DataHelper;
2637
import de.symeda.sormas.api.utils.InfoProvider;
2738
import de.symeda.sormas.api.utils.VersionHelper;
28-
import org.apache.commons.lang3.StringUtils;
29-
import org.apache.commons.validator.routines.UrlValidator;
30-
import org.slf4j.Logger;
31-
import org.slf4j.LoggerFactory;
32-
33-
import javax.annotation.Resource;
34-
import javax.ejb.LocalBean;
35-
import javax.ejb.Stateless;
36-
import java.util.Locale;
37-
import java.util.Properties;
3839

3940
/**
4041
* Provides the application configuration settings
@@ -87,6 +88,8 @@ public class ConfigFacadeEjb implements ConfigFacade {
8788

8889
private static final String GEOCODING_OSGTS_ENDPOINT = "geocodingOsgtsEndpoint";
8990

91+
public static final double DEFAULT_NAME_SIMILARITY_THRESHOLD = 0.65D;
92+
9093
private final Logger logger = LoggerFactory.getLogger(getClass());
9194

9295
@Resource(lookup = "sormas/Properties")
@@ -276,7 +279,7 @@ public String getSmsAuthSecret() {
276279

277280
@Override
278281
public double getNameSimilarityThreshold() {
279-
return getDouble(NAME_SIMILARITY_THRESHOLD, PersonHelper.DEFAULT_NAME_SIMILARITY_THRESHOLD);
282+
return getDouble(NAME_SIMILARITY_THRESHOLD, DEFAULT_NAME_SIMILARITY_THRESHOLD);
280283
}
281284

282285
@Override

sormas-backend/src/main/java/de/symeda/sormas/backend/user/User.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public void setRegion(Region region) {
203203
this.region = region;
204204
}
205205

206-
@ElementCollection(fetch = FetchType.EAGER)
206+
@ElementCollection(fetch = FetchType.LAZY)
207207
@Enumerated(EnumType.STRING)
208208
@CollectionTable(name = TABLE_NAME_USERROLES,
209209
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = User.ID, nullable = false),

sormas-ui/src/test/java/de/symeda/sormas/ui/contact/importer/ContactImporterTest.java

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
package de.symeda.sormas.ui.contact.importer;
22

3+
import static org.junit.Assert.assertEquals;
4+
5+
import java.io.File;
6+
import java.io.IOException;
7+
import java.io.OutputStream;
8+
import java.io.OutputStreamWriter;
9+
import java.io.Writer;
10+
import java.text.Normalizer;
11+
import java.util.ArrayList;
12+
import java.util.Collections;
13+
import java.util.Date;
14+
import java.util.List;
15+
import java.util.function.Consumer;
16+
import java.util.regex.Pattern;
17+
18+
import org.apache.commons.lang3.StringUtils;
19+
import org.junit.Test;
20+
import org.simmetrics.metrics.StringMetrics;
21+
322
import de.symeda.sormas.api.Disease;
423
import de.symeda.sormas.api.FacadeProvider;
524
import de.symeda.sormas.api.caze.CaseClassification;
@@ -9,35 +28,20 @@
928
import de.symeda.sormas.api.contact.ContactDto;
1029
import de.symeda.sormas.api.importexport.InvalidColumnException;
1130
import de.symeda.sormas.api.person.PersonDto;
12-
import de.symeda.sormas.api.person.PersonHelper;
13-
import de.symeda.sormas.api.person.PersonIndexDto;
1431
import de.symeda.sormas.api.person.PersonNameDto;
1532
import de.symeda.sormas.api.person.PersonSimilarityCriteria;
1633
import de.symeda.sormas.api.person.SimilarPersonDto;
1734
import de.symeda.sormas.api.user.UserDto;
1835
import de.symeda.sormas.api.user.UserReferenceDto;
1936
import de.symeda.sormas.api.user.UserRole;
37+
import de.symeda.sormas.backend.common.ConfigFacadeEjb;
2038
import de.symeda.sormas.backend.contact.ContactFacadeEjb;
2139
import de.symeda.sormas.backend.contact.ContactFacadeEjb.ContactFacadeEjbLocal;
2240
import de.symeda.sormas.ui.AbstractBeanTest;
2341
import de.symeda.sormas.ui.TestDataCreator.RDCF;
2442
import de.symeda.sormas.ui.importer.ContactImportSimilarityResult;
2543
import de.symeda.sormas.ui.importer.ImportResultStatus;
2644
import de.symeda.sormas.ui.importer.ImportSimilarityResultOption;
27-
import org.junit.Test;
28-
29-
import java.io.File;
30-
import java.io.IOException;
31-
import java.io.OutputStream;
32-
import java.io.OutputStreamWriter;
33-
import java.io.Writer;
34-
import java.util.ArrayList;
35-
import java.util.Collections;
36-
import java.util.Date;
37-
import java.util.List;
38-
import java.util.function.Consumer;
39-
40-
import static org.junit.Assert.assertEquals;
4145

4246
public class ContactImporterTest extends AbstractBeanTest {
4347

@@ -77,8 +81,12 @@ protected void handleSimilarity(PersonDto newPerson, Consumer<ContactImportSimil
7781

7882
List<SimilarPersonDto> entries = new ArrayList<>();
7983
for (PersonNameDto person : persons) {
80-
if (PersonHelper
81-
.areNamesSimilar(newPerson.getFirstName(), newPerson.getLastName(), person.getFirstName(), person.getLastName(), null)) {
84+
if (areNamesSimilar(
85+
newPerson.getFirstName(),
86+
newPerson.getLastName(),
87+
person.getFirstName(),
88+
person.getLastName(),
89+
ConfigFacadeEjb.DEFAULT_NAME_SIMILARITY_THRESHOLD)) {
8290
entries.addAll(FacadeProvider.getPersonFacade().getSimilarPersonsByUuids(Collections.singletonList(person.getUuid())));
8391
}
8492
}
@@ -195,4 +203,40 @@ public void write(int b) throws IOException {
195203
});
196204
}
197205
}
206+
207+
/**
208+
* Calculates the trigram distance between both names and returns true
209+
* if the similarity is high enough to consider them a possible match.
210+
*/
211+
protected static boolean areFullNamesSimilar(final String firstName, final String secondName, Double similarityThreshold) {
212+
final String name = normalizeString(firstName);
213+
final String otherName = normalizeString(secondName);
214+
return StringMetrics.qGramsDistance().compare(name, otherName) >= (similarityThreshold);
215+
}
216+
217+
/**
218+
* Calculates the trigram distance between firstName/lastname (also viceversa lastname/firstname) and otherFirstName/otherLastName,
219+
* returns true if the similarity is high enough to consider them a possible match.
220+
*/
221+
public static boolean areNamesSimilar(
222+
final String firstName,
223+
final String lastName,
224+
final String otherFirstName,
225+
final String otherLastName,
226+
Double similarityThreshold) {
227+
final String name = createFullName(firstName, lastName);
228+
final String nameInverted = createFullName(lastName, firstName);
229+
final String otherName = createFullName(otherFirstName, otherLastName);
230+
return areFullNamesSimilar(name, otherName, similarityThreshold) || areFullNamesSimilar(nameInverted, otherName, similarityThreshold);
231+
}
232+
233+
private static String createFullName(String firstName, String lastName) {
234+
return firstName + StringUtils.SPACE + lastName;
235+
}
236+
237+
public static String normalizeString(String str) {
238+
String nfdNormalizedString = Normalizer.normalize(str, Normalizer.Form.NFD).toLowerCase();
239+
Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
240+
return pattern.matcher(nfdNormalizedString).replaceAll("");
241+
}
198242
}

0 commit comments

Comments
 (0)