Skip to content

Commit ade61d0

Browse files
Merge pull request #13378 from SORMAS-Foundation/feature-13375-updated_doctor_declaration_for_pertussis
#13375 - Added EPI data hanling for doctor declaration processing
2 parents 0957a7a + 387d057 commit ade61d0

File tree

5 files changed

+203
-2
lines changed

5 files changed

+203
-2
lines changed

sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageDto.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ public class ExternalMessageDto extends SormasToSormasShareableDto {
103103
public static final String TREATMENT_STARTED = "treatmentStarted";
104104
public static final String TREATMENT_STARTED_DATE = "treatmentStartedDate";
105105
public static final String DIAGNOSTIC_DATE = "diagnosticDate";
106+
public static final String ACTIVITIES_AS_CASE = "activitiesAsCase";
107+
public static final String EXPOSURES = "exposures";
106108

107109
@AuditIncludeProperty
108110
private ExternalMessageType type;
@@ -220,6 +222,9 @@ public class ExternalMessageDto extends SormasToSormasShareableDto {
220222
@Size(max = FieldConstraints.CHARACTER_LIMIT_SMALL, message = Validations.textTooLong)
221223
private String notifierPhone;
222224

225+
private String activitiesAsCase;
226+
private String exposures;
227+
223228
public ExternalMessageType getType() {
224229
return type;
225230
}
@@ -722,4 +727,20 @@ public void setPersonAdditionalDetails(String personAdditionalDetails) {
722727
this.personAdditionalDetails = personAdditionalDetails;
723728
}
724729

730+
public String getActivitiesAsCase() {
731+
return activitiesAsCase;
732+
}
733+
734+
public void setActivitiesAsCase(String activitiesAsCase) {
735+
this.activitiesAsCase = activitiesAsCase;
736+
}
737+
738+
public String getExposures() {
739+
return exposures;
740+
}
741+
742+
public void setExposures(String exposures) {
743+
this.exposures = exposures;
744+
}
745+
725746
}

sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/doctordeclaration/AbstractDoctorDeclarationMessageProcessingFlow.java

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* SORMAS® - Surveillance Outbreak Response Management & Analysis System
3-
* Copyright © 2016-2023 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
3+
* Copyright © 2016-2026 SORMAS Foundation gGmbH
44
* This program is free software: you can redistribute it and/or modify
55
* it under the terms of the GNU General Public License as published by
66
* the Free Software Foundation, either version 3 of the License, or
@@ -15,21 +15,31 @@
1515

1616
package de.symeda.sormas.api.externalmessage.processing.labmessage;
1717

18+
import java.util.ArrayList;
1819
import java.util.Date;
20+
import java.util.List;
1921

2022
import org.slf4j.Logger;
2123
import org.slf4j.LoggerFactory;
2224

25+
import com.fasterxml.jackson.core.type.TypeReference;
26+
import com.fasterxml.jackson.databind.ObjectMapper;
27+
28+
import de.symeda.sormas.api.activityascase.ActivityAsCaseDto;
29+
import de.symeda.sormas.api.activityascase.ActivityAsCaseType;
2330
import de.symeda.sormas.api.caze.CaseDataDto;
2431
import de.symeda.sormas.api.caze.CaseOutcome;
2532
import de.symeda.sormas.api.caze.CaseSelectionDto;
2633
import de.symeda.sormas.api.caze.InvestigationStatus;
2734
import de.symeda.sormas.api.caze.surveillancereport.SurveillanceReportDto;
2835
import de.symeda.sormas.api.contact.ContactDto;
2936
import de.symeda.sormas.api.contact.SimilarContactDto;
37+
import de.symeda.sormas.api.epidata.EpiDataDto;
3038
import de.symeda.sormas.api.event.EventDto;
3139
import de.symeda.sormas.api.event.EventParticipantDto;
3240
import de.symeda.sormas.api.event.SimilarEventParticipantDto;
41+
import de.symeda.sormas.api.exposure.ExposureDto;
42+
import de.symeda.sormas.api.exposure.ExposureType;
3343
import de.symeda.sormas.api.externalmessage.ExternalMessageDto;
3444
import de.symeda.sormas.api.externalmessage.ExternalMessageStatus;
3545
import de.symeda.sormas.api.externalmessage.processing.AbstractMessageProcessingFlowBase;
@@ -45,6 +55,7 @@
4555
import de.symeda.sormas.api.therapy.TreatmentDto;
4656
import de.symeda.sormas.api.user.UserDto;
4757
import de.symeda.sormas.api.utils.DtoCopyHelper;
58+
import de.symeda.sormas.api.utils.YesNoUnknown;
4859
import de.symeda.sormas.api.utils.dataprocessing.ProcessingResult;
4960
import de.symeda.sormas.api.utils.dataprocessing.flow.FlowThen;
5061

@@ -231,11 +242,21 @@ protected void postBuildCase(CaseDataDto caseDto, ExternalMessageDto externalMes
231242

232243
postBuildCaseSymptoms(caseDto, externalMessageDto);
233244
postBuildCaseTherapy(caseDto, externalMessageDto);
245+
postBuildActivitiesAsCase(caseDto, externalMessageDto);
246+
postBuildExposure(caseDto, externalMessageDto);
234247

235248
caseDto.setInvestigationStatus(InvestigationStatus.PENDING);
236249
caseDto.setOutcome(CaseOutcome.NO_OUTCOME);
237250
}
238251

252+
/**
253+
* Sets the symptoms for the case from the external message, if present.
254+
*
255+
* @param caseDto
256+
* The case data transfer object to update.
257+
* @param externalMessageDto
258+
* The external message containing symptom data.
259+
*/
239260
protected void postBuildCaseSymptoms(CaseDataDto caseDto, ExternalMessageDto externalMessageDto) {
240261
if (externalMessageDto.getCaseSymptoms() != null) {
241262
final SymptomsDto symptomsDto = SymptomsDto.build();
@@ -246,6 +267,14 @@ protected void postBuildCaseSymptoms(CaseDataDto caseDto, ExternalMessageDto ext
246267
}
247268
}
248269

270+
/**
271+
* Sets the therapy information for the case from the external message, if present.
272+
*
273+
* @param caseDto
274+
* The case data transfer object to update.
275+
* @param externalMessageDto
276+
* The external message containing therapy data.
277+
*/
249278
protected void postBuildCaseTherapy(CaseDataDto caseDto, ExternalMessageDto externalMessageDto) {
250279

251280
TherapyDto therapyDto = caseDto.getTherapy();
@@ -258,6 +287,112 @@ protected void postBuildCaseTherapy(CaseDataDto caseDto, ExternalMessageDto exte
258287

259288
}
260289

290+
/**
291+
* Sets the activities as case for the case from the external message, if present.
292+
* <p>
293+
* This method deserializes the activities as case from a JSON string in the external message
294+
* into a list of {@link ActivityAsCaseDto} objects. Each activity is built and copied, and if
295+
* the activity type is missing, it is set to {@link ActivityAsCaseType#UNKNOWN}.
296+
* If there are activities, the {@code activityAsCaseDetailsKnown} field is set to {@link YesNoUnknown#YES}.
297+
* </p>
298+
*
299+
* @param caseDto
300+
* The case data transfer object to update.
301+
* @param externalMessageDto
302+
* The external message containing activities as case data in JSON format.
303+
*/
304+
protected void postBuildActivitiesAsCase(CaseDataDto caseDto, ExternalMessageDto externalMessageDto) {
305+
306+
if (externalMessageDto.getActivitiesAsCase() != null && !externalMessageDto.getActivitiesAsCase().isEmpty()) {
307+
final ArrayList<ActivityAsCaseDto> activitiesAsCase = new ArrayList<>();
308+
309+
try {
310+
ObjectMapper objectMapper = new ObjectMapper();
311+
List<ActivityAsCaseDto> deserialActivityAsCaseDtos =
312+
objectMapper.readValue(externalMessageDto.getActivitiesAsCase(), new TypeReference<List<ActivityAsCaseDto>>() {
313+
});
314+
for (ActivityAsCaseDto activityAsCaseDto : deserialActivityAsCaseDtos) {
315+
ActivityAsCaseDto newActivityAsCase = ActivityAsCaseDto.build(activityAsCaseDto.getActivityAsCaseType());
316+
if (newActivityAsCase.getActivityAsCaseType() == null) {
317+
newActivityAsCase.setActivityAsCaseType(ActivityAsCaseType.UNKNOWN);
318+
}
319+
DtoCopyHelper.copyDtoValues(newActivityAsCase, activityAsCaseDto, true, "uuid");
320+
activitiesAsCase.add(newActivityAsCase);
321+
}
322+
} catch (Exception e) {
323+
logger.error("[POST BUILD CASE] Error while processing activities as case for case with UUID: {}", caseDto.getUuid(), e);
324+
return;
325+
}
326+
327+
if (!activitiesAsCase.isEmpty()) {
328+
EpiDataDto epiData = caseDto.getEpiData();
329+
if (epiData == null) {
330+
epiData = EpiDataDto.build();
331+
caseDto.setEpiData(epiData);
332+
}
333+
334+
epiData.setActivityAsCaseDetailsKnown(YesNoUnknown.YES);
335+
epiData.setActivitiesAsCase(activitiesAsCase);
336+
}
337+
338+
} else {
339+
logger.debug("[POST BUILD CASE] No activities to set for case with UUID: {}", caseDto.getUuid());
340+
}
341+
}
342+
343+
/**
344+
* Sets the exposure information for the case from the external message, if present.
345+
* <p>
346+
* This method deserializes the exposures from a JSON string in the external message
347+
* into a list of {@link ExposureDto} objects. Each exposure is built and copied, and if
348+
* the exposure type is missing, it is set to {@link ExposureType#UNKNOWN}.
349+
* If there are exposures, the {@code exposureDetailsKnown} field is set to {@link YesNoUnknown#YES}.
350+
* </p>
351+
*
352+
* @param caseDto
353+
* The case data transfer object to update.
354+
* @param externalMessageDto
355+
* The external message containing exposure data in JSON format.
356+
*/
357+
protected void postBuildExposure(CaseDataDto caseDto, ExternalMessageDto externalMessageDto) {
358+
359+
if (externalMessageDto.getExposures() != null && !externalMessageDto.getExposures().isEmpty()) {
360+
final ArrayList<ExposureDto> exposures = new ArrayList<>();
361+
362+
try {
363+
ObjectMapper objectMapper = new ObjectMapper();
364+
List<ExposureDto> deserialExposureDtos =
365+
objectMapper.readValue(externalMessageDto.getExposures(), new TypeReference<List<ExposureDto>>() {
366+
});
367+
for (ExposureDto exposureDto : deserialExposureDtos) {
368+
ExposureDto newExposure = ExposureDto.build(exposureDto.getExposureType());
369+
if (newExposure.getExposureType() == null) {
370+
newExposure.setExposureType(ExposureType.UNKNOWN);
371+
}
372+
DtoCopyHelper.copyDtoValues(newExposure, exposureDto, true, "uuid");
373+
exposures.add(newExposure);
374+
}
375+
} catch (Exception e) {
376+
logger.error("[POST BUILD CASE] Error while processing exposures for case with UUID: {}", caseDto.getUuid(), e);
377+
return;
378+
}
379+
380+
if (!exposures.isEmpty()) {
381+
EpiDataDto epiData = caseDto.getEpiData();
382+
if (epiData == null) {
383+
epiData = EpiDataDto.build();
384+
caseDto.setEpiData(epiData);
385+
}
386+
387+
epiData.setExposureDetailsKnown(YesNoUnknown.YES);
388+
epiData.setExposures(exposures);
389+
}
390+
391+
} else {
392+
logger.debug("[POST BUILD CASE] No exposures to set for case with UUID: {}", caseDto.getUuid());
393+
}
394+
}
395+
261396
/**
262397
* Custom logic to execute after building a person.
263398
*

sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessage.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323

2424
import org.hibernate.annotations.Type;
2525
import org.hibernate.annotations.TypeDef;
26+
import org.hibernate.annotations.TypeDefs;
2627

2728
import com.vladmihalcea.hibernate.type.array.ListArrayType;
29+
import com.vladmihalcea.hibernate.type.json.JsonBinaryType;
2830

2931
import de.symeda.sormas.api.Disease;
3032
import de.symeda.sormas.api.caze.CaseClassification;
@@ -46,7 +48,10 @@
4648
import de.symeda.sormas.backend.user.User;
4749

4850
@Entity(name = ExternalMessage.TABLE_NAME)
49-
@TypeDef(name = "list-array", typeClass = ListArrayType.class)
51+
@TypeDefs({
52+
@TypeDef(name = "list-array", typeClass = ListArrayType.class),
53+
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
54+
@TypeDef(name = "json", typeClass = JsonBinaryType.class) })
5055
public class ExternalMessage extends AbstractDomainObject {
5156

5257
public static final String TABLE_NAME = "externalmessage";
@@ -107,6 +112,9 @@ public class ExternalMessage extends AbstractDomainObject {
107112
public static final String TREATMENT_STARTED_DATE = "treatmentStartedDate";
108113
public static final String DIAGNOSTIC_DATE = "diagnosticDate";
109114

115+
public static final String ACTIVITIES_AS_CASE = "activitiesAsCase";
116+
public static final String EXPOSURES = "exposures";
117+
110118
private ExternalMessageType type;
111119
private Disease disease;
112120
private String diseaseVariantValue;
@@ -182,6 +190,9 @@ public class ExternalMessage extends AbstractDomainObject {
182190
@Column(length = CHARACTER_LIMIT_SMALL)
183191
private String notifierPhone;
184192

193+
private String activitiesAsCase;
194+
private String exposures;
195+
185196
@Enumerated(EnumType.STRING)
186197
public ExternalMessageType getType() {
187198
return type;
@@ -692,4 +703,24 @@ public Date getDiagnosticDate() {
692703
public void setDiagnosticDate(Date diagnosticDate) {
693704
this.diagnosticDate = diagnosticDate;
694705
}
706+
707+
@Column
708+
@Type(type = "jsonb")
709+
public String getActivitiesAsCase() {
710+
return activitiesAsCase;
711+
}
712+
713+
public void setActivitiesAsCase(String activitiesAsCase) {
714+
this.activitiesAsCase = activitiesAsCase;
715+
}
716+
717+
@Column
718+
@Type(type = "jsonb")
719+
public String getExposures() {
720+
return exposures;
721+
}
722+
723+
public void setExposures(String exposures) {
724+
this.exposures = exposures;
725+
}
695726
}

sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ ExternalMessage fillOrBuildEntity(@NotNull ExternalMessageDto source, ExternalMe
196196
target.setTreatmentStarted(source.getTreatmentStarted());
197197
target.setTreatmentStartedDate(source.getTreatmentStartedDate());
198198
target.setDiagnosticDate(source.getDiagnosticDate());
199+
target.setActivitiesAsCase(source.getActivitiesAsCase());
200+
target.setExposures(source.getExposures());
199201

200202
target.setReportId(source.getReportId());
201203
if (source.getAssignee() != null) {
@@ -396,6 +398,8 @@ public ExternalMessageDto toDto(ExternalMessage source) {
396398
target.setTreatmentStarted(source.getTreatmentStarted());
397399
target.setTreatmentStartedDate(source.getTreatmentStartedDate());
398400
target.setDiagnosticDate(source.getDiagnosticDate());
401+
target.setActivitiesAsCase(source.getActivitiesAsCase());
402+
target.setExposures(source.getExposures());
399403

400404
target.setReportId(source.getReportId());
401405
if (source.getSampleReports() != null) {

sormas-backend/src/main/resources/sql/sormas_schema.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14241,4 +14241,14 @@ ALTER TABLE symptoms_history ADD COLUMN coughsprovokevomiting varchar(255);
1424114241

1424214242
INSERT INTO schema_version (version_number, comment) VALUES (571, 'Update Pertussis symptoms #13373');
1424314243

14244+
14245+
-- 2025-06-02 Add epi data to external message #13375
14246+
14247+
ALTER TABLE externalmessage ADD COLUMN activitiesascase jsonb;
14248+
ALTER TABLE externalmessage_history ADD COLUMN activitiesascase jsonb;
14249+
ALTER TABLE externalmessage ADD COLUMN exposures jsonb;
14250+
ALTER TABLE externalmessage_history ADD COLUMN exposures jsonb;
14251+
14252+
INSERT INTO schema_version (version_number, comment) VALUES (572, 'Updated doctor declaration for Pertussis #13375');
14253+
1424414254
-- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. ***

0 commit comments

Comments
 (0)