Skip to content

Commit fd8e870

Browse files
authored
Merge pull request #13943 from SORMAS-Foundation/fix/13938-pathogen-test-form-issues
Fix/13938 pathogen test form issues
2 parents 7ffc9c3 + 9bb1d49 commit fd8e870

19 files changed

Lines changed: 155 additions & 36 deletions

File tree

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2389,11 +2389,14 @@ public interface Captions {
23892389
String PathogenTest_rifampicinResistant = "PathogenTest.rifampicinResistant";
23902390
String PathogenTest_rsv_testedDiseaseVariant = "PathogenTest.rsv.testedDiseaseVariant";
23912391
String PathogenTest_rsv_testedDiseaseVariantDetails = "PathogenTest.rsv.testedDiseaseVariantDetails";
2392+
String PathogenTest_seroGrouping_INVASIVE_PNEUMOCOCCAL_INFECTION = "PathogenTest.seroGrouping.INVASIVE_PNEUMOCOCCAL_INFECTION";
23922393
String PathogenTest_seroGroupSpecification = "PathogenTest.seroGroupSpecification";
23932394
String PathogenTest_seroGroupSpecificationText = "PathogenTest.seroGroupSpecificationText";
23942395
String PathogenTest_serotype = "PathogenTest.serotype";
23952396
String PathogenTest_serotypeText = "PathogenTest.serotypeText";
2397+
String PathogenTest_serotypeText_INVASIVE_PNEUMOCOCCAL_INFECTION = "PathogenTest.serotypeText.INVASIVE_PNEUMOCOCCAL_INFECTION";
23962398
String PathogenTest_seroTypingMethod = "PathogenTest.seroTypingMethod";
2399+
String PathogenTest_seroTypingMethod_INVASIVE_PNEUMOCOCCAL_INFECTION = "PathogenTest.seroTypingMethod.INVASIVE_PNEUMOCOCCAL_INFECTION";
23972400
String PathogenTest_seroTypingMethodText = "PathogenTest.seroTypingMethodText";
23982401
String PathogenTest_specie = "PathogenTest.specie";
23992402
String PathogenTest_specieText = "PathogenTest.specieText";

sormas-api/src/main/java/de/symeda/sormas/api/sample/PathogenTestType.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,6 @@ public enum PathogenTestType {
278278

279279
@Diseases(value = {
280280
Disease.INVASIVE_MENINGOCOCCAL_INFECTION,
281-
Disease.INVASIVE_PNEUMOCOCCAL_INFECTION,
282281
Disease.SALMONELLOSIS })
283282
@RevealsTestTypeText(diseases = Disease.SALMONELLOSIS)
284283
SEROTYPING,
@@ -350,8 +349,8 @@ public static String toString(PathogenTestType value, String details, Disease di
350349
return "";
351350
}
352351

353-
if (value == PathogenTestType.OTHER) {
354-
return DataHelper.toStringNullable(details);
352+
if (value == PathogenTestType.OTHER && !DataHelper.isNullOrEmpty(details)) {
353+
return details;
355354
}
356355

357356
if (revealsTestTypeText(value, disease) && !DataHelper.isNullOrEmpty(details)) {

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,13 +1957,16 @@ PathogenTest.prescriberCity=City
19571957
PathogenTest.prescriberCountry=Country
19581958
PathogenTest.rifampicinResistant=Rifampicin resistant
19591959
PathogenTest.isoniazidResistant=Isoniazid resistant
1960-
PathogenTest.specie=Specie
1960+
PathogenTest.specie=Species
19611961
PathogenTest.patternProfile=Pattern profile
19621962
PathogenTest.strainCallStatus=Strain Call Status
19631963
PathogenTest.testScale=Test Scale
19641964
PathogenTest.drugSusceptibility=Drug Susceptibility
19651965
PathogenTest.seroTypingMethod=Sero Typing Method
19661966
PathogenTest.seroTypingMethodText=Specify Sero Typing Method
1967+
PathogenTest.seroTypingMethod.INVASIVE_PNEUMOCOCCAL_INFECTION=Serotyping Method
1968+
PathogenTest.serotypeText.INVASIVE_PNEUMOCOCCAL_INFECTION=Serotype
1969+
PathogenTest.seroGrouping.INVASIVE_PNEUMOCOCCAL_INFECTION=Serotyping
19671970
PathogenTest.seroGroupSpecification=Serogroup Specification
19681971
PathogenTest.seroGroupSpecificationText=Specify Serogroup Specification Method
19691972
PathogenTest.genoType=Genotype result
@@ -1980,7 +1983,7 @@ PathogenTest.antibodyTitre=Antibody titre
19801983
PathogenTest.performedByReferenceLaboratory=Performed by reference laboratory
19811984
PathogenTest.retestRequested=Retest requested
19821985
PathogenTest.resultDetails=Result details
1983-
PathogenTest.specieText=Specify specie text
1986+
PathogenTest.specieText=Specify species text
19841987
# Person
19851988
personPersonsList=Person list
19861989
personCreateNew=Create a new person

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,7 @@ PathogenTestType.TST = Tuberculine Skin Test
11921192
PathogenTestType.BEIJINGGENOTYPING = Beijing Genotyping
11931193
PathogenTestType.SPOLIGOTYPING = Spoligotyping
11941194
PathogenTestType.MIRU_PATTERN_CODE = MIRU Pattern Code
1195-
PathogenTestType.ANTIBIOTIC_SUSCEPTIBILITY = Antibiotic susceptibility
1195+
PathogenTestType.ANTIBIOTIC_SUSCEPTIBILITY = Antibiotic Susceptibility Testing (AST)
11961196
PathogenTestType.MULTILOCUS_SEQUENCE_TYPING = Multilocus Sequence Typing (MLST)
11971197
PathogenTestType.CGMLST = Core Genome MLST (cgMLST)
11981198
PathogenTestType.SNP_TYPING = SNP Typing

sormas-backend/src/main/java/de/symeda/sormas/backend/customizableenum/CustomizableEnumFacadeEjb.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,10 +351,12 @@ public <T extends CustomizableEnum> List<T> getEnumValues(CustomizableEnumType t
351351

352352
Stream<String> diseaseValuesStream;
353353
if (innerDisease.isPresent()) {
354-
// combine specific and unspecific values
355-
diseaseValuesStream = Stream.concat(
356-
enumValuesByDisease.get(enumClass).get(innerDisease).stream(),
357-
enumValuesByDisease.get(enumClass).get(Optional.empty()).stream());
354+
// combine specific and unspecific values, removing duplicates that appear in both sets
355+
diseaseValuesStream = Stream
356+
.concat(
357+
enumValuesByDisease.get(enumClass).get(innerDisease).stream(),
358+
enumValuesByDisease.get(enumClass).get(Optional.empty()).stream())
359+
.distinct();
358360
} else {
359361
diseaseValuesStream = enumValuesByDisease.get(enumClass).get(Optional.empty()).stream();
360362
}

sormas-backend/src/test/java/de/symeda/sormas/backend/customizableenum/CustomizableEnumFacadeEjbTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,36 @@ public void getEnumValues() {
5858

5959
}
6060

61+
@Test
62+
public void getEnumValuesDoesNotReturnDuplicatesWhenValueIsBothDiseaseSpecificAndUnspecific() {
63+
64+
// Same value defined for a specific disease and for no disease (unspecific)
65+
String sharedValue = "TYPE_A_B";
66+
67+
CustomizableEnumValue specific = new CustomizableEnumValue();
68+
specific.setDataType(CustomizableEnumType.DISEASE_VARIANT);
69+
specific.setValue(sharedValue);
70+
Set<Disease> diseases = new HashSet<>();
71+
diseases.add(Disease.NEW_INFLUENZA);
72+
specific.setDiseases(diseases);
73+
specific.setCaption("Type A+B");
74+
specific.setActive(true);
75+
getCustomizableEnumValueService().ensurePersisted(specific);
76+
77+
CustomizableEnumValue unspecific = new CustomizableEnumValue();
78+
unspecific.setDataType(CustomizableEnumType.DISEASE_VARIANT);
79+
unspecific.setValue(sharedValue);
80+
unspecific.setCaption("Type A+B");
81+
unspecific.setActive(true);
82+
getCustomizableEnumValueService().ensurePersisted(unspecific);
83+
84+
getCustomizableEnumFacade().loadData();
85+
86+
List<CustomizableEnum> enumValues = getCustomizableEnumFacade().getEnumValues(CustomizableEnumType.DISEASE_VARIANT, Disease.NEW_INFLUENZA);
87+
long occurrences = enumValues.stream().filter(e -> sharedValue.equals(e.getValue())).count();
88+
assertEquals(1, occurrences);
89+
}
90+
6191
@Test
6292
public void tetGetUnknownDiseaseVariantWithNullDisease() throws CustomEnumNotFoundException {
6393
assertThrows(

sormas-ui/src/main/java/de/symeda/sormas/ui/samples/PathogenTestController.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@
5656
import de.symeda.sormas.api.i18n.Captions;
5757
import de.symeda.sormas.api.i18n.I18nProperties;
5858
import de.symeda.sormas.api.i18n.Strings;
59+
import de.symeda.sormas.api.i18n.Validations;
5960
import de.symeda.sormas.api.sample.PathogenTestDto;
6061
import de.symeda.sormas.api.sample.PathogenTestFacade;
6162
import de.symeda.sormas.api.sample.PathogenTestResultType;
6263
import de.symeda.sormas.api.sample.PathogenTestType;
6364
import de.symeda.sormas.api.sample.SampleDto;
65+
import de.symeda.sormas.api.sample.SamplePurpose;
6466
import de.symeda.sormas.api.sample.SampleReferenceDto;
6567
import de.symeda.sormas.api.user.UserRight;
6668
import de.symeda.sormas.api.utils.DataHelper;
@@ -140,6 +142,18 @@ public CommitDiscardWrapperComponent<PathogenTestForm> getPathogenTestCreateComp
140142
pathogenTest.setTestedDisease(associatedEventOrCaseOrContactDisease);
141143
createForm.setValue(pathogenTest);
142144

145+
// Lab is mandatory for non-internal samples (consistent with creating a test alongside a new sample)
146+
createForm.setLabRequired(!SamplePurpose.INTERNAL.equals(sampleDto.getSamplePurpose()));
147+
148+
// Trigger the "test date not before sample date" alarm when saving the test itself,
149+
// rather than only later when saving the sample
150+
createForm.addTestDateAfterSampleDateValidator(
151+
sampleDto::getSampleDateTime,
152+
I18nProperties.getValidationError(
153+
Validations.afterDate,
154+
I18nProperties.getPrefixCaption(PathogenTestDto.I18N_PREFIX, PathogenTestDto.TEST_DATE_TIME),
155+
I18nProperties.getPrefixCaption(SampleDto.I18N_PREFIX, SampleDto.SAMPLE_DATE_TIME)));
156+
143157
final CommitDiscardWrapperComponent<PathogenTestForm> editView =
144158
new CommitDiscardWrapperComponent<>(createForm, UiUtil.permitted(UserRight.PATHOGEN_TEST_CREATE), createForm.getFieldGroup());
145159

sormas-ui/src/main/java/de/symeda/sormas/ui/samples/PathogenTestForm.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import de.symeda.sormas.ui.samples.diseasesection.DiseaseSectionFactory;
6161
import de.symeda.sormas.ui.samples.diseasesection.PathogenTestFormConfig;
6262
import de.symeda.sormas.ui.samples.events.DiseaseChangedEvent;
63+
import de.symeda.sormas.ui.samples.events.SetTestResultEvent;
6364
import de.symeda.sormas.ui.utils.AbstractEditForm;
6465
import de.symeda.sormas.ui.utils.FieldAccessHelper;
6566
import de.symeda.sormas.ui.utils.FormComponent;
@@ -243,13 +244,11 @@ private void wireEvents() {
243244
swapDiseaseSection(newDisease);
244245
}));
245246

246-
// Disease variant → auto-set test result
247+
// Disease variant → auto-set test result to positive when a variant is selected.
247248
eventRegistrations.add(diseaseVariantComponent.getDiseaseVariantField().addValueChangeListener(e -> {
248249
DiseaseVariant variant = e.getValue();
249250
if (variant != null) {
250-
testResultComponent.getTestResultField().setValue(PathogenTestResultType.POSITIVE);
251-
} else {
252-
testResultComponent.getTestResultField().clear();
251+
eventBus.fire(new SetTestResultEvent(PathogenTestResultType.POSITIVE));
253252
}
254253
}));
255254
}

sormas-ui/src/main/java/de/symeda/sormas/ui/samples/components/CtCqValueComponent.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,15 @@ private void bindFloatField(TextField field, ValueProvider<PathogenTestDto, Floa
9696
}
9797

9898
public void updateCtVisibility(Disease disease, PathogenTestType testType) {
99-
if (disease == null || !java.util.Arrays.asList(Disease.TUBERCULOSIS).contains(disease)) {
100-
setCtFieldsVisible(PathogenTestType.PCR_RT_PCR == testType);
101-
} else {
102-
setCtFieldsVisible(false);
103-
}
99+
// CT-target fields (E, N, RdRp, S, ORF1, RdRp/S) are SARS-CoV-2 specific
100+
setCtFieldsVisible(disease == Disease.CORONAVIRUS && PathogenTestType.PCR_RT_PCR == testType);
104101
}
105102

106103
public void updateCqVisibility(Disease disease, PathogenTestType testType, PathogenTestResultType testResult) {
107-
if (disease == null || !java.util.Arrays.asList(Disease.TUBERCULOSIS).contains(disease)) {
108-
if ((testType == PathogenTestType.PCR_RT_PCR && testResult == PathogenTestResultType.POSITIVE)
104+
// CQ value is only meaningful for a positive result, regardless of test type/disease
105+
boolean positive = testResult == PathogenTestResultType.POSITIVE;
106+
if (positive && (disease == null || !java.util.Arrays.asList(Disease.TUBERCULOSIS).contains(disease))) {
107+
if (testType == PathogenTestType.PCR_RT_PCR
109108
|| testType == PathogenTestType.CQ_VALUE_DETECTION
110109
|| (disease == Disease.MALARIA && testType == PathogenTestType.Q_PCR)) {
111110
cqValueField.setVisible(true);

sormas-ui/src/main/java/de/symeda/sormas/ui/samples/components/DiseaseVariantComponent.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@
3838
import de.symeda.sormas.api.i18n.Captions;
3939
import de.symeda.sormas.api.i18n.I18nProperties;
4040
import de.symeda.sormas.api.sample.PathogenTestDto;
41+
import de.symeda.sormas.api.sample.PathogenTestResultType;
4142
import de.symeda.sormas.api.sample.PathogenTestType;
4243
import de.symeda.sormas.ui.samples.events.DiseaseChangedEvent;
44+
import de.symeda.sormas.ui.samples.events.TestResultChangedEvent;
4345
import de.symeda.sormas.ui.samples.events.TestTypeChangedEvent;
4446
import de.symeda.sormas.ui.utils.FormComponent;
4547
import de.symeda.sormas.ui.utils.FormEventBus;
@@ -63,6 +65,7 @@ public class DiseaseVariantComponent extends FormComponent<PathogenTestDto> {
6365
private final FormEventBus eventBus;
6466
private Disease currentDisease;
6567
private PathogenTestType currentTestType;
68+
private PathogenTestResultType currentResult;
6669
private List<DiseaseVariant> currentVariants;
6770

6871
private ComboBox<DiseaseVariant> diseaseVariantField;
@@ -126,6 +129,12 @@ private void wireEvents() {
126129
currentTestType = event.getTestType();
127130
updateVisibility();
128131
}));
132+
133+
// Test result change -> the subtype/variant is only relevant for a positive result
134+
track(eventBus.on(TestResultChangedEvent.class, event -> {
135+
currentResult = event.getTestResult();
136+
updateVisibility();
137+
}));
129138
}
130139

131140
private void refreshVariantItems() {
@@ -134,7 +143,10 @@ private void refreshVariantItems() {
134143
}
135144

136145
private void updateVisibility() {
146+
// Subtype/variant is only meaningful for a positive result; any other result
147+
// (pending, negative, indeterminate, not done, or none) hides and clears it.
137148
boolean visible = currentDisease != null
149+
&& currentResult == PathogenTestResultType.POSITIVE
138150
&& DiseaseHelper.SUBTYPE_ALLOWED_DISEASES.contains(currentDisease)
139151
&& VARIANT_ALLOWED_TEST_TYPES.contains(currentTestType)
140152
&& CollectionUtils.isNotEmpty(currentVariants);

0 commit comments

Comments
 (0)