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

Commit 2a91a1d

Browse files
author
barnabartha
committed
SORMAS-Foundation#3253 - send sms for a single case + show information about already sent messages
1 parent 22889fe commit 2a91a1d

13 files changed

Lines changed: 308 additions & 15 deletions

File tree

sormas-api/src/main/java/de/symeda/sormas/api/caze/CaseFacade.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import de.symeda.sormas.api.CaseMeasure;
2828
import de.symeda.sormas.api.Disease;
2929
import de.symeda.sormas.api.Language;
30+
import de.symeda.sormas.api.messaging.ManualMessageLogDto;
3031
import de.symeda.sormas.api.messaging.MessageType;
3132
import de.symeda.sormas.api.contact.ContactReferenceDto;
3233
import de.symeda.sormas.api.contact.DashboardQuarantineDataDto;
@@ -178,4 +179,6 @@ List<DashboardQuarantineDataDto> getQuarantineDataForDashBoard(
178179
void sendMessage(List<String> caseUuids, String subject, String messageContent, MessageType... messageTypes);
179180

180181
long countCasesWithMissingMessageType(List<String> caseUuids, MessageType messageType);
182+
183+
List<ManualMessageLogDto> getMessageLog(String caseUuid, MessageType messageType);
181184
}

sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,8 @@ public interface Captions {
10721072
String moreActions = "moreActions";
10731073
String name = "name";
10741074
String nationalHealthId = "nationalHealthId";
1075+
String noPhoneNumberForCasePerson = "noPhoneNumberForCasePerson";
1076+
String noSmsSentForCase = "noSmsSentForCase";
10751077
String notAvailableShort = "notAvailableShort";
10761078
String notSpecified = "notSpecified";
10771079
String numberOfCharacters = "numberOfCharacters";
@@ -1369,6 +1371,8 @@ public interface Captions {
13691371
String sampleSamplesList = "sampleSamplesList";
13701372
String sampleShipped = "sampleShipped";
13711373
String sampleSpecimenNotAdequate = "sampleSpecimenNotAdequate";
1374+
String sendSMS = "sendSMS";
1375+
String sentBy = "sentBy";
13721376
String sex = "sex";
13731377
String showPlacesOnMap = "showPlacesOnMap";
13741378
String singleDayEventDate = "singleDayEventDate";

sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public interface Strings {
1919
String between = "between";
2020
String bpm = "bpm";
2121
String by = "by";
22+
String cancel = "cancel";
2223
String classificationAllOf = "classificationAllOf";
2324
String classificationClassificationRules = "classificationClassificationRules";
2425
String classificationConfirmed = "classificationConfirmed";
@@ -127,6 +128,7 @@ public interface Strings {
127128
String DiseaseNetworkDiagram_subheading = "DiseaseNetworkDiagram.subheading";
128129
String done = "done";
129130
String edit = "edit";
131+
String email = "email";
130132
String entityAction = "entityAction";
131133
String entityActions = "entityActions";
132134
String entityAdditionalTest = "entityAdditionalTest";
@@ -813,6 +815,9 @@ public interface Strings {
813815
String quarterShort = "quarterShort";
814816
String reportedBy = "reportedBy";
815817
String reportedOn = "reportedOn";
818+
String send = "send";
819+
String sendingSms = "sendingSms";
820+
String sms = "sms";
816821
String step = "step";
817822
String SurvnetGateway_notificationEntryNotSent = "SurvnetGateway.notificationEntryNotSent";
818823
String SurvnetGateway_notificationEntrySent = "SurvnetGateway.notificationEntrySent";

sormas-api/src/main/resources/captions.properties

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1967,4 +1967,10 @@ SurvnetGateway.send=send to own SurvNet instance
19671967
patientDiaryRegistrationError=Could not register person in the patient diary.
19681968
patientDiaryPersonNotExportable=Cannot export the person to the patient diary. The person needs a valid birthdate and either a valid phone number or email address.
19691969

1970-
showPlacesOnMap=Show
1970+
showPlacesOnMap=Show
1971+
1972+
# Messaging
1973+
sendSMS=Send SMS
1974+
sentBy=Sent by
1975+
noSmsSentForCase=No SMS sent to case person
1976+
noPhoneNumberForCasePerson=Case person has no phone number

sormas-api/src/main/resources/strings.properties

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,3 +881,10 @@ SurvnetGateway.notificationEntryNotSent = Entry could not be sent
881881
SurvnetGateway.notificationErrorSending = Error when sending entry
882882

883883
warningDashboardMapTooManyMarkers = There are %d places to display and it is possible that your browser will freeze while displaying them.
884+
885+
# Messaging
886+
sms = SMS
887+
email = Email
888+
send = Send
889+
cancel = Cancel
890+
sendingSms = Send new SMS

sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseFacadeEjb.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
import de.symeda.sormas.api.Disease;
7070
import de.symeda.sormas.api.DiseaseHelper;
7171
import de.symeda.sormas.api.Language;
72-
import de.symeda.sormas.api.messaging.MessageType;
7372
import de.symeda.sormas.api.VisitOrigin;
7473
import de.symeda.sormas.api.caze.AgeAndBirthDateDto;
7574
import de.symeda.sormas.api.caze.BirthDateDto;
@@ -122,6 +121,8 @@
122121
import de.symeda.sormas.api.importexport.ExportConfigurationDto;
123122
import de.symeda.sormas.api.infrastructure.InfrastructureHelper;
124123
import de.symeda.sormas.api.location.LocationDto;
124+
import de.symeda.sormas.api.messaging.ManualMessageLogDto;
125+
import de.symeda.sormas.api.messaging.MessageType;
125126
import de.symeda.sormas.api.person.ApproximateAgeType;
126127
import de.symeda.sormas.api.person.CauseOfDeath;
127128
import de.symeda.sormas.api.person.PersonDto;
@@ -175,6 +176,7 @@
175176
import de.symeda.sormas.backend.common.AbstractAdoService;
176177
import de.symeda.sormas.backend.common.AbstractDomainObject;
177178
import de.symeda.sormas.backend.common.ConfigFacadeEjb.ConfigFacadeEjbLocal;
179+
import de.symeda.sormas.backend.common.messaging.ManualMessageLogService;
178180
import de.symeda.sormas.backend.common.messaging.MessageSubject;
179181
import de.symeda.sormas.backend.common.messaging.MessagingService;
180182
import de.symeda.sormas.backend.common.messaging.NotificationDeliveryFailedException;
@@ -364,6 +366,8 @@ public class CaseFacadeEjb implements CaseFacade {
364366
private CaseJurisdictionChecker caseJurisdictionChecker;
365367
@EJB
366368
private SormasToSormasFacadeEjbLocal sormasToSormasFacade;
369+
@EJB
370+
private ManualMessageLogService manualMessageLogService;
367371

368372
@Override
369373
public List<CaseDataDto> getAllActiveCasesAfter(Date date) {
@@ -3120,10 +3124,23 @@ public long countCasesWithMissingMessageType(List<String> caseUuids, MessageType
31203124
return caseService.count((cb, root) -> {
31213125
final Join<Object, Object> personJoin = root.join(Case.PERSON, JoinType.LEFT);
31223126
final String messageTypeColumn = messageType == MessageType.EMAIL ? Person.EMAIL_ADDRESS : Person.PHONE;
3123-
return cb.and(root.get(Case.ID).in(caseUuids), cb.isNull(personJoin.get(messageTypeColumn)));
3127+
return cb.and(root.get(Case.UUID).in(caseUuids), cb.isNull(personJoin.get(messageTypeColumn)));
31243128
});
31253129
}
31263130

3131+
@Override
3132+
public List<ManualMessageLogDto> getMessageLog(String caseUuid, MessageType messageType) {
3133+
return manualMessageLogService.getByCaseUuid(caseUuid, messageType)
3134+
.stream()
3135+
.map(
3136+
mml -> new ManualMessageLogDto(
3137+
mml.getMessageType(),
3138+
mml.getSentDate(),
3139+
mml.getSendingUser().toReference(),
3140+
mml.getRecipientPerson().toReference()))
3141+
.collect(Collectors.toList());
3142+
}
3143+
31273144
@LocalBean
31283145
@Stateless
31293146
public static class CaseFacadeEjbLocal extends CaseFacadeEjb {

sormas-backend/src/main/java/de/symeda/sormas/backend/common/messaging/ManualMessageLogService.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
import javax.persistence.criteria.From;
1010
import javax.persistence.criteria.Predicate;
1111
import javax.persistence.criteria.Root;
12+
import javax.persistence.criteria.Subquery;
1213
import javax.validation.constraints.NotNull;
1314

14-
import de.symeda.sormas.api.messaging.ManualMessageLogDto;
15+
import de.symeda.sormas.api.messaging.MessageType;
16+
import de.symeda.sormas.backend.caze.Case;
1517
import de.symeda.sormas.backend.common.AbstractAdoService;
1618
import de.symeda.sormas.backend.person.Person;
1719

@@ -30,21 +32,23 @@ public Predicate createUserFilter(CriteriaBuilder cb, CriteriaQuery cq, From<?,
3032
return null;
3133
}
3234

33-
public List<ManualMessageLogDto> getByPersonUuid(@NotNull String personUuid) {
35+
public List<ManualMessageLog> getByCaseUuid(@NotNull String caseUuid, MessageType messageType) {
3436

35-
CriteriaBuilder cb = em.getCriteriaBuilder();
36-
CriteriaQuery<ManualMessageLogDto> cq = cb.createQuery(ManualMessageLogDto.class);
37-
Root<ManualMessageLog> manualMessageLogRoot = cq.from(ManualMessageLog.class);
37+
final CriteriaBuilder cb = em.getCriteriaBuilder();
38+
final CriteriaQuery<ManualMessageLog> cq = cb.createQuery(ManualMessageLog.class);
39+
final Root<ManualMessageLog> manualMessageLogRoot = cq.from(ManualMessageLog.class);
3840

39-
cq.multiselect(
40-
manualMessageLogRoot.get(ManualMessageLog.MESSAGE_TYPE),
41-
manualMessageLogRoot.get(ManualMessageLog.SENT_DATE),
42-
manualMessageLogRoot.get(ManualMessageLog.SENDING_USER),
43-
manualMessageLogRoot.get(ManualMessageLog.RECIPIENT_PERSON));
41+
final Subquery<Person> casePersonSubQuery = cq.subquery(Person.class);
42+
final Root<Case> caseRoot = casePersonSubQuery.from(Case.class);
43+
casePersonSubQuery.where(cb.equal(caseRoot.get(Case.UUID), caseUuid));
44+
casePersonSubQuery.select(caseRoot.get(Case.PERSON));
4445

4546
Predicate filter = createUserFilter(cb, cq, manualMessageLogRoot);
46-
filter =
47-
AbstractAdoService.and(cb, filter, cb.equal(manualMessageLogRoot.get(ManualMessageLog.RECIPIENT_PERSON).get(Person.UUID), personUuid));
47+
filter = AbstractAdoService.and(
48+
cb,
49+
filter,
50+
cb.equal(manualMessageLogRoot.get(ManualMessageLog.RECIPIENT_PERSON), casePersonSubQuery),
51+
cb.equal(manualMessageLogRoot.get(ManualMessageLog.MESSAGE_TYPE), messageType));
4852

4953
cq.where(filter);
5054
cq.orderBy(cb.desc(manualMessageLogRoot.get(ManualMessageLog.SENT_DATE)));

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@
4848
import de.symeda.sormas.api.person.EducationType;
4949
import de.symeda.sormas.api.person.OccupationType;
5050
import de.symeda.sormas.api.person.PersonDto;
51+
import de.symeda.sormas.api.person.PersonReferenceDto;
5152
import de.symeda.sormas.api.person.PresentCondition;
5253
import de.symeda.sormas.api.person.Sex;
5354
import de.symeda.sormas.api.person.SymptomJournalStatus;
55+
import de.symeda.sormas.api.user.UserReferenceDto;
5456
import de.symeda.sormas.backend.caze.Case;
5557
import de.symeda.sormas.backend.common.AbstractDomainObject;
5658
import de.symeda.sormas.backend.common.messaging.ManualMessageLog;
@@ -671,6 +673,10 @@ public void setManualMessageLogs(List<ManualMessageLog> manualMessageLogs) {
671673
this.manualMessageLogs = manualMessageLogs;
672674
}
673675

676+
public PersonReferenceDto toReference() {
677+
return new PersonReferenceDto(getUuid(), getFirstName(), getLastName());
678+
}
679+
674680
@Override
675681
public String toString() {
676682
return PersonDto.buildCaption(firstName, lastName);

sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataView.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public class CaseDataView extends AbstractCaseView {
6060
public static final String SAMPLES_LOC = "samples";
6161
public static final String EVENTS_LOC = "events";
6262
public static final String SORMAS_TO_SORMAS_LOC = "sormasToSormas";
63+
public static final String SMS_LOC = "sms";
6364

6465
private CommitDiscardWrapperComponent<CaseDataForm> editComponent;
6566

@@ -80,6 +81,7 @@ protected void initView(String params) {
8081
LayoutUtil.fluidColumnLoc(4, 0, 6, 0, SAMPLES_LOC),
8182
LayoutUtil.fluidColumnLoc(4, 0, 6, 0, EVENTS_LOC),
8283
LayoutUtil.fluidColumnLoc(4, 0, 6, 0, SORMAS_TO_SORMAS_LOC),
84+
LayoutUtil.fluidColumnLoc(4, 0, 6, 0, SMS_LOC),
8385
LayoutUtil.fluidColumnLoc(4, 0, 6, 0, SurvnetGateway.SURVNET_GATEWAY_LOC),
8486
LayoutUtil.fluidColumnLoc(4, 0, 6, 0, DocGenerationComponent.QUARANTINE_LOC));
8587

@@ -112,6 +114,12 @@ protected void initView(String params) {
112114
taskList.addStyleName(CssStyles.SIDE_COMPONENT);
113115
layout.addComponent(taskList, TASKS_LOC);
114116

117+
if (UserProvider.getCurrent().hasUserRight(UserRight.SEND_MANUAL_EXTERNAL_MESSAGES)) {
118+
SmsListComponent smsList = new SmsListComponent(getCaseRef());
119+
smsList.addStyleName(CssStyles.SIDE_COMPONENT);
120+
layout.addComponent(smsList, SMS_LOC);
121+
}
122+
115123
if (UserProvider.getCurrent().hasUserRight(UserRight.SAMPLE_VIEW) && !caze.isUnreferredPortHealthCase()) {
116124
VerticalLayout sampleLocLayout = new VerticalLayout();
117125
sampleLocLayout.setMargin(false);
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package de.symeda.sormas.ui.caze;
2+
3+
import com.vaadin.ui.Alignment;
4+
import com.vaadin.ui.Component;
5+
import com.vaadin.ui.HorizontalLayout;
6+
import com.vaadin.ui.Label;
7+
import com.vaadin.ui.TextArea;
8+
import com.vaadin.ui.VerticalLayout;
9+
import com.vaadin.v7.ui.CustomField;
10+
11+
public class SmsComponent extends CustomField<String> {
12+
13+
private TextArea smsTextArea;
14+
15+
@Override
16+
protected Component initContent() {
17+
final VerticalLayout mainLayout = new VerticalLayout();
18+
mainLayout.setSpacing(false);
19+
mainLayout.setSizeUndefined();
20+
mainLayout.setWidth(100, Unit.PERCENTAGE);
21+
22+
smsTextArea = new TextArea();
23+
smsTextArea.setWidth(100, Unit.PERCENTAGE);
24+
smsTextArea.setRows(4);
25+
mainLayout.addComponent(smsTextArea);
26+
27+
final HorizontalLayout charactersLayout = new HorizontalLayout();
28+
charactersLayout.setSpacing(false);
29+
charactersLayout.addComponent(new Label("Characters: "));
30+
final Label characterLeftLabel = new Label("0");
31+
charactersLayout.addComponent(characterLeftLabel);
32+
charactersLayout.addComponent(new Label(" / 160 => Nr. of messages: "));
33+
final Label nrOfMessagesLabel = new Label("1");
34+
charactersLayout.addComponent(nrOfMessagesLabel);
35+
36+
mainLayout.addComponent(charactersLayout);
37+
mainLayout.setComponentAlignment(charactersLayout, Alignment.BOTTOM_RIGHT);
38+
39+
smsTextArea.addValueChangeListener(e -> {
40+
final int nrOfCharacters = e.getValue().length();
41+
characterLeftLabel.setValue(String.valueOf(nrOfCharacters));
42+
nrOfMessagesLabel.setValue(String.valueOf(1 + nrOfCharacters / 160));
43+
});
44+
45+
return mainLayout;
46+
}
47+
48+
@Override
49+
public Class<? extends String> getType() {
50+
return String.class;
51+
}
52+
53+
@Override
54+
public String getValue() {
55+
return smsTextArea.getValue();
56+
}
57+
}

0 commit comments

Comments
 (0)