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

Commit 13f21a2

Browse files
author
FredrikSchäferVitagroup
committed
SORMAS-Foundation#3294 Refactored the ExternalJournalService.shouldNotify(...) to consider relevant contact and case data
1 parent 0363f33 commit 13f21a2

6 files changed

Lines changed: 159 additions & 27 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package de.symeda.sormas.api.contact;
2+
3+
import java.io.Serializable;
4+
5+
public class FollowUpStatusDto implements Serializable {
6+
7+
private static final long serialVersionUID = 6075542609471404489L;
8+
9+
private String uuid;
10+
private FollowUpStatus followUpStatus;
11+
12+
public FollowUpStatusDto(String uuid, FollowUpStatus followUpStatus) {
13+
14+
this.uuid = uuid;
15+
this.followUpStatus = followUpStatus;
16+
17+
}
18+
19+
public String getUuid() {
20+
return uuid;
21+
}
22+
23+
public void setUuid(String uuid) {
24+
this.uuid = uuid;
25+
}
26+
27+
public FollowUpStatus getFollowUpStatus() {
28+
return followUpStatus;
29+
}
30+
31+
public void setFollowUpStatus(FollowUpStatus followUpStatus) {
32+
this.followUpStatus = followUpStatus;
33+
}
34+
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.symeda.sormas.api.person;
22

3+
import de.symeda.sormas.api.contact.FollowUpStatus;
34
import de.symeda.sormas.api.utils.PersonalData;
45
import de.symeda.sormas.api.utils.SensitiveData;
56

@@ -33,6 +34,8 @@ public class JournalPersonDto implements Serializable {
3334
private Sex sex;
3435
@SensitiveData
3536
private Date latestFollowUpEndDate;
37+
@SensitiveData
38+
private FollowUpStatus followUpStatus;
3639

3740
public String getUuid() {
3841
return uuid;
@@ -122,4 +125,11 @@ public void setLatestFollowUpEndDate(Date latestFollowUpEndDate) {
122125
this.latestFollowUpEndDate = latestFollowUpEndDate;
123126
}
124127

128+
public FollowUpStatus getFollowUpStatus() {
129+
return followUpStatus;
130+
}
131+
132+
public void setFollowUpStatus(FollowUpStatus followUpStatus) {
133+
this.followUpStatus = followUpStatus;
134+
}
125135
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import de.symeda.sormas.api.Disease;
2727
import de.symeda.sormas.api.caze.CaseCriteria;
28+
import de.symeda.sormas.api.contact.FollowUpStatus;
2829
import de.symeda.sormas.api.region.DistrictReferenceDto;
2930
import de.symeda.sormas.api.user.UserReferenceDto;
3031

@@ -69,6 +70,8 @@ public interface PersonFacade {
6970

7071
Date getLatestFollowUpEndDateByUuid(String uuid);
7172

73+
FollowUpStatus getMostRelevantFollowUpStatusByUuid(String uuid);
74+
7275
boolean setSymptomJournalStatus(String personUuid, SymptomJournalStatus status);
7376

7477
}

sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactFacadeEjb.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ public class ContactFacadeEjb implements ContactFacade {
211211
private FeatureConfigurationFacadeEjbLocal featureConfigurationFacade;
212212
@EJB
213213
private EventService eventService;
214+
@EJB
215+
private PersonFacadeEjb.PersonFacadeEjbLocal personFacade;
214216

215217
@Override
216218
public List<String> getAllActiveUuids() {
@@ -298,7 +300,7 @@ public ContactDto saveContact(ContactDto dto, boolean handleChanges) {
298300

299301
}
300302
if (existingContact != null) {
301-
handleExternalJournalPerson(existingContactDto, dto);
303+
handleExternalJournalPerson(dto);
302304
}
303305

304306
if (handleChanges) {
@@ -316,9 +318,14 @@ public ContactDto saveContact(ContactDto dto, boolean handleChanges) {
316318
}
317319

318320
// 5 second delay added before notifying of update so that current transaction can complete and new data can be retrieved from DB
319-
private void handleExternalJournalPerson(ContactDto existingContact, ContactDto updatedContact) {
321+
private void handleExternalJournalPerson(ContactDto updatedContact) {
320322
final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
321-
Runnable notify = () -> externalJournalService.notifyExternalJournalFollowUpUntilUpdate(updatedContact, existingContact.getFollowUpUntil());
323+
/**
324+
* The .getPersonForJournal(...) here gets the person in the state it is (most likely) known to an external journal.
325+
* Changes of related data is assumed to be not yet persisted in the database.
326+
*/
327+
Runnable notify =
328+
() -> externalJournalService.notifyExternalJournalPersonUpdate(personFacade.getPersonForJournal(updatedContact.getPerson().getUuid()));
322329
executorService.schedule(notify, 5, TimeUnit.SECONDS);
323330
}
324331

sormas-backend/src/main/java/de/symeda/sormas/backend/externaljournal/ExternalJournalService.java

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import javax.ws.rs.core.MediaType;
1919
import javax.ws.rs.core.Response;
2020

21+
import de.symeda.sormas.api.person.JournalPersonDto;
2122
import org.apache.commons.lang3.ObjectUtils;
2223
import org.apache.commons.lang3.StringUtils;
2324
import org.apache.commons.validator.routines.EmailValidator;
@@ -162,7 +163,7 @@ private String getPatientDiaryAuthTokenInternal() {
162163
* @param contact
163164
* a contact assigned to a person already available in the external journal
164165
* @param previousFollowUpUntilDate
165-
* the follow-up end date before the update
166+
* the follow-up end date before the update
166167
*/
167168
public void notifyExternalJournalFollowUpUntilUpdate(ContactDto contact, Date previousFollowUpUntilDate) {
168169
SymptomJournalStatus savedStatus = personFacade.getPersonByUuid(contact.getPerson().getUuid()).getSymptomJournalStatus();
@@ -181,18 +182,16 @@ public void notifyExternalJournalFollowUpUntilUpdate(ContactDto contact, Date pr
181182
/**
182183
* Notify external journals that a person has been updated
183184
*
184-
* @param existingPerson
185+
* @param existingJournalPerson
185186
* the person already available in the external journal
186-
* @param updatedPerson
187-
* the updated person in SORMAS
188187
*/
189-
public void notifyExternalJournalPersonUpdate(PersonDto existingPerson, PersonDto updatedPerson) {
190-
if (shouldNotify(existingPerson, updatedPerson)) {
188+
public void notifyExternalJournalPersonUpdate(JournalPersonDto existingJournalPerson) {
189+
if (shouldNotify(existingJournalPerson)) {
191190
if (configFacade.getSymptomJournalConfig().getUrl() != null) {
192-
notifySymptomJournal(existingPerson.getUuid());
191+
notifySymptomJournal(existingJournalPerson.getUuid());
193192
}
194193
if (configFacade.getPatientDiaryConfig().getUrl() != null) {
195-
notifyPatientDiary(existingPerson.getUuid());
194+
notifyPatientDiary(existingJournalPerson.getUuid());
196195
}
197196
}
198197
}
@@ -201,19 +200,12 @@ public void notifyExternalJournalPersonUpdate(PersonDto existingPerson, PersonDt
201200
* Note: This method just checks for changes in the Person data.
202201
* It can not check for Contact related data such as FollowUpUntil dates.
203202
*/
204-
private boolean shouldNotify(PersonDto existingPerson, PersonDto updatedPerson) {
205-
boolean relevantPerson = SymptomJournalStatus.ACCEPTED.equals(existingPerson.getSymptomJournalStatus())
206-
|| SymptomJournalStatus.REGISTERED.equals(existingPerson.getSymptomJournalStatus());
207-
boolean relevantFieldsUpdated = Comparator.comparing(PersonDto::getFirstName, Comparator.nullsLast(Comparator.naturalOrder()))
208-
.thenComparing(PersonDto::getLastName, Comparator.nullsLast(Comparator.naturalOrder()))
209-
.thenComparing(PersonDto::getEmailAddress, Comparator.nullsLast(Comparator.naturalOrder()))
210-
.thenComparing(PersonDto::getPhone, Comparator.nullsLast(Comparator.naturalOrder()))
211-
.thenComparing(PersonDto::getBirthdateDD, Comparator.nullsLast(Comparator.naturalOrder()))
212-
.thenComparing(PersonDto::getBirthdateMM, Comparator.nullsLast(Comparator.naturalOrder()))
213-
.thenComparing(PersonDto::getBirthdateYYYY, Comparator.nullsLast(Comparator.naturalOrder()))
214-
.thenComparing(PersonDto::getSex, Comparator.nullsLast(Comparator.naturalOrder()))
215-
.compare(existingPerson, updatedPerson)
216-
!= 0;
203+
private boolean shouldNotify(JournalPersonDto existingJournalPerson) {
204+
PersonDto detailedExistingPerson = personFacade.getPersonByUuid(existingJournalPerson.getUuid());
205+
boolean relevantPerson = SymptomJournalStatus.ACCEPTED.equals(detailedExistingPerson.getSymptomJournalStatus())
206+
|| SymptomJournalStatus.REGISTERED.equals(detailedExistingPerson.getSymptomJournalStatus());
207+
JournalPersonDto updatedJournalPerson = personFacade.getPersonForJournal(existingJournalPerson.getUuid());
208+
boolean relevantFieldsUpdated = !existingJournalPerson.equals(updatedJournalPerson);
217209
return relevantPerson && relevantFieldsUpdated;
218210
}
219211

@@ -272,6 +264,7 @@ public Optional<ExternalPatientDto> getPatientDiaryPerson(String personUuid) {
272264
/**
273265
* Attempts to register a new patient in the CLIMEDO patient diary.
274266
* Sets the person symptom journal status to REGISTERED if successful.
267+
*
275268
* @param person
276269
* the person to register as a patient in CLIMEDO
277270
* @return true if the registration was successful, false otherwise
@@ -311,7 +304,9 @@ private Invocation.Builder getExternalDataPersonInvocationBuilder(String personU
311304

312305
/**
313306
* Check whether a person has valid data in orderd to be registered in the patient diary
314-
* @param person the person to validate
307+
*
308+
* @param person
309+
* the person to validate
315310
* @return the result of the validation
316311
*/
317312
public ExternalPersonValidation validatePatientDiaryPerson(PersonDto person) {

sormas-backend/src/main/java/de/symeda/sormas/backend/person/PersonFacadeEjb.java

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*******************************************************************************/
1818
package de.symeda.sormas.backend.person;
1919

20+
import java.io.Serializable;
2021
import java.sql.Timestamp;
2122
import java.util.ArrayList;
2223
import java.util.Collections;
@@ -28,6 +29,7 @@
2829
import java.util.concurrent.ScheduledExecutorService;
2930
import java.util.concurrent.TimeUnit;
3031
import java.util.stream.Collectors;
32+
import java.util.stream.Stream;
3133

3234
import javax.ejb.EJB;
3335
import javax.ejb.LocalBean;
@@ -49,7 +51,9 @@
4951
import de.symeda.sormas.api.caze.CaseDataDto;
5052
import de.symeda.sormas.api.caze.CaseOutcome;
5153
import de.symeda.sormas.api.contact.FollowUpStatus;
54+
import de.symeda.sormas.api.contact.FollowUpStatusDto;
5255
import de.symeda.sormas.api.externaljournal.ExternalPersonValidation;
56+
import de.symeda.sormas.api.followup.FollowUpDto;
5357
import de.symeda.sormas.api.i18n.I18nProperties;
5458
import de.symeda.sormas.api.i18n.Validations;
5559
import de.symeda.sormas.api.location.LocationDto;
@@ -292,6 +296,7 @@ public JournalPersonDto getPersonForJournal(String uuid) {
292296
exportPerson.setBirthdateDD(detailedPerson.getBirthdateDD());
293297
exportPerson.setSex(detailedPerson.getSex());
294298
exportPerson.setLatestFollowUpEndDate(getLatestFollowUpEndDateByUuid(uuid));
299+
exportPerson.setFollowUpStatus(getMostRelevantFollowUpStatusByUuid(uuid));
295300
return exportPerson;
296301
} else {
297302
return null;
@@ -331,7 +336,11 @@ private void handleExternalJournalPerson(PersonDto existingPerson, PersonDto upd
331336
}
332337
// 5 second delay added before notifying of update so that current transaction can complete and new data can be retrieved from DB
333338
final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
334-
Runnable notify = () -> externalJournalService.notifyExternalJournalPersonUpdate(existingPerson, updatedPerson);
339+
/**
340+
* The .getPersonForJournal(...) here gets the person in the state it is (most likely) known to an external journal.
341+
* Changes of related data is assumed to be not yet persisted in the database.
342+
*/
343+
Runnable notify = () -> externalJournalService.notifyExternalJournalPersonUpdate(getPersonForJournal(existingPerson.getUuid()));
335344
executorService.schedule(notify, 5, TimeUnit.SECONDS);
336345
}
337346

@@ -420,8 +429,82 @@ public Date getLatestFollowUpEndDateByUuid(String uuid) {
420429
cq.orderBy(cb.desc(contactRoot.get(Contact.FOLLOW_UP_UNTIL)));
421430

422431
List<PersonFollowUpEndDto> resultlist = em.createQuery(cq).getResultList().stream().distinct().collect(Collectors.toList());
423-
return resultlist.get(0).getLatestFollowUpEndDate();
432+
if (resultlist.isEmpty()) {
433+
return null;
434+
} else {
435+
return resultlist.get(0).getLatestFollowUpEndDate();
436+
}
437+
438+
}
439+
440+
@Override
441+
public FollowUpStatus getMostRelevantFollowUpStatusByUuid(String uuid) {
442+
443+
CriteriaBuilder cb = em.getCriteriaBuilder();
444+
CriteriaQuery<FollowUpStatusDto> cq = cb.createQuery(FollowUpStatusDto.class);
424445

446+
Root<Contact> contactRoot = cq.from(Contact.class);
447+
Join<Contact, Person> personContactJoin = contactRoot.join(Contact.PERSON, JoinType.LEFT);
448+
Predicate contactFilter = contactService.createUserFilter(cb, cq, contactRoot);
449+
if (uuid != null) {
450+
contactFilter = PersonService.and(cb, contactFilter, cb.equal(personContactJoin.get(Person.UUID), uuid));
451+
}
452+
if (contactFilter != null) {
453+
cq.where(contactFilter);
454+
}
455+
cq.multiselect(personContactJoin.get(Person.UUID), contactRoot.get(Contact.FOLLOW_UP_STATUS));
456+
List<FollowUpStatusDto> contactResultList = em.createQuery(cq).getResultList().stream().distinct().collect(Collectors.toList());
457+
458+
cq = cb.createQuery(FollowUpStatusDto.class);
459+
Root<Case> caseRoot = cq.from(Case.class);
460+
Join<Case, Person> personCaseJoin = caseRoot.join(Case.PERSON, JoinType.LEFT);
461+
Predicate caseFilter = caseService.createUserFilter(cb, cq, caseRoot);
462+
if (uuid != null) {
463+
caseFilter = PersonService.and(cb, caseFilter, cb.equal(personCaseJoin.get(Person.UUID), uuid));
464+
465+
}
466+
if (caseFilter != null) {
467+
cq.where(caseFilter);
468+
}
469+
cq.multiselect(personCaseJoin.get(Person.UUID), caseRoot.get(Case.FOLLOW_UP_STATUS));
470+
List<FollowUpStatusDto> caseResultList = em.createQuery(cq).getResultList().stream().distinct().collect(Collectors.toList());
471+
472+
List<FollowUpStatusDto> resultList = Stream.concat(contactResultList.stream(), caseResultList.stream()).collect(Collectors.toList());
473+
474+
if (resultList.isEmpty()) {
475+
return null;
476+
} else {
477+
for (FollowUpStatusDto status : resultList) {
478+
if (FollowUpStatus.FOLLOW_UP.equals(status.getFollowUpStatus())) {
479+
return FollowUpStatus.FOLLOW_UP;
480+
}
481+
}
482+
if (listOnlyContainsStatus(resultList, FollowUpStatus.CANCELED)) {
483+
return FollowUpStatus.CANCELED;
484+
} else if (listOnlyContainsStatus(resultList, FollowUpStatus.COMPLETED)) {
485+
return FollowUpStatus.COMPLETED;
486+
} else if (listOnlyContainsStatus(resultList, FollowUpStatus.LOST)) {
487+
return FollowUpStatus.LOST;
488+
} else {
489+
return FollowUpStatus.NO_FOLLOW_UP;
490+
}
491+
}
492+
}
493+
494+
private boolean listOnlyContainsStatus(List<FollowUpStatusDto> list, FollowUpStatus prameterStatus) {
495+
if (list.isEmpty()) {
496+
return false;
497+
}
498+
if (prameterStatus == null) {
499+
throw new IllegalArgumentException("Object given as parameter must not be null.");
500+
}
501+
502+
for (FollowUpStatusDto status : list) {
503+
if (!prameterStatus.equals(status)) {
504+
return false;
505+
}
506+
}
507+
return true;
425508
}
426509

427510
@Override

0 commit comments

Comments
 (0)