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

Commit f954732

Browse files
authored
Feature 3408 first contact date (SORMAS-Foundation#3607)
* SORMAS-Foundation#3408 add multiday contact with first date to contact and web ui * SORMAS-Foundation#3408 add multiday and first date to contact * SORMAS-Foundation#3408 rename methods for contact date validation * SORMAS-Foundation#3408 update mobile validation for contact dates and added fields on read view * SORMAS-Foundation#3408 updated unit tests * SORMAS-Foundation#3408 added visibility dependecy to first contact fields * SORMAS-Foundation#3408 removed unneeded value cleanup
1 parent 08205a0 commit f954732

20 files changed

Lines changed: 332 additions & 27 deletions

File tree

sormas-api/src/main/java/de/symeda/sormas/api/contact/ContactDto.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public class ContactDto extends PseudonymizableDto {
5555
public static final String CAZE = "caze";
5656
public static final String REPORT_DATE_TIME = "reportDateTime";
5757
public static final String REPORTING_USER = "reportingUser";
58+
public static final String MULTI_DAY_CONTACT = "multiDayContact";
59+
public static final String FIRST_CONTACT_DATE = "firstContactDate";
5860
public static final String LAST_CONTACT_DATE = "lastContactDate";
5961
public static final String CONTACT_IDENTIFICATION_SOURCE = "contactIdentificationSource";
6062
public static final String CONTACT_IDENTIFICATION_SOURCE_DETAILS = "contactIdentificationSourceDetails";
@@ -138,6 +140,9 @@ public class ContactDto extends PseudonymizableDto {
138140
private DistrictReferenceDto district;
139141
private CommunityReferenceDto community;
140142
@Required
143+
private boolean multiDayContact;
144+
private Date firstContactDate;
145+
@Required
141146
private Date lastContactDate;
142147
@HideForCountriesExcept
143148
private ContactIdentificationSource contactIdentificationSource;
@@ -320,6 +325,22 @@ public void setReportingUser(UserReferenceDto reportingUser) {
320325
this.reportingUser = reportingUser;
321326
}
322327

328+
public boolean isMultiDayContact() {
329+
return multiDayContact;
330+
}
331+
332+
public void setMultiDayContact(boolean multiDayContact) {
333+
this.multiDayContact = multiDayContact;
334+
}
335+
336+
public Date getFirstContactDate() {
337+
return firstContactDate;
338+
}
339+
340+
public void setFirstContactDate(Date firstContactDate) {
341+
this.firstContactDate = firstContactDate;
342+
}
343+
323344
public Date getLastContactDate() {
324345
return lastContactDate;
325346
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ public interface Captions {
451451
String Contact_endOfQuarantineReasonDetails = "Contact.endOfQuarantineReasonDetails";
452452
String Contact_epiData = "Contact.epiData";
453453
String Contact_externalID = "Contact.externalID";
454+
String Contact_firstContactDate = "Contact.firstContactDate";
454455
String Contact_firstName = "Contact.firstName";
455456
String Contact_followUpComment = "Contact.followUpComment";
456457
String Contact_followUpStatus = "Contact.followUpStatus";
@@ -462,6 +463,7 @@ public interface Captions {
462463
String Contact_lastName = "Contact.lastName";
463464
String Contact_latestEventId = "Contact.latestEventId";
464465
String Contact_latestEventTitle = "Contact.latestEventTitle";
466+
String Contact_multiDayContact = "Contact.multiDayContact";
465467
String Contact_numberOfVisits = "Contact.numberOfVisits";
466468
String Contact_overwriteFollowUpUntil = "Contact.overwriteFollowUpUntil";
467469
String Contact_person = "Contact.person";

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ Contact.disease=Disease of source case
564564
Contact.district=Responsible district
565565
Contact.epiData=Epidemiological data
566566
Contact.externalID=External ID
567+
Contact.firstContactDate=Date of first contact
567568
Contact.firstName=First name of contact person
568569
Contact.followUpComment=Follow-up status comment
569570
Contact.followUpStatus=Follow-up status
@@ -573,6 +574,7 @@ Contact.lastContactDate=Date of last contact
573574
Contact.lastName=Last name of contact person
574575
Contact.latestEventId=Latest event ID
575576
Contact.latestEventTitle=Latest event title
577+
Contact.multiDayContact=Multi-day contact
576578
Contact.numberOfVisits=Number of visits
577579
Contact.person=Contact person
578580
Contact.quarantine=Quarantine

sormas-api/src/main/resources/captions_de-DE.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ Contact.disease=Krankheit des Indexfalls
564564
Contact.district=Zuständige/r Landkreis/Kreisfreie Stadt
565565
Contact.epiData=Epidemiologische Daten
566566
Contact.externalID=Externe ID
567+
Contact.firstContactDate=Datum des ersten Kontaktes
567568
Contact.firstName=Vorname der Kontaktperson
568569
Contact.followUpComment=Nachverfolgungs-Status-Kommentar
569570
Contact.followUpStatus=Nachverfolgungs-Status

sormas-app/app/src/main/java/de/symeda/sormas/app/backend/common/DatabaseHelper.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,11 @@ public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int
17731773
+ "SELECT exposureDetailsKnown, contactWithSourceCaseKnown, wildbirds, changeDate, creationDate, id, lastOpenedDate, localChangeDate, modified, snapshot, uuid, pseudonymized "
17741774
+ "FROM tmp_epidata;");
17751775
getDao(EpiData.class).executeRaw("DROP TABLE tmp_epidata;");
1776+
case 247:
1777+
currentVersion = 247;
1778+
1779+
getDao(Contact.class).executeRaw("ALTER TABLE contacts ADD column multidaycontact boolean default false;");
1780+
getDao(Contact.class).executeRaw("ALTER TABLE contacts ADD column firstcontactdate timestamp;");
17761781

17771782
// ATTENTION: break should only be done after last version
17781783
break;

sormas-app/app/src/main/java/de/symeda/sormas/app/backend/contact/Contact.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ public class Contact extends PseudonymizableAdo {
6767
public static final String DISEASE = "disease";
6868
public static final String REPORT_DATE_TIME = "reportDateTime";
6969
public static final String REPORTING_USER = "reportingUser";
70+
public static final String MULTI_DAY_CONTACT = "multiDayContact";
71+
public static final String FIRST_CONTACT_DATE = "firstContactDate";
7072
public static final String LAST_CONTACT_DATE = "lastContactDate";
7173
public static final String CONTACT_IDENTIFICATION_SOURCE = "contactIdentificationSource";
7274
public static final String CONTACT_IDENTIFICATION_SOURCE_DETAILS = "contactIdentificationSourceDetails";
@@ -112,7 +114,11 @@ public class Contact extends PseudonymizableAdo {
112114
private Disease disease;
113115
@Column(length = COLUMN_LENGTH_DEFAULT)
114116
private String diseaseDetails;
115-
@DatabaseField(dataType = DataType.DATE_LONG, canBeNull = true)
117+
@DatabaseField
118+
private boolean multiDayContact;
119+
@DatabaseField(dataType = DataType.DATE_LONG)
120+
private Date firstContactDate;
121+
@DatabaseField(dataType = DataType.DATE_LONG)
116122
private Date lastContactDate;
117123
@Enumerated(EnumType.STRING)
118124
private ContactIdentificationSource contactIdentificationSource;
@@ -253,6 +259,22 @@ public void setReportingUser(User reportingUser) {
253259
this.reportingUser = reportingUser;
254260
}
255261

262+
public boolean isMultiDayContact() {
263+
return multiDayContact;
264+
}
265+
266+
public void setMultiDayContact(boolean multiDayContact) {
267+
this.multiDayContact = multiDayContact;
268+
}
269+
270+
public Date getFirstContactDate() {
271+
return firstContactDate;
272+
}
273+
274+
public void setFirstContactDate(Date firstContactDate) {
275+
this.firstContactDate = firstContactDate;
276+
}
277+
256278
public Date getLastContactDate() {
257279
return lastContactDate;
258280
}

sormas-app/app/src/main/java/de/symeda/sormas/app/backend/contact/ContactDtoHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public void fillInnerFromDto(Contact target, ContactDto source) {
8787
target.setReportDateTime(source.getReportDateTime());
8888
target.setContactOfficer(DatabaseHelper.getUserDao().getByReferenceDto(source.getContactOfficer()));
8989

90+
target.setMultiDayContact(source.isMultiDayContact());
9091
target.setLastContactDate(source.getLastContactDate());
9192
target.setContactIdentificationSource(source.getContactIdentificationSource());
9293
target.setContactIdentificationSourceDetails(source.getContactIdentificationSourceDetails());

sormas-app/app/src/main/java/de/symeda/sormas/app/contact/edit/ContactEditFragment.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ private void setUpFieldVisibilities(FragmentContactEditLayoutBinding contentBind
116116
contentBinding.createCase.setVisibility(GONE);
117117
}
118118

119+
if (!record.isMultiDayContact()) {
120+
contentBinding.contactFirstContactDate.setValue(null);
121+
}
122+
119123
if (!ConfigProvider.isConfiguredServer(CountryHelper.COUNTRY_CODE_GERMANY)) {
120124
contentBinding.contactImmunosuppressiveTherapyBasicDisease.setVisibility(GONE);
121125
contentBinding.contactImmunosuppressiveTherapyBasicDiseaseDetails.setVisibility(GONE);
@@ -200,6 +204,8 @@ public void onLayoutBinding(FragmentContactEditLayoutBinding contentBinding) {
200204
.setItems(DataUtils.toItems(Arrays.asList(ContactProximity.getValues((Disease) e.getValue(), ConfigProvider.getServerLocale()))));
201205
});
202206

207+
contentBinding.contactFirstContactDate.addValueChangedListener(e -> contentBinding.contactLastContactDate.setRequired(e.getValue() != null));
208+
203209
contentBinding.contactContactProximity
204210
.setItems(DataUtils.toItems(Arrays.asList(ContactProximity.getValues(record.getDisease(), ConfigProvider.getServerLocale()))));
205211

@@ -396,6 +402,7 @@ public void onAfterLayoutBinding(FragmentContactEditLayoutBinding contentBinding
396402
contentBinding.contactEndOfQuarantineReason.initializeSpinner(endOfQuarantineReasons);
397403

398404
// Initialize ControlDateFields
405+
contentBinding.contactFirstContactDate.initializeDateField(getFragmentManager());
399406
contentBinding.contactLastContactDate.initializeDateField(getFragmentManager());
400407
contentBinding.contactReportDateTime.initializeDateField(getFragmentManager());
401408
contentBinding.contactQuarantineFrom.initializeDateField(getFragmentManager());

sormas-app/app/src/main/java/de/symeda/sormas/app/contact/edit/ContactNewFragment.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import de.symeda.sormas.app.backend.contact.Contact;
4040
import de.symeda.sormas.app.caze.edit.CaseNewFragment;
4141
import de.symeda.sormas.app.component.Item;
42+
import de.symeda.sormas.app.component.controls.ControlCheckBoxField;
43+
import de.symeda.sormas.app.component.controls.ControlDateField;
4244
import de.symeda.sormas.app.databinding.FragmentContactNewLayoutBinding;
4345
import de.symeda.sormas.app.util.DataUtils;
4446
import de.symeda.sormas.app.util.DiseaseConfigurationCache;
@@ -104,6 +106,8 @@ public void onLayoutBinding(FragmentContactNewLayoutBinding contentBinding) {
104106
initialCommunities,
105107
record.getCommunity());
106108

109+
contentBinding.contactFirstContactDate.addValueChangedListener(e -> contentBinding.contactLastContactDate.setRequired(e.getValue() != null));
110+
107111
contentBinding.contactDisease.initializeSpinner(diseaseList, DiseaseConfigurationCache.getInstance().getDefaultDisease());
108112
contentBinding.contactDisease.addValueChangedListener(e -> {
109113
contentBinding.contactContactProximity.setVisibility(e.getValue() == null ? GONE : VISIBLE);
@@ -181,6 +185,8 @@ public void onAfterLayoutBinding(FragmentContactNewLayoutBinding contentBinding)
181185
contentBinding.personSex.initializeSpinner(sexList);
182186
contentBinding.contactRelationToCase.initializeSpinner(relationshipList);
183187
contentBinding.contactContactCategory.initializeSpinner(categoryList);
188+
189+
contentBinding.contactFirstContactDate.initializeDateField(getFragmentManager());
184190
contentBinding.contactLastContactDate.initializeDateField(getFragmentManager());
185191
contentBinding.contactReportDateTime.initializeDateField(getFragmentManager());
186192

sormas-app/app/src/main/java/de/symeda/sormas/app/contact/edit/ContactValidator.java

Lines changed: 86 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import de.symeda.sormas.api.i18n.I18nProperties;
2424
import de.symeda.sormas.api.i18n.Validations;
2525
import de.symeda.sormas.app.backend.contact.Contact;
26+
import de.symeda.sormas.app.component.controls.ControlDateField;
2627
import de.symeda.sormas.app.databinding.FragmentContactEditLayoutBinding;
2728
import de.symeda.sormas.app.databinding.FragmentContactNewLayoutBinding;
2829
import de.symeda.sormas.app.util.ResultCallback;
@@ -31,45 +32,110 @@ public final class ContactValidator {
3132

3233
public static void initializeValidation(final Contact contact, final FragmentContactEditLayoutBinding contentBinding) {
3334
if (contact != null) {
35+
ResultCallback<Boolean> firstDateCallBack = () -> {
36+
Date firstContactDate = contentBinding.contactFirstContactDate.getValue();
37+
38+
if (firstContactDate == null) {
39+
return false;
40+
}
41+
42+
return validateFirstContactDateIsBeforeLastContactDate(contentBinding.contactFirstContactDate, contentBinding.contactLastContactDate);
43+
};
44+
45+
contentBinding.contactFirstContactDate.setValidationCallback(firstDateCallBack);
46+
3447
ResultCallback<Boolean> lastContactDateCallback = () -> {
3548
Date lastContactDate = contentBinding.contactLastContactDate.getValue();
3649
Date contactReferenceDate = contact.getReportDateTime();
37-
if (lastContactDate != null
38-
&& contactReferenceDate != null
39-
&& DateTimeComparator.getDateOnlyInstance().compare(lastContactDate, contactReferenceDate) > 0) {
40-
contentBinding.contactLastContactDate.enableErrorState(
41-
I18nProperties.getValidationError(
42-
Validations.beforeDate,
43-
contentBinding.contactLastContactDate.getCaption(),
44-
contentBinding.contactReportDateTime.getCaption()));
45-
return true;
50+
51+
if(lastContactDate == null) {
52+
return false;
4653
}
4754

48-
return false;
55+
return validateLastContactDateIsAfterFirstContactDate(contentBinding.contactFirstContactDate, contentBinding.contactLastContactDate)
56+
|| validateLastContactDateIsBeforeReportDate(contentBinding.contactLastContactDate, contactReferenceDate);
4957
};
5058
contentBinding.contactLastContactDate.setValidationCallback(lastContactDateCallback);
5159
}
5260
}
5361

5462
public static void initializeValidation(final Contact contact, final FragmentContactNewLayoutBinding contentBinding) {
5563
if (contact != null) {
64+
ResultCallback<Boolean> firstDateCallBack = () -> {
65+
Date firstContactDate = contentBinding.contactFirstContactDate.getValue();
66+
67+
if (firstContactDate == null) {
68+
return false;
69+
}
70+
71+
return validateFirstContactDateIsBeforeLastContactDate(contentBinding.contactFirstContactDate, contentBinding.contactLastContactDate);
72+
};
73+
74+
contentBinding.contactFirstContactDate.setValidationCallback(firstDateCallBack);
75+
5676
ResultCallback<Boolean> lastContactDateCallback = () -> {
5777
Date lastContactDate = contentBinding.contactLastContactDate.getValue();
5878
Date contactReferenceDate = contact.getReportDateTime();
59-
if (lastContactDate != null
60-
&& contactReferenceDate != null
61-
&& DateTimeComparator.getDateOnlyInstance().compare(lastContactDate, contactReferenceDate) > 0) {
62-
contentBinding.contactLastContactDate.enableErrorState(
63-
I18nProperties.getValidationError(
64-
Validations.beforeDate,
65-
contentBinding.contactLastContactDate.getCaption(),
66-
I18nProperties.getPrefixCaption(ContactDto.I18N_PREFIX, ContactDto.REPORT_DATE_TIME)));
67-
return true;
79+
80+
if(lastContactDate == null) {
81+
return false;
6882
}
6983

70-
return false;
84+
return validateLastContactDateIsAfterFirstContactDate(contentBinding.contactFirstContactDate, contentBinding.contactLastContactDate)
85+
|| validateLastContactDateIsBeforeReportDate(contentBinding.contactLastContactDate, contactReferenceDate);
7186
};
7287
contentBinding.contactLastContactDate.setValidationCallback(lastContactDateCallback);
7388
}
7489
}
90+
91+
private static boolean validateFirstContactDateIsBeforeLastContactDate(final ControlDateField firstContactDateField, final ControlDateField lastContactDateField) {
92+
Date firstContactDate = firstContactDateField.getValue();
93+
Date lastContactDate = lastContactDateField.getValue();
94+
95+
DateTimeComparator dateOnlyComparator = DateTimeComparator.getDateOnlyInstance();
96+
97+
if (firstContactDate != null && lastContactDate != null && dateOnlyComparator.compare(firstContactDate, lastContactDate) >= 0) {
98+
firstContactDateField.enableErrorState(
99+
I18nProperties.getValidationError(
100+
Validations.beforeDate,
101+
firstContactDateField.getCaption(),
102+
I18nProperties.getPrefixCaption(ContactDto.I18N_PREFIX, ContactDto.LAST_CONTACT_DATE)));
103+
return true;
104+
}
105+
return false;
106+
}
107+
108+
private static boolean validateLastContactDateIsAfterFirstContactDate(final ControlDateField firstContactDateField, final ControlDateField lastContactDateField) {
109+
Date firstContactDate = firstContactDateField.getValue();
110+
Date lastContactDate = lastContactDateField.getValue();
111+
112+
DateTimeComparator dateOnlyComparator = DateTimeComparator.getDateOnlyInstance();
113+
114+
if (firstContactDate != null && lastContactDate != null && dateOnlyComparator.compare(firstContactDate, lastContactDate) >= 0) {
115+
lastContactDateField.enableErrorState(
116+
I18nProperties.getValidationError(
117+
Validations.afterDate,
118+
lastContactDateField.getCaption(),
119+
I18nProperties.getPrefixCaption(ContactDto.I18N_PREFIX, ContactDto.FIRST_CONTACT_DATE)));
120+
return true;
121+
}
122+
return false;
123+
}
124+
125+
private static boolean validateLastContactDateIsBeforeReportDate(final ControlDateField lastContactDateField, Date contactReferenceDate) {
126+
Date lastContactDate = lastContactDateField.getValue();
127+
128+
DateTimeComparator dateOnlyComparator = DateTimeComparator.getDateOnlyInstance();
129+
130+
if (contactReferenceDate != null && dateOnlyComparator.compare(lastContactDate, contactReferenceDate) > 0) {
131+
lastContactDateField.enableErrorState(
132+
I18nProperties.getValidationError(
133+
Validations.beforeDate,
134+
lastContactDateField.getCaption(),
135+
I18nProperties.getPrefixCaption(ContactDto.I18N_PREFIX, ContactDto.REPORT_DATE_TIME)));
136+
return true;
137+
}
138+
139+
return false;
140+
}
75141
}

0 commit comments

Comments
 (0)