diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/Disease.java b/sormas-api/src/main/java/de/symeda/sormas/api/Disease.java index 80b43944c95..a6e57247fb0 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/Disease.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/Disease.java @@ -75,7 +75,7 @@ public enum Disease INFLUENZA_B(false, true, true, false, false, 0, true, false, false), H_METAPNEUMOVIRUS(true, false, true, false, false, 0, true, false, false), RESPIRATORY_SYNCYTIAL_VIRUS(true, false, true, false, false, 0, true, false, false), - PARAINFLUENZA_1_4(true, false, true, false, false, 0, true, false, false), + PARAINFLUENZA_1_4(false, false, true, false, false, 0, true, false, false), ADENOVIRUS(true, false, true, false, false, 0, true, false, false), RHINOVIRUS(true, false, true, false, false, 0, true, false, false), ENTEROVIRUS(true, false, true, false, false, 0, true, false, false), diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java index c05286cd27c..b71b5255a2b 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java @@ -2275,6 +2275,8 @@ public interface Captions { String PathogenTest_prescriberPostalCode = "PathogenTest.prescriberPostalCode"; String PathogenTest_reportDate = "PathogenTest.reportDate"; String PathogenTest_rifampicinResistant = "PathogenTest.rifampicinResistant"; + String PathogenTest_rsv_testedDiseaseVariant = "PathogenTest.rsv.testedDiseaseVariant"; + String PathogenTest_rsv_testedDiseaseVariantDetails = "PathogenTest.rsv.testedDiseaseVariantDetails"; String PathogenTest_seroGroupSpecification = "PathogenTest.seroGroupSpecification"; String PathogenTest_seroGroupSpecificationText = "PathogenTest.seroGroupSpecificationText"; String PathogenTest_serotype = "PathogenTest.serotype"; diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/sample/PathogenTestType.java b/sormas-api/src/main/java/de/symeda/sormas/api/sample/PathogenTestType.java index 412360367dc..d615fec03c6 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/sample/PathogenTestType.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/sample/PathogenTestType.java @@ -161,7 +161,9 @@ public enum PathogenTestType { @Diseases(value = { Disease.RESPIRATORY_SYNCYTIAL_VIRUS, - Disease.MEASLES }) + Disease.MEASLES, + Disease.INVASIVE_PNEUMOCOCCAL_INFECTION, + Disease.INVASIVE_MENINGOCOCCAL_INFECTION }) SEQUENCING, @Diseases(value = { diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java index abbd2d0fbf1..cc8d1449c2a 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java @@ -2525,7 +2525,8 @@ public static SymptomsDto build() { UNDEFINED, OTHER }) @HideForCountriesExcept(countries = { - CountryHelper.COUNTRY_CODE_SWITZERLAND }) + CountryHelper.COUNTRY_CODE_SWITZERLAND, + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) @SymptomGrouping(SymptomGroup.GENERAL) private SymptomState fatigue; @Diseases({ diff --git a/sormas-api/src/main/resources/captions.properties b/sormas-api/src/main/resources/captions.properties index 6cfe07455e7..b961118aba0 100644 --- a/sormas-api/src/main/resources/captions.properties +++ b/sormas-api/src/main/resources/captions.properties @@ -1149,7 +1149,7 @@ EpiData.caseImportedStatus=Case imported status EpiData.clusterRelated=Cluster Related EpiData.clusterType=Cluster type EpiData.clusterTypeText=Specify cluster type text -EpiData.modeOfTransmission=Mode of transmission +EpiData.modeOfTransmission=Suspected main mode of transmission EpiData.modeOfTransmissionType= Specify mode of transmission EpiData.infectionSource= Suspected vehicle or source of infection EpiData.infectionSourceText= Specify source of infection @@ -1872,6 +1872,7 @@ PathogenTest.pcrTestSpecification=PCR/RT-PCR test specification PathogenTest.testTypeText=Specify test details PathogenTest.testedDisease=Tested disease PathogenTest.testedDiseaseVariant=Tested disease variant +PathogenTest.rsv.testedDiseaseVariant=Tested disease subtype PathogenTest.testedDiseaseDetails=Tested disease name PathogenTest.testedPathogen=Tested pathogen PathogenTest.testedPathogenDetails=Tested pathogen details @@ -1884,6 +1885,7 @@ PathogenTest.externalId=External ID PathogenTest.reportDate=Report date PathogenTest.viaLims=Via LIMS PathogenTest.testedDiseaseVariantDetails=Disease variant details +PathogenTest.rsv.testedDiseaseVariantDetails=Disease subtype details PathogenTest.externalOrderId=External order ID PathogenTest.preliminary=Preliminary PathogenTest.ctValueE=Ct target E diff --git a/sormas-api/src/main/resources/enum.properties b/sormas-api/src/main/resources/enum.properties index c2f98151146..9574f121ae8 100644 --- a/sormas-api/src/main/resources/enum.properties +++ b/sormas-api/src/main/resources/enum.properties @@ -775,9 +775,9 @@ ExposureType.BURIAL=Burial ExposureType.ANIMAL_CONTACT=Animal Contact ExposureType.OTHER=Other ExposureType.UNKNOWN=Unknown -ExposureType.RECREATIONAL_WATER=Exposure to recreational water -ExposureType.FOOD=Exposure to food -ExposureType.SEXUAL_CONTACT=Sexual exposure +ExposureType.RECREATIONAL_WATER=Recreational water +ExposureType.FOOD=Food +ExposureType.SEXUAL_CONTACT=Sexual contact ExposureType.SYMPTOMATIC_CONTACT=Contact with symptomatic individuals ExposureType.FLOOD_EXPOSURE=Flooded area diff --git a/sormas-backend/src/main/resources/sql/sormas_schema.sql b/sormas-backend/src/main/resources/sql/sormas_schema.sql index 4bfc47af429..62e246ed67a 100644 --- a/sormas-backend/src/main/resources/sql/sormas_schema.sql +++ b/sormas-backend/src/main/resources/sql/sormas_schema.sql @@ -14820,4 +14820,11 @@ ALTER TABLE symptoms_history ALTER COLUMN timeoffworkdays TYPE float4 USING time ALTER TABLE healthconditions_history DROP COLUMN IF EXISTS immunodepression; INSERT INTO schema_version (version_number, comment) VALUES (595, 'RSV issue fixes and minor observations of Giardiasis and Cryptosporidiosis #13540 #13613'); + +-- 2025-10-29 - Included new Disease variant/subtype for RSV #13543 +INSERT INTO customizableenumvalue(id, uuid, changedate, creationdate, datatype, value, caption, diseases) +VALUES (nextval('entity_seq'), generate_base32_uuid(), now(), now(), 'DISEASE_VARIANT', 'INDETERMINATE', 'Indeterminate', + 'RESPIRATORY_SYNCYTIAL_VIRUS'); +INSERT INTO schema_version (version_number, comment) VALUES (596, 'Included new Disease variant/subtype for RSV #13543'); + -- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. *** diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java index 49ed602ef39..3e482947ed7 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseDataForm.java @@ -476,6 +476,10 @@ protected void addFields() { TextField diseaseVariantDetailsField = addField(CaseDataDto.DISEASE_VARIANT_DETAILS, TextField.class); diseaseVariantDetailsField.setVisible(false); diseaseVariantField.setNullSelectionAllowed(true); + if (disease == Disease.RESPIRATORY_SYNCYTIAL_VIRUS) { + diseaseVariantField.setCaption(I18nProperties.getCaption(Captions.PathogenTest_rsv_testedDiseaseVariant)); + diseaseVariantDetailsField.setCaption(I18nProperties.getCaption(Captions.PathogenTest_rsv_testedDiseaseVariantDetails)); + } addField(CaseDataDto.DISEASE_DETAILS, TextField.class); addField(CaseDataDto.PLAGUE_TYPE, NullableOptionGroup.class); addField(CaseDataDto.DENGUE_FEVER_TYPE, NullableOptionGroup.class); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventDataForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventDataForm.java index 20d98fc1e25..fa474868dd7 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventDataForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventDataForm.java @@ -259,8 +259,8 @@ protected void addFields() { EventDto.EVENT_INVESTIGATION_STATUS, Arrays.asList(EventInvestigationStatus.ONGOING, EventInvestigationStatus.DONE, EventInvestigationStatus.DISCARDED), true); - DateComparisonValidator.addStartEndValidators(investigationStartDate, investigationEndDate, false); - DateComparisonValidator.addStartEndValidators(startDate, investigationStartDate, false); + DateComparisonValidator.addStartEndValidators(investigationStartDate, investigationEndDate, true); + DateComparisonValidator.addStartEndValidators(startDate, investigationStartDate, true); TextField title = addField(EventDto.EVENT_TITLE, TextField.class); title.addStyleName(CssStyles.SOFT_REQUIRED); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java index ca1707cc8ac..2d6981a5019 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java @@ -247,8 +247,15 @@ protected void addFields() { initializeAccessAndAllowedAccesses(); if (List.of(Disease.GIARDIASIS, Disease.CRYPTOSPORIDIOSIS).contains(caze.getDisease())) { - FieldHelper - .setRequiredWhenNotNull(getFieldGroup(), HospitalizationDto.ADMITTED_TO_HEALTH_FACILITY, HospitalizationDto.HOSPITALIZATION_REASON); + // Make hospitalization reason required when admitted to health facility or currently hospitalized + admittedToHealthFacilityField.addValueChangeListener(e -> { + YesNoUnknown value = (YesNoUnknown) admittedToHealthFacilityField.getNullableValue(); + hospitalizationReason.setRequired(value == YesNoUnknown.YES); + }); + currentlyHospitalizedField.addValueChangeListener(e -> { + YesNoUnknown value = (YesNoUnknown) currentlyHospitalizedField.getNullableValue(); + hospitalizationReason.setRequired(value == YesNoUnknown.YES); + }); durationOfHospitalization.setVisible(true); } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/AbstractSampleForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/AbstractSampleForm.java index 7469935628e..d3a1326ed15 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/AbstractSampleForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/AbstractSampleForm.java @@ -285,7 +285,7 @@ protected void addValidators() { new DateComparisonValidator( shipmentDate, sampleDateField, - false, + true, false, I18nProperties.getValidationError(Validations.afterDate, shipmentDate.getCaption(), sampleDateField.getCaption()))); shipmentDate.addValidator( diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/PathogenTestForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/PathogenTestForm.java index 3a16ded4552..f76f74234a5 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/PathogenTestForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/PathogenTestForm.java @@ -23,6 +23,8 @@ import static de.symeda.sormas.ui.utils.LayoutUtil.fluidRowLocs; import static de.symeda.sormas.ui.utils.LayoutUtil.loc; +import java.time.LocalTime; +import java.time.ZoneId; import java.util.Arrays; import java.util.Collection; import java.util.Date; @@ -302,7 +304,7 @@ public void setValue(PathogenTestDto newFieldValue) throws ReadOnlyException, Co testTypeField.setValue(newFieldValue.getTestType()); pcrTestSpecification.setValue(newFieldValue.getPcrTestSpecification()); testTypeTextField.setValue(newFieldValue.getTestTypeText()); - if(!testResultField.isReadOnly()) { + if (!testResultField.isReadOnly()) { testResultField.setValue(newFieldValue.getTestResult()); } typingIdField.setValue(newFieldValue.getTypingId()); @@ -331,17 +333,37 @@ protected void addFields() { testTypeTextField = addField(PathogenTestDto.TEST_TYPE_TEXT, TextField.class); FieldHelper.addSoftRequiredStyle(testTypeTextField); DateTimeField testDateField = addField(PathogenTestDto.TEST_DATE_TIME, DateTimeField.class); + testDateField.removeAllValidators(); testDateField.addValidator( new DateComparisonValidator( testDateField, this::getSampleDate, false, false, + true, I18nProperties.getValidationError( Validations.afterDateWithDate, testDateField.getCaption(), I18nProperties.getPrefixCaption(SampleDto.I18N_PREFIX, SampleDto.SAMPLE_DATE_TIME), DateFormatHelper.formatDate(getSampleDate())))); + testDateField.addValueChangeListener(e -> { + boolean hasTime = !getSampleDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().equals(LocalTime.MIDNIGHT); + if (hasTime) { + testDateField.removeAllValidators(); + testDateField.addValidator( + new DateComparisonValidator( + testDateField, + this::getSampleDate, + false, + false, + false, + I18nProperties.getValidationError( + Validations.afterDateWithDate, + testDateField.getCaption(), + I18nProperties.getPrefixCaption(SampleDto.I18N_PREFIX, SampleDto.SAMPLE_DATE_TIME), + DateFormatHelper.formatLocalDateTime(getSampleDate())))); + } + }); ComboBox lab = addInfrastructureField(PathogenTestDto.LAB); lab.addItems(FacadeProvider.getFacilityFacade().getAllActiveLaboratories(true)); TextField labDetails = addField(PathogenTestDto.LAB_DETAILS, TextField.class); @@ -354,9 +376,13 @@ protected void addFields() { addField(PathogenTestDto.TESTED_DISEASE_DETAILS, TextField.class); ComboBox diseaseVariantField = addCustomizableEnumField(PathogenTestDto.TESTED_DISEASE_VARIANT); diseaseVariantField.setNullSelectionAllowed(true); + diseaseVariantField.setVisible(false); TextField diseaseVariantDetailsField = addField(PathogenTestDto.TESTED_DISEASE_VARIANT_DETAILS, TextField.class); diseaseVariantDetailsField.setVisible(false); - + if (disease == Disease.RESPIRATORY_SYNCYTIAL_VIRUS) { + diseaseVariantField.setCaption(I18nProperties.getCaption(Captions.PathogenTest_rsv_testedDiseaseVariant)); + diseaseVariantDetailsField.setCaption(I18nProperties.getCaption(Captions.PathogenTest_rsv_testedDiseaseVariantDetails)); + } ComboBox genoTypingCB = addField(PathogenTestDto.GENOTYPE_RESULT, ComboBox.class); genoTypingCB.setVisible(true); TextField genoTypingResultTextTF = addField(PathogenTestDto.GENOTYPE_RESULT_TEXT, TextField.class); @@ -492,7 +518,11 @@ protected void addFields() { { put( PathogenTestDto.TESTED_DISEASE, - Arrays.asList(Disease.TUBERCULOSIS, Disease.LATENT_TUBERCULOSIS, Disease.INVASIVE_MENINGOCOCCAL_INFECTION, Disease.INVASIVE_PNEUMOCOCCAL_INFECTION)); + Arrays.asList( + Disease.TUBERCULOSIS, + Disease.LATENT_TUBERCULOSIS, + Disease.INVASIVE_MENINGOCOCCAL_INFECTION, + Disease.INVASIVE_PNEUMOCOCCAL_INFECTION)); put(PathogenTestDto.TEST_TYPE, Arrays.asList(PathogenTestType.ANTIBIOTIC_SUSCEPTIBILITY)); } }; @@ -503,7 +533,11 @@ protected void addFields() { { put( PathogenTestDto.TESTED_DISEASE, - Arrays.asList(Disease.TUBERCULOSIS, Disease.LATENT_TUBERCULOSIS, Disease.INVASIVE_MENINGOCOCCAL_INFECTION, Disease.INVASIVE_PNEUMOCOCCAL_INFECTION)); + Arrays.asList( + Disease.TUBERCULOSIS, + Disease.LATENT_TUBERCULOSIS, + Disease.INVASIVE_MENINGOCOCCAL_INFECTION, + Disease.INVASIVE_PNEUMOCOCCAL_INFECTION)); put( PathogenTestDto.TEST_TYPE, Arrays.asList( @@ -1037,6 +1071,17 @@ protected void addFields() { FieldHelper .setVisibleWhen(getFieldGroup(), PathogenTestDto.GENOTYPE_RESULT_TEXT, PathogenTestDto.GENOTYPE_RESULT, GenoTypeResult.OTHER, true); + //RSV subtype specification + Map> rsvSubTypeDependencies = new HashMap<>() { + + { + put(PathogenTestDto.TESTED_DISEASE, Arrays.asList(Disease.RESPIRATORY_SYNCYTIAL_VIRUS)); + put(PathogenTestDto.TEST_TYPE, Arrays.asList(PathogenTestType.SEQUENCING, PathogenTestType.WHOLE_GENOME_SEQUENCING)); + put(PathogenTestDto.TEST_RESULT, Arrays.asList(PathogenTestResultType.POSITIVE)); + } + }; + FieldHelper.setVisibleWhen(getFieldGroup(), PathogenTestDto.TESTED_DISEASE_VARIANT, rsvSubTypeDependencies, true); + Consumer updateDiseaseVariantField = disease -> { List diseaseVariants = FacadeProvider.getCustomizableEnumFacade().getEnumValues(CustomizableEnumType.DISEASE_VARIANT, disease); @@ -1103,7 +1148,7 @@ protected void addFields() { ImmutableList.of(PathogenTestType.GENOTYPING)); BiConsumer resultField = (disease, testType) -> { - if(testResultField.isReadOnly()) { + if (testResultField.isReadOnly()) { return; } if (resultFieldDecisionMap.containsKey(disease) && resultFieldDecisionMap.get(disease).contains(testType)) { diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/pathogentestlink/PathogenTestListEntry.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/pathogentestlink/PathogenTestListEntry.java index c74afa03b62..ec370e9bae8 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/pathogentestlink/PathogenTestListEntry.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/samples/pathogentestlink/PathogenTestListEntry.java @@ -105,6 +105,7 @@ public PathogenTestListEntry(PathogenTestDto pathogenTest, boolean showTestResul if (pathogenTest.getTestedDiseaseVariant() != null) { Label labelBottomLeft = new Label(pathogenTest.getTestedDiseaseVariant().toString()); + CssStyles.style(labelBottomLeft, CssStyles.LABEL_BOLD, CssStyles.LABEL_UPPERCASE, CssStyles.LABEL_CRITICAL); bottomLabelLayout.addComponent(labelBottomLeft); } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/DateComparisonValidator.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/DateComparisonValidator.java index 8c64cea5c06..406fd009f04 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/DateComparisonValidator.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/DateComparisonValidator.java @@ -59,6 +59,32 @@ public DateComparisonValidator( this.changeInvalidCommitted = changeInvalidCommitted; } + /** + * To compare the time along with dates, set dateOnly to false. + * + * @param dateField + * @param referenceDateSupplier + * @param earlierOrSame + * @param changeInvalidCommitted + * @param dateOnly + * @param errorMessage + */ + public DateComparisonValidator( + Field dateField, + Supplier referenceDateSupplier, + boolean earlierOrSame, + boolean changeInvalidCommitted, + boolean dateOnly, + String errorMessage) { + + super(errorMessage); + this.dateField = dateField; + this.referenceDateSupplier = referenceDateSupplier; + this.earlierOrSame = earlierOrSame; + this.dateOnly = dateOnly; + this.changeInvalidCommitted = changeInvalidCommitted; + } + public DateComparisonValidator( Field dateField, Supplier referenceDateSupplier, @@ -94,7 +120,6 @@ public DateComparisonValidator( this(dateField, () -> referenceDate, earlierOrSame, changeInvalidCommitted, errorMessage); } - @Override protected boolean isValidValue(Date date) {